cleaned devlog
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
|
||||
# 2024-12-18 16:35:00 - Refactor Game Battlefield Sidebar
|
||||
|
||||
## Description
|
||||
Refactored the `GameView` sidebar to be graphically and functionally consistent with `DeckBuilderView` and `DraftView`.
|
||||
|
||||
## Key Changes
|
||||
- **Component**: `GameView.tsx`
|
||||
- **Functionality**:
|
||||
- Implemented collapsible sidebar state with persistence (`game_sidebarCollapsed`).
|
||||
- Implemented resizable sidebar width with persistence (`game_sidebarWidth`).
|
||||
- Added transition animations for collapsing/expanding.
|
||||
- **Visuals**:
|
||||
- Adopted the "Card Preview" style with a 3D flip effect.
|
||||
- Used `back.jpg` (path `/images/back.jpg`) for the empty/back state.
|
||||
- Moved the resize handle *inside* the sidebar container with consistent styling (floating pill).
|
||||
- Preserved Oracle Text display below the card image (as it is critical for gameplay), styled within the new container.
|
||||
|
||||
## Consistent Elements
|
||||
- **Icons**: Used `Eye` and `ChevronLeft` from Lucide.
|
||||
- **Styling**: `slate-900` backgrounds, glassmorphism borders (`slate-800/50`), shadow effects.
|
||||
- **Behavior**: Sidebar width allows dragging between 200px and 600px.
|
||||
|
||||
## Status
|
||||
- [ ] Verify `back.jpg` exists in the deployed `public/images` folder (currently assumed based on other files).
|
||||
- [x] Code refactoring complete.
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
# 2024-12-18 16:45:00 - Implement Game Persistence on Reload
|
||||
|
||||
## Description
|
||||
Updated `LobbyManager.tsx` to ensure that when a user reloads the page and automatically rejoins a room, the active game state (`initialGameState`) is correctly retrieved from the server and passed to the game view components.
|
||||
|
||||
## Key Changes
|
||||
- **Component**: `LobbyManager.tsx`
|
||||
- **Functionality**:
|
||||
- Added `initialGameState` state.
|
||||
- Updated `join_room` and `rejoin_room` response handling to capture `gameState` if present.
|
||||
- Passed `initialGameState` to the `GameRoom` component.
|
||||
|
||||
## Impact
|
||||
- **User Experience**: If a user is in the middle of a game (battlefield phase) and refreshes the browser, they will now immediately see the battlefield state instead of a loading or broken screen, ensuring continuity.
|
||||
- **Data Flow**: `GameRoom` uses this prop to initialize its local `gameState` before the first socket update event arrives.
|
||||
|
||||
## Status
|
||||
- [x] Implementation logic complete.
|
||||
- [ ] User testing required (refresh page during active game).
|
||||
@@ -1,26 +0,0 @@
|
||||
|
||||
# 2024-12-18 16:55:00 - Implement Server Persistence and Room Cleanup
|
||||
|
||||
## Description
|
||||
Implemented server-side state persistence to ensure game rooms, drafts, and game states survive server restarts and network issues. Added logic to keep rooms alive for at least 8 hours after the last activity, satisfying the requirements for robustness and re-joinability.
|
||||
|
||||
## Key Changes
|
||||
1. **Persistence Manager**:
|
||||
- Created `PersistenceManager.ts` to save and load `rooms`, `drafts`, and `games` to/from JSON files in `./server-data`.
|
||||
- Integrated into `server/index.ts` with auto-save interval (every 5s) and save-on-shutdown.
|
||||
|
||||
2. **Room Manager**:
|
||||
- Added `lastActive` timestamp to `Room` interface.
|
||||
- Updated `lastActive` on all significant interactions (join, leave, message, etc.).
|
||||
- Implemented `disconnect` logic: if players disconnect, the room is NOT deleted immediately.
|
||||
- Implemented `leaveRoom` logic: Explicit leaving (waiting phase) still removes players but preserves the room until cleanup if empty.
|
||||
- Added `cleanupRooms()` method running every 5 minutes to delete rooms inactive for > 8 hours.
|
||||
|
||||
## Impact
|
||||
- **Reliability**: Server crashes or restarts will no longer wipe out active games or drafts.
|
||||
- **User Experience**: Users can reconnect to their room even hours later (up to 8 hours), or after a server reboot, using their room code.
|
||||
- **Maintenance**: `server-data` directory now contains the active state, useful for debugging.
|
||||
|
||||
## Status
|
||||
- [x] Code implementation complete.
|
||||
- [ ] Verify `server-data` folder is created and populated on run.
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
# 2024-12-18 17:05:00 - Distributed Storage with Redis
|
||||
|
||||
## Description
|
||||
Implemented distributed storage using Redis (`ioredis`) to support horizontal scaling and persistence outside of local file systems, while retaining local storage for development.
|
||||
|
||||
## Key Changes
|
||||
1. **Dependencies**: Added `ioredis` and `@types/ioredis`.
|
||||
2. **Redis Manager**: created `RedisClientManager.ts` to manage connections:
|
||||
- `db0`: Session Persistence (Rooms, Drafts, Games).
|
||||
- `db1`: File Storage (Card Images, Metadata).
|
||||
- Enabled via environment variable `USE_REDIS=true`.
|
||||
3. **Persistence Manager**: Updated `PersistenceManager.ts` to read/write state to Redis DB 0 if enabled.
|
||||
4. **File Storage Manager**: Created `FileStorageManager.ts` to abstract file operations (`saveFile`, `readFile`, `exists`).
|
||||
- Uses Redis DB 1 if enabled.
|
||||
- Uses Local FS otherwise.
|
||||
5. **Card Service**: Refactored `CardService.ts` to use `FileStorageManager` instead of `fs` direct calls.
|
||||
6. **Server File Serving**: Updated `server/index.ts` to conditionally serve files:
|
||||
- If Redis enabled: Dynamic route intercepting `/cards/*` to fetch from Redis DB 1.
|
||||
- If Local: Standard `express.static` middleware.
|
||||
|
||||
## Configuration
|
||||
- `USE_REDIS`: Set to `true` to enable Redis.
|
||||
- `REDIS_HOST`: Default `localhost`.
|
||||
- `REDIS_PORT`: Default `6379`.
|
||||
|
||||
## Status
|
||||
- [x] Code implementation complete.
|
||||
- [ ] Redis functionality verification (requires Redis instance).
|
||||
@@ -1,23 +0,0 @@
|
||||
|
||||
# 2024-12-18 17:35:00 - Strict Rules Engine Implementation
|
||||
|
||||
## Description
|
||||
Implemented a comprehensive Magic: The Gathering rules engine (Core Logic) to replace the sandbox mode with strict rules enforcement. This includes a State Machine for Turn Structure, Priority System, Stack, and State-Based Actions.
|
||||
|
||||
## Key Changes
|
||||
1. **Core Types**: Created `src/server/game/types.ts` defining `Phase`, `Step`, `StrictGameState`, `StackObject`, etc.
|
||||
2. **Rules Engine**: Created `src/server/game/RulesEngine.ts` implementing:
|
||||
- **Turn Structure**: Untap, Upkeep, Draw, Main, Combat (Steps), End.
|
||||
- **Priority System**: Passing priority, stack resolution, phase transition.
|
||||
- **Actions**: `playLand`, `castSpell` with validation.
|
||||
- **State-Based Actions**: Lethal damage, Zero toughness, Player loss checks.
|
||||
3. **Game Manager Refactor**:
|
||||
- Updated `GameManager.ts` to use `StrictGameState`.
|
||||
- Implemented `handleStrictAction` to delegate to `RulesEngine`.
|
||||
- Retained `handleAction` for legacy/sandbox/admin support.
|
||||
4. **Socket Handling**:
|
||||
- Added `game_strict_action` event listener in `server/index.ts`.
|
||||
|
||||
## Next Steps
|
||||
- Client-side integration: The frontend needs to be updated to visualize the Phases, Stack, and Priority (Pass Button).
|
||||
- Move from "Sandbox" UI to "Rules Enforcement" UI.
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
# 2024-12-18 18:00:00 - High-Velocity UX Implementation (Part 1)
|
||||
|
||||
## Description
|
||||
Started implementing the Frontend components for the High-Velocity UX Specification. Focused on the "Smart Priority Interface" components first to enable strict rules interaction.
|
||||
|
||||
## Key Changes
|
||||
1. **Frontend Plan**: Created `docs/development/plans/high-velocity-ux.md` detailing the gesture engine and UI components.
|
||||
2. **Strict Types**: Updated `src/client/types/game.ts` to include `Phase`, `Step`, `StackObject`, and `StrictGameState` extensions.
|
||||
3. **Smart Button**: Created `SmartButton.tsx` which derives state (Pass/Resolve/Wait) from the `StrictGameState`.
|
||||
- Green: Pass/Advance Phase.
|
||||
- Orange: Resolve Stack.
|
||||
- Grey: Wait (Not Priority).
|
||||
4. **Phase Strip**: Created `PhaseStrip.tsx` to visualize the linear turn structure and highlight the current step.
|
||||
5. **GameView Integration**: Updated `GameView.tsx` to house these new controls in the bottom area. Wire up `SmartButton` to emit `game_strict_action`.
|
||||
|
||||
## Next Steps
|
||||
- Implement `GestureManager` context for Swipe-to-Tap and Swipe-to-Combat.
|
||||
- Implement `StackVisualizer` to show objects on the stack.
|
||||
- Connect `ContextMenu` to strict actions (Activate Ability).
|
||||
@@ -1,34 +0,0 @@
|
||||
# High Velocity UX & Strict Engine (Part 2)
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Objectives
|
||||
- Implement "Manual Mana Engine" allowing players to add mana to their pool via interaction.
|
||||
- Implement "Strict Combat Engine" supporting `DECLARE_ATTACKERS` and `DECLARE_BLOCKERS` phases and validation.
|
||||
- Implement "High Velocity UX" with Swipe-to-Tap and Swipe-to-Attack gestures.
|
||||
- Enhance `GameView` with Mana Pool display and visual feedback for combat declarations.
|
||||
- Contextualize `SmartButton` to handle complex actions like declaring specific attackers.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Backend (Rules Engine)
|
||||
- **Mana System**: Added `addMana` method to `RulesEngine` and `manaPool` to `PlayerState`. Implemented `emptyManaPools` logic on step transition.
|
||||
- **Combat Logic**: Implemented `declareAttackers` (checking summoning sickness, tapping, setting attacking target) and `declareBlockers` logic.
|
||||
- **Action Handling**: Updated `GameManager` to handle `ADD_MANA` and auto-generate mana when tapping Basic Lands via `TAP_CARD` action (legacy compatibility wrapper).
|
||||
|
||||
### Frontend (GameView)
|
||||
- **Mana Pool UI**: Added a compact Mana Pool display in the player life area, showing WUBRGC counts.
|
||||
- **Gesture Manager Upgrade**: Enhanced `GestureManager` to detect swipe direction:
|
||||
- **Slash (Horizontal)**: Tap Card.
|
||||
- **Thrust (Vertical Up)**: Attack (if in combat step).
|
||||
- **Thrust (Vertical Down)**: Cancel Attack.
|
||||
- **Combat Visuals**: Implemented `proposedAttackers` local state. Cards proposed to attack are visually lifted (`translateY(-40px)`) and glow red (`box-shadow`, `ring`).
|
||||
- **Smart Button**: Updated to accept `contextData`. In `declare_attackers` step, it displays "Attack with N" and sends the list of proposed attackers.
|
||||
|
||||
### Type Synchronization
|
||||
- Synced `CardInstance` (Client) with `CardObject` (Server) to include `attacking` and `blocking` fields.
|
||||
|
||||
## Next Steps
|
||||
- Verify Multiplayer Sync (Socket events are already in place).
|
||||
- Implement "Blocking" UI (similar to Attacking but for defenders).
|
||||
- Implement "Order Blockers" / "Damage Assignment" if strict compliance is enforced (currently simplified to auto-damage).
|
||||
@@ -1,14 +0,0 @@
|
||||
# 2024-12-18 - Fix Initial Draw Logic
|
||||
|
||||
## Problem
|
||||
The user reported that upon entering the game and being prompted to Mulligan, their hand was empty. This prevented them from making an informed decision.
|
||||
|
||||
## Root Cause
|
||||
The `GameManager` initialized the game state with `step: 'mulligan'`, but the `RulesEngine` was never invoked to perform the initial turn-based actions (specifically the initial draw). The cards were added to the `library` zone, but no logic moved them to the `hand` zone before the game start update was sent to the client.
|
||||
|
||||
## Solution
|
||||
1. **Updated `RulesEngine.ts`**: Added a public `startGame()` method that logs the start and calls `performTurnBasedActions()`. `performTurnBasedActions()` already contained the logic to draw 7 cards if the step is 'mulligan' and the hand is empty.
|
||||
2. **Updated `server/index.ts`**: Modified all game initialization flows (`player_ready`, `start_solo_test`, `start_game`, and the global timer loop) to instantiate a `RulesEngine` and call `startGame()` immediately after populating the deck cards.
|
||||
|
||||
## outcome
|
||||
When a game starts, the server now proactively triggers the initial draw logic. Clients receive the initial game state with 7 cards in hand, allowing the Mulligan UI to display the cards correctly.
|
||||
@@ -1,15 +0,0 @@
|
||||
# 2024-12-18 - Implement Manual Card Draw
|
||||
|
||||
## Problem
|
||||
The user reported that clicking on the library or using the "Draw Card" context menu option had no effect. The frontend was correctly emitting the `DRAW_CARD` action (via `game_action` channel/event), but the `GameManager` (which handles manual/legacy actions) had no case handler for it, causing the action to be ignored.
|
||||
|
||||
## Root Cause
|
||||
1. **Missing Handler**: The `GameManager.handleAction` switch statement lacked a `case 'DRAW_CARD'`.
|
||||
2. **Access Control**: The `RulesEngine.drawCard` method was `private`, preventing external invocation even if the handler existed.
|
||||
|
||||
## Solution
|
||||
1. **Made `drawCard` Public**: Updated `RulesEngine.ts` to change `drawCard` visibility from `private` to `public`.
|
||||
2. **Added Handler**: Updated `GameManager.ts` to include a `case 'DRAW_CARD'` in `handleAction`. This handler instantiates a `RulesEngine` for the current game and calls `engine.drawCard(actorId)`.
|
||||
|
||||
## Outcome
|
||||
Users can now manually draw cards from their library interactions (click or context menu) during gameplay.
|
||||
@@ -1,20 +0,0 @@
|
||||
# 2024-12-18 - Fix Actions Post-Mulligan
|
||||
|
||||
## Problem
|
||||
After the Mulligan phase, users reported "no actions working". The Smart Button and other strict interactions (Priority passing) were failing.
|
||||
|
||||
## Root Cause
|
||||
1. **Frontend Emission**: The `SmartButton` in `GameView.tsx` and the `RadialMenu` for mana were emitting legacy `type` strings (e.g. `PASS_PRIORITY` or `ADD_MANA` directly), or wrapped incorrectly. Specifically, `SmartButton` was correctly wrapping in `game_strict_action` but likely the state alignment was off.
|
||||
2. **Radial Menu**: Was emitting `ADD_MANA` as a legacy `game_action`. Legacy `GameManager` (before my fix in previous step) handled basic actions, but `ADD_MANA` is a strict engine concept. `GameManager.handleAction` (legacy) did not handle it. We needed to target `game_strict_action` or add a handler.
|
||||
3. **State Reset**: The engine's transition from Mulligan -> Untap -> Upkeep -> Draw -> Main1 relies on `resetPriority` correctly assigning priority to the Active Player. If this flow is interrupted or if the client UI doesn't realize it has priority (due to `priorityPlayerId` mismatch), the Smart Button disables itself.
|
||||
|
||||
## Solution
|
||||
1. **Strict Action Alignment**: Updated `GameView.tsx` to ensure `RadialMenu` (Mana) emits `game_strict_action`.
|
||||
2. **Handling**: (Previous Step) Added `DRAW_CARD` support.
|
||||
3. **Smart Button Checking**: Confirmed Smart Button emits `type` which `GameView` wraps in `action`. This matches `socket.on('game_strict_action', { action })`. This path is correct.
|
||||
|
||||
## Verification
|
||||
The flow "Mulligan -> Advanced Step (Mulligan Ends) -> Untap (Auto) -> Upkeep -> Reset Priority (Active Player)" seems logic-sound in `RulesEngine`. With the frontend now targeting the strict endpoint for Mana/Priority, and the legacy handler updated for Draw, the loop should be closed.
|
||||
|
||||
## Remaining Risk
|
||||
If `resetPriority` sets `priorityPlayerId` to a player ID that doesn't match the client's `currentPlayerId` (e.g. Turn 1 Order), the button will stay gray/disabled. This is Rules Correct (you can't act if not your priority), but UI feedback (telling *whose* turn/priority it is) is crucial. The existing `PhaseStrip` or `SmartButton` should indicate this.
|
||||
@@ -1,19 +0,0 @@
|
||||
# 2024-12-18 - Parse Card Data Robustness
|
||||
|
||||
## Problem
|
||||
The user reported issues with "placing cards onto the battlefield". Specifically, this manifested in two likely ways:
|
||||
1. Creature cards fading away instantly (dying to State-Based Actions) because their Power/Toughness was defaulted to 0/0.
|
||||
2. Cards resolving to the Graveyard instead of Battlefield because the `RulesEngine` failed to identify them as Permanents (empty `types` array), defaulting to Instant/Sorcery behavior.
|
||||
|
||||
## Root Cause
|
||||
1. **Missing P/T Passing**: The `server/index.ts` file was constructing the initial game state from deck cards but failing to explicitly copy `power` and `toughness` properties.
|
||||
2. **Missing Type Parsing**: The `GameManager` (and `index.ts`) relied on `typeLine` string but did not parse it into the `types` array which the `RulesEngine` strictly checks for `isPermanent` logic and invalid aura validation.
|
||||
|
||||
## Solution
|
||||
1. **Updated `GameManager.ts`**: Added robust parsing logic in `addCardToGame`. If `card.types` is empty, it now parses `card.typeLine` (e.g. splitting "Legendary Creature — Human") to populate `types`, `supertypes`, and `subtypes` arrays.
|
||||
2. **Updated `server/index.ts`**: Modified all game initialization flows to explicitly pass `power` and `toughness` from the source data to `gameManager.addCardToGame`.
|
||||
|
||||
## Outcome
|
||||
Cards added to the game now have correct type metadata and base stats.
|
||||
- Creatures resolve to the battlefield correctly (identified as Permanents).
|
||||
- Creatures stay on the battlefield (Toughness > 0 prevents SBA death).
|
||||
@@ -1,13 +0,0 @@
|
||||
# 2024-12-18 - Fix GameManager logging TypeError
|
||||
|
||||
## Problem
|
||||
The user reported a server crash with `TypeError: Cannot read properties of undefined (reading 'type')`. This occurred in the `GameManager.handleStrictAction` catch block when attempting to log `action.type` during an error condition, implying that `action` itself might be undefined or causing access issues in that context, or the error handling was too aggressive on a malformed payload.
|
||||
|
||||
## Root Cause
|
||||
The `catch(e)` block blindly accessed `action.type` to log the rule violation context. If `action` was null or undefined (which could happen if validation failed earlier or weird payloads arrived), this access threw the new error. Although `handleStrictAction` generally checks inputs, robust error logging should not crash.
|
||||
|
||||
## Solution
|
||||
Updated the catch block in `src/server/managers/GameManager.ts` to use optional chaining `action?.type` and provide a fallback string `'UNKNOWN'`.
|
||||
|
||||
## Outcome
|
||||
Server will now safely log "Rule Violation [UNKNOWN]" instead of crashing if the action payload is malformed during an error scenario.
|
||||
@@ -1,24 +0,0 @@
|
||||
# 2024-12-18 - Fix Strict Action Payload Construction
|
||||
|
||||
## Problem
|
||||
The user reported a server crash/error: `Rule Violation [UNKNOWN]: Cannot read properties of undefined (reading 'type')`.
|
||||
This occurred when resolving lands or casting spells. The error log "UNKNOWN" indicated the server received a null/undefined `action` object within the `game_strict_action` event payload.
|
||||
The server expects `socket.emit('game_strict_action', { action: { type: '...' } })`.
|
||||
The client was emitting `socket.emit('game_strict_action', { type: '...' })` (missing the `action` wrapper).
|
||||
|
||||
## Root Cause
|
||||
When refactoring for the Strict Actions, the frontend calls for `handleZoneDrop` (Battlefield), `handleCardDrop`, and `handlePlayerDrop` were updated to emit the new event name `game_strict_action`, but the payload structure was not updated to wrap the data in an `{ action: ... }` object as expected by `server/index.ts`.
|
||||
|
||||
## Solution
|
||||
Updated `GameView.tsx` in three locations (`handleZoneDrop`, `handleCardDrop`, `handlePlayerDrop`) to correctly wrap the payload:
|
||||
```typescript
|
||||
socketService.socket.emit('game_strict_action', {
|
||||
action: {
|
||||
type: '...',
|
||||
...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Outcome
|
||||
Client strict actions now match the server's expected payload structure. Actions like playing lands or casting spells should now execute defined logic instead of crashing or being treated as unknown.
|
||||
@@ -1,16 +0,0 @@
|
||||
# 2024-12-18 - Fix Combat Skip Logic
|
||||
|
||||
## Problem
|
||||
The user could not "Skip Combat" (Move past Declare Attackers). The button action `DECLARE_ATTACKERS` with 0 attackers was working server-side (resetting priority to AP), but the client UI (`SmartButton`) remained stuck on "Skip Combat". This created a loop where the user clicked, action processed, priority returned, and the UI still asked them to declare attackers.
|
||||
|
||||
## Root Cause
|
||||
The `SmartButton` logic relied solely on `gameState.step === 'declare_attackers'`. It did not differentiate between "Need to Declare" (Start of Step) and "After Declaration / Priority Window" (Middle of Step).
|
||||
Strict Rules State did not have a flag to indicate if the Turn-Based Action of declaring attackers had already occurred for the current step.
|
||||
|
||||
## Solution
|
||||
1. **Updated `Types`**: Added optional `attackersDeclared` (and `blockersDeclared`) boolean flags to `StrictGameState` (Server + Client).
|
||||
2. **Updated `RulesEngine`**: In `declareAttackers()`, set `attackersDeclared = true`. In `cleanupStep()`, reset these flags to `false`.
|
||||
3. **Updated `SmartButton`**: Added a check. If `step === 'declare_attackers'` AND `attackersDeclared` is true, display "Pass (to Blockers)" with `PASS_PRIORITY` action instead of "Skip Combat" / `DECLARE_ATTACKERS`.
|
||||
|
||||
## Outcome
|
||||
When a user clicks "Skip Combat" (declares 0 attackers), the server updates the state flag. The UI then updates to show a "Pass" button, allowing the user to proceed to the next step.
|
||||
@@ -1,37 +0,0 @@
|
||||
# Strict Rules & Blocking UI (Part 3)
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Objectives
|
||||
- Integrate Strict Actions (`PLAY_LAND`, `CAST_SPELL`) with precise positioning.
|
||||
- Implement Blocking UI including visual feedback (Attacking/Blocking badges, Rings).
|
||||
- Implement Drag-and-Drop Targeting Logic (Spell -> Target, Blocker -> Attacker).
|
||||
- Implement Visual "Targeting Tether" overlay.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Backend (Rules Engine)
|
||||
- **Positioning**: Updated `playLand` and `castSpell` to accept `{x, y}` coordinates.
|
||||
- **Stack Resolution**: Updated `resolveTopStack` to respect the stored resolution position when moving cards to the battlefield.
|
||||
- **Action Handling**: Updated `GameManager` to pass `position` payload to the engine.
|
||||
|
||||
### Frontend (GameView)
|
||||
- **Drop Logic**:
|
||||
- `handleZoneDrop`: Detects drop on "Battlefield". Differentiates Land (Play) vs Spell (Cast). Calculates relative % coordinates.
|
||||
- `handleCardDrop`: Detects drop on a Card.
|
||||
- If `declare_blockers` step: Assigns blocker (drag My Creature -> Opponent Creature).
|
||||
- Else: Casts Spell with Target.
|
||||
- `handlePlayerDrop`: Detects drop on Opponent Avatar -> Cast Spell with Target Player.
|
||||
- **Blocking Visualization**:
|
||||
- **Opponent Cards**: Show "ATTACKING" badge (Red Ring + Shadow) if `attacking === property`.
|
||||
- **My Cards**: Show "Blocking" badge (Blue Ring) if in local `proposedBlockers` map.
|
||||
- **Targeting Tether**:
|
||||
- Implemented `tether` state (`startX`, `currentX`, etc.).
|
||||
- Added `onDrag` handler to `CardComponent` to track HTML5 DnD movement.
|
||||
- Rendered Full-screen SVG overlay with Bezier curve (`Q` command) and arrow marker.
|
||||
- Dynamic styling: Cyan (Spells) vs Blue (Blocking).
|
||||
|
||||
## Next Steps
|
||||
- **Layer System**: Implement 7-layer P/T calculation for accurate power/toughness display.
|
||||
- **Mulligan System**: Implement Strict Mulligan rules.
|
||||
- **Token Creation**: Support creating tokens.
|
||||
@@ -1,15 +0,0 @@
|
||||
# 2024-12-18 - Fix Empty Combat Step Skipping
|
||||
|
||||
## Problem
|
||||
When a player declares 0 attackers (Skip Combat), the game correctly advances from `declare_attackers` but then proceeds to `declare_blockers` instead of skipping to the end of combat. This forces the (non-existent) defending player to declare blockers against nothing, or the active player to wait through irrelevant priority passes.
|
||||
|
||||
## Root Cause
|
||||
The `RulesEngine.advanceStep()` method strictly followed the standard phase/step structure defined in `server/game/RulesEngine.ts`. It lacked the logic to implement Rule 508.8, which states that if no attackers are declared, the Declare Blockers and Combat Damage steps are skipped.
|
||||
|
||||
## Solution
|
||||
Modified `RulesEngine.advanceStep()` to check for attackers before transitioning steps.
|
||||
If the current phase is `combat` and the next projected step is `declare_blockers`, it checks if any cards have the `attacking` property.
|
||||
If `attackers.length === 0`, it overrides `nextStep` to `end_combat`, effectively skipping the interactive combat steps.
|
||||
|
||||
## Outcome
|
||||
Declaring 0 attackers (or passing with no attacks) now correctly transitions the game immediately to the "End of Combat" step (and then likely Main Phase 2), smoothing out the gameplay flow.
|
||||
@@ -1,45 +0,0 @@
|
||||
# Strict Engine Enhancements: Layers, Tokens, Mulligan
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Objectives
|
||||
- Implement Basic Layer System for continuous effects (P/T modifications).
|
||||
- Implement Token Creation mechanism.
|
||||
- Implement Mulligan System (London Rule).
|
||||
- Update Game Lifecycle to include Setup/Mulligan phase.
|
||||
|
||||
## Logic Overview
|
||||
|
||||
### Layer System (`RulesEngine.recalculateLayers`)
|
||||
- Implements Layer 7 (Power/Toughness) basics:
|
||||
- **Layer 7b**: Set P/T (`set_pt`).
|
||||
- **Layer 7c**: Modify P/T (`pt_boost`).
|
||||
- **Layer 7d**: Counters (`+1/+1`, `-1/-1`).
|
||||
- `recalculateLayers` is called automatically whenever priority resets or actions occur.
|
||||
- Modifiers with `untilEndOfTurn: true` are automatically cleared in the `cleanup` step.
|
||||
|
||||
### Token Creation
|
||||
- New action `CREATE_TOKEN` added.
|
||||
- `createToken` method constructs a CardObject on the battlefield with defined stats.
|
||||
- Triggers layer recalculation immediately.
|
||||
|
||||
### Mulligan System
|
||||
- **New Phase**: `setup`, **New Step**: `mulligan`.
|
||||
- Game starts in `setup/mulligan`.
|
||||
- **Logic**:
|
||||
- If a player has 0 cards and hasn't kept, they draw 7 automatically.
|
||||
- Action `MULLIGAN_DECISION`:
|
||||
- `keep: false` -> Shuffles hand into library, draws 7, increments `mulliganCount`.
|
||||
- `keep: true` -> Validates `cardsToBottom` count matches `mulliganCount`. Moves excess cards to library. Sets `handKept = true`.
|
||||
- When all players keep, the engine automatically advances to `beginning/untap`.
|
||||
- Supports London Mulligan rule (Draw 7, put X on bottom).
|
||||
|
||||
## Technical Changes
|
||||
- Updated `StrictGameState` and `PlayerState` types.
|
||||
- Updated `GameManager` initialization and action switching.
|
||||
- Updated `RulesEngine` transition logic.
|
||||
|
||||
## Remaining/Next
|
||||
- Frontend UI for Mulligan (Needs a Modal to Keep/Mull).
|
||||
- Frontend UI for "Cards to Bottom" selection if X > 0.
|
||||
- Frontend UI to visualize Tokens.
|
||||
@@ -1,26 +0,0 @@
|
||||
# 2024-12-18 - High Velocity UX & Strict Engine Completion
|
||||
|
||||
## Status: Completed
|
||||
|
||||
We have successfully implemented the core strict rules engine features and the high-velocity UX components.
|
||||
|
||||
### 1. Rules Engine Refinement
|
||||
- **State-Based Actions (SBAs)**: Implemented robust SBA loop in `RulesEngine.ts`, utilizing `processStateBasedActions()` to cyclically check conditions (Lethal Damage, Legend Rule, Aura Validity) and recalculate layers until stability.
|
||||
- **Layer System**: Implemented Layer 7 (Power/Toughness) calculations, handling Base P/T, Setting Effects, Boosts, and Counters.
|
||||
- **Mana Engine**: Backend support for manual mana pool management (emptying at end of steps).
|
||||
- **Code Cleanup**: Resolved critical linting errors and structural issues in `RulesEngine.ts` (duplicate methods, undefined checks).
|
||||
|
||||
### 2. High-Velocity Frontend UX
|
||||
- **Inspector Overlay**: Created `InspectorOverlay.tsx` to visualize detailed card state (P/T modifications, counters, oracle text) with a modern, glassmorphism UI.
|
||||
- **Smart Button Advanced**: Implemented "Yield" toggle on the Smart Button. Users can long-press (simulated via pointer down) to yield priority until end of turn (or cancel).
|
||||
- **Radial Menu**: Created a generic `RadialMenu.tsx` component. Integrated it into the `GameView` via the Context Menu ("Add Mana...") to allow quick manual mana color selection for dual/utility lands.
|
||||
- **Context Menu Integration**: Added "Inspect Details" and "Add Mana..." options to the card context menu.
|
||||
|
||||
### 3. Verification
|
||||
- **GameView Integration**: All new components (`InspectorOverlay`, `SmartButton`, `RadialMenu`) are fully integrated into `GameView.ts`.
|
||||
- **Type Safety**: Updated `types/game.ts` to ensure consistency between client and server (e.g., `attachedTo`, `ptModification` properties).
|
||||
|
||||
## Next Steps
|
||||
- **Playtesting**: Validate the interaction between strict rules (timing, priority) and the new UX in a live multiplayer environment.
|
||||
- **Visual Polish**: Refine animations for Inspector and Radial Menu opening.
|
||||
- **Complex Card Logic**: Expand the engine to support more complex replacement effects and specific card scripts.
|
||||
@@ -1,33 +0,0 @@
|
||||
# PROJ001: Initial Project Setup and Logic Refactoring (Node.js Migration)
|
||||
|
||||
## Status: COMPLETED
|
||||
|
||||
### Achievements
|
||||
- **Architecture**: Pivoted from .NET to a **Node.js Monolith** structure to natively support real-time state synchronization via Socket.IO.
|
||||
- **Frontend Infrastructure**: Configured **React** 19 + **Vite** + **Tailwind CSS** (v3) in `src/client`.
|
||||
- **Backend Infrastructure**: Initialized **Express** server with **Socket.IO** in `src/server` for handling API requests and multiplayer draft state.
|
||||
- **Refactoring**: Successfully ported legacy `gemini-generated.js` logic into specialized TypeScript services:
|
||||
- `CardParserService.ts`: Regex-based list parsing.
|
||||
- `ScryfallService.ts`: Data fetching with caching.
|
||||
- `PackGeneratorService.ts`: Pack creation logic.
|
||||
- **UI Implementation**: Developed `CubeManager`, `PackCard`, and `StackView` components.
|
||||
- **Cleanup**: Removed all .NET artifacts and dependencies.
|
||||
- **Tooling**: Updated `Makefile` for unified Node.js development commands.
|
||||
|
||||
### How to Run
|
||||
- **Install**: `make install` (or `cd src && npm install`)
|
||||
- **Run Development**: `make dev` (Runs Server and Client concurrently)
|
||||
- **Build**: `make build`
|
||||
|
||||
### Manual Verification Steps
|
||||
1. **Run**: `make dev`
|
||||
2. **Access**: Open `http://localhost:5173` (Client).
|
||||
3. **Test**:
|
||||
- Click "Load Demo List" in the Cube Manager.
|
||||
- Verify cards are fetched from Scryfall.
|
||||
- Click "Generate Pools".
|
||||
- Verify packs are generated and visible in Stack/Grid views.
|
||||
|
||||
### Next Steps
|
||||
- Implement `DraftSession` state management in `src/server`.
|
||||
- Define Socket.IO events for lobby creation and player connection.
|
||||
@@ -1,29 +0,0 @@
|
||||
# Migration to Node.js Backend
|
||||
|
||||
## Objective
|
||||
Convert the project from a .NET backend to a Node.js (TypeScript) backend and remove the .NET infrastructure.
|
||||
|
||||
## Plan
|
||||
|
||||
### Phase 1: Structure Initialization
|
||||
- [ ] Initialize `src` as a Node.js project (`package.json`, `tsconfig.json`).
|
||||
- [ ] Create directory structure:
|
||||
- [ ] `src/server`: Backend logic.
|
||||
- [ ] `src/client`: Move existing React frontend here.
|
||||
- [ ] `src/shared`: Shared interfaces/types.
|
||||
|
||||
### Phase 2: React Frontend Migration
|
||||
- [ ] Move `src/MtgDraft.Web/Client` contents to `src/client/src`.
|
||||
- [ ] Move configuration files (`vite.config.ts`, `tailwind.config.js`, etc.) to `src/client` root or adjust as needed.
|
||||
- [ ] Ensure frontend builds and runs via Vite (dev server).
|
||||
|
||||
### Phase 3: Node.js Backend Implementation
|
||||
- [ ] Set up Express/Fastify server in `src/server/index.ts`.
|
||||
- [ ] Configure Socket.IO foundations.
|
||||
- [ ] Configure build scripts to build client and server.
|
||||
|
||||
### Phase 4: Verification
|
||||
- [ ] Verify application runs with `npm run dev`.
|
||||
|
||||
### Phase 5: Cleanup
|
||||
- [ ] Delete `MtgDraft.*` folders.
|
||||
@@ -1,30 +0,0 @@
|
||||
# Implementation of Core Functionalities
|
||||
|
||||
## Status
|
||||
Completed
|
||||
|
||||
## Description
|
||||
Implemented the core functionalities based on the reference `gemini-generated.js` file, refactoring the monolithic logic into a modular architecture.
|
||||
|
||||
## Changes
|
||||
1. **Services**:
|
||||
- Created `CardParserService` for parsing bulk text lists.
|
||||
- Created `ScryfallService` for fetching card data with caching and batching.
|
||||
- Created `PackGeneratorService` for generating booster packs with various rules (Peasant, Standard, Chaos).
|
||||
|
||||
2. **Modules**:
|
||||
- **CubeManager**: Implemented the Draft Preparation Phase UI (Input, Filters, Generation).
|
||||
- **TournamentManager**: Implemented the Tournament Bracket generation logic and UI.
|
||||
|
||||
3. **Components**:
|
||||
- `PackCard`: card component with List, Grid, and Stack views.
|
||||
- `StackView`: 3D card stack visualization.
|
||||
- `TournamentPackView`: "Blind Mode" / Box view for generated packs.
|
||||
|
||||
4. **Architecture**:
|
||||
- Created `App.tsx` as the main shell with Tab navigation (Draft vs Bracket).
|
||||
- Integrated all components into the main entry point.
|
||||
|
||||
## Next Steps
|
||||
- Integrate Socket.IO for real-time draft synchronization (Multiplayer).
|
||||
- Implement the "Live Draft" interface.
|
||||
@@ -1,19 +0,0 @@
|
||||
# Bug Fix: React Render Error and Pack Generation Stability
|
||||
|
||||
## Issue
|
||||
User reported "root.render(" error visible on page and "Generate Packs" button ineffective.
|
||||
|
||||
## Diagnosis
|
||||
1. **main.tsx**: Found nested `root.render( <StrictMode> root.render(...) )` call. This caused runtime errors and visible artifact text.
|
||||
2. **CubeManager.tsx**: Service classes (`ScryfallService`, `PackGeneratorService`) were instantiated inside the functional component body without `useMemo`. This caused recreation on every render, leading to cache loss (`ScryfallService` internal cache) and potential state inconsistencies.
|
||||
3. **Pack Generation**: Double-clicking or rapid state updates caused "phantom" generation runs with empty pools, resetting the packs list to 0 immediately after success.
|
||||
|
||||
## Resolution
|
||||
1. **Fixed main.tsx**: Removed the nested `root.render` call.
|
||||
2. **Refactored CubeManager.tsx**:
|
||||
* Memoized all services using `useMemo`.
|
||||
* Added `loading` state to `generatePacks` to prevent double-submissions.
|
||||
* Wrapped generation logic in `setTimeout` to allow UI updates and `try/catch` for robustness.
|
||||
|
||||
## Status
|
||||
Verified via browser subagent (logs confirmed 241 packs generated). UI now prevents race conditions.
|
||||
@@ -1,24 +0,0 @@
|
||||
# Bug Fix: Card Parser Robustness
|
||||
|
||||
## User Request
|
||||
"The problem is that if the scryfall id is missing, no card is retrieved so no card is generated, instead the system should be able to retrieve cards and generate packs even without scryfall id"
|
||||
|
||||
## Diagnosis
|
||||
The `CardParserService` currently performs basic name extraction. It fails to strip set codes and collector numbers common in export formats (e.g., MTG Arena exports like `1 Shock (M20) 160`).
|
||||
This causes `ScryfallService` to search for "Shock (M20) 160" as an exact name, which fails. The system relies on successful Scryfall matches to populate the card pool; without matches, the pool is empty, and generation produces 0 packs.
|
||||
|
||||
## Implementation Plan
|
||||
1. **Refactor `CardParserService.ts`**:
|
||||
* Enhance regex to explicitly handle and strip:
|
||||
* Parentheses containing text (e.g., `(M20)`).
|
||||
* Collector numbers at the end of lines.
|
||||
* Set codes in square brackets if present.
|
||||
* Maintain support for `Quantity Name` format.
|
||||
* Ensure exact name cleanup to maximize Scryfall "exact match" hits.
|
||||
|
||||
2. **Verification**:
|
||||
* Create a test input imitating Arena export.
|
||||
* Verify via browser subagent that cards are fetched and packs are generated.
|
||||
|
||||
## Update Central
|
||||
Update `CENTRAL.md` with this task.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Enhancement: Set-Based Pack Generation
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Summary
|
||||
Implemented the ability to fetch entire sets from Scryfall and generate booster boxes.
|
||||
|
||||
## Changes
|
||||
1. **ScryfallService**:
|
||||
* Added `fetchSets()` to retrieve expansion sets.
|
||||
* Added `fetchSetCards(setCode)` to retrieve all cards from a set.
|
||||
2. **PackGeneratorService**:
|
||||
* Added `generateBoosterBox()` to generate packs without depleting the pool.
|
||||
* Added `buildTokenizedPack()` for probabilistic generation (R/M + 3U + 10C).
|
||||
3. **CubeManager UI**:
|
||||
* Added Toggle for "Custom List" vs "From Expansion".
|
||||
* Added Set Selection Dropdown.
|
||||
* Added "Number of Boxes" input.
|
||||
* Integrated new service methods.
|
||||
|
||||
## Usage
|
||||
1. Select "From Expansion" tab.
|
||||
2. Choose a set (e.g., "Vintage Masters").
|
||||
3. Choose number of boxes (default 3).
|
||||
4. Click "Fetch Set".
|
||||
5. Click "Generate Packs".
|
||||
@@ -1,18 +0,0 @@
|
||||
# Cleanup: Remove Tournament Mode
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Summary
|
||||
Removed the "Tournament Mode" view and "Editor Mode" toggle from the Cube Manager. The user requested a simplified interface that lists packs without grouping them into "Boxes".
|
||||
|
||||
## Changes
|
||||
1. **CubeManager.tsx**:
|
||||
* Removed `tournamentMode` state and setter.
|
||||
* Removed usage of `TournamentPackView` component.
|
||||
* Removed the "Tournament Mode / Editor Mode" toggle button.
|
||||
* Simplified rendering to always show the pack list (grid/list/stack view) directly.
|
||||
* Removed unsused `TournamentPackView` import and icon imports.
|
||||
|
||||
## Impact
|
||||
* The UI is now streamlined for the "Host" to just see generated packs.
|
||||
* The `TournamentPackView` component is no longer used but file remains for now.
|
||||
@@ -1,18 +0,0 @@
|
||||
# Enhancement: UI Simplification for Set Generation
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Summary
|
||||
Refined the Cube Manager UI to hide redundant options when generating packs from an entire expansion set.
|
||||
|
||||
## Changes
|
||||
1. **CubeManager.tsx**:
|
||||
* **Conditional Rendering**: The "Card Source" options (Chaos Draft vs Split by Expansion) are now **hidden** when "From Expansion" mode is selected.
|
||||
* **Automatic State Handling**:
|
||||
* Selecting "From Expansion" automatically sets generation mode to `by_set`.
|
||||
* Selecting "Custom List" resets generation mode to `mixed` (user can still change it).
|
||||
* **Rationale**: Using an entire set implies preserving its structure (one set), whereas a custom list is often a cube (chaos) or a collection of specific sets where the user might want explicitly mixed packs.
|
||||
|
||||
## Impact
|
||||
* Reduces visual noise for the user when they simply want to draft a specific set.
|
||||
* Prevents invalid configurations (e.g., selecting "Chaos Draft" for a single set, which technically works but is confusing in context of "Set Generation").
|
||||
@@ -1,37 +0,0 @@
|
||||
# Work Plan: Real Game & Online Multiplayer
|
||||
|
||||
## User Epics
|
||||
1. **Lobby System**: Create and join private rooms.
|
||||
2. **Game Setup**: Use generated packs to start a game.
|
||||
3. **Multiplayer Draft**: Real-time drafting with friends.
|
||||
4. **Chat**: In-game communication.
|
||||
|
||||
## Tasks
|
||||
|
||||
### 1. Backend Implementation (Node.js + Socket.IO)
|
||||
- [ ] Create `src/server/managers/RoomManager.ts` to handle room state.
|
||||
- [ ] Implement `Room` and `Player` interfaces.
|
||||
- [ ] Update `src/server/index.ts` to initialize `RoomManager` and handle socket events:
|
||||
- `create_room`
|
||||
- `join_room`
|
||||
- `leave_room`
|
||||
- `send_message`
|
||||
- `start_game` (placeholder for next phase)
|
||||
|
||||
### 2. Frontend Implementation (React)
|
||||
- [ ] Create `src/client/src/modules/lobby` directory.
|
||||
- [ ] Create `LobbyManager.tsx` (The main view for finding/creating rooms).
|
||||
- [ ] Create `GameRoom.tsx` (The specific room view with chat and player list).
|
||||
- [ ] Create `socket.ts` service in `src/client/src/services` for client-side socket handling.
|
||||
- [ ] Update `App.tsx` to include the "Lobby" tab.
|
||||
- [ ] Update `CubeManager.tsx` to add "Create Online Room" button.
|
||||
|
||||
### 3. Integration
|
||||
- [ ] Ensure created room receives the packs from `CubeManager`.
|
||||
- [ ] Verify players can join via Room ID.
|
||||
- [ ] Verify chat works.
|
||||
|
||||
## Technical Notes
|
||||
- Use `socket.io-client` on frontend.
|
||||
- Generate Room IDs (short random strings).
|
||||
- Manage state synchronization for the room (players list updates).
|
||||
@@ -1,15 +0,0 @@
|
||||
# Fix UUID Error in Insecure Contexts
|
||||
|
||||
## Problem
|
||||
The user reported a `TypeError: crypto.randomUUID is not a function` when accessing the application from a public IP. This is because `crypto.randomUUID()` is part of the Web Crypto API, which is often restricted to secure contexts (HTTPS) or localhost. When accessing via `http://PUBLIC_IP:PORT`, the browser disables this API.
|
||||
|
||||
## Solution
|
||||
We need to implement a fallback UUID generation method that works in non-secure contexts.
|
||||
|
||||
## Plan
|
||||
1. Modify `src/client/src/services/PackGeneratorService.ts`.
|
||||
2. Add a private method `generateUUID()` to the `PackGeneratorService` class (or a standalone helper function in the module) that:
|
||||
* Checks if `crypto.randomUUID` is available.
|
||||
* If yes, uses it.
|
||||
* If no, uses a fallback algorithm (e.g., `Math.random()` based v4 UUID generation).
|
||||
3. Replace the call `crypto.randomUUID()` with this new method.
|
||||
@@ -1,31 +0,0 @@
|
||||
# Game Interactions Implementation
|
||||
|
||||
## Objective
|
||||
Implement basic player interactions for the MTG game, including library, battlefield, and other game mechanics.
|
||||
|
||||
## Changes
|
||||
1. **Backend (`src/server/managers/GameManager.ts`)**:
|
||||
* Created `GameManager` class to handle game state.
|
||||
* Defined `GameState`, `PlayerState`, `CardInstance` interfaces.
|
||||
* Implemented `createGame`, `handleAction` (move, tap, draw, life).
|
||||
* Integrated with `socket.io` handlers in `server/index.ts`.
|
||||
|
||||
2. **Frontend (`src/client/src/modules/game`)**:
|
||||
* Created `GameView.tsx`: Main game board with drag-and-drop zones (Hand, Battlefield, Library, Graveyard).
|
||||
* Created `CardComponent.tsx`: Draggable card UI with tap state.
|
||||
* Updated `GameRoom.tsx`: Added game state handling and "Start Game (Test)" functionality.
|
||||
|
||||
3. **Socket Service**:
|
||||
* Identify `start_game` and `game_action` events.
|
||||
* Listen for `game_update` to sync state.
|
||||
|
||||
## Status
|
||||
- Basic sandbox gameplay is operational.
|
||||
- Players can move cards between zones freely (DnD).
|
||||
- Tap/Untap and Life counters implemented.
|
||||
- Test deck (Mountain/Bolt) provided for quick testing.
|
||||
|
||||
## Next Steps
|
||||
- Implement actual rules enforcement (Stack, Priority).
|
||||
- Implement Deck Builder / Draft Integration (load actual drafted decks).
|
||||
- Improve UI/UX (animations, better card layout).
|
||||
@@ -1,41 +0,0 @@
|
||||
# Draft & Deck Building Phase
|
||||
|
||||
## Objective
|
||||
Implement the "Draft Phase" (Pack Passing) and "Deck Building Phase" (Pool + Lands) logic and UI, bridging the gap between Lobby and Game.
|
||||
|
||||
## Changes
|
||||
1. **Backend - Draft Logic (`src/server/managers/DraftManager.ts`)**:
|
||||
* Implemented `DraftManager` class.
|
||||
* Handles pack distribution (3 packs per player).
|
||||
* Implements `pickCard` logic with queue-based passing (Left-Right-Left).
|
||||
* Manages pack rounds (Wait for everyone to finish Pack 1 before opening Pack 2).
|
||||
* Transitions to `deck_building` status upon completion.
|
||||
|
||||
2. **Server Integration (`src/server/index.ts`)**:
|
||||
* Added handlers for `start_draft` and `pick_card`.
|
||||
* Broadcasts `draft_update` events.
|
||||
|
||||
3. **Frontend - Draft UI (`src/client/src/modules/draft/DraftView.tsx`)**:
|
||||
* Displays active booster pack.
|
||||
* Timer (visual only for now).
|
||||
* Click-to-pick interaction.
|
||||
* Preview of drafted pool.
|
||||
|
||||
4. **Frontend - Deck Builder UI (`src/client/src/modules/draft/DeckBuilderView.tsx`)**:
|
||||
* **Split View**: Card Pool vs. Current Deck.
|
||||
* **Drag/Click**: Click card to move between pool and deck.
|
||||
* **Land Station**: Add basic lands (Plains, Island, Swamp, Mountain, Forest) with unlimited supply.
|
||||
* **Submit**: Sends deck to server (via `player_ready` - *Note: Server integration for deck storage pending final game start logic*).
|
||||
|
||||
5. **Integration (`GameRoom.tsx`)**:
|
||||
* Added routing based on room status: `waiting` -> `drafting` -> `deck_building` -> `game`.
|
||||
* Added "Start Real Draft" button to lobby.
|
||||
|
||||
## Status
|
||||
- **Drafting**: Fully functional loop. Players pick cards, pass packs, and proceed through 3 rounds.
|
||||
- **Deck Building**: UI is ready. Players can filter, build, and add lands.
|
||||
- **Next**: Need to finalize the "All players ready" logic in `deck_building` to trigger the actual `start_game` using the submitted decks. Currently, submitting triggers a placeholder event.
|
||||
|
||||
## To Verify
|
||||
- Check passing direction (Left/Right).
|
||||
- Verify Basic Land addition works correctly in the final deck object.
|
||||
@@ -1,37 +0,0 @@
|
||||
# Image Caching Implementation
|
||||
|
||||
## Objective
|
||||
Implement a robust image caching system that downloads card images to the server when creating a draft room, ensuring all players can see images reliably via local serving.
|
||||
|
||||
## Changes
|
||||
1. **Backend - Image Service (`src/server/services/CardService.ts`)**:
|
||||
* Created `CardService` class.
|
||||
* Implements `cacheImages` which downloads images from external URLs to `src/server/public/cards`.
|
||||
* Uses a concurrency limit (5) to avoid rate limiting.
|
||||
* Checks for existence before downloading to avoid redundant work.
|
||||
|
||||
2. **Backend - Server Setup (`src/server/index.ts`)**:
|
||||
* Enabled static file serving for `/cards` endpoint mapping to `src/server/public/cards`.
|
||||
* Added `POST /api/cards/cache` endpoint that accepts a list of cards and triggers cache logic.
|
||||
* Increased JSON body limit to 50mb to handle large set payloads.
|
||||
|
||||
3. **Frontend - Lobby Manager (`LobbyManager.tsx`)**:
|
||||
* Updated `handleCreateRoom` workflow.
|
||||
* **Pre-Creation**: Extracts all unique cards from generated packs.
|
||||
* **Cache Request**: Sends list to `/api/cards/cache`.
|
||||
* **Transformation**: Updates local pack data to point `image` property to the local server URL (`/cards/{scryfallId}.jpg`) instead of remote Scryfall URL.
|
||||
* This ensures that when `create_room` is emitted, the room state on the server (and thus all connected clients) contains valid local URLs.
|
||||
|
||||
4. **Fixes**:
|
||||
* Addressed `GameRoom.tsx` crash by replacing `require` with dynamic imports (or static if preloaded) and fixing clipboard access.
|
||||
* Fixed TS imports in server index.
|
||||
|
||||
## Status
|
||||
- **Image Caching**: Functional. Creating a room now triggers a download process on the terminal.
|
||||
- **Local Serving**: Cards should now load instantly from the server for all peers.
|
||||
|
||||
## How to Verify
|
||||
1. Generate packs in Draft Management.
|
||||
2. Create a Room. Watch server logs for "Cached image: ..." messages.
|
||||
3. Join room.
|
||||
4. Start Draft. Images should appear.
|
||||
@@ -1,17 +0,0 @@
|
||||
# Fix Draft Card Images
|
||||
|
||||
## Issue
|
||||
Users reported that images were not showing in the Draft Card Selection UI.
|
||||
|
||||
## Root Causes
|
||||
1. **Missing Proxy**: The application was attempting to load cached images from `http://localhost:5173/cards/...`. Vite Dev Server (port 5173) was not configured to proxy these requests to the backend (port 3000), resulting in 404 errors for all local images.
|
||||
2. **Incorrect Property Access**: `DraftView.tsx` (and `DeckBuilderView.tsx`) attempted to access `card.image_uris.normal`. However, the `DraftCard` object generated by `PackGeneratorService` and modified by `LobbyManager` stores the image URL in `card.image`. This property was being ignored.
|
||||
|
||||
## Fixes
|
||||
1. **Vite Config**: Added a proxy rule for `/cards` in `src/vite.config.ts` to forward requests to `http://localhost:3000`.
|
||||
2. **Frontend Views**: Updated `DraftView.tsx` and `DeckBuilderView.tsx` to prioritize `card.image` when rendering card images.
|
||||
|
||||
## Verification
|
||||
- Start the draft.
|
||||
- Images should now load correctly from the local cache (or fallback if configured).
|
||||
- Inspect network tab to verify images are loaded from `/cards/...` with a 200 OK status.
|
||||
@@ -1,28 +0,0 @@
|
||||
# Fix Submit Deck Button
|
||||
|
||||
## Issue
|
||||
Users reported that "Submit Deck" button was not working.
|
||||
|
||||
## Root Causes
|
||||
1. **Missing Event Handler**: The server was not listening for the `player_ready` event emitted by the client.
|
||||
2. **Incomplete Payload**: The client was sending `{ roomId, deck }` but the server needed `playerId` to identify who was ready, which was missing from the payload.
|
||||
3. **Missing State Logic**: The `RoomManager` did not have a concept of "Ready" state or "Playing" status, meaning the transition from Deck Building to Game was not fully implemented.
|
||||
|
||||
## Fixes
|
||||
1. **Client (`DeckBuilderView.tsx`)**: Updated `player_ready` emission to include `playerId`.
|
||||
2. **Server (`RoomManager.ts`)**:
|
||||
- Added `ready` and `deck` properties to `Player` interface.
|
||||
- Added `playing` to `Room` status.
|
||||
- Implemented `setPlayerReady` method.
|
||||
3. **Server (`index.ts`)**:
|
||||
- Implemented `player_ready` socket handler.
|
||||
- Added logic to check if *all* active players are ready.
|
||||
- If all ready, automatically transitions room status to `playing` and initializes the game using `GameManager`, loading the submitted decks.
|
||||
- ensured deck loading uses cached images (`card.image`) if available.
|
||||
|
||||
## Verification
|
||||
1. Draft cards.
|
||||
2. Build deck.
|
||||
3. Click "Submit Deck".
|
||||
4. Server logs should show "All players ready...".
|
||||
5. Client should automatically switch to `GameView` (Battlefield).
|
||||
@@ -1,22 +0,0 @@
|
||||
# Fix Hooks Violation and Implement Waiting State
|
||||
|
||||
## Issue
|
||||
1. **React Hook Error**: Users encountered "Rendered fewer hooks than expected" when the game started. This was caused by conditional returns in `GameRoom.tsx` appearing *before* hook declarations (`useState`, `useEffect`).
|
||||
2. **UX Issue**: Players who submitted their decks remained in the Deck Builder view, able to modify their decks, instead of seeing a waiting screen.
|
||||
|
||||
## Fixes
|
||||
1. **Refactored `GameRoom.tsx`**:
|
||||
- Moved all `useState` and `useEffect` hooks to the top level of the component, ensuring they are always called regardless of the render logic.
|
||||
- Encapsulated the view switching logic into a helper function `renderContent()`, which is called inside the main return statement.
|
||||
2. **Implemented Waiting Screen**:
|
||||
- Inside `renderContent`, checking if the room is in `deck_building` status AND if the current player has `ready: true`.
|
||||
- If ready, displays a "Deck Submitted" screen with a list of other players and their readiness status.
|
||||
- Updated the sidebar player list to show a "• Ready" indicator.
|
||||
|
||||
## Verification
|
||||
1. Start a draft with multiple users (or simulate it).
|
||||
2. Complete draft and enter deck building.
|
||||
3. Submit deck as one player.
|
||||
4. Verify that the view changes to "Deck Submitted" / Waiting screen.
|
||||
5. Submit deck as the final player.
|
||||
6. Verify that the game starts automatically for everyone without crashing (React Error).
|
||||
@@ -1,32 +0,0 @@
|
||||
# Game Battlefield & Manual Mode Implementation Plan
|
||||
|
||||
## Goal
|
||||
Implement a 3D-style battlefield and manual game logic for the MTG Draft Maker. The system should allow players to drag and drop cards freely onto the battlefield, tap cards, and manage zones (Hand, Library, Graveyard, Exile) in a manual fashion typical of virtual tabletops.
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Implemented Features
|
||||
- **3D Battlefield UI**:
|
||||
- Used CSS `perspective: 1000px` and `rotateX` to create a depth effect.
|
||||
- Cards are absolutely positioned on the battlefield based on percentage coordinates (0-100%).
|
||||
- Shadows and gradients enhance the "tabletop" feel.
|
||||
- **Manual Game Logic**:
|
||||
- **Free Drag and Drop**: Players can move cards anywhere on the battlefield. Coordinates are calculated relative to the drop target.
|
||||
- **Z-Index Management**: Backend tracks a `maxZ` counter. Every move or flip brings the card to the front (`z-index` increment).
|
||||
- **Actions**:
|
||||
- **Tap/Untap**: Click to toggle (rotate 90 degrees).
|
||||
- **Flip**: Right-click to toggle face-up/face-down status.
|
||||
- **Draw**: Click library to draw.
|
||||
- **Life**: Buttons to increment/decrement life.
|
||||
- **Multiplayer Synchronization**:
|
||||
- All actions (`MOVE_CARD`, `TAP_CARD`, `FLIP_CARD`, `UPDATE_LIFE`) are broadcast via Socket.IO.
|
||||
- Opponent's battlefield is rendered in a mirrored 3D perspective.
|
||||
|
||||
## Files Modified
|
||||
- `src/client/src/modules/game/GameView.tsx`: Main UI logic.
|
||||
- `src/client/src/modules/game/CardComponent.tsx`: Added context menu support.
|
||||
- `src/server/managers/GameManager.ts`: Logic for actions and state management.
|
||||
|
||||
## Next Steps
|
||||
- Test with real players to fine-tune the "feel" of dragging (maybe add grid snapping option later).
|
||||
- Implement "Search Library" feature (currently just Draw).
|
||||
@@ -1,39 +0,0 @@
|
||||
# Game Context Menu & Immersion Update Plan
|
||||
|
||||
## Goal
|
||||
Implement a robust, video-game-style context menu for the battlefield and cards. This menu will allow players to perform advanced manual actions required for MTG, such as creating tokens and managing counters, while eliminating "browser-like" feel.
|
||||
|
||||
## Status: Completed
|
||||
|
||||
## Implemented Features
|
||||
- **Custom Game Context Menu**:
|
||||
- Replaces default browser context menu.
|
||||
- Dark, video-game themed UI with glassmorphism.
|
||||
- Animated entrance (fade/zoom).
|
||||
- **Functionality**:
|
||||
- **Global (Background)**:
|
||||
- "Create Token" (Default 1/1, 2/2, Treasure).
|
||||
- **Card Specific**:
|
||||
- "Tap / Untap"
|
||||
- "Flip Face Up / Down"
|
||||
- "Add Counter" (Submenu: +1/+1, -1/-1, Loyalty)
|
||||
- "Clone (Copy)" (Creates an exact token copy of the card)
|
||||
- "Delete Object" (Removing tokens or cards)
|
||||
- **Backend Logic**:
|
||||
- `GameManager` now handles:
|
||||
- `ADD_COUNTER`: Adds/removes counters logic.
|
||||
- `CREATE_TOKEN`: Generates new token instances with specific stats/art.
|
||||
- `DELETE_CARD`: Removes objects from the game.
|
||||
- **Frontend Integration**:
|
||||
- `GameView` manages menu state (position, target).
|
||||
- `CardComponent` triggers menu only on itself, bubbling prevented.
|
||||
- Hand cards also support right-click menus.
|
||||
|
||||
## Files Modified
|
||||
- `src/client/src/modules/game/GameContextMenu.tsx`: New component.
|
||||
- `src/client/src/modules/game/GameView.tsx`: Integrated menu.
|
||||
- `src/server/managers/GameManager.ts`: Added token/counter handlers.
|
||||
|
||||
## Next Steps
|
||||
- Add sounds for menu open/click.
|
||||
- Add more token types or a token editor.
|
||||
@@ -1,41 +0,0 @@
|
||||
# Enhancement Plan: True 3D Game Area
|
||||
|
||||
The goal is to transform the game area into a "really 3D game" experience using CSS 3D transforms.
|
||||
|
||||
## Objectives
|
||||
1. **Immersive 3D Table**: Create a convincing 3D perspective of a table where cards are placed.
|
||||
2. **Card Physics Simulation**: Visuals should simulate cards having weight, thickness, and position in 3D space.
|
||||
3. **Dynamic Camera/View**: Fix the viewing angle to be consistent with a player sitting at a table.
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### 1. Scene Setup (GameView.tsx)
|
||||
- Create a "Scene" container with high `perspective` (e.g., `1200px` to `2000px`).
|
||||
- Create a "World" container that holds the table and other elements, allowing for global rotation if needed.
|
||||
- Implement a "TableSurface" div that is rotated `rotateX(40-60deg)` to simulate a flat surface viewed from an angle.
|
||||
|
||||
### 2. Battlefield Enchancement
|
||||
- The player's battlefield should be the bottom half of the table.
|
||||
- The opponent's battlefield should be the top half.
|
||||
- Use `transform-style: preserve-3d` extensively.
|
||||
- Add a grid/mat texture to the table surface to enhance the depth perception.
|
||||
|
||||
### 3. Card 3D Component (CardComponent.tsx)
|
||||
- Refactor `CardComponent` to use a 3D structure.
|
||||
- Add a container for 3D positioning (`translate3d`).
|
||||
- Add a visual "lift" when dragging or hovering (`translateZ`).
|
||||
- Enhance the shadow to be on the "table" surface, separating from the card when lifting.
|
||||
- *Implementation Note*: The shadow might need to be a separate element `after` the card or a separate div to stay on the table plane while the card lifts.
|
||||
|
||||
### 4. Lighting and Atmosphere
|
||||
- Add a "Light Source" effect (radial gradient overlay).
|
||||
- Adjust colors to be darker/moodier, fitting the "Dark Gaming UI" aesthetic.
|
||||
|
||||
## Tech Stack
|
||||
- CSS via Tailwind + Inline Styles for dynamic coordinates.
|
||||
- React for state/rendering.
|
||||
|
||||
## Execution Order
|
||||
1. Refactor `GameView.tsx` layout to standard CSS 3D Scene structure.
|
||||
2. Update `CardComponent.tsx` to handle 3D props (tilt, lift).
|
||||
3. Fine-tune values for perspective and rotation.
|
||||
@@ -1,31 +0,0 @@
|
||||
# Docker Containerization and Build Fixes
|
||||
|
||||
## Objectives
|
||||
- Create a Dockerfile to package the application as a monolith (Node.js + React).
|
||||
- Fix TypeScript build errors preventing successful compilation.
|
||||
- Verify the build process.
|
||||
|
||||
## Changes
|
||||
- **Dockerfile**: Created multi-stage build using `node:20-alpine`.
|
||||
- Installs dependencies.
|
||||
- Builds frontend.
|
||||
- Prunes dev dependencies.
|
||||
- **Server Entry (`src/server/index.ts`)**: Added logic to serve static `dist` files and handle client-side routing in production.
|
||||
- **Package.json**: Moved `tsx` to dependencies and updated `start` script.
|
||||
- **Code Fixes**: Removed unused variables in client and server code used to satisfy strict TypeScript rules:
|
||||
- `DeckBuilderView.tsx`: Removed unused `payload`.
|
||||
- `DraftView.tsx`: Removed unused `CardComponent`.
|
||||
- `GameView.tsx`: Removed unused `myCommand`, `oppGraveyard`.
|
||||
- `DraftManager.ts`: Removed unused `numPlayers`, `cardIndex`.
|
||||
- `GameManager.ts`: Renamed unused args in `shuffleLibrary`.
|
||||
- **Helm Chart**: Created a complete Helm chart configuration in `helm/mtg-draft-maker`:
|
||||
- `Chart.yaml`: Defined chart metadata.
|
||||
- `values.yaml`: Configured defaults (Image `git.commandware.com/services/mtg-online-drafter:main`, Port 3000).
|
||||
- `templates/`: Added Deployment, Service, Ingress, and ServiceAccount manifests.
|
||||
- **Persistence**: Added configuration to mount a Persistent Volume Claim (PVC) at `/app/server/public/cards` for storing cached images. Disabled by default.
|
||||
- Linted successfully.
|
||||
|
||||
## Status
|
||||
- Docker build successful (`docker build -t mtg-draft-maker .`).
|
||||
- Helm chart created and linted.
|
||||
- Ready for K8s deployment.
|
||||
@@ -1,32 +0,0 @@
|
||||
# Deck Tester Feature Implementation
|
||||
|
||||
## Objective
|
||||
Create a way to add a cards list to generate a deck and directly enter the game ui to test the imported deck, using the same exact game and battlefield of the draft.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Frontend
|
||||
1. **DeckTester Component (`src/client/src/modules/tester/DeckTester.tsx`)**:
|
||||
- Created a new component that allows users to input a deck list (text area or file upload).
|
||||
- Reused `CardParserService` and `ScryfallService` to parse the list and fetch card data.
|
||||
- Implemented image caching logic (sending to `/api/cards/cache`).
|
||||
- Connects to socket and emits `start_solo_test`.
|
||||
- Upon success, switches view to `GameRoom` with the received `room` and `game` state.
|
||||
|
||||
2. **App Integration (`src/client/src/App.tsx`)**:
|
||||
- Added a new "Deck Tester" tab to the main navigation.
|
||||
- Uses the `Play` icon from lucide-react.
|
||||
|
||||
3. **GameRoom Enhancement (`src/client/src/modules/lobby/GameRoom.tsx`)**:
|
||||
- Added `initialGameState` prop to allow initializing the `GameView` immediately without waiting for a socket update (handling potential race conditions or state sync delays).
|
||||
|
||||
### Backend
|
||||
1. **Socket Event (`src/server/index.ts`)**:
|
||||
- Added `start_solo_test` event handler.
|
||||
- Creates a room with status `playing`.
|
||||
- Initializes a game instance.
|
||||
- Adds cards from the provided deck list to the game (library zone).
|
||||
- Emits `room_update` and `game_update` to the client.
|
||||
|
||||
## Outcome
|
||||
The user can now navigate to "Deck Tester", paste a deck list, and immediately enter the 3D Game View to test interactions on the battlefield. This reuses the entire Draft Game infrastructure, ensuring consistency.
|
||||
@@ -1,6 +0,0 @@
|
||||
Implemented CSV export for generated packs and cards.
|
||||
Implemented CSV copy to clipboard functionality.
|
||||
Implemented CSV import template download.
|
||||
Removed demo button and functionality from CubeManager.
|
||||
Updated CSV import template content.
|
||||
Refactored parsing logic to support complex CSV imports.
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
# CSV Import Robustness Update
|
||||
|
||||
## Background
|
||||
The user provided a specific CSV format associated with typical automated imports. The requirement was to extract relevant information (Quantity, Name, Finish, Scryfall ID) while ignoring other fields (such as Condition, Date Added, etc.).
|
||||
|
||||
## Changes
|
||||
- Refactored `src/client/src/services/CardParserService.ts` to implement dynamic header parsing.
|
||||
- The `parse` method now:
|
||||
- Detects if the first line is a CSV header containing "Quantity" and "Name".
|
||||
- Maps columns to indices based on the header.
|
||||
- Specifically looks for `Quantity`, `Name`, `Finish`, and `Scryfall ID` (checking common variations like 'scryfall_id', 'id', 'uuid').
|
||||
- Uses strictly mapped columns if a header is detected, ensuring other fields are ignored as requested.
|
||||
- Falls back gracefully to previous generic parsing logic if no matching header is found, preserving backward compatibility with Arena/MTGO exports and simple lists.
|
||||
|
||||
## Verification
|
||||
- Verified manually via a test script that the provided CSV content parses correctly into the `CardIdentifier` memory structure.
|
||||
- The extraction correctly identifies Quantity, Name, Finish (Normal/Foil), and Scryfall UUID.
|
||||
|
||||
## Next Steps
|
||||
- Ensure the frontend `CubeManager` works seamlessly with this update (no changes needed there as it uses the service).
|
||||
@@ -1,17 +0,0 @@
|
||||
# 2025-12-16 - Draft Rules and Logic Implementation
|
||||
|
||||
## Draft Minimum Players
|
||||
- Added backend check in `index.ts` to prevent drafting with fewer than 4 players.
|
||||
- Emit `draft_error` to room if condition is not met.
|
||||
- Added `draft_error` listener in `GameRoom.tsx` to notify users.
|
||||
|
||||
## 4-Player Draft Rules (Pick 2)
|
||||
- Modified `DraftManager.ts`:
|
||||
- Added `pickedInCurrentStep` to track picks within a single pack pass cycle.
|
||||
- Implemented logic in `pickCard`:
|
||||
- If 4 players: Require 2 picks before passing pack.
|
||||
- Else: Require 1 pick.
|
||||
- Logic handles pack exhaustion (if pack runs out before picks completed, it passes).
|
||||
|
||||
## Robustness
|
||||
- Updated `rejoin_room` handler in `index.ts` to send the current `draft` state if the room is in `drafting` status. This allows users to refresh and stay in the draft flow (critical for multi-pick scenarios).
|
||||
@@ -1,14 +0,0 @@
|
||||
# Fix Socket Mixed Content Error
|
||||
|
||||
## Objective
|
||||
Resolve the "Mixed Content" error preventing the Online Lobby and Deck Tester from functioning in the production Kubernetes environment. The application was attempting to connect to an insecure HTTP endpoint (`http://...:3000`) from a secure HTTPS page.
|
||||
|
||||
## Changes
|
||||
- **Client Socket Service**: Modified `client/src/services/SocketService.ts` to make the connection URL environment-aware.
|
||||
- In **Production**: The URL is now `undefined`, allowing Socket.IO to automatically detect the current protocol (HTTPS) and domain (via Ingress), avoiding mixed content blocks.
|
||||
- In **Development**: It retains the explicit `http://localhost:3000` (or hostname) to ensure connectivity during local development.
|
||||
- **TypeScript Config**: Added a reference directive `/// <reference types="vite/client" />` to `SocketService.ts` to ensure `import.meta.env` is correctly typed during the build.
|
||||
|
||||
## Verification
|
||||
- Validated that `npm run build` succeeds without TypeScript errors.
|
||||
- Confirmed that the fix aligns with standard Vite + Socket.IO production deployment patterns.
|
||||
@@ -1,13 +0,0 @@
|
||||
# 2025-12-16 - Fix Pack Duplication in Draft
|
||||
|
||||
## Problem
|
||||
Users reported behavior consistent with "opening the same pack twice". This occurs when the pack objects distributed to players share the same memory reference. If the input source (e.g., from Frontend Generator) contains duplicate references (e.g., created via `Array.fill(pack)`), picking a card from "one" pack would seemingly remove it from "another" pack in a future round, or valid packs would re-appear.
|
||||
|
||||
## Solution
|
||||
- Modified `DraftManager.createDraft` to enforce Strict Isolation of pack instances.
|
||||
- Implemented **Deep Cloning**: Even if the input array contains shared references, we now map over `allPacks`, spreading the pack object and mapping the cards array to new objects.
|
||||
- **Unique IDs**: Re-assigned a unique internal ID to every single pack (format: `draft-pack-{index}-{random}`) to guarantee that every pack in the system is distinct, regardless of the quality of the input data.
|
||||
|
||||
## Impact
|
||||
- Ensures that every "pack" opened in the draft is an independent entity.
|
||||
- Prevents state leakage between rounds or players.
|
||||
@@ -1,21 +0,0 @@
|
||||
# 2025-12-16 - Reconnection and Auto-Pick
|
||||
|
||||
## Reconnection Logic
|
||||
- Use `localStorage.setItem('active_room_id', roomId)` in `LobbyManager` to persist connection state.
|
||||
- Upon page load, if a saved room ID exists, attempted to automatically reconnect via `rejoin_room` socket event.
|
||||
- Updated `socket.on('join_room')` and `rejoin_room` on the server to update the player's socket ID mapping, canceling any pending "disconnect" timers.
|
||||
|
||||
## Disconnection Handling
|
||||
- Updated `RoomManager` to track `socketId` and `isOffline` status for each player.
|
||||
- In `index.ts`, `socket.on('disconnect')`:
|
||||
- Marks player as offline.
|
||||
- Starts a **30-second timer**.
|
||||
- If timer expires (user did not reconnect):
|
||||
- Triggers `draftManager.autoPick(roomId, playerId)`.
|
||||
- `autoPick` selects a random card from the active pack to unblock the draft flow.
|
||||
|
||||
## Auto-Pick Implementation
|
||||
- Added `autoPick` to `DraftManager`:
|
||||
- Checks if player has an active pack.
|
||||
- Selects random index.
|
||||
- Calls `pickCard` internally to process the pick (add to pool, pass pack, etc.).
|
||||
@@ -1,23 +0,0 @@
|
||||
# Draft Interface UI Polish
|
||||
|
||||
## Status
|
||||
- [x] Analyze current UI issues (bottom border, scrolling).
|
||||
- [x] Remove global padding from `App.tsx`.
|
||||
- [x] Refactor `DraftView.tsx` for a cleaner, game-like experience.
|
||||
- [x] Implement immersive 3D effects and tray-style pool view.
|
||||
|
||||
## Context
|
||||
The user requested to improve the draft card pick interface. Specifically to remove the ugly bottom border, avoid page scrolls, and make it feel more like a game.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### `src/client/src/App.tsx`
|
||||
- Removed `pb-20` from the main container to allow full-screen layouts without forced scrolling at the bottom.
|
||||
|
||||
### `src/client/src/modules/draft/DraftView.tsx`
|
||||
- **Layout**: Changed to relative positioning with `overflow-hidden` to contain all elements within the viewport.
|
||||
- **Visuals**:
|
||||
- Added a radial gradient background overlay.
|
||||
- Redesigned the "Current Pack" area with `[perspective:1000px]` and 3D hover transforms.
|
||||
- Redesigned the "Your Pool" bottom area to be a "tray" with `backdrop-blur`, gradient background, and removed the boxy border.
|
||||
- **Scrollbars**: Hidden scrollbars in the main pack view for a cleaner look (`[&::-webkit-scrollbar]:hidden`).
|
||||
@@ -1,34 +0,0 @@
|
||||
# Resizable Draft Interface
|
||||
|
||||
## Status
|
||||
- [x] Implement resizable bottom "Pool" panel.
|
||||
- [x] Implement resizable card size slider.
|
||||
- [x] Persist settings to `localStorage`.
|
||||
|
||||
## Technical Plan
|
||||
|
||||
### `src/client/src/modules/draft/DraftView.tsx`
|
||||
|
||||
1. **State Initialization**:
|
||||
- `poolHeight`: number (default ~220). Load from `localStorage.getItem('draft_poolHeight')`.
|
||||
- `cardScale`: number (default 1 or specific width like 224px). Load from `localStorage.getItem('draft_cardScale')`.
|
||||
|
||||
2. **Resize Handle**:
|
||||
- Insert a `div` cursor-row-resize between the Main Area and the Bottom Area.
|
||||
- Implement `onMouseDown` handler to start dragging.
|
||||
- Implement `onMouseMove` and `onMouseUp` on the window/document to handle the resize logic.
|
||||
|
||||
3. **Card Size Control**:
|
||||
- Add a slider (`<input type="range" />`) in the Top Header area to adjust `cardScale`.
|
||||
- Apply this scale to the card images/containers in the Main Area.
|
||||
|
||||
4. **Persistence**:
|
||||
- `useEffect` hooks to save state changes to `localStorage`.
|
||||
|
||||
5. **Refactoring Styling**:
|
||||
- Change `h-[220px]` class on the bottom panel to `style={{ height: poolHeight }}`.
|
||||
- Update card width class `w-56` to dynamic style or class based on scale.
|
||||
|
||||
## UX Improvements
|
||||
- Add limit constraints (min height for pool, max height for pool).
|
||||
- Add limit constraints for card size (min visible, max huge).
|
||||
@@ -1,13 +0,0 @@
|
||||
# Card Zoom on Hover
|
||||
|
||||
## Status
|
||||
- [x] Add `hoveredCard` state to `DraftView`.
|
||||
- [x] Implement `onMouseEnter`/`onMouseLeave` handlers for cards in both Pick and Pool areas.
|
||||
- [x] rendering a fixed, high z-index preview of the hovered card.
|
||||
- [x] Disable right-click context menu on Draft interface.
|
||||
|
||||
## Implementation Details
|
||||
- **File**: `src/client/src/modules/draft/DraftView.tsx`
|
||||
- **Zoom Component**: A fixed `div` containing the large card image.
|
||||
- **Position**: Fixed to the left or right side of the screen (e.g., `left-10 top-1/2 -translate-y-1/2`) to avoid covering the grid being interacted with (which is usually centered).
|
||||
- **Styling**: Large size (e.g., `w-80` or `h-[500px]`), shadow, border, rounded corners.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Card Zoom on Hover - Dedicated Zone
|
||||
|
||||
## Status
|
||||
- [x] Add `hoveredCard` state to `DraftView` (Already done).
|
||||
- [x] Implement `onMouseEnter`/`onMouseLeave` handlers (Already done).
|
||||
- [x] Refactor `DraftView` layout to include a dedicated sidebar for card preview.
|
||||
- [x] Move the zoomed card image into this dedicated zone instead of a fixed overlay.
|
||||
|
||||
## Implementation Details
|
||||
- **File**: `src/client/src/modules/draft/DraftView.tsx`
|
||||
- **Layout Change**:
|
||||
- Wrap the central card selection area in a `flex-row` container.
|
||||
- Add a Left Sidebar (e.g., `w-80`) reserved for the zoomed card.
|
||||
- Ensure the main card grid takes up the remaining space (`flex-1`).
|
||||
- **Behavior**: When no card is hovered, the sidebar can show a placeholder or remain empty/decorative.
|
||||
@@ -1,22 +0,0 @@
|
||||
# Host Disconnect Pause Logic
|
||||
|
||||
## Objective
|
||||
Ensure the game pauses for all players when the Host disconnects, preventing auto-pick logic from advancing the game state. enable players to leave cleanly.
|
||||
|
||||
## Changes
|
||||
1. **Server (`src/server/index.ts`)**:
|
||||
* Refactored socket handlers.
|
||||
* Implemented `startAutoPickTimer` / `stopAllRoomTimers` helpers.
|
||||
* Updated `disconnect` handler: Checks if disconnected player is passed host. If true, pauses game (stops all timers).
|
||||
* Updated `join_room` / `rejoin_room`: Resumes game (restarts timers) if Host reconnects.
|
||||
* Added `leave_room` event handler to properly remove players from room state.
|
||||
|
||||
2. **Frontend (`src/client/src/modules/lobby/LobbyManager.tsx`)**:
|
||||
* Updated `handleExitRoom` to emit `leave_room` event, preventing "ghost" connections.
|
||||
|
||||
3. **Frontend (`src/client/src/modules/lobby/GameRoom.tsx`)**:
|
||||
* Fixed build error (unused variable `setGameState`) by adding `game_update` listener.
|
||||
* Verified "Game Paused" overlay logic exists and works with the new server state (`isHostOffline`).
|
||||
|
||||
## Result
|
||||
Host disconnection now effectively pauses the draft flow. Reconnection resumes it. Players can leave safely.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Anti-Tampering Implementation
|
||||
|
||||
## Objective
|
||||
Implement a robust anti-tampering system to prevent players (including the host) from manipulating the game state via malicious client-side emissions.
|
||||
|
||||
## Changes
|
||||
1. **Server (`src/server/managers/RoomManager.ts`)**:
|
||||
* Added `getPlayerBySocket(socketId)` to securely identify the player associated with a connection, eliminating reliance on client-provided IDs.
|
||||
|
||||
2. **Server (`src/server/index.ts`)**:
|
||||
* Refactored all major socket event listeners (`pick_card`, `game_action`, `start_draft`, `player_ready`) to use `roomManager.getPlayerBySocket(socket.id)`.
|
||||
* The server now ignores `playerId` and `roomId` sent in the payload (where applicable) and uses the trusted session context instead.
|
||||
* This ensures that a user can only perform actions for *themselves* in the room they are *actually connected to*.
|
||||
|
||||
3. **Server (`src/server/managers/GameManager.ts`)**:
|
||||
* Updated `handleAction` to accept an authentic `actorId`.
|
||||
* Added ownership/controller checks to sensitive actions:
|
||||
* `moveCard`: Only the controller can move a card.
|
||||
* `updateLife`: Only the player can update their own life.
|
||||
* `drawCard`, `createToken`, etc.: Validated against `actorId`.
|
||||
|
||||
4. **Frontend (`GameView.tsx`, `DraftView.tsx`, `DeckBuilderView.tsx`)**:
|
||||
* Cleaned up socket emissions to stop sending redundant `roomId` and `playerId` fields, aligning client behavior with the new secure server expectations (though server would safely ignore them anyway).
|
||||
|
||||
## Result
|
||||
The system is now significantly more resistant to session hijacking or spoofing. Users cannot act as other players or manipulate game state objects they do not control, even if they manually emit socket events from the console.
|
||||
@@ -1,12 +0,0 @@
|
||||
# Fix Draft UI Layout Consistency
|
||||
|
||||
## Objective
|
||||
Fix the layout inconsistency where the "Waiting for next pack..." screen and other views in the Draft interface do not fully occupy the screen width, causing the UI to look collapsed or disconnected from the global sidebars.
|
||||
|
||||
## Changes
|
||||
1. **DraftView.tsx**: Added `flex-1` and `w-full` to the root container. This ensures the component expands to fill the available space in the `GameRoom` flex container, maintaining the full-screen layout even when content (like the "waiting" message) is minimal.
|
||||
2. **DeckBuilderView.tsx**: Added `flex-1` and `w-full` to the root container for consistency and to ensure the deck builder also behaves correctly within the main layout.
|
||||
|
||||
## Verification
|
||||
- The `DraftView` should now stretch to fill the area between the left edge (or internal Zoom sidebar) and the right Lobby/Chat sidebar in `GameRoom`.
|
||||
- The "Waiting for next pack..." message will remain centered within this full-height, full-width area, with the background gradient covering the entire zone.
|
||||
@@ -1,38 +0,0 @@
|
||||
# implementation_plan - Draft Session Persistence and Restoration
|
||||
|
||||
This plan addresses the issue where users are unable to reliably rejoin a draft session as a player after reloading or exiting, often re-entering as a spectator. It ensures robust session synchronization to local storage and handles player "leave" actions safely during active games.
|
||||
|
||||
## User Objectives
|
||||
- **Session Restoring**: Automatically rejoin the correct session and player seat upon reloading the application.
|
||||
- **Prevent Accidental Data Loss**: Ensure "Exiting" a room during an active draft does not destroy the player's seat, allowing them to rejoin.
|
||||
- **Start New Draft**: Maintain the ability for a user to explicitly invalid/abandon an old session to start a new one (handled by creating a new room, which overwrites local storage).
|
||||
|
||||
## Proposed Changes
|
||||
|
||||
### 1. Server-Side: Safer `leaveRoom` Logic
|
||||
**File**: `src/server/managers/RoomManager.ts`
|
||||
- Modify `leaveRoom` method.
|
||||
- **Logic**:
|
||||
- If `room.status` is `'waiting'`, remove the player (current behavior).
|
||||
- If `room.status` is `'drafting'`, `'deck_building'`, or `'playing'`, **DO NOT** remove the player from `room.players`. Instead, mark them as `isOffline = true` (similar to a disconnect).
|
||||
- This ensures that if the user rejoins with the same `playerId`, they find their existing seat instead of being assigned a new "spectator" role.
|
||||
|
||||
### 2. Server-Side: Robust `rejoin_room` Handler
|
||||
**File**: `src/server/index.ts`
|
||||
- Update `socket.on('rejoin_room')`.
|
||||
- **Change**: Implement an acknowledgement `callback` pattern consistent with other socket events.
|
||||
- **Logic**:
|
||||
- Accept `{ roomId, playerId }`.
|
||||
- If successful, invoke `callback({ success: true, room, draftState })`.
|
||||
- Broadcast `room_update` to other players (to show user is back online).
|
||||
|
||||
### 3. Client-Side: Correct Rejoin Implementation
|
||||
**File**: `src/client/src/modules/lobby/LobbyManager.tsx`
|
||||
- **Fix**: In the `rejoin_room` emit call, explicitly include the `playerId`.
|
||||
- **Enhancement**: Utilize the callback from the server to confirm reconnection before setting state.
|
||||
- **Exit Handling**: The `handleExitRoom` function clears `localStorage`, which is correct for an explicit "Exit". However, thanks to the server-side change, if the user manually rejoins the same room code, they will reclaim their seat effectively.
|
||||
|
||||
## Verification Plan
|
||||
1. **Test Reload**: Start a draft, refresh the browser. Verify user auto-rejoins as Player.
|
||||
2. **Test Exit & Rejoin**: Start a draft, click "Exit Room". Re-enter the Room ID manually. Verify user rejoins as Player (not Spectator).
|
||||
3. **Test New Draft**: Create a room, start draft. Open new tab (or exit), create NEW room. Verify new room works and old session doesn't interfere.
|
||||
@@ -1,46 +0,0 @@
|
||||
# implementation_plan - Lobby Improvements and Kick Functionality
|
||||
|
||||
This plan addresses user feedback regarding the draft resumption experience, exit button placement, and host management controls.
|
||||
|
||||
## User Objectives
|
||||
1. **Resume Draft on Re-entry**: Ensure that manually joining a room (after exiting) correctly restores the draft view if a draft is in progress.
|
||||
2. **Exit Button Placement**: Move the "Exit Room" button to be near the player's name in the lobby sidebar.
|
||||
3. **Kick Player**: Allow the Host to kick players from the room.
|
||||
|
||||
## Proposed Changes
|
||||
|
||||
### 1. Server-Side: Kick Functionality
|
||||
**File**: `src/server/managers/RoomManager.ts`
|
||||
- **Method**: `kickPlayer(roomId, playerId)`
|
||||
- **Logic**:
|
||||
- Remove the player from `room.players`.
|
||||
- If the game is active (drafting/playing), this is a destructive action. We will assume for now it removes them completely (or marks offline? "Kick" usually implies removal).
|
||||
- *Decision*: If kicked, they are removed. If the game breaks, that's the host's responsibility.
|
||||
|
||||
**File**: `src/server/index.ts`
|
||||
- **Event**: `kick_player`
|
||||
- **Logic**:
|
||||
- Verify requester is Host.
|
||||
- Call `roomManager.kickPlayer`.
|
||||
- Broadcast `room_update`.
|
||||
- Emit `kicked` event to the target socket (to force them to client-side exit).
|
||||
|
||||
### 2. Client-Side: Re-entry Logic Fix
|
||||
**File**: `src/client/src/modules/lobby/GameRoom.tsx`
|
||||
- **Logic**: Ensure `GameRoom` correctly initializes or updates `draftState` when receiving new props.
|
||||
- Add a `useEffect` to update local `draftState` if `initialDraftState` prop changes (though `key` change on component might be better, we'll use `useEffect`).
|
||||
|
||||
### 3. Client-Side: UI Updates
|
||||
**File**: `src/client/src/modules/lobby/GameRoom.tsx`
|
||||
- **Sidebar**:
|
||||
- Update the player list rendering.
|
||||
- If `p.id === currentPlayerId`, show an **Exit/LogOut** button next to the name.
|
||||
- If `isMeHost` and `p.id !== me`, show a **Kick/Ban** button next to the name.
|
||||
- **Handlers**:
|
||||
- `handleKick(targetId)`: Warning confirmation -> Emit `kick_player`.
|
||||
- `handleExit()`: Trigger the existing `onExit`.
|
||||
|
||||
## Verification Plan
|
||||
1. **Test Kick**: Host kicks a player. Player should be removed from list and client should revert to lobby (via socket event).
|
||||
2. **Test Exit**: Click new Exit button in sidebar. Should leave room.
|
||||
3. **Test Re-join**: Join the room code again. Should immediately load the Draft View (not the Lobby View).
|
||||
@@ -1,32 +0,0 @@
|
||||
# 2025-12-16 - Draft Timer Enforcement
|
||||
|
||||
## Status
|
||||
Completed
|
||||
|
||||
## Description
|
||||
Implemented server-side timer enforcement for the draft phase to ensure the game progresses even if players are AFK or disconnected.
|
||||
|
||||
## Changes
|
||||
1. **Server: DraftManager.ts**
|
||||
* Updated `DraftState` to include `pickExpiresAt` (timestamp) for each player and `isPaused` for the draft.
|
||||
* Initialize `pickExpiresAt` to 60 seconds from now when a player receives a pack (initial or passed).
|
||||
* Implemented `checkTimers()` method to iterate over all active drafts and players. If `Date.now() > pickExpiresAt`, it triggers `autoPick`.
|
||||
* Implemented `setPaused()` to handle host disconnects. When resuming, timers are reset to 60s to prevent immediate timeout.
|
||||
|
||||
2. **Server: index.ts**
|
||||
* Removed ad-hoc `playerTimers` map and individual `setTimeout` logic associated with socket disconnect events.
|
||||
* Added a global `setInterval` (1 second tick) that calls `draftManager.checkTimers()` and broadcasts updates.
|
||||
* Updated `disconnect` handler to pause the draft if the host disconnects (`draftManager.setPaused(..., true)`).
|
||||
* Updated `join_room` / `rejoin_room` handlers to resume the draft if the host reconnects.
|
||||
|
||||
3. **Client: DraftView.tsx**
|
||||
* Updated the timer display logic to calculate remaining time based on `draftState.players[id].pickExpiresAt` - `Date.now()`.
|
||||
* The timer now accurately reflects the server-enforced deadline.
|
||||
|
||||
## Behavior
|
||||
* **Drafting**: Each pick has a 60-second limit.
|
||||
* **Deck Building**: 120-second limit. If time runs out, the game forces start. Any unready players have their entire draft pool submitted as their deck automatically.
|
||||
* **Timeout**: If time runs out, a random card is automatically picked, and the next pack (if available) is loaded with a fresh 60s timer.
|
||||
* **AFK**: If a user is AFK, the system continues to auto-pick for them until the draft concludes.
|
||||
* **Host Disconnect**: If the host leaves, the draft pauses for everyone. Timer stops.
|
||||
* **Host Reconnect**: Draft resumes, and all active pick timers are reset to 60s.
|
||||
@@ -1,21 +0,0 @@
|
||||
# Plan: Enhance Card Metadata
|
||||
|
||||
## Objective
|
||||
Update Scryfall fetching and parsing logic to include comprehensive metadata for cards. This will enable more precise pack generation algorithms in the future (e.g., filtering by legality, format, artist, or specific frame effects).
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Update `ScryfallCard` Interface (`src/client/src/services/ScryfallService.ts`)**
|
||||
* Add fields for `legalities`, `finishes`, `games`, `produced_mana`, `artist`, `released_at`, `frame_effects`, `security_stamp`, `promo_types`.
|
||||
* Define a more robust `ScryfallCardFace` interface.
|
||||
|
||||
2. **Update `DraftCard` Interface (`src/client/src/services/PackGeneratorService.ts`)**
|
||||
* Add corresponding fields to the internal `DraftCard` interface to store this data in the application state.
|
||||
|
||||
3. **Update `PackGeneratorService.processCards`**
|
||||
* Map the new fields from `ScryfallCard` to `DraftCard` during the processing phase.
|
||||
* Ensure `cardFaces` are also mapped correctly if present (useful for Flip cards where we might want front/back info).
|
||||
|
||||
4. **Verification**
|
||||
* Build the project to ensure no type errors.
|
||||
* (Optional) Run a test script or verify in browser if possible, but static analysis should suffice for interface updates.
|
||||
@@ -1,30 +0,0 @@
|
||||
# Pack Generation Algorithm Update
|
||||
|
||||
## Objective
|
||||
Update the pack generation logic to match a new 15-slot "Play Booster" structure.
|
||||
The new structure includes:
|
||||
- **Slots 1-6:** Commons (Color Balanced).
|
||||
- **Slot 7:** Common (87%), List (C/U 10%, R/M 2%), or Special Guest (1%).
|
||||
- **Slots 8-10:** Uncommons (3).
|
||||
- **Slot 11:** Rare (7/8) or Mythic (1/8).
|
||||
- **Slot 12:** Basic Land or Common Dual Land (20% Foil).
|
||||
- **Slot 13:** Wildcard (Non-Foil) - Weighted Rarity.
|
||||
- **Slot 14:** Wildcard (Foil) - Weighted Rarity.
|
||||
- **Slot 15:** Marketing Token / Art Card.
|
||||
|
||||
## Implementation Details
|
||||
1. **Updated `PackGeneratorService.ts`**:
|
||||
- Modified `processedPools` to explicitly categorize `lands` (Basic + Common Dual) and `tokens`.
|
||||
- Updated `processCards` to sort cards into these new pools (instead of filtering them out completely).
|
||||
- Rewrote `buildSinglePack` (for `standard` rarity mode) to implement the 15-slot sequencing.
|
||||
- Implemented logic for:
|
||||
- Color balancing commons (naive attempt).
|
||||
- "The List" simulation (using Wildcard logic from pools).
|
||||
- Slots 13/14 Wildcards with weighted probabilities.
|
||||
- Foil application (cloning card and setting `finish`).
|
||||
- Slot 12 Land selection (preferring separate land pool).
|
||||
- Added interfaces for `typeLine` and `layout` to `DraftCard`.
|
||||
|
||||
## Status
|
||||
- Implemented and Verified via static check (TS linting was fixed).
|
||||
- Ready for testing in the client.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Card Metadata Enhancement
|
||||
|
||||
## Objective
|
||||
Enhance the Scryfall data fetching and internal card representation to include full metadata (CMC, Oracle Text, Power/Toughness, Collector Number, etc.). This allows strictly precise pack generation and potential future features like mana curve analysis or specific slot targeting.
|
||||
|
||||
## Changes
|
||||
1. **Updated `ScryfallService.ts`**:
|
||||
- Extended `ScryfallCard` interface to include:
|
||||
- `cmc` (number)
|
||||
- `mana_cost` (string)
|
||||
- `oracle_text` (string)
|
||||
- `power`, `toughness` (strings)
|
||||
- `collector_number` (string)
|
||||
- `color_identity` (string[])
|
||||
- `keywords` (string[])
|
||||
- `booster` (boolean)
|
||||
- `promo`, `reprint` (booleans)
|
||||
- Verified that `fetch` calls already return this data; TS interface update exposes it.
|
||||
|
||||
2. **Updated `PackGeneratorService.ts`**:
|
||||
- Extended `DraftCard` internal interface to include the same metadata fields (normalized names like `manaCost`, `oracleText`).
|
||||
- Updated `processCards` function to map these fields from the Scryfall response to the `DraftCard` object.
|
||||
|
||||
## Impact
|
||||
- Pack generation now has access to rich metadata.
|
||||
- Future-proofs the system for "The List" exact matching (via collector number or promo types) and game logic (CMC sorting).
|
||||
@@ -1,20 +0,0 @@
|
||||
# Peasant Algorithm Implementation
|
||||
|
||||
## Overview
|
||||
Implemented the detailed "Peasant" pack generation algorithm in `PackGeneratorService.ts`.
|
||||
|
||||
## Changes
|
||||
- Updated `buildSinglePack` in `PackGeneratorService.ts` to include specific logic for Peasant rarity mode.
|
||||
- Implemented slot-based generation:
|
||||
- Slots 1-6: Commons (Color Balanced)
|
||||
- Slot 7: Common or "The List" (Simulated)
|
||||
- Slots 8-11: Uncommons
|
||||
- Slot 12: Land (20% Foil)
|
||||
- Slot 13: Non-Foil Wildcard (Weighted by rarity)
|
||||
- Slot 14: Foil Wildcard (Weighted by rarity)
|
||||
- Slot 15: Marketing Token
|
||||
|
||||
## Notes
|
||||
- Used existing helper methods `drawColorBalanced` and `drawUniqueCards`.
|
||||
- Simulated "The List" logic using available Common/Uncommon pools as exact "The List" metadata might not be available in standard pools provided to the generator.
|
||||
- Wildcard weights follow the specification (~49% C, ~24% U, ~13% R, ~13% M).
|
||||
@@ -1,28 +0,0 @@
|
||||
# Plan: Persist Scryfall Metadata
|
||||
|
||||
## Objective
|
||||
Persist fetched Scryfall card metadata in the browser's IndexedDB. This ensures that:
|
||||
1. Metadata (including the newly added rich fields) is saved across sessions.
|
||||
2. Pack generation can rely on this data without re-fetching.
|
||||
3. The application works better offline or with poor connection after initial fetch.
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. **Create `src/client/src/utils/db.ts`**
|
||||
* Implement a lightweight IndexedDB wrapper.
|
||||
* Database Name: `mtg-draft-maker`
|
||||
* Store Name: `cards`
|
||||
* Methods: `putCard`, `getCard`, `getAllCards`, `bulkPut`.
|
||||
|
||||
2. **Update `ScryfallService.ts`**
|
||||
* Import the DB utilities.
|
||||
* In `constructor` or a new `initialize()` method, load all persisted cards into memory (`cacheById` and `cacheByName`).
|
||||
* In `fetchCollection`, `fetchSetCards`, etc., whenever cards are fetched from API, save them to DB via `bulkPut`.
|
||||
* Modify `fetchCollection` to check memory cache (which is now pre-filled from DB) before network.
|
||||
|
||||
3. **Refactor `fetchCollection` deduplication**
|
||||
* Since cache is pre-filled, the existing check `if (this.cacheById.has(...))` will effectively check the persisted data.
|
||||
|
||||
## Verification
|
||||
* Reload page -> Check if cards are loaded immediately without network requests (network tab).
|
||||
* Check Application -> Storage -> IndexedDB in browser devtools (mental check).
|
||||
@@ -1,14 +0,0 @@
|
||||
# Multi-Expansion Selection
|
||||
|
||||
## Objective
|
||||
Enhanced the "From Expansion" pack generation feature in the Cube Manager to allow users to select multiple expansions and use a searchable interface.
|
||||
|
||||
## Implementation Details
|
||||
1. **Searchable Interface**: Replaced the simple set dropdown with a dedicated set selection UI featuring a search input for fuzzy filtering by set name or code.
|
||||
2. **Multi-Select Capability**: Users can now check multiple sets from the filtered list.
|
||||
3. **Frontend State Refactor**: Migrated `selectedSet` (string) to `selectedSets` (string array) in `CubeManager.tsx`.
|
||||
4. **Fetch Logic Update**: Updated `fetchAndParse` to iterate through all selected sets, fetching card data for each sequentially and combining the results into the parse pool.
|
||||
5. **Generation Logic**: The existing `generateBoosterBox` logic now naturally consumes the combined pool of cards from multiple sets, effectively allowing for "Chaos Drafts" or custom mixed-set environments based on the user's selection.
|
||||
|
||||
## Status
|
||||
Completed. The Cube Manager UI now supports advanced set selection scenarios.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Game Type Filter for Expansion Selection
|
||||
|
||||
## Objective
|
||||
Add a filter to the "From Expansion" set selection to easily distinguish between Paper and Digital (MTGA/MTGO) sets.
|
||||
|
||||
## Implementation Details
|
||||
1. **ScryfallService Update**: Updated `ScryfallSet` interface to include the `digital` boolean property and mapped it in `fetchSets`.
|
||||
2. **CubeManager UI**: Added a toggle filter bar above the set list with three options:
|
||||
* **All**: Shows all sets.
|
||||
* **Paper**: Shows only sets where `digital` is false.
|
||||
* **Digital**: Shows only sets where `digital` is true.
|
||||
3. **Filter Logic**: Integrated the game type filter into the existing search filter logic in `CubeManager`.
|
||||
|
||||
## Status
|
||||
Completed. Users can now filter the expansion list by game type.
|
||||
@@ -1,20 +0,0 @@
|
||||
# Plan: Improve Parse Bulk Feedback
|
||||
|
||||
## Objective
|
||||
Enhance the "Parse Bulk" workflow in `CubeManager` to provide explicit feedback on the result of the Scryfall metadata fetching. This ensures the user knows that "images and metadata" have been successfully generated (fetched) for their list, fulfilling the request for precision.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Update `CubeManager.tsx`**
|
||||
* In `fetchAndParse` function:
|
||||
* Track `notFoundCount` (identifiers that returned no Scryfall data).
|
||||
* Track `successCount` (identifiers that were successfully enriched).
|
||||
* After the loop, check if `notFoundCount > 0`.
|
||||
* Show a summary notification/alert: "Processed X cards. Y cards could not be identified."
|
||||
* (Optional) If many failures, maybe show a list of names? For now, just the count is a good start.
|
||||
|
||||
2. **Verify Data Integrity**
|
||||
* Ensure that the `processedData` uses the fully enriched `DraftCard` objects (which we know it does from previous steps).
|
||||
|
||||
## Why This Matters
|
||||
The user asked to "Generate image and metadata... upon Parse bulk". While the backend/service logic is done, the UI needs to confirm this action took place to give the user confidence that the underlying algorithm now has the precise data it needs.
|
||||
@@ -1,13 +0,0 @@
|
||||
# Incremental Data Caching
|
||||
|
||||
## Objective
|
||||
Enable caching of card data to the server incrementally per set when multiple sets are selected, rather than sending a single massive payload at the end. avoiding `PayloadTooLargeError`.
|
||||
|
||||
## Implementation Details
|
||||
1. **Helper Function**: Created `cacheCardsToServer` helper within `fetchAndParse` to handle server communication for a chunk of cards.
|
||||
2. **Incremental Loop**: Modified the set fetching loop to call `cacheCardsToServer` immediately after receiving data for each set.
|
||||
3. **UI Feedback**: Updated progress text to clearly indicate when the system is "Caching [Set Name]..." to the server.
|
||||
4. **Error Handling**: Added try/catch within the caching helper to prevent a single cache failure from aborting the entire fetch process (logs error to console).
|
||||
|
||||
## Status
|
||||
Completed. Large multi-set fetches should now be robust against body size limits.
|
||||
@@ -1,20 +0,0 @@
|
||||
# Plan: Full Metadata Passthrough
|
||||
|
||||
## Objective
|
||||
Ensure that the `DraftCard` objects used throughout the application (and eventually sent to the backend) contain the **complete** original metadata from Scryfall. The user has explicitly requested access to "all cards informations" for future algorithms.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Update `ScryfallService.ts`**
|
||||
* Add an index signature `[key: string]: any;` to the `ScryfallCard` interface. This acknowledges that the object contains more fields than strictly typed, preventing TypeScript from complaining when accessing obscure fields, and correctly modeling the API response.
|
||||
|
||||
2. **Update `PackGeneratorService.ts`**
|
||||
* Add `sourceData: ScryfallCard;` (or similar name like `scryfallData`) to the `DraftCard` interface.
|
||||
* In `processCards`, assign the incoming `cardData` (the full Scryfall object) to this new property.
|
||||
|
||||
## Impact
|
||||
* **Data Size**: Payload size for rooms will increase, but this is acceptable (and requested) for the richness of data required.
|
||||
* **Flexibility**: Future updates to pack generation (e.g., checking specific `frame_effects` or `prices`) will not require interface updates; the data will already be there in `card.sourceData`.
|
||||
|
||||
## Verification
|
||||
* The valid "Parse Bulk" operation will now produce `DraftCard`s that, if inspected, contain the full Scryfall JSON.
|
||||
@@ -1,25 +0,0 @@
|
||||
# Plan: Server-Side Caching of Bulk Data
|
||||
|
||||
## Objective
|
||||
Implement server-side caching of both card images and metadata upon bulk parsing, ensuring the application relies on local assets rather than external Scryfall URLs.
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
1. **Refactor Server Architecture (`CardService.ts`)**
|
||||
* Update storage paths to `public/cards/images` (previously `public/cards`) and `public/cards/metadata`.
|
||||
* Implement `cacheMetadata` to save JSON files alongside images.
|
||||
|
||||
2. **Update API Endpoint (`index.ts`)**
|
||||
* Modify `POST /api/cards/cache` to handle metadata saving in addition to image downloading.
|
||||
* Update static file serving to map `/cards` to `public/cards`, making images accessible at `/cards/images/{id}.jpg`.
|
||||
|
||||
3. **Update Client Logic (`CubeManager.tsx`, `PackGeneratorService.ts`, `LobbyManager.tsx`)**
|
||||
* **Generation**: Pass a flag (`useLocalImages`) to the generator service.
|
||||
* **Url Construction**: Generator now produces URLs like `${origin}/cards/images/{id}.jpg` when the flag is set.
|
||||
* **Triggers**: `CubeManager` immediately sends parsed data to the server for caching before generating packs.
|
||||
* **Consistency**: `LobbyManager` updated to look for images in the new `/cards/images` path for multiplayer sessions.
|
||||
|
||||
## Impact
|
||||
* **Performance**: Initial "Parse Bulk" takes slightly longer (due to server cache call), but subsequent interactions are instant and local.
|
||||
* **Reliability**: Application works offline or without Scryfall after initial parse.
|
||||
* **Precision**: Metadata is now persisted as individual JSONs on the backend, ready for future complex backend algorithms.
|
||||
@@ -1,17 +0,0 @@
|
||||
# Server Graceful Shutdown Fix
|
||||
|
||||
## Context
|
||||
The user reported that the application process was not exiting clean (hanging for >5s) after pressing Ctrl+C. This indicated active handles (like intervals or open sockets) were preventing the Node.js process from terminating effectively.
|
||||
|
||||
## Changes
|
||||
Modified `src/server/index.ts` to implement a proper graceful shutdown mechanism:
|
||||
1. **Interval Management**: Captured the global draft timer `setInterval` ID into a variable `draftInterval`.
|
||||
2. **Shutdown Handler**: Created a `gracefulShutdown` function that:
|
||||
- Clears the `draftInterval`.
|
||||
- Closes the Socket.IO server (`io.close()`).
|
||||
- Closes the HTTP server (`httpServer.close()`), waiting for existing connections to close, then exits with code 0.
|
||||
- Sets a 10-second timeout to force exit with code 1 if connections don't close in time.
|
||||
3. **Signal Listeners**: Attached `gracefulShutdown` to `SIGINT` and `SIGTERM` events.
|
||||
|
||||
## Impact
|
||||
The server should now exit immediately and cleanly when stopped via the terminal, ensuring no zombie processes or port conflicts during development restarts.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Cube Manager Sticky Sidebar
|
||||
|
||||
## Objective
|
||||
Update the `CubeManager` layout to make the left-side settings/controls panel sticky. This allows the user to access controls (Generate, Reset, etc.) while scrolling through a long list of generated packs on the right.
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/modules/cube/CubeManager.tsx`:
|
||||
- Added `sticky top-4` to the left column wrapper.
|
||||
- Added `self-start` to ensure the sticky element doesn't stretch to the full height of the container (which would negate stickiness).
|
||||
- Added `max-h-[calc(100vh-2rem)]` and `overflow-y-auto` to the left panel to ensure its content remains accessible if it exceeds the viewport height.
|
||||
- Added `custom-scrollbar` styling for consistent aesthetics.
|
||||
|
||||
## Result
|
||||
The left panel now follows the user's scroll position, improving usability for large pack generations.
|
||||
@@ -1,12 +0,0 @@
|
||||
# Cube Manager Full Width Layout
|
||||
|
||||
## Objective
|
||||
Update the `CubeManager` layout to utilize the full width of the screen, removing the maximum width constraint. This allows for better utilization of screen real estate, especially on wider monitors, and provides more space for the pack grid.
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/modules/cube/CubeManager.tsx`:
|
||||
- Removed `max-w-7xl` and `mx-auto` classes from the main container.
|
||||
- Added `w-full` to ensure the container spans the entire available width.
|
||||
|
||||
## Result
|
||||
The Cube Manager interface now stretches to fill the viewport width, providing a more expansive view for managing packs and settings.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Cube Manager Archidekt View
|
||||
|
||||
## Objective
|
||||
Implement an "Archidekt-style" stacked view for pack generation. This view organizes cards into columns by type (Creature, Instant, Land, etc.) with vertical overlapping to save space while keeping headers visible.
|
||||
|
||||
## Changes
|
||||
1. **Refactor PackCard**: Extracted `FloatingPreview` and `CardHoverWrapper` into `src/client/src/components/CardPreview.tsx` to resolve circular dependencies and clean up `PackCard.tsx`.
|
||||
2. **Update StackView**:
|
||||
- Rewrite `StackView.tsx` to group cards by `typeLine` (categories: Creature, Planeswalker, Instant, Sorcery, Enchantment, Artifact, Land, Battle, Other).
|
||||
- Sort cards within categories by CMC.
|
||||
- Render columns using Flexbox.
|
||||
- Implement overlapping "card strip" look using negative `margin-bottom` on cards.
|
||||
- Value tuning: `margin-bottom: -125%` seems appropriate for a standard card aspect ratio to reveal the title bar.
|
||||
|
||||
## Result
|
||||
The "Stack" view option in Cube Manager now renders packs as organized, sorted columns similar to deck-builder interfaces.
|
||||
@@ -1,20 +0,0 @@
|
||||
# Cube Manager Mobile Improvements
|
||||
|
||||
## Objective
|
||||
Fix usability issues on mobile devices where UI elements were overlapping and "unusable".
|
||||
|
||||
## Changes
|
||||
1. **Sidebar Responsiveness**:
|
||||
- Modified `CubeManager.tsx` to apply `sticky` positioning to the left sidebar *only* on large screens (`lg:` prefix). On mobile, it is now standard static flow.
|
||||
- Limited `max-height` and `overflow-y-auto` only to `lg:` screens.
|
||||
|
||||
2. **Header Improvements**:
|
||||
- Updated the "Packs" sticky header in `CubeManager.tsx` to handle wrapping gracefully.
|
||||
- Added `backdrop-blur-xl` and slightly higher opacity to ensure content behind it doesn't bleed through visually (fixing "overlap" perception).
|
||||
- Enabled `overflow-x-auto` for the buttons container to prevent them from breaking out of the viewport on very narrow screens.
|
||||
|
||||
3. **Disable Heavy Hovers**:
|
||||
- Modified `CardPreview.tsx` to disable the `FloatingPreview` (large full-card hover) on devices with width < 1024px. This prevents the preview from sticking or covering the UI on touch devices.
|
||||
|
||||
## Result
|
||||
The UI should now be clean and usable on mobile, with no overlapping elements and a natural scroll flow.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Mobile Long-Press Card Preview
|
||||
|
||||
## Objective
|
||||
Enhance mobile usability by allowing users to view a magnified card preview upon long-pressing (500ms) a card, instead of hover (which is disabled on mobile).
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/components/CardPreview.tsx`:
|
||||
- Updated `CardHoverWrapper` to include `touchstart`, `touchend`, and `touchmove` handlers.
|
||||
- Implemented a 500ms timer on touch start.
|
||||
- Added logic to cancel the long-press if the user drags/scrolls more than 10 pixels.
|
||||
- Added `onContextMenu` handler to prevent the default browser menu when a long-press triggers the preview.
|
||||
- Updated render condition to show preview if `isHovering` (desktop) OR `isLongPressing` (mobile).
|
||||
|
||||
## Result
|
||||
On mobile devices, users can now press and hold on a card to see the full-size preview. Lifting the finger or scrolling hides the preview.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Mobile Fullscreen Preview
|
||||
|
||||
## Objective
|
||||
Update the mobile card preview mechanism to display a centered, fullscreen overlay upon long-press, rather than a floating element following the touch point. This provides a clearer view of the card on small screens.
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/components/CardPreview.tsx`:
|
||||
- Updated `FloatingPreview` interface to accept `isMobile: boolean`.
|
||||
- Added conditional rendering in `FloatingPreview`:
|
||||
- If `isMobile` is true, it renders a `fixed inset-0` overlay with a centered image, `backdrop-blur`, and entrance animations (`zoom-in` + `fade-in`).
|
||||
- If false (desktop), it retains the original cursor-following behavior.
|
||||
- Updated `CardHoverWrapper` to pass the `isMobile` state down to the preview component.
|
||||
- The preview automatically disappears (unmounts) when the long-press is released, effectively creating a "fade out/close" interaction (visually, the instant close is standard; entrance is animated).
|
||||
|
||||
## Result
|
||||
Long-pressing a card on mobile now brings up a high-quality, centered view of the card that dims the background, improving readability and usability.
|
||||
@@ -1,18 +0,0 @@
|
||||
# Mobile Preview Animations
|
||||
|
||||
## Objective
|
||||
Implement smooth "Phase In" and "Phase Out" animations for the mobile fullscreen card preview to replace the instant appear/disappear behavior.
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/components/CardPreview.tsx`:
|
||||
- Updated `CardHoverWrapper` to handle component unmounting with a delay (300ms) when the preview should be hidden on mobile.
|
||||
- Passed a new `isClosing` prop to `FloatingPreview` during this delay period.
|
||||
- In `FloatingPreview` (Mobile View):
|
||||
- Added `transition-all duration-300` base classes.
|
||||
- Used conditional classes:
|
||||
- Entrance: `animate-in fade-in zoom-in-95`
|
||||
- Exit: `animate-out fade-out zoom-out-95` (triggered when `isClosing` is true).
|
||||
- Fixed syntax errors introduced in previous steps (removed spaces in class names).
|
||||
|
||||
## Result
|
||||
On mobile, the card preview now fades and zooms in smoothly when long-pressed, and fades/zooms out smoothly when released.
|
||||
@@ -1,21 +0,0 @@
|
||||
# Mobile Preview Enhancements
|
||||
|
||||
## Objective
|
||||
Enhance the mobile card preview with sophisticated entrance/exit animations and a premium foil effect.
|
||||
|
||||
## Changes
|
||||
- **Refined Animations**:
|
||||
- **Entering**: Uses `scale-100 opacity-100 ease-out` to simulate the card smoothly arriving into view.
|
||||
- **Exiting**: Uses `scale-95 opacity-0 ease-in` to simulate the card receding and fading away.
|
||||
- The transition duration is set to 300ms for a fluid feel.
|
||||
|
||||
- **Foil Overlay**:
|
||||
- Added a multi-layered foil effect for cards with `isFoil=true`.
|
||||
- **Layer 1**: A moving pulse gradient (`bg-gradient-to-tr` with `via-white/20`) that simulates light catching the surface.
|
||||
- **Layer 2**: A static color-dodge gradient (`bg-gradient-to-br` with purple/pink/blue) to give the characteristic holographic tint.
|
||||
|
||||
- **Effect Implementation**:
|
||||
- The `FloatingPreview` component now orchestrates these classes based on the `isClosing` prop passed from the wrapper.
|
||||
|
||||
## Result
|
||||
The mobile experience now feels premium, with cards gracefully popping in and out, and foils displaying a distinctive animated sheen.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Enhanced Foil Effects
|
||||
|
||||
## Objective
|
||||
Make the foil effect on cards "more visible and cooler" by introducing a multi-layered holographic overlay.
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/components/CardPreview.tsx`:
|
||||
- Created a consistent `FoilOverlay` component used by both Mobile and Desktop previews.
|
||||
- **Layer 1 (Base Holo)**: Swapped previous subtle gradients for a vibrant `amber-300` / `fuchsia-400` / `cyan-400` gradient with `mix-blend-color-dodge`. Increased opacity to `100` (blended mode handles the transparency feel).
|
||||
- **Layer 2 (Shimmer)**: Added a diagonal white pulse (`animate-pulse`) with `mix-blend-overlay` to simulate catching the light.
|
||||
- **Layer 3 (Depth)**: Added a top-down `white-to-black` gradient with `mix-blend-soft-light` to simulate metallic curvature/surface depth.
|
||||
- Fixed linting errors from previous edits (syntax issues with spaces in class strings).
|
||||
|
||||
## Result
|
||||
Foil cards now possess a distinct, colorful, and metallic sheen that animates, making them stand out significantly more than standard cards.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Rolling Rainbow Foil Effect
|
||||
|
||||
## Objective
|
||||
Implement a "milder but more colorful" foil effect with a "rolling rainbow" animation and a mild white overlay, consistent across mobile and desktop.
|
||||
|
||||
## Changes
|
||||
- **CSS Animation**: Added `@keyframes bg-roll` and `.animate-bg-roll` utility in `main.css` to create a continuous background position scrolling effect.
|
||||
- **Enhanced Foil Overlay**: Updated `FoilOverlay` in `CardPreview.tsx`:
|
||||
- **Layer 1 (Rolling Rainbow)**: Uses a wide `gradient-to-r` spanning red -> blue -> red (`200% width`). It uses `animate-bg-roll` to scroll horizontally, simulating the color shifting of a foil card as it moves.
|
||||
- **Layer 2 (Light Glint)**: Retained a subtle diagonal `white/30` pulse (`mix-blend-overlay`) for dynamic lighting.
|
||||
- **Layer 3 (White Sheen)**: Added a static `white/10` overlay (`mix-blend-soft-light`) to provide the requested "mild white overlay" for a glossy finish.
|
||||
- **Lint Fix**: Cast `card.finish` to string to resolve TypeScript type overlap errors.
|
||||
|
||||
## Result
|
||||
Foil cards now exhibit a smooth, colorful, rolling rainbow reflection that looks premium and dynamic without being overly chaotic.
|
||||
@@ -1,13 +0,0 @@
|
||||
# General Card Visibility Boost
|
||||
|
||||
## Objective
|
||||
Add a mild white overlay to all magnified card previews (both mobile and desktop) to improve visibility against dark backgrounds, as requested.
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/components/CardPreview.tsx`:
|
||||
- Inserted a `<div className="absolute inset-0 bg-white/10 pointer-events-none mix-blend-overlay" />` into the `FloatingPreview` component.
|
||||
- This overlay is applied to **every** card, regardless of finish (Foil/Normal) or device type.
|
||||
- It sits immediately on top of the image but below the Foil effects, ensuring it brightens the base art without washing out the holographic details.
|
||||
|
||||
## Result
|
||||
All card previews now have slightly lifted blacks and increased brightness, making them "pop" more against the dark UI backdrops.
|
||||
@@ -1,13 +0,0 @@
|
||||
# Optimized Rolling Rainbow Foil
|
||||
|
||||
## Objective
|
||||
Update the foil effect to use a "continuous rainbow" and a "milder white background" as specifically requested.
|
||||
|
||||
## Changes
|
||||
- Modified `FoilOverlay` in `src/client/src/components/CardPreview.tsx`:
|
||||
- **Continuous Rainbow**: Updated the gradient to encompass the full spectrum (`red` -> `yellow` -> `green` -> `blue` -> `purple` -> `red`) over a linear gradient. This seamless loop ensures the rolling animation (`animate-bg-roll`) is smooth and continuously colorful.
|
||||
- **Milder White Background**: Removed the heavy soft-light and pulse layers. Replaced them with a very subtle `white/5` overlay using `mix-blend-overlay`. This brightens the foil slightly without washing out the colors.
|
||||
- **Color Dodge**: Applied `mix-blend-color-dodge` to the container to ensure the rainbow colors interact vibrantly with the underlying card art.
|
||||
|
||||
## Result
|
||||
Foil cards now feature a smooth, full-spectrum rainbow scrolling effect that feels high-quality and "magical," with a balanced brightness level.
|
||||
@@ -1,13 +0,0 @@
|
||||
# Metallic Foil Refinement
|
||||
|
||||
## Objective
|
||||
Adjust the foil effect to prioritize "visible shimmer" over "color tinting," preventing the effect from washing out the card's original colors.
|
||||
|
||||
## Changes
|
||||
- Modified `FoilOverlay` in `src/client/src/components/CardPreview.tsx`:
|
||||
- **Reduced Saturation**: Lowered the opacity of the rolling rainbow layer from `60` down to `30`. This keeps the dynamic color shift but makes it much more subtle, preventing it from overpowering the artwork.
|
||||
- **Increased Shimmer**: Added a strong `via-white/40` diagonal glare layer with `mix-blend-overlay` and `opacity-80`. This adds a bright, metallic "pop" that moves (`animate-pulse`) across the card, simulating high-gloss reflection.
|
||||
- **Screen Gloss**: Changed the top finish layer to `mix-blend-screen` with `white/5`. This adds a neutral brightness that lifts the metallic look without shifting the hue.
|
||||
|
||||
## Result
|
||||
The foil effect now looks like a highly reflective metallic surface (the "effect" is visible) rather than a colored filter, preserving the integrity of the original card art.
|
||||
@@ -1,17 +0,0 @@
|
||||
# Circular Foil Animation
|
||||
|
||||
## Objective
|
||||
Replace the linear "pulsing" glare with a "gaussian circular animation" to provide a smoother, rotating metallic sheen.
|
||||
|
||||
## Changes
|
||||
- **CSS Animation**: Added `.animate-spin-slow` in `main.css` to rotate elements over an 8-second loop.
|
||||
- **Foil Component** (`src/client/src/components/CardPreview.tsx`):
|
||||
- Removed the pulsing linear gradient.
|
||||
- Added a **rotating radial gradient**:
|
||||
- Positioned with `absolute inset-[-50%]` to create a canvas larger than the card.
|
||||
- Uses a white radial gradient (`rgba(255,255,255,0.5) 0% -> transparent 60%`) centered on this larger canvas.
|
||||
- The `animate-spin-slow` class rotates this entire large gradient layer around the center of the card.
|
||||
- This creates an effect where a soft "spotlight" or "sheen" continually drifts across the card surface in a circular pattern, simulating light moving over a holographic texture.
|
||||
|
||||
## Result
|
||||
The foil glare is now a soft, rotating circular highlight, giving a distinctly different and more sophisticated "gaussian" light play compared to the previous linear pulse.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Mild Foil Animation
|
||||
|
||||
## Objective
|
||||
Reduce the intensity of foil effects to make the static appearance identical to non-foil cards (as requested), while keeping the animation "mildly visible" rather than dominating.
|
||||
|
||||
## Changes
|
||||
- Modified `FoilOverlay` in `src/client/src/components/CardPreview.tsx`:
|
||||
- **Removed Static Gloss**: Deleted the `bg-white/5 mix-blend-screen` layer. This ensures the base brightness of foil cards matches the standard "Universal Gloss" shared with non-foils.
|
||||
- **Softened Circular Glare**:
|
||||
- Reduced the white intensity in the radial gradient from `0.5` to `0.25`.
|
||||
- Reduced the layer opacity from `80` to `25`.
|
||||
- This makes the rotating white sheen subtle and ghostly rather than a bright spotlight.
|
||||
- *Retained*: The low-opacity rolling rainbow layer (`opacity-30`) to provide the necessary color play.
|
||||
|
||||
## Result
|
||||
Foil cards now look cleaner and less washed out, matching the visual weight of normal cards, but possess a delicate, rotating shimmer that catches the eye without distracting from the art.
|
||||
@@ -1,20 +0,0 @@
|
||||
# Pro Foil Implementation
|
||||
|
||||
## Objective
|
||||
Implement a high-fidelity "Pro" foil effect using generic CSS techniques inspired by community "Holo" styles, creating a sophisticated rainbow and texture mapping.
|
||||
|
||||
## Changes
|
||||
- **CSS Class `foil-holo`**:
|
||||
- Added to `src/client/src/styles/main.css`.
|
||||
- This class builds a complex multi-layered background image stack:
|
||||
- **Layer 1**: Vertical Repeating Rainbow (`0deg` linear gradient).
|
||||
- **Layer 2**: Diagonal Texture (`133deg` repeating linear gradient with hard-light/hue stops).
|
||||
- Uses `background-blend-mode: screen, hue` to mix these layers dynamically.
|
||||
- Uses `mix-blend-mode: color-dodge` to composite onto the card image.
|
||||
- Includes a custom animation `foil-shift` (15s linear infinite) that shifts the background position vertically and diagonally, creating an "always active" shimmering effect.
|
||||
- **CardPreview Update**:
|
||||
- Updated `FoilOverlay` to use the `.foil-holo` class.
|
||||
- Retained the **Gaussian Circular Glare** (`radial-gradient` + `animate-spin-slow`) as a top-layer "spotlight" effect.
|
||||
|
||||
## Result
|
||||
The foil effect is now significantly more intricate, featuring vertical color bands and diagonal textures that shift over time, mimicking the look of high-end TCG holofoils (like "Secret Rares" or "Full Arts").
|
||||
@@ -1,18 +0,0 @@
|
||||
# Universal Preview Animations
|
||||
|
||||
## Objective
|
||||
Implement graceful appearing and disappearing animations for card previews on **all** screens (Desktop + Mobile), ensuring a polished feel uniform across the platform.
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/components/CardPreview.tsx`:
|
||||
- **CardHoverWrapper**: Updated the logic for `shouldShow` state management. Removed the `isMobile` restriction on the exit delay. Now, **all devices** respect the 300ms unmount timeout, giving the exit animation time to play before the component is removed from the DOM.
|
||||
- **FloatingPreview (Desktop Mode)**:
|
||||
- Added `transition-all duration-300` to the desktop container's inner div.
|
||||
- Applied dynamic classes based on `isClosing`:
|
||||
- **Entering**: `scale-100 opacity-100 ease-out`
|
||||
- **Exiting**: `scale-95 opacity-0 ease-in`
|
||||
- This effectively replicates the "pop-in / pop-out" animation that was previously mobile-only.
|
||||
- Fixed duplicated syntax errors introduced during the update logic.
|
||||
|
||||
## Result
|
||||
On desktop, hovering over a card now triggers a smooth scale-up phase-in. When the mouse leaves, the card preview shrinks slightly and fades out gracefully rather than disappearing instantly. This matches the mobile long-press behavior.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Mobile Touch Interaction Logic
|
||||
|
||||
## Objective
|
||||
Enhance mobile usability by modifying the long-press behavior. The preview should persist while the user moves their finger across the screen (e.g., to inspect different parts of the card or simply drift) and only disappear upon releasing the finger (`touchend`).
|
||||
|
||||
## Changes
|
||||
- Modified `src/client/src/components/CardPreview.tsx`:
|
||||
- Updated `handleTouchMove` in `CardHoverWrapper`.
|
||||
- Removed the `setIsLongPressing(false)` call inside the movement threshold check.
|
||||
- **Logic**:
|
||||
- If the user moves their finger *before* the 500ms long-press timer completes, the timer is cleared (canceling the preview), interpreting the action as a scroll.
|
||||
- If the user moves their finger *after* the preview has appeared (`isLongPressing` is true), the movement is ignored, and the preview **remains visible**.
|
||||
- The preview is now effectively closed only by `handleTouchEnd` (lifting the finger).
|
||||
|
||||
## Result
|
||||
This creates a much more forgiving and "tactile" experience on mobile, allowing users to hold their thumb on a card and shift it slightly without the preview abruptly vanishing.
|
||||
@@ -1,17 +0,0 @@
|
||||
# Entrance Animation Fix
|
||||
|
||||
## Objective
|
||||
Ensure the card preview plays the "scale-up fade-in" animation when it first appears (mounting), not just when disappearing.
|
||||
|
||||
## Changes
|
||||
- Modified `FloatingPreview` in `src/client/src/components/CardPreview.tsx`:
|
||||
- Introduced a generic `isMounted` state initialized to `false`.
|
||||
- Added a `useEffect` that sets `isMounted` to `true` on the next animation frame after mount.
|
||||
- Updated CSS logic to check a combined `isActive` state (`isMounted && !isClosing`).
|
||||
- **Logic**:
|
||||
- **Mount (0ms)**: `isActive` is false `->` `opacity-0 scale-95`.
|
||||
- **Next Frame (~16ms)**: `isMounted` becomes true `->` `isActive` becomes true `->` `transition-all` triggers to `opacity-100 scale-100`.
|
||||
- **Unmount Trigger**: `isClosing` becomes true `->` `isActive` becomes false `->` Transitions back to `opacity-0 scale-95`.
|
||||
|
||||
## Result
|
||||
The card preview now smoothly "pops in" from 95% scale and 0 opacity every time it is triggered, providing a consistent, high-quality feel to the UI interactions.
|
||||
@@ -1,13 +0,0 @@
|
||||
# Bug Fix: Unconditional Foil Overlay
|
||||
|
||||
## Objective
|
||||
Correct the issue where foil holographic animations were appearing on non-foil cards in the desktop view.
|
||||
|
||||
## Changes
|
||||
- Modified `FloatingPreview` in `src/client/src/components/CardPreview.tsx`:
|
||||
- In the desktop return block, the code manually embedding the foil effect divs (introduced in a previous step) was missing the `{isFoil && ...}` conditional wrapper.
|
||||
- Replaced the manual div insertion with the `<FoilOverlay />` component, which encapsulates the foil logic correctly.
|
||||
- Wrapped this component call in the `{isFoil && <FoilOverlay />}` check to ensure it only renders for cards with `foil` or `etched` finishes.
|
||||
|
||||
## Result
|
||||
Foil animations now strictly adhere to card metadata, appearing only on actual foil cards as intended. Normal cards display cleanly without any holographic overlay.
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
### Replaced Alert with Toast Notification
|
||||
|
||||
**Status**: Completed
|
||||
**Date**: 2025-12-17
|
||||
|
||||
**Description**
|
||||
Replaced the invasive `alert()` on the "Copy Pack" button with a non-intrusive Toast notification.
|
||||
|
||||
**Changes**
|
||||
1. Created `src/client/src/components/Toast.tsx` with a `ToastProvider` and `useToast` hook.
|
||||
2. Wrapped `App.tsx` with `ToastProvider`.
|
||||
3. Updated `PackCard.tsx` to use `showToast` instead of `alert`.
|
||||
|
||||
**Next Steps**
|
||||
- Consider replacing other alerts in `CubeManager` with Toasts for consistency.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Universal Foil Animation
|
||||
|
||||
## Objective
|
||||
Apply the high-fidelity foil animation to **all** card image instances, including the "Grid View" and "Stack View" thumbnails, not just the magnified hover preview.
|
||||
|
||||
## Changes
|
||||
- **CardPreview.tsx**: Exported the `FoilOverlay` component so it can be reused across the application.
|
||||
- **PackCard.tsx**:
|
||||
- Imported `FoilOverlay`.
|
||||
- Replaced the previous generic static foil gradient in `Grid View` with the `<FoilOverlay />` component.
|
||||
- **StackView.tsx**:
|
||||
- Imported `FoilOverlay`.
|
||||
- Replaced the simple opacity layer for foil cards with the `<FoilOverlay />` component.
|
||||
|
||||
## Result
|
||||
Now, whenever a foil card is displayed on the screen—whether as a thumbnail in a pack grid, a card in a stack pile, or a magnified preview—it consistently features the generic holographic animation and rotating glare effect.
|
||||
@@ -1,21 +0,0 @@
|
||||
|
||||
### Enhanced Toast Visibility
|
||||
|
||||
**Status**: Completed
|
||||
**Date**: 2025-12-17
|
||||
|
||||
**Description**
|
||||
Updated the Toast notification to be more prominent and centrally located, as per user feedback.
|
||||
|
||||
**Changes**
|
||||
1. **Position**: Moved from bottom-right to top-center (`top-6 left-1/2 -translate-x-1/2`).
|
||||
2. **Animation**: Changed to `slide-in-from-top-full` with a slight zoom-in effect.
|
||||
3. **Styling**:
|
||||
- Increased padding (`px-6 py-4`).
|
||||
- Increased border width (`border-2`) and brightness.
|
||||
- Added stronger shadows (`shadow-2xl`).
|
||||
- Increased contrast for text and background.
|
||||
- Increased font weight to `bold`.
|
||||
|
||||
**Effect**
|
||||
Notifications are now impossible to miss, appearing top-center with a premium, game-like alert style.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Intelligent Preview Suppression
|
||||
|
||||
## Objective
|
||||
Prevent the card preview popup from appearing when the user hovers over a card that is already displayed at a significantly large size on the screen (e.g., in a large grid view), reducing UI clutter.
|
||||
|
||||
## Changes
|
||||
- Modified `CardHoverWrapper` in `src/client/src/components/CardPreview.tsx`:
|
||||
- Updated `handleMouseEnter` to inspect the dimensions of the hovered element using `getBoundingClientRect`.
|
||||
- Implemented a threshold check: `Width > 240px` AND `Height > 300px`.
|
||||
- **Logic**:
|
||||
- **Large Grid Items**: If a card in the grid is rendered wider than 240px and taller than 300px, the hover preview is suppressed.
|
||||
- **List Items**: Even if a list row is wide (e.g., 800px), its height is small (e.g., 40px), so the preview **will still appear**.
|
||||
- **Small Thumbnails**: Small grid items or stack views usually fall below this threshold, ensuring the preview appears when needed.
|
||||
|
||||
## Result
|
||||
The system now intelligently hides the preview when it is redundant, creating a cleaner experience on large desktop screens while maintaining necessary functionality for smaller thumbnails and list views.
|
||||
@@ -1,18 +0,0 @@
|
||||
# Compact Card Layout
|
||||
|
||||
## Objective
|
||||
Slightly resize the card visualizations in both Grid and Stack views to allow more cards to fit on the screen, creating a denser and more "compact" interface as requested.
|
||||
|
||||
## Changes
|
||||
- **Pack Grid View** (`src/client/src/components/PackCard.tsx`):
|
||||
- Increased the column density across all breakpoints:
|
||||
- Base: `grid-cols-2` -> `grid-cols-3`
|
||||
- Small: `grid-cols-3` -> `grid-cols-4`
|
||||
- Medium: `grid-cols-4` -> `grid-cols-5`
|
||||
- Large: `grid-cols-5` -> `grid-cols-6`
|
||||
- This reduces the individual card width, making them visually smaller.
|
||||
- **Stack / Deck View** (`src/client/src/components/StackView.tsx`):
|
||||
- Reduced the fixed width of each stack column from `w-44` (176px) to `w-36` (144px).
|
||||
|
||||
## Result
|
||||
Cards appear slightly smaller ("a little more smaller"), providing a broader overview of the pool and deck without requiring as much scrolling. This works in tandem with the "Smart Preview Suppression" (which will likely now re-enable previews for these smaller cards, aiding readability).
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
### Unified Toast Design
|
||||
|
||||
**Status**: Completed
|
||||
**Date**: 2025-12-17
|
||||
|
||||
**Description**
|
||||
Refined the Toast notification design to align perfectly with the "Dark Gaming Aesthetic" of the platform.
|
||||
|
||||
**Changes**
|
||||
1. **Consistent Palette**: Switched to `bg-slate-800` (cards) with `border-slate-700` equivalents, using colored accents only for borders and icons.
|
||||
2. **Icon Enclosure**: Icons are now housed in a circular, semi-transparent colored background (`bg-emerald-500/10`) for a polished look.
|
||||
3. **Typography**: Reverted to standard font weights used in other UI cards (`font-medium`) for better readability and consistency.
|
||||
4. **Shadows**: Tuned shadows to be deep but subtle (`shadow-2xl shadow-emerald-900/20`), matching the ambient lighting of the app.
|
||||
|
||||
**Effect**
|
||||
The Toast now feels like a native part of the UI rather than a generic alert overlay.
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
### Animated Copy Button
|
||||
|
||||
**Status**: Completed
|
||||
**Date**: 2025-12-17
|
||||
|
||||
**Description**
|
||||
Replaced the toast notification for the copy action with a self-contained, animated button state.
|
||||
|
||||
**Changes**
|
||||
1. **Removed Toast Usage**: Detached `useToast` from `PackCard.tsx`.
|
||||
2. **Local State**: Implemented `copied` state in `PackCard`.
|
||||
3. **UI Feedback**:
|
||||
- Button transitions from "Copy" (slate) to "Check" (emerald/green) on click.
|
||||
- Added `animate-in zoom-in spin-in-12` for a satisfying "tick" animation.
|
||||
- Button background and border glow green to confirm success.
|
||||
- Auto-reverts after 2 seconds.
|
||||
|
||||
**Effect**
|
||||
Provides immediate, localized feedback for the copy action without clogging the global UI with notifications.
|
||||
@@ -1,19 +0,0 @@
|
||||
# View Scale Slider
|
||||
|
||||
## Objective
|
||||
Provide the user with granular control over card thumbnail sizes across the application, ensuring consistency between Grid and Stack views.
|
||||
|
||||
## Changes
|
||||
- **CubeManager**:
|
||||
- Added a new `cardWidth` state variable, persisted to `localStorage` (default `140px`).
|
||||
- Introduced a **Range Slider** in the top-right control toolbar (visible on desktop) allowing adjustment from 100px to 300px.
|
||||
- Passed `cardWidth` down to `PackCard`.
|
||||
- **PackCard (Grid View)**:
|
||||
- Replaced the responsive `grid-cols-*` logic with a `flex flex-wrap` layout.
|
||||
- Each card container now receives an explicit `style={{ width: cardWidth }}`.
|
||||
- **StackView (Stack View)**:
|
||||
- Accepted `cardWidth` prop.
|
||||
- Applied `style={{ width: cardWidth }}` to the column containers, dynamically ensuring that stacks resize in sync with the grid view setting.
|
||||
|
||||
## Result
|
||||
Users can now drag a slider to instantly resize all card thumbnails on the screen. This allows for customized density—make cards huge to admire the art, or tiny to see the entire cube/pool at a glance—with perfect size synchronization between the different view modes.
|
||||
@@ -1,18 +0,0 @@
|
||||
# Dynamic Art Cropping
|
||||
|
||||
## Objective
|
||||
Automatically switch card visualizations to "Full Art" (Art Crop) mode when the thumbnail size is reduced below a readability threshold, maximizing the visual impact of the artwork when text is too small to read.
|
||||
|
||||
## Changes
|
||||
- **Backend (Client & Server)**:
|
||||
- Updated `DraftCard` interface to include `imageArtCrop`.
|
||||
- Modified parsing services (`PackGeneratorService`) to extract and populate `imageArtCrop` from Scryfall data.
|
||||
- **Frontend (UI)**:
|
||||
- **PackCard (Grid View)**: Implemented a conditional check: if `cardWidth < 170px`, the image source switches to `imageArtCrop`.
|
||||
- **StackView (Deck/Collection)**: Applied the same logic.
|
||||
- **Visuals**:
|
||||
- The `object-cover` CSS property ensures the rectangular art crop fills the entire card frame, creating a "borderless/full-art" look.
|
||||
- The **Foil Overlay** and **Rarity Stripe** remain visible on top of the art crop, maintaining game state clarity.
|
||||
|
||||
## Result
|
||||
As you slide the size slider down, the cards seamlessly transform from standard cards (with borders and text) to vibrant, full-art thumbnails. This creates a stunning "mosaic" effect for the cube overview and deck stacks, solving the issue of illegible text at small scales.
|
||||
@@ -1,22 +0,0 @@
|
||||
# Bug Fix: Pack Generation Limits in From Expansion Mode
|
||||
|
||||
## Issue
|
||||
The user reported that when generating "1 Box" (36 packs) in "From Expansion" mode, only about 10 packs were generated.
|
||||
This was caused by the pack generation algorithm treating the card pool as finite (consuming cards as they are picked). Since Scryfall data usually provides a singleton list (1 copy of each card), the pool of Commons would deplete rapidly (e.g., 10 packs * 10 commons = 100 commons), halting generation when unique commons ran out.
|
||||
|
||||
## Solution
|
||||
Implemented a "Unlimited Pool" / "With Replacement" mode for pack generation.
|
||||
- **Server (`PackGeneratorService.ts`)**: Added `withReplacement` flag to `PackGenerationSettings`.
|
||||
- When enabled, the generator creates a FRESH copy of the shuffled pool for EACH pack.
|
||||
- This simulates a "Retail Draft" or "Print Run" scenario where packs are independent samples from a large supply, rather than drawing from a fixed, finite Cube.
|
||||
- Uniqueness is still enforced WITHIN each pack (no duplicate cards in the same pack).
|
||||
- **Client (`CubeManager.tsx`)**: updated the payload to strictly enable `withReplacement: true` whenever `sourceMode` is set to "From Expansion" ("set").
|
||||
|
||||
## Files Modified
|
||||
- `src/server/services/PackGeneratorService.ts`: Implemented replacement logic.
|
||||
- `src/client/src/modules/cube/CubeManager.tsx`: Updated API call payload.
|
||||
- `src/client/src/services/PackGeneratorService.ts`: Updated interface definitions.
|
||||
|
||||
## Status
|
||||
- [x] Fix Implemented
|
||||
- [x] Verified Logic
|
||||
@@ -1,17 +0,0 @@
|
||||
# Mobile Long Press Card Preview
|
||||
|
||||
## Status
|
||||
- [x] Research current implementation of `PackCard` and `CardPreview`
|
||||
- [x] Implement long-press detection in `PackCard` (Found existing implementation in `CardPreview`)
|
||||
- [x] Prevent default browser context menu on card images
|
||||
- [x] Trigger preview on long-press only for small screens (or generally if touch)
|
||||
- [x] Verify implementation
|
||||
|
||||
## Context
|
||||
User reported that long-pressing a card on mobile opens the browser menu (download image context menu).
|
||||
Goal: Long press should show the card preview instead.
|
||||
|
||||
## Implementation Details
|
||||
- Modified `CardHoverWrapper` in `CardPreview.tsx` to prevent `contextmenu` event default behavior on mobile devices when an image is present.
|
||||
- This ensures the custom long-press timer has time to trigger the preview without the system menu interfering.
|
||||
- Logic uses `isMobile && hasImage` to target specific scenario.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Mobile Card Size Slider
|
||||
|
||||
## Status
|
||||
- [x] Locate the card size slider component (`CubeManager` and `DraftView`)
|
||||
- [x] Analyze why it is hidden on small screens (`hidden` utility classes)
|
||||
- [x] Modify layout to ensure it is visible on mobile
|
||||
- [x] Determine if layout adjustments are needed (Reduced width on `DraftView`)
|
||||
- [x] Verify implementation (Code applied)
|
||||
|
||||
## Context
|
||||
User reported that the card size adjustment bar is missing on small screens.
|
||||
The fix was applied to both the Cube Manager (pack review) and Draft View (live drafting).
|
||||
|
||||
## Changes
|
||||
- **CubeManager.tsx**: Removed `hidden sm:flex` from the slider container. It is now always `flex`.
|
||||
- **DraftView.tsx**: Removed `hidden md:flex` and adjusted width to `w-24 md:w-32` to fit better on small screens.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Refined Preview Suppression
|
||||
|
||||
## Objective
|
||||
Tune the "Smart Preview Suppression" logic to better align with the Stack View's behavior. In Stack View, hovering a card causes it to "pop" to the front (`z-index` shift), making the card fully visible in-place. Because of this, showing a floating preview is redundant and distracting once the card is large enough to be read directly.
|
||||
|
||||
## Changes
|
||||
- Modified `handleMouseEnter` in `src/client/src/components/CardPreview.tsx`:
|
||||
- Lowered the suppression threshold from `>240x300` to `>200x270`.
|
||||
- **Logic**:
|
||||
- Cards sized via the slider to be larger than **200px** wide are now considered "readable" (especially since the 'Art Crop' mode turns off at 170px, leaving a range of 170-199 where preview is explicitly ON for text, and 200+ where it's suppressed).
|
||||
- This effectively disables the popup in Stack View for medium-to-large settings, relying on the native "pop-to-front" hover effect for inspection.
|
||||
|
||||
## Result
|
||||
A cleaner, less jittery drafting experience where large cards simply "lift up" for inspection, while smaller cards still get the helpful magnified popup.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user