Skip to main content

F045 — Deal Animation

FieldValue
IDF045
Phase3 — UI
JiraHEDGE-173
StatusTS ❌ · JS ⬜ · UMA ✅

Business Rule

When a deal is triggered (HOLE stage), each hole card flies out from the card shuffler sprite in the top-right corner of the game canvas and travels to its assigned position within the relevant hand panel. Cards are launched in sequence with staggered delays so each one is visually distinct. The animation gives the player a clear sense of cards being physically dealt from a shoe. Community cards are not dealt with this animation — they appear via a flip reveal in place.

Technical Design

Card Shuffler (Shoe) Sprite

The card shuffler is a three-layer composite:
LayerAtlas framePosition (landscape)
Shoe bodydealer_shoe_body.png(1181, 49)
Shoe glassdealer_shoe_glass.png(1181, 49)
Card in shoeback.png (card back)(1118, 79)
The card-in-shoe sprite sits at (1118, 79) with:
  • angle: -59°
  • skewY: -0.3
  • scale: (0.58, 0.6)

Animation Sequence

Each hole card goes through two phases:

Phase 1 — Quick Launch from Shoe (~100 ms)

The card group snaps from the shoe position toward a mid-air launch point near (1090, 150) while the angle sweeps from -59°-40°. This phase is linear and brief, simulating the card being pushed out.

Phase 2 — Main Flight to Target (~300–500 ms, path-dependent)

Five parallel tweens run simultaneously:
TweenPropertyEasing
ShadowshadowX, shadowY, skewY → 0Sinusoidal.Out
Card skewskewY → 0Sinusoidal.Out
Position + anglex, y, angle → targetSinusoidal.Out
ScalescaleX, scaleY → targetSinusoidal.Out
Group resetcontainer x, y, angle → 0Sinusoidal.Out
Flight duration is dynamically calculated from the Euclidean distance between the launch point and the card’s final position:
pathX = 1100 − containerX − targetX
pathY = containerY + targetY − 150
path  = √(pathX² + pathY²)
flyTime = path / (2 / PLAY_SPEED)          // desktop
flyTime = path / (4 / PLAY_SPEED)          // device/mobile
Typical flight time: 300–500 ms at normal speed (PLAY_SPEED = 1).

Stagger Delays

Cards are launched in order with a stagger interval based on their index:
// For hole cards (id < 5):
delay = (id + 2 × HANDS_NUMBER + 1) × CARD_FLY_STEP × PLAY_SPEED

// For community cards (id ≥ 5):
delay = (id − 3) × CARD_FLY_STEP × PLAY_SPEED
CARD_FLY_STEP = 300 ms — interval between successive card launches.

Timing Constants

ConstantValuePurpose
CARD_FLY_STEP300 msDelay between successive card launches
CARD_FLIP_TIME150 msHalf-duration of flip reveal (community cards)
CARD_SHOW_WIN_TIME350 msWin highlight animation duration
PLAY_SPEED0.5 (fast) / 1 (normal)Global speed multiplier

V6 Implementation Notes

V6 should replicate this using Phaser 3’s this.tweens.add(). Key differences from v5 (Phaser 2):
  • Use this.tweens.add({ targets, x, y, angle, duration, ease: 'Sine.easeOut' }) instead of game.add.tween
  • Phaser 3 has no native skewY — omit skew or approximate with scaleX oscillation
  • Use this.time.addEvent({ delay, callback }) for the stagger scheduling
  • The shoe sprite should already exist in the v6 atlas (desktop_glass_ui or main_game_desktop) — confirm frame name before implementing
  • Launch sequence: start from shoe card position (1118, 79) with angle: -59, tween to mid-air waypoint (1090, 150) at -40°, then continue to final hand-panel target

V6 Canvas Coordinates

The v6 canvas is 1280 × 720 (landscape). The v5 canvas was the same resolution for desktop. Shoe position (1181, 49) transfers directly.

Mathematics

Dynamic flight duration:
d = √((1100 − cx − tx)² + (cy + ty − 150)²)
t = d / 2   [ms at PLAY_SPEED=1, desktop]
Where (cx, cy) is the hand container origin and (tx, ty) is the card’s local position within the container. For a card in hand 1 at approximately (200, 400):
d = √((1100 − 0 − 200)² + (0 + 400 − 150)²) = √(900² + 250²) ≈ 933 px
t ≈ 933 / 2 ≈ 467 ms

Acceptance Criteria

IDCriterionPlaywright assertion
AC-F045-01On HOLE stage, each hole card starts at the shoe position (~1118, 79) before animatingexpect(cardSprite.x).toBeCloseTo(1118, 0) at frame 0
AC-F045-02Each hole card reaches its final target position within 800 ms of its launchAssert card position equals target within timeout
AC-F045-03Cards are launched sequentially with ≥ 250 ms stagger between eachMeasure launch timestamps per card
AC-F045-04Card angle transitions from −59° at shoe to ≈ 0° (or hand target angle) at destinationexpect(cardSprite.angle).toBeCloseTo(0, 5) after animation
AC-F045-05Community cards do not use the fly animation — they flip in placeAssert community card x/y does not change during reveal
AC-F045-06Animation completes correctly at both PLAY_SPEED = 1 (normal) and PLAY_SPEED = 0.5 (fast)Run game in both speed modes and assert final positions

Version Parity

VersionStatusNotes
JS (v4)⬜ Not yet auditedLikely implemented — same Phaser 2 codebase as v5
TypeScript (v6)❌ Not implementedCards placed statically; no fly animation
UMA (v5)✅ ImplementedFull two-phase fly with 5 parallel tweens, stagger delays

V5 Source References

FileLinesPurpose
source/client_source/src/js/Game/gameTypes/cardDesktop.js42–127throwCard() + _launchCard() — desktop deal animation
source/client_source/src/js/Game/gameTypes/cardDevice.js72–135Mobile variant (faster fly speed: path/4)
source/client_source/src/js/Game/gameView.js349–365Shoe/shuffler sprite setup and coordinates
source/client_source/src/js/Game/gameView.js51–54Animation timing constants