From 0f44464c4fc3613c78d8860ba95a82c4a57ca949 Mon Sep 17 00:00:00 2001 From: "Aaron D. Lee" Date: Sun, 25 Jan 2026 18:49:18 -0500 Subject: [PATCH] Additional animation work and AI strategy enhancements and logging for performance analytics. --- client/app.js | 616 ++++++++++++++++------------------------------ client/index.html | 12 +- client/style.css | 86 ++++--- server/games.db | Bin 16637952 -> 17104896 bytes 4 files changed, 281 insertions(+), 433 deletions(-) diff --git a/client/app.js b/client/app.js index 7251def..7a840a2 100644 --- a/client/app.js +++ b/client/app.js @@ -1,9 +1,5 @@ // Golf Card Game - Client Application -// Feature flag for new persistent card system -// Disabled - using improved legacy system instead -const USE_NEW_CARD_SYSTEM = false; - class GolfGame { constructor() { this.ws = null; @@ -19,37 +15,24 @@ class GolfGame { this.soundEnabled = true; this.audioCtx = null; - // Swap animation state (legacy) + // Swap animation state this.swapAnimationInProgress = false; this.swapAnimationCardEl = null; this.swapAnimationFront = null; this.pendingGameState = null; - // New card system state - this.previousState = null; - this.isAnimating = false; - // Track cards we've locally flipped (for immediate feedback during selection) this.locallyFlippedCards = new Set(); // Animation lock - prevent overlapping animations on same elements this.animatingPositions = new Set(); + // Track round winners for visual highlight + this.roundWinnerNames = new Set(); + this.initElements(); this.initAudio(); this.bindEvents(); - - // Initialize new card system - if (USE_NEW_CARD_SYSTEM) { - this.initNewCardSystem(); - - // Update card positions on resize - window.addEventListener('resize', () => { - if (this.cardManager && this.gameState) { - this.cardManager.updateAllPositions((pid, pos) => this.getSlotRect(pid, pos)); - } - }); - } } initAudio() { @@ -128,167 +111,10 @@ class GolfGame { this.playSound('click'); } - initNewCardSystem() { - const cardLayer = document.getElementById('card-layer'); - this.cardManager = new CardManager(cardLayer); - this.stateDiffer = new StateDiffer(); - this.animationQueue = new AnimationQueue( - this.cardManager, - (playerId, position) => this.getSlotRect(playerId, position), - (location) => this.getLocationRectNew(location), - (type) => this.playSound(type) - ); - } - - // Get the bounding rect of a card slot - getSlotRect(playerId, position) { - // Try to find by data attribute first (new system) - const slotByData = document.querySelector(`.card-slot[data-player="${playerId}"][data-position="${position}"]`); - if (slotByData) { - const rect = slotByData.getBoundingClientRect(); - if (rect.width > 0) return rect; - } - - // Fallback: Check if it's the local player - if (playerId === this.playerId) { - const slots = this.playerCards.querySelectorAll('.card, .card-slot'); - if (slots[position]) { - return slots[position].getBoundingClientRect(); - } - } - return null; - } - - // Get rect for deck/discard/holding locations - getLocationRectNew(location) { - switch (location) { - case 'deck': - return this.deck.getBoundingClientRect(); - case 'discard': - return this.discard.getBoundingClientRect(); - case 'holding': { - const rect = this.discard.getBoundingClientRect(); - return { - left: rect.left, - top: rect.top, - width: rect.width, - height: rect.height - }; - } - default: - return null; - } - } - - // Initialize persistent cards for a new game/round - initializePersistentCards() { - if (!this.cardManager || !this.gameState) return; - - this.cardManager.initializeCards( - this.gameState, - this.playerId, - (pid, pos) => this.getSlotRect(pid, pos), - () => this.deck.getBoundingClientRect(), - () => this.discard.getBoundingClientRect() - ); - - // Retry positioning a few times to handle layout delays - let retries = 0; - const tryPosition = () => { - const positioned = this.cardManager.updateAllPositions((pid, pos) => this.getSlotRect(pid, pos)); - retries++; - if (retries < 5) { - requestAnimationFrame(tryPosition); - } - }; - requestAnimationFrame(tryPosition); - } - - // Animate persistent cards based on detected movements - async animatePersistentCards(movements, newState) { - if (!this.cardManager) return; - - for (const movement of movements) { - switch (movement.type) { - case 'flip': - this.playSound('flip'); - await this.cardManager.flipCard( - movement.playerId, - movement.position, - movement.card - ); - break; - - case 'swap': - this.playSound('flip'); - await this.cardManager.animateSwap( - movement.playerId, - movement.position, - movement.oldCard, - movement.newCard, - (pid, pos) => this.getSlotRect(pid, pos), - () => this.discard.getBoundingClientRect() - ); - this.playSound('card'); - break; - - case 'draw-deck': - case 'draw-discard': - this.playSound('card'); - await this.delay(200); - break; - } - - // Small pause between animations - await this.delay(100); - } - } - delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } - // Update persistent card positions and visual states - updatePersistentCards() { - if (!this.cardManager || !this.gameState) return; - - // If cards haven't been created yet, initialize them - if (this.cardManager.handCards.size === 0) { - // Need to wait for DOM to have slots - already handled in game_started - return; - } - - // Update positions (in case window resized) - this.cardManager.updateAllPositions((pid, pos) => this.getSlotRect(pid, pos)); - - // Update card visual states (clickable, selected) - const myData = this.getMyPlayerData(); - if (myData) { - for (let i = 0; i < 6; i++) { - const cardInfo = this.cardManager.getHandCard(this.playerId, i); - if (cardInfo) { - const card = myData.cards[i]; - const isClickable = ( - (this.gameState.waiting_for_initial_flip && !card.face_up) || - (this.drawnCard) || - (this.waitingForFlip && !card.face_up) - ); - const isSelected = this.selectedCards.includes(i); - - cardInfo.element.classList.toggle('clickable', isClickable); - cardInfo.element.classList.toggle('selected', isSelected); - - // Make card clickable - if (!cardInfo.element._hasClickHandler) { - const pos = i; - cardInfo.element.addEventListener('click', () => this.handleCardClick(pos)); - cardInfo.element._hasClickHandler = true; - } - } - } - } - } - initElements() { // Screens this.lobbyScreen = document.getElementById('lobby-screen'); @@ -345,6 +171,7 @@ class GolfGame { this.discardContent = document.getElementById('discard-content'); this.discardBtn = document.getElementById('discard-btn'); this.playerCards = document.getElementById('player-cards'); + this.playerArea = this.playerCards.closest('.player-area'); this.swapAnimation = document.getElementById('swap-animation'); this.swapCardFromHand = document.getElementById('swap-card-from-hand'); this.scoreboard = document.getElementById('scoreboard'); @@ -479,6 +306,11 @@ class GolfGame { case 'game_started': case 'round_started': + // Clear any countdown from previous hole + this.clearNextHoleCountdown(); + this.nextRoundBtn.classList.remove('waiting'); + // Clear round winner highlights + this.roundWinnerNames = new Set(); this.gameState = data.game_state; // Deep copy for previousState to avoid reference issues this.previousState = JSON.parse(JSON.stringify(data.game_state)); @@ -487,78 +319,40 @@ class GolfGame { this.animatingPositions = new Set(); this.playSound('shuffle'); this.showGameScreen(); - if (USE_NEW_CARD_SYSTEM && this.cardManager) { - this.cardManager.clear(); // Clear any leftover cards - } this.renderGame(); - // Initialize persistent cards after DOM is ready - if (USE_NEW_CARD_SYSTEM && this.cardManager) { - setTimeout(() => this.initializePersistentCards(), 50); - } break; case 'game_state': - if (USE_NEW_CARD_SYSTEM) { - // New card system: animate persistent cards directly - if (this.isAnimating) { - this.pendingGameState = data.game_state; - break; - } + // State updates are instant, animations are fire-and-forget + // Exception: Local player's swap animation defers state until complete - const movements = this.stateDiffer.diff(this.previousState, data.game_state); - - if (movements.length > 0) { - this.isAnimating = true; - this.animatePersistentCards(movements, data.game_state).then(() => { - this.isAnimating = false; - this.gameState = data.game_state; - this.previousState = JSON.parse(JSON.stringify(data.game_state)); - this.renderGame(); - - if (this.pendingGameState) { - const pending = this.pendingGameState; - this.pendingGameState = null; - this.handleMessage({ type: 'game_state', game_state: pending }); - } - }); - } else { - this.gameState = data.game_state; - this.previousState = JSON.parse(JSON.stringify(data.game_state)); - this.renderGame(); - } - } else { - // Legacy animation system - simplified - // Principle: State updates are instant, animations are fire-and-forget - // Exception: Local player's swap animation defers state until complete - - // If local swap animation is running, defer this state update - if (this.swapAnimationInProgress) { - this.updateSwapAnimation(data.game_state.discard_top); - this.pendingGameState = data.game_state; - break; - } - - const oldState = this.gameState; - const newState = data.game_state; - - // Update state FIRST (always) - this.gameState = newState; - - // Clear local flip tracking if server confirmed our flips - if (!newState.waiting_for_initial_flip && oldState?.waiting_for_initial_flip) { - this.locallyFlippedCards = new Set(); - } - - // Detect and fire animations (non-blocking, errors shouldn't break game) - try { - this.triggerAnimationsForStateChange(oldState, newState); - } catch (e) { - console.error('Animation error:', e); - } - - // Render immediately with new state - this.renderGame(); + // If local swap animation is running, defer this state update + if (this.swapAnimationInProgress) { + this.updateSwapAnimation(data.game_state.discard_top); + this.pendingGameState = data.game_state; + break; } + + const oldState = this.gameState; + const newState = data.game_state; + + // Update state FIRST (always) + this.gameState = newState; + + // Clear local flip tracking if server confirmed our flips + if (!newState.waiting_for_initial_flip && oldState?.waiting_for_initial_flip) { + this.locallyFlippedCards = new Set(); + } + + // Detect and fire animations (non-blocking, errors shouldn't break game) + try { + this.triggerAnimationsForStateChange(oldState, newState); + } catch (e) { + console.error('Animation error:', e); + } + + // Render immediately with new state + this.renderGame(); break; case 'your_turn': @@ -820,16 +614,7 @@ class GolfGame { this.hideDrawnCard(); } - // New card system swap animation - animateSwapNew(position) { - if (!this.drawnCard) return; - - // Send swap immediately - animation happens via state diff - this.send({ type: 'swap', position }); - this.drawnCard = null; - this.hideDrawnCard(); - } - + // Animate player swapping drawn card with a card in their hand animateSwap(position) { const cardElements = this.playerCards.querySelectorAll('.card'); const handCardEl = cardElements[position]; @@ -838,7 +623,7 @@ class GolfGame { return; } - // Check if card is already face-up (no flip needed) + // Check if card is already face-up const myData = this.getMyPlayerData(); const card = myData?.cards[position]; const isAlreadyFaceUp = card?.face_up; @@ -850,6 +635,7 @@ class GolfGame { // Set up the animated card at hand position const swapCard = this.swapCardFromHand; const swapCardFront = swapCard.querySelector('.swap-card-front'); + const swapCardInner = swapCard.querySelector('.swap-card-inner'); // Position at the hand card location swapCard.style.left = handRect.left + 'px'; @@ -862,8 +648,8 @@ class GolfGame { swapCardFront.innerHTML = ''; swapCardFront.className = 'swap-card-front'; - // If already face-up, show the card content immediately if (isAlreadyFaceUp && card) { + // FACE-UP CARD: Show card content immediately, then slide to discard if (card.rank === '★') { swapCardFront.classList.add('joker'); const jokerIcon = card.suit === 'hearts' ? '🐉' : '👹'; @@ -873,72 +659,76 @@ class GolfGame { const suitSymbol = { hearts: '♥', diamonds: '♦', clubs: '♣', spades: '♠' }[card.suit]; swapCardFront.innerHTML = `${card.rank}
${suitSymbol}`; } - swapCard.classList.add('flipping'); // Start showing front immediately - } + swapCard.classList.add('flipping'); // Show front immediately - // Hide the actual hand card - handCardEl.classList.add('swap-out'); + // Hide the actual hand card and discard + handCardEl.classList.add('swap-out'); + this.discard.classList.add('swap-to-hand'); + this.swapAnimation.classList.remove('hidden'); - // Hide the discard (drawn card) - this.discard.classList.add('swap-to-hand'); + // Mark animating + this.swapAnimationInProgress = true; + this.swapAnimationCardEl = handCardEl; + this.swapAnimationContentSet = true; - // Show animation overlay - this.swapAnimation.classList.remove('hidden'); + // Send swap + this.send({ type: 'swap', position }); + this.drawnCard = null; - // Mark that we're animating - defer game state renders - this.swapAnimationInProgress = true; - this.swapAnimationCardEl = handCardEl; - this.swapAnimationFront = swapCardFront; - this.swapAnimationContentSet = isAlreadyFaceUp; // Skip updateSwapAnimation if we already set content - - // Send swap immediately so server can respond - this.send({ type: 'swap', position }); - this.drawnCard = null; - - // Timing depends on whether we need to flip first - const flipDelay = isAlreadyFaceUp ? 0 : 450; - - // Step 1: Flip the card over (only if face-down) - if (!isAlreadyFaceUp) { + // Slide to discard setTimeout(() => { - swapCard.classList.add('flipping'); + swapCard.classList.add('moving'); + swapCard.style.left = discardRect.left + 'px'; + swapCard.style.top = discardRect.top + 'px'; }, 50); - } - // Step 2: Move to discard position - setTimeout(() => { - swapCard.classList.add('moving'); - swapCard.style.left = discardRect.left + 'px'; - swapCard.style.top = discardRect.top + 'px'; - }, flipDelay + 50); + // Complete + setTimeout(() => { + this.swapAnimation.classList.add('hidden'); + swapCard.classList.remove('flipping', 'moving'); + handCardEl.classList.remove('swap-out'); + this.discard.classList.remove('swap-to-hand'); + this.swapAnimationInProgress = false; + this.hideDrawnCard(); - // Step 3: Card has landed - pause to show the card - setTimeout(() => { - swapCard.classList.remove('moving'); - }, flipDelay + 400); + if (this.pendingGameState) { + this.gameState = this.pendingGameState; + this.pendingGameState = null; + this.renderGame(); + } + }, 500); + } else { + // FACE-DOWN CARD: Just slide card-back to discard (no flip mid-air) + // The new card will appear instantly when state updates - // Step 4: Complete animation and render final state - setTimeout(() => { - // Hide animation overlay - this.swapAnimation.classList.add('hidden'); - swapCard.classList.remove('flipping', 'moving'); - - // Reset card states - handCardEl.classList.remove('swap-out'); - this.discard.classList.remove('swap-to-hand'); - - // Now allow renders and show the final state - this.swapAnimationInProgress = false; + // Don't use overlay for face-down - just send swap and let state handle it + // This avoids the clunky "flip to empty front" issue + this.swapAnimationInProgress = true; + this.swapAnimationCardEl = handCardEl; this.swapAnimationContentSet = false; - this.hideDrawnCard(); - // Render the pending game state if we have one - if (this.pendingGameState) { - this.gameState = this.pendingGameState; - this.pendingGameState = null; - this.renderGame(); - } - }, flipDelay + 900); + // Send swap + this.send({ type: 'swap', position }); + this.drawnCard = null; + + // Brief visual feedback - hide drawn card area + this.discard.classList.add('swap-to-hand'); + handCardEl.classList.add('swap-out'); + + // Short timeout then let state update handle it + setTimeout(() => { + this.discard.classList.remove('swap-to-hand'); + handCardEl.classList.remove('swap-out'); + this.swapAnimationInProgress = false; + this.hideDrawnCard(); + + if (this.pendingGameState) { + this.gameState = this.pendingGameState; + this.pendingGameState = null; + this.renderGame(); + } + }, 300); + } } // Update the animated card with actual card content when server responds @@ -1327,11 +1117,7 @@ class GolfGame { // Swap with drawn card if (this.drawnCard) { - if (USE_NEW_CARD_SYSTEM) { - this.animateSwapNew(position); - } else { - this.animateSwap(position); - } + this.animateSwap(position); this.hideToast(); return; } @@ -1347,8 +1133,10 @@ class GolfGame { } nextRound() { + this.clearNextHoleCountdown(); this.send({ type: 'next_round' }); this.gameButtons.classList.add('hidden'); + this.nextRoundBtn.classList.remove('waiting'); } newGame() { @@ -1387,10 +1175,6 @@ class GolfGame { this.isHost = false; this.gameState = null; this.previousState = null; - // Clear card layer - if (USE_NEW_CARD_SYSTEM && this.cardManager) { - this.cardManager.clear(); - } } showWaitingRoom() { @@ -1417,27 +1201,23 @@ class GolfGame { this.leaveGameBtn.textContent = this.isHost ? 'End Game' : 'Leave'; // Update active rules bar this.updateActiveRulesBar(); - // Clear card layer for new card system - if (USE_NEW_CARD_SYSTEM && this.cardManager) { - this.cardManager.clear(); - } } updateActiveRulesBar() { - if (!this.gameState || !this.gameState.active_rules) { + if (!this.gameState) { this.activeRulesBar.classList.add('hidden'); return; } - const rules = this.gameState.active_rules; + const rules = this.gameState.active_rules || []; if (rules.length === 0) { - this.activeRulesBar.classList.add('hidden'); - return; + // Show "Standard Rules" when no variants selected + this.activeRulesList.innerHTML = 'Standard'; + } else { + this.activeRulesList.innerHTML = rules + .map(rule => `${rule}`) + .join(''); } - - this.activeRulesList.innerHTML = rules - .map(rule => `${rule}`) - .join(''); this.activeRulesBar.classList.remove('hidden'); } @@ -1526,11 +1306,17 @@ class GolfGame { return; } + const isFinalTurn = this.gameState.phase === 'final_turn'; const currentPlayer = this.gameState.players.find(p => p.id === this.gameState.current_player_id); + if (currentPlayer && currentPlayer.id !== this.playerId) { - this.setStatus(`${currentPlayer.name}'s turn`); + const prefix = isFinalTurn ? '⚡ Final turn: ' : ''; + this.setStatus(`${prefix}${currentPlayer.name}'s turn`); } else if (this.isMyTurn()) { - this.setStatus('Your turn - draw a card', 'your-turn'); + const message = isFinalTurn + ? '⚡ Final turn! Draw a card' + : 'Your turn - draw a card'; + this.setStatus(message, 'your-turn'); } else { this.setStatus(''); } @@ -1646,11 +1432,16 @@ class GolfGame { const showingScore = this.calculateShowingScore(me.cards); this.yourScore.textContent = showingScore; + // Check if player won the round + const isRoundWinner = this.roundWinnerNames.has(me.name); + this.playerArea.classList.toggle('round-winner', isRoundWinner); + // Update player name in header (truncate if needed) const displayName = me.name.length > 12 ? me.name.substring(0, 11) + '…' : me.name; const checkmark = me.all_face_up ? ' ✓' : ''; + const crownEmoji = isRoundWinner ? ' 👑' : ''; // Set text content before the score span - this.playerHeader.childNodes[0].textContent = displayName + checkmark; + this.playerHeader.childNodes[0].textContent = displayName + checkmark + crownEmoji; } // Update discard pile (skip if holding a drawn card) @@ -1708,25 +1499,21 @@ class GolfGame { div.classList.add('current-turn'); } + const isRoundWinner = this.roundWinnerNames.has(player.name); + if (isRoundWinner) { + div.classList.add('round-winner'); + } + const displayName = player.name.length > 12 ? player.name.substring(0, 11) + '…' : player.name; const showingScore = this.calculateShowingScore(player.cards); + const crownEmoji = isRoundWinner ? ' 👑' : ''; - if (USE_NEW_CARD_SYSTEM) { - // Render empty slots - cards are in card-layer - div.innerHTML = ` -

