v3.0.0: V3 features, server refactoring, and documentation overhaul

- Extract WebSocket handlers from main.py into handlers.py
- Add V3 feature docs (dealer rotation, dealing animation, round end reveal,
  column pair celebration, final turn urgency, opponent thinking, score tallying,
  card hover/selection, knock early drama, column pair indicator, swap animation
  improvements, draw source distinction, card value tooltips, active rules context,
  discard pile history, realistic card sounds)
- Add V3 refactoring docs (ai.py, main.py/game.py, misc improvements)
- Add installation guide with Docker, systemd, and nginx setup
- Add helper scripts (install.sh, dev-server.sh, docker-build.sh)
- Add animation flow diagrams documentation
- Add test files for handlers, rooms, and V3 features
- Add e2e test specs for V3 features
- Update README with complete project structure and current tech stack
- Update CLAUDE.md with full architecture tree and server layer descriptions
- Update .env.example to reflect PostgreSQL (remove SQLite references)
- Update .gitignore to exclude virtualenv files, .claude/, and .db files
- Remove tracked virtualenv files (bin/, lib64, pyvenv.cfg)
- Remove obsolete game_log.py (SQLite) and games.db

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-02-14 10:03:45 -05:00
parent 13ab5b9017
commit 9fc6b83bba
60 changed files with 11791 additions and 1639 deletions

View File

