@@ -41,6 +41,7 @@ const GAME_H = 600;
4141const AI_DELAY = 600 ; // ms before AI chooses
4242const AI_SHOW_DRAW_DELAY = 1000 ; // ms to show drawn card before moving
4343const ANIM_DURATION = 300 ; // ms for animations
44+ const SWAP_ANIM_DURATION = ANIM_DURATION * 1.5 ; // ms for swap/discard-and-flip
4445
4546// Layout positions (designed to fit two 3x3 grids + piles in 600px height)
4647const AI_GRID_Y = 105 ; // center Y of AI grid
@@ -722,23 +723,63 @@ export class GolfScene extends Phaser.Scene {
722723 const sprite = sprites [ idx ] ;
723724 const grid = this . session . gameState . playerStates [ result . playerIndex ] . grid ;
724725
725- // Flip animation: scale X to 0, change texture, scale back
726+ // Compute destination positions
727+ const gridSlotPos = this . gridCellPosition ( idx , playerKey ) ;
728+ const discardPos = { x : DISCARD_X , y : PILE_Y } ;
729+
730+ // Track completion of both parallel tweens
731+ let completed = 0 ;
732+ const checkDone = ( ) => {
733+ completed ++ ;
734+ if ( completed === 2 ) {
735+ // Restore grid card depth after transit
736+ sprite . setDepth ( 0 ) ;
737+ wrappedOnComplete ( ) ;
738+ }
739+ } ;
740+
741+ // Raise grid card depth so it renders above other grid cards during transit
742+ sprite . setDepth ( 10 ) ;
743+
744+ // 1. Grid card: flip (reveal face) + translate to discard pile
745+ // First half: scaleX → 0 while moving halfway to discard
726746 this . tweens . add ( {
727747 targets : sprite ,
728748 scaleX : 0 ,
729- duration : ANIM_DURATION / 2 ,
749+ x : ( sprite . x + discardPos . x ) / 2 ,
750+ y : ( sprite . y + discardPos . y ) / 2 ,
751+ duration : SWAP_ANIM_DURATION / 2 ,
730752 ease : 'Power2' ,
731753 onComplete : ( ) => {
754+ // Reveal the card's actual face at the midpoint of the flip
732755 sprite . setTexture ( this . getCardTexture ( grid [ idx ] ) ) ;
756+ // Second half: scaleX → 1 while completing movement to discard
733757 this . tweens . add ( {
734758 targets : sprite ,
735759 scaleX : 1 ,
736- duration : ANIM_DURATION / 2 ,
760+ x : discardPos . x ,
761+ y : discardPos . y ,
762+ duration : SWAP_ANIM_DURATION / 2 ,
737763 ease : 'Power2' ,
738- onComplete : wrappedOnComplete ,
764+ onComplete : checkDone ,
739765 } ) ;
740766 } ,
741767 } ) ;
768+
769+ // 2. Drawn card: translate from display position to vacated grid slot
770+ if ( this . drawnCardSprite ) {
771+ this . tweens . add ( {
772+ targets : this . drawnCardSprite ,
773+ x : gridSlotPos . x ,
774+ y : gridSlotPos . y ,
775+ duration : SWAP_ANIM_DURATION ,
776+ ease : 'Power2' ,
777+ onComplete : checkDone ,
778+ } ) ;
779+ } else {
780+ // Edge case: no drawn card sprite (shouldn't happen, but be safe)
781+ checkDone ( ) ;
782+ }
742783 } else {
743784 // Discard-and-flip: flip the target card
744785 const idx = result . move . row * 3 + result . move . col ;
0 commit comments