${displayName}${player.all_face_up ? ' ✓' : ''}${showingScore}

-
- ${player.cards.map((_, i) => `
`).join('')} -
- `; - } else { - div.innerHTML = ` -

${displayName}${player.all_face_up ? ' ✓' : ''}${showingScore}

-
- ${player.cards.map(card => this.renderCard(card, false, false)).join('')} -
- `; - } + div.innerHTML = ` +

${displayName}${player.all_face_up ? ' ✓' : ''}${crownEmoji}${showingScore}

+
+ ${player.cards.map(card => this.renderCard(card, false, false)).join('')} +
+ `; this.opponentsRow.appendChild(div); }); @@ -1736,51 +1523,27 @@ class GolfGame { if (myData) { this.playerCards.innerHTML = ''; - if (USE_NEW_CARD_SYSTEM) { - // Render empty slots - cards are in card-layer - myData.cards.forEach((card, index) => { - const isClickable = ( - (this.gameState.waiting_for_initial_flip && !card.face_up) || - (this.drawnCard) || - (this.waitingForFlip && !card.face_up) - ); - const isSelected = this.selectedCards.includes(index); + myData.cards.forEach((card, index) => { + // Check if this card was locally flipped (immediate feedback) + const isLocallyFlipped = this.locallyFlippedCards.has(index); - const slotEl = document.createElement('div'); - slotEl.className = 'card-slot'; - slotEl.dataset.player = this.playerId; - slotEl.dataset.position = index; - if (isClickable) slotEl.classList.add('clickable'); - if (isSelected) slotEl.classList.add('selected'); - slotEl.addEventListener('click', () => this.handleCardClick(index)); - this.playerCards.appendChild(slotEl); - }); + // Create a display card that shows face-up if locally flipped + const displayCard = isLocallyFlipped + ? { ...card, face_up: true } + : card; - // Update persistent card positions and states - this.updatePersistentCards(); - } else { - myData.cards.forEach((card, index) => { - // Check if this card was locally flipped (immediate feedback) - const isLocallyFlipped = this.locallyFlippedCards.has(index); + const isClickable = ( + (this.gameState.waiting_for_initial_flip && !card.face_up && !isLocallyFlipped) || + (this.drawnCard) || + (this.waitingForFlip && !card.face_up) + ); + const isSelected = this.selectedCards.includes(index); - // Create a display card that shows face-up if locally flipped - const displayCard = isLocallyFlipped - ? { ...card, face_up: true } - : card; - - const isClickable = ( - (this.gameState.waiting_for_initial_flip && !card.face_up && !isLocallyFlipped) || - (this.drawnCard) || - (this.waitingForFlip && !card.face_up) - ); - const isSelected = this.selectedCards.includes(index); - - const cardEl = document.createElement('div'); - cardEl.innerHTML = this.renderCard(displayCard, isClickable, isSelected); - cardEl.firstChild.addEventListener('click', () => this.handleCardClick(index)); - this.playerCards.appendChild(cardEl.firstChild); - }); - } + const cardEl = document.createElement('div'); + cardEl.innerHTML = this.renderCard(displayCard, isClickable, isSelected); + cardEl.firstChild.addEventListener('click', () => this.handleCardClick(index)); + this.playerCards.appendChild(cardEl.firstChild); + }); } // Show flip prompt for initial flip @@ -1916,6 +1679,16 @@ class GolfGame { showScoreboard(scores, isFinal, rankings) { this.scoreTable.innerHTML = ''; + // Find round winner(s) - lowest round score (not total) + const roundScores = scores.map(s => s.score); + const minRoundScore = Math.min(...roundScores); + this.roundWinnerNames = new Set( + scores.filter(s => s.score === minRoundScore).map(s => s.name) + ); + + // Re-render to show winner highlights + this.renderGame(); + const minScore = Math.min(...scores.map(s => s.total || s.score || 0)); scores.forEach(score => { @@ -1954,13 +1727,58 @@ class GolfGame { // Show game buttons this.gameButtons.classList.remove('hidden'); + this.newGameBtn.classList.add('hidden'); + this.nextRoundBtn.classList.remove('hidden'); - if (this.isHost) { - this.nextRoundBtn.classList.remove('hidden'); - this.newGameBtn.classList.add('hidden'); - } else { - this.nextRoundBtn.classList.add('hidden'); - this.newGameBtn.classList.add('hidden'); + // Start countdown for next hole + this.startNextHoleCountdown(); + } + + startNextHoleCountdown() { + // Clear any existing countdown + if (this.nextHoleCountdownInterval) { + clearInterval(this.nextHoleCountdownInterval); + } + + const COUNTDOWN_SECONDS = 15; + let remaining = COUNTDOWN_SECONDS; + + const updateButton = () => { + if (this.isHost) { + this.nextRoundBtn.textContent = `Next Hole (${remaining}s)`; + this.nextRoundBtn.disabled = false; + } else { + this.nextRoundBtn.textContent = `Next hole in ${remaining}s...`; + this.nextRoundBtn.disabled = true; + this.nextRoundBtn.classList.add('waiting'); + } + }; + + updateButton(); + + this.nextHoleCountdownInterval = setInterval(() => { + remaining--; + + if (remaining <= 0) { + clearInterval(this.nextHoleCountdownInterval); + this.nextHoleCountdownInterval = null; + + // Auto-advance if host + if (this.isHost) { + this.nextRound(); + } else { + this.nextRoundBtn.textContent = 'Waiting for host...'; + } + } else { + updateButton(); + } + }, 1000); + } + + clearNextHoleCountdown() { + if (this.nextHoleCountdownInterval) { + clearInterval(this.nextHoleCountdownInterval); + this.nextHoleCountdownInterval = null; } } diff --git a/client/index.html b/client/index.html index 9309d7d..93ffe81 100644 --- a/client/index.html +++ b/client/index.html @@ -205,17 +205,17 @@
-
Hole 1/9
-
+
+
Hole 1/9
-
-
-
-
+
+
+
+
diff --git a/client/style.css b/client/style.css index 4be6d01..2c88c4a 100644 --- a/client/style.css +++ b/client/style.css @@ -285,6 +285,23 @@ input::placeholder { color: #1a472a; } +/* Pulsing glow for Next Hole button */ +#next-round-btn:not(.hidden) { + animation: glow-pulse 1.5s ease-in-out infinite; +} + +@keyframes glow-pulse { + 0%, 100% { + box-shadow: 0 0 5px rgba(244, 164, 96, 0.4), + 0 0 10px rgba(244, 164, 96, 0.2); + } + 50% { + box-shadow: 0 0 15px rgba(244, 164, 96, 0.8), + 0 0 30px rgba(244, 164, 96, 0.4), + 0 0 45px rgba(244, 164, 96, 0.2); + } +} + .btn-secondary { background: #fff; color: #1a472a; @@ -314,6 +331,14 @@ input::placeholder { box-shadow: none; } +/* Waiting state for non-host next hole button */ +#next-round-btn.waiting { + animation: none; + background: rgba(244, 164, 96, 0.4); + color: rgba(255, 255, 255, 0.8); + box-shadow: none; +} + .divider { text-align: center; margin: 30px 0; @@ -462,9 +487,9 @@ input::placeholder { /* Game Screen */ .game-header { - display: flex; + display: grid; + grid-template-columns: 1fr 2fr 1fr; align-items: center; - justify-content: space-between; padding: 10px 20px; background: rgba(0,0,0,0.35); font-size: 0.9rem; @@ -473,29 +498,30 @@ input::placeholder { box-sizing: border-box; } +.header-col { + display: flex; + align-items: center; +} + +.header-col-left { + justify-content: flex-start; + gap: 12px; +} + +.header-col-center { + justify-content: center; +} + +.header-col-right { + justify-content: flex-end; + gap: 8px; +} + .game-header .round-info { font-weight: 600; white-space: nowrap; } -.game-header-center { - display: flex; - align-items: center; - gap: 40px; -} - -.game-header .turn-info { - font-weight: 600; - color: #f4a460; - white-space: nowrap; -} - -.game-header .header-buttons { - display: flex; - align-items: center; - gap: 8px; -} - #leave-game-btn { padding: 6px 12px; font-size: 0.8rem; @@ -521,7 +547,6 @@ input::placeholder { .active-rules-bar { display: flex; align-items: center; - justify-content: center; gap: 6px; font-size: 0.85rem; } @@ -550,6 +575,11 @@ input::placeholder { font-weight: 600; } +.active-rules-bar .rule-tag.standard { + background: rgba(255, 255, 255, 0.15); + color: rgba(255, 255, 255, 0.7); +} + /* Card Styles */ .card { width: clamp(65px, 5.5vw, 100px); @@ -1027,15 +1057,15 @@ input::placeholder { box-shadow: 0 0 0 2px #f4a460; } -/* Toast Notification */ -/* Header status area */ -.header-status { - display: flex; - align-items: center; - justify-content: center; - min-width: 200px; +/* Round winner highlight */ +.opponent-area.round-winner h4, +.player-area.round-winner h4 { + background: rgba(200, 255, 50, 0.7); + box-shadow: 0 0 15px rgba(200, 255, 50, 0.9), 0 0 30px rgba(200, 255, 50, 0.5); + color: #0a2a10; } +/* Status message in header */ .status-message { padding: 6px 16px; border-radius: 4px; diff --git a/server/games.db b/server/games.db index 378dc147a0cd7088281cbd151d848f1e7d841955..3d728781261f7ab3dfadaa96bdcdef04458998d5 100644 GIT binary patch delta 113664 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 SmtLr5Httve+QSk1lKu}Tsah=n delta 6787 zcmZ9R3tUvi_rQ1Vd+)uw3;2k{1O?1R#Wj1Ff~kKqGbL0s1n;I%$+%N&Y4+F zdsouRei7Xrgn=fL$yU2QWB@bJbildGCUvq+FtR?%R{f%AK$-FaL$#2~!-A0p3WpMc z3;f!NwrVV69cFuAyJKs%HQMTJmu%GEZ^cRScj3bVgw(McWHs@|Xp>2HT^N0+{<)5R zO!WPOv5X(Np3ORkupe6&We;}_Wi7Hy{fmyB_^ZC7MRAkt&9efAa)(SN^?;QPW~MGk zS~6+sYg1-3!6uXC+CQ@Tw_<-z`Lp57qNLfcO`1A$;gqBWQ~M8A!6acZ^Qxh69TNi@vNDN zA}Aosxa;@min7$qvQ&aF2#atCj|j9ZRUG6mqPz)Ynh6FvJ0`-w*qh_v6Yb@F;9f8C zd6S-GNhnRdn*TU~cP{5U;N)g83?^R5&+y)nbx^ihv_tWNv^d&pQQBw)%nQ@fbdRWe zB;8}yJ+kgmbdRcgEV{?4du+PLPxtuio&ep`M)$PUJ?(T)d)@Ph?g`XAkCu2kkP6Q7 zv?-qQuAoc?-1CF%kj4jRz{dGOVPu6jgJv!aI%tL|mNjFc$rTh1BgNnZ$eAA$3S;@; zh|Gl4AfzMm7)?m+w0pV8)tE+^8ciFA2hVt!Oe0Ywz|_c};|qCBTq?$xJ4&~u&!uhr zIKGD%COi--yJBz})Q#>J30O4@?pG(rLq=6{ z49qnL_h96}zf0aU2X|*IPH^0sT<9yjpa$EIQi6LxU1hQzf@_k;K!XxI&ihD4Lmh)( z^0pd$7Mzwf zI}(9(M|#jAYtOj(*dVX<+yhQ$$F>mA(!U$HM@8~`ZfZSo1y_`1ll8GyDB9+NFU@$5oJq(Z~SgIb_u;P`$>g-;H?jPsQ}1!St}Mrby)x(+s7cf$$#r8} z*(&yZHl3Zo_F{SFB6GmI(A$`P6h1@?2NlBST_C{KW(PRGv+7kpk3)z(}8{K0K zxj$Y=?hY>VuxhV0gXyzdE8+cltz&(5Bc0;c?ApNIV!xxJYwldu@YXXRXa%QaHZGh? z8yC*&*pVA;jGeRP)%K{^^RmFtGXodBQ|Jb+qtkh~>7MrhdVfFU>;F84(eF3R>vEG* zr?Y;rpx(Z{BNiaBfg&vQM?5<8uk=`!=IWrPX*L;55z z{G(8pKFSWa*10CKmh+ff*EW(dzXt9>Id;AV!;nc{_s0#i_VK>#NyRMJLqbLX?{+PO zH{Grt3_p$TcDp*W{PQLg9|79N(RO+_+w}#@C~wd*aJ6A!5ByxoRH@i|Oewa=

2#TW2YL;hX}Z_E!PA>e$wTV&oMtQB4w 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