@@ -2,17 +2,26 @@
This document describes the unified animation system for the Golf card game client.
For detailed animation flow diagrams (what triggers what, in what order, with what flags), see [`docs/ANIMATION-FLOWS.md`](../docs/ANIMATION-FLOWS.md).
## Architecture
**All card animations use anime.js.** There are no CSS transitions on card elements.
**When to use anime.js vs CSS:**
- **Anime.js (CardAnimations)**: Card movements, flips, swaps, draws - anything involving card elements
- **CSS keyframes/transitions**: Simple UI feedback (button hover, badge entrance, status message fades) - non-card elements
**General rule:** If it moves a card, use anime.js. If it's UI chrome, CSS is fine.
| What | How |
|------|-----|
| Card movements | anime.js |
| Card flips | anime.js |
| Swap animations | anime.js |
| Pulse/glow effects | anime.js |
| Hover states | CSS `:hover` only |
| Pulse/glow effects on cards | anime.js |
| Button hover/active states | CSS transitions |
| Badge entrance/exit | CSS transitions |
| Status message fades | CSS transitions |
| Card hover states | anime.js `hoverIn()`/`hoverOut()` |
| Show/hide | CSS `.hidden` class only |
### Why anime.js?
@@ -130,6 +139,27 @@ cardAnimations.cleanup()
---
## Animation Coordination
### Server-Client Timing
Server CPU timing (in `server/ai.py` `CPU_TIMING`) must account for client animation durations:
- `post_draw_settle`: Must be >= draw animation duration (~1.1s for deck draw)
- `post_action_pause`: Must be >= swap/discard animation duration (~0.5s)
### Preventing Animation Overlap
Animation overlay cards are marked with `data-animating="true"` while active.
Methods like `animateUnifiedSwap` and `animateOpponentDiscard` check for active
animations and wait before starting new ones.
### Card Hover Initialization
Call `cardAnimations.initHoverListeners(container)` after dynamically creating cards.
This is done automatically in `renderGame()` for player and opponent card areas.
---
## Animation Overlay Pattern
For complex animations (flips, swaps), the system:
@@ -150,24 +180,34 @@ All timing values are in `timing-config.js` and exposed as `window.TIMING`.
### Key Durations
| Animation | Duration | Notes |
|-----------|----------|-------|
| Flip | 245ms | 3D rotateY animation |
| Deck lift | 63ms | Before moving to hold |
| Deck move | 105ms | To hold position |
| Discard lift | 25ms | Quick grab |
| Discard move | 76ms | To hold position |
| Swap pulse | 400ms | Scale + brightness |
| Turn shake | 400ms | Every 3 seconds |
All durations are configured in `timing-config.js` and read via `window.TIMING`.
| Animation | Duration | Config Key | Notes |
|-----------|----------|------------|-------|
| Flip | 320ms | `card.flip` | 3D rotateY with slight overshoot |
| Deck lift | 120ms | `draw.deckLift` | Visible lift before travel |
| Deck move | 250ms | `draw.deckMove` | Smooth travel to hold position |
| Deck flip | 320ms | `draw.deckFlip` | Reveal drawn card |
| Discard lift | 80ms | `draw.discardLift` | Quick decisive grab |
| Discard move | 200ms | `draw.discardMove` | Travel to hold position |
| Swap lift | 100ms | `swap.lift` | Pickup before arc travel |
| Swap arc | 320ms | `swap.arc` | Arc travel between positions |
| Swap settle | 100ms | `swap.settle` | Landing with gentle overshoot |
| Swap pulse | 400ms | — | Scale + brightness (face-up swap) |
| Turn shake | 400ms | — | Every 3 seconds |
### Easing Functions
Custom cubic bezier curves give cards natural weight and momentum:
```javascript
window.TIMING.anime.easing = {
flip: 'easeInOutQuad', // Smooth acceleration/deceleration
move: 'easeOutCubic', // Fast start, gentle settle
lift: 'easeOutQuad', // Quick lift
pulse: 'easeInOutSine', // Smooth oscillation
flip: 'cubicBezier(0.34, 1.2, 0.64, 1)', // Slight overshoot snap
move: 'cubicBezier(0.22, 0.68, 0.35, 1.0)', // Smooth deceleration
lift: 'cubicBezier(0.0, 0.0, 0.2, 1)', // Quick out, soft stop
settle: 'cubicBezier(0.34, 1.05, 0.64, 1)', // Tiny overshoot on landing
arc: 'cubicBezier(0.45, 0, 0.15, 1)', // Smooth S-curve for arcs
pulse: 'easeInOutSine', // Smooth oscillation (loops)
}
```
@@ -179,14 +219,19 @@ window.TIMING.anime.easing = {
- Static card appearance (colors, borders, sizing)
- Layout and positioning
- Hover states (`:hover` scale/shadow)
- Card hover states (`:hover` scale/shadow - no movement)
- Show/hide via `.hidden` class
- **UI chrome animations** (buttons, badges, status messages):
- Button hover/active transitions
- Badge entrance/exit animations
- Status message fade in/out
- Modal transitions
### What CSS Does NOT Do
### What CSS Does NOT Do (on card elements)
- No `transition` on any card element
- No `@keyframes` for card animations
- No `.flipped`, `.moving`, `.flipping` transition triggers
- No `transition` on any card element (`.card`, `.card-inner`, `.real-card`, `.swap-card`, `.held-card-floating`)
- No `@keyframes` for card movements or flips
- No `.flipped`, `.moving`, `.flipping` transition triggers for cards
### Important Classes
@@ -218,14 +263,15 @@ if (!this.isDrawAnimating && /* other conditions */) {
Use anime.js timelines for coordinated sequences:
```javascript
const T = window.TIMING;
const timeline = anime.timeline({
easing: 'easeOutQuad',
easing: T.anime.easing.move,
complete: () => { /* cleanup */ }
});
timeline.add({ targets: el, translateY: -15, duration: 100 });
timeline.add({ targets: el, left: x, top: y, duration: 200 });
timeline.add({ targets: inner, rotateY: 0, duration: 245 });
timeline.add({ targets: el, translateY: -15, duration: T.card.lift, easing: T.anime.easing.lift });
timeline.add({ targets: el, left: x, top: y, duration: T.card.move });
timeline.add({ targets: inner, rotateY: 0, duration: T.card.flip, easing: T.anime.easing.flip });
```
### Fire-and-Forget Animations