Files

118 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Multiplayer Tetris Battle Royale game with 2-8 player real-time battles via WebSocket. Players clear rows to send garbage to opponents; last player standing wins.
## Commands
```bash
# Install dependencies
cd server && npm install
# Start server
cd server && npm start
# or
cd server && node index.js
# Run tests
cd server && npm test
# Run tests with coverage
cd server && npm run test:coverage
# Server runs on http://localhost:3000 (or PORT env variable)
```
## Architecture
### Server-Client Model
- **Server** (`server/`): Express + Socket.io handles all game logic authoritatively
- `index.js`: Main server with socket.io event handlers
- `game-logic.js`: Pure game logic functions (testable, no socket dependencies)
- `__tests__/`: Jest test suite with 57 tests
- Manages single global lobby with `lobby.players` Map
- Game tick runs at 50ms intervals via `gameTick()`
- Broadcasts state updates to all connected clients via `broadcastState()`
- **Client** (`public/js/`): Vanilla JavaScript with module pattern
- `network.js`: Socket.io client wrapper, manages player/game state caching
- `game.js`: `TetrisGame` class for local game state (mirrors server)
- `renderer.js`: `TetrisRenderer` class - Canvas rendering for all player boards
- `ui.js`: `UIManager` class - screen transitions, DOM manipulation
- `app.js`: Entry point, ties modules together, game loop via `requestAnimationFrame`
### Game Flow
1. **Lobby**: Players join global lobby via `join-lobby` socket event
2. **Ready**: All players click READY; game starts when all ready (2-8 players)
3. **Game**: Server authoritative - client inputs sent via socket, server broadcasts state
4. **Elimination**: Player eliminated when piece locks at top or garbage fills board
5. **Victory**: Game ends when 1 active player remains
### Key Constants
- `BOARD_WIDTH = 10`, `BOARD_HEIGHT = 20`, `CELL_SIZE = 24`
- `LOBBY_ROOM = 'global-lobby'` - Socket.io room for all players
- Garbage rules: 2 lines cleared -> 1 garbage row, 3 -> 2, 4 (Tetris) -> 4 rows
### Piece Spawn & Negative Y Overflow
Pieces spawn at `y = 0` (top of visible board). The coordinate system allows **negative Y values** as intentional overflow space:
- **Negative Y allowed**: `isValidPosition()` only checks `newY >= BOARD_HEIGHT`, not `newY < 0`
- **Purpose**: When garbage is received, pieces are pushed up (y decreases) into negative territory
- **Player survival**: Player is NOT eliminated when piece goes negative — only when piece LOCKS with blocks in rows 0-1
- **Recovery**: Piece drops naturally back into visible area; player can continue playing
This design gives players a buffer zone to recover from garbage attacks before elimination.
### Socket Events
| Event | Direction | Payload |
|-------|-----------|---------|
| `join-lobby` | Client->Server | `{ playerName }` |
| `ready` | Client->Server | - |
| `player-move` | Client->Server | `{ playerId, direction }` |
| `player-rotate` | Client->Server | `{ playerId }` |
| `player-drop` | Client->Server | `{ playerId, hard }` |
| `player-hold` | Client->Server | `{ playerId }` |
| `zone-activate` | Client->Server | `{ playerId }` |
| `player-joined` | Server->Client | `{ player, players }` |
| `player-left` | Server->Client | `{ playerId, players }` |
| `game-started` | Server->Client | `{ players, states }` |
| `state-update` | Server->Client | `states[]` |
| `game-over` | Server->Client | `{ states }` |
### Zone Mechanic (Tetris Effect)
Zone is a time-based attack mechanic imported from Tetris Effect. When players clear lines, their **Zone meter** fills:
- **1 line** = +15 Zone meter
- **2 lines** = +25 Zone meter
- **3 lines** = +35 Zone meter
- **4 lines (Tetris)** = +50 Zone meter
**When Zone meter reaches 100:**
- Press **Z key** or **ZONE button** to activate Zone
- Gravity pauses for **20 seconds**
- Players control pieces manually but gravity is frozen
- Cleared lines **cascade to bottom** instead of disappearing
- After 20 seconds, Zone ends and sends garbage to opponents
**Zone Attack Calculation:**
- Lines cleared during Zone × 1.5 = garbage rows sent
- Example: 4 Zone lines = 6 garbage rows, 8 Zone lines = 12 garbage rows
- Garbage is distributed randomly among remaining opponents
Zone provides a high-risk, high-reward tactic for eliminating opponents with massive attacks.
### Rendering
- Each player gets a dynamically created board with canvas + info divs
- `renderer.setActivePlayer()` marks current player's board as `.main`, others as `.spectator`
- Zone meter shown below each player board with fill bar and status indicator
- Battle grid layout classes: `.single-player`, `.two-players`, `.multi-player`