More host UI refinements, fun logo animations, etc.
This commit is contained in:
parent
c72fe44cfa
commit
23657f6b0c
@ -840,6 +840,33 @@ class GolfGame {
|
|||||||
const previousPlayerId = oldState.current_player_id;
|
const previousPlayerId = oldState.current_player_id;
|
||||||
const wasOtherPlayer = previousPlayerId && previousPlayerId !== this.playerId;
|
const wasOtherPlayer = previousPlayerId && previousPlayerId !== this.playerId;
|
||||||
|
|
||||||
|
// Detect which pile opponent drew from and pulse it
|
||||||
|
if (wasOtherPlayer && discardChanged) {
|
||||||
|
const oldPlayer = oldState.players.find(p => p.id === previousPlayerId);
|
||||||
|
const newPlayer = newState.players.find(p => p.id === previousPlayerId);
|
||||||
|
|
||||||
|
if (oldPlayer && newPlayer && oldDiscard) {
|
||||||
|
// Check if any of their cards now matches the old discard top
|
||||||
|
// This means they took from discard pile
|
||||||
|
let tookFromDiscard = false;
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
const newCard = newPlayer.cards[i];
|
||||||
|
if (newCard?.face_up &&
|
||||||
|
newCard.rank === oldDiscard.rank &&
|
||||||
|
newCard.suit === oldDiscard.suit) {
|
||||||
|
tookFromDiscard = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pulse the appropriate pile
|
||||||
|
this.pulseDrawPile(tookFromDiscard ? 'discard' : 'deck');
|
||||||
|
} else {
|
||||||
|
// No old discard or couldn't detect - assume deck
|
||||||
|
this.pulseDrawPile('deck');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (discardChanged && wasOtherPlayer) {
|
if (discardChanged && wasOtherPlayer) {
|
||||||
// Check if the previous player actually SWAPPED (has a new face-up card)
|
// Check if the previous player actually SWAPPED (has a new face-up card)
|
||||||
// vs just discarding the drawn card (no hand change)
|
// vs just discarding the drawn card (no hand change)
|
||||||
@ -886,6 +913,17 @@ class GolfGame {
|
|||||||
// The swap animation handles showing the card at the correct position
|
// The swap animation handles showing the card at the correct position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pulse animation on deck or discard pile to show where opponent drew from
|
||||||
|
pulseDrawPile(source) {
|
||||||
|
const pile = source === 'discard' ? this.discard : this.deck;
|
||||||
|
pile.classList.remove('draw-pulse');
|
||||||
|
// Trigger reflow to restart animation
|
||||||
|
void pile.offsetWidth;
|
||||||
|
pile.classList.add('draw-pulse');
|
||||||
|
// Remove class after animation completes
|
||||||
|
setTimeout(() => pile.classList.remove('draw-pulse'), 600);
|
||||||
|
}
|
||||||
|
|
||||||
// Fire animation for discard without swap (card goes deck -> discard)
|
// Fire animation for discard without swap (card goes deck -> discard)
|
||||||
fireDiscardAnimation(discardCard) {
|
fireDiscardAnimation(discardCard) {
|
||||||
const deckRect = this.deck.getBoundingClientRect();
|
const deckRect = this.deck.getBoundingClientRect();
|
||||||
@ -1546,14 +1584,10 @@ class GolfGame {
|
|||||||
// Update player name in header (truncate if needed)
|
// Update player name in header (truncate if needed)
|
||||||
const displayName = me.name.length > 12 ? me.name.substring(0, 11) + '…' : me.name;
|
const displayName = me.name.length > 12 ? me.name.substring(0, 11) + '…' : me.name;
|
||||||
const checkmark = me.all_face_up ? ' ✓' : '';
|
const checkmark = me.all_face_up ? ' ✓' : '';
|
||||||
// Remove old crown if exists
|
// Update player name span with crown if winner
|
||||||
const existingCrown = this.playerHeader.querySelector('.winner-crown');
|
const playerNameSpan = this.playerHeader.querySelector('.player-name');
|
||||||
if (existingCrown) existingCrown.remove();
|
const crownHtml = isRoundWinner ? '<span class="winner-crown">👑</span>' : '';
|
||||||
// Set content - crown goes at the start
|
playerNameSpan.innerHTML = crownHtml + displayName + checkmark;
|
||||||
this.playerHeader.firstChild.textContent = displayName + checkmark;
|
|
||||||
if (isRoundWinner) {
|
|
||||||
this.playerHeader.insertAdjacentHTML('afterbegin', '<span class="winner-crown">👑</span>');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update discard pile (skip if holding a drawn card)
|
// Update discard pile (skip if holding a drawn card)
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<!-- Lobby Screen -->
|
<!-- Lobby Screen -->
|
||||||
<div id="lobby-screen" class="screen active">
|
<div id="lobby-screen" class="screen active">
|
||||||
<h1><span class="golfer-swing">🏌️</span><span class="kicked-ball">⚪</span> <span class="golf-title">Golf</span></h1>
|
<h1><span class="golfer-swing">🏌️</span><span class="kicked-ball">⚪</span> <span class="golf-title">Golf</span></h1>
|
||||||
<p class="subtitle">6-Card Golf Card Game <button id="rules-btn" class="btn btn-link">Rules</button></p>
|
<p class="subtitle">6-Card Golf Card Game <button id="rules-btn" class="btn btn-small btn-rules">Rules</button></p>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="player-name">Your Name</label>
|
<label for="player-name">Your Name</label>
|
||||||
@ -247,7 +247,7 @@
|
|||||||
|
|
||||||
<div class="player-section">
|
<div class="player-section">
|
||||||
<div class="player-area">
|
<div class="player-area">
|
||||||
<h4 id="player-header">You<span id="your-score" class="player-showing">0</span></h4>
|
<h4 id="player-header"><span class="player-name">You</span><span id="your-score" class="player-showing">0</span></h4>
|
||||||
<div id="player-cards" class="card-grid"></div>
|
<div id="player-cards" class="card-grid"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -985,6 +985,24 @@ input::placeholder {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pulse animation when opponent draws from a pile */
|
||||||
|
#deck.draw-pulse,
|
||||||
|
#discard.draw-pulse {
|
||||||
|
animation: draw-pulse 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes draw-pulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(244, 164, 96, 0.8);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 0 12px rgba(244, 164, 96, 0.4);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 20px rgba(244, 164, 96, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Card flip animation for discard pile */
|
/* Card flip animation for discard pile */
|
||||||
.card-flip-in {
|
.card-flip-in {
|
||||||
animation: cardFlipIn 0.5s ease-out;
|
animation: cardFlipIn 0.5s ease-out;
|
||||||
@ -2555,19 +2573,25 @@ input::placeholder {
|
|||||||
color: rgba(255, 255, 255, 0.85);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rules link button in lobby */
|
/* Rules button in lobby */
|
||||||
.btn-link {
|
.btn-rules {
|
||||||
background: transparent;
|
background: rgba(244, 164, 96, 0.2);
|
||||||
border: none;
|
border: 1px solid #f4a460;
|
||||||
color: #f4a460;
|
color: #ffb366;
|
||||||
text-decoration: underline;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.95rem;
|
font-size: 0.65rem;
|
||||||
margin-bottom: 15px;
|
padding: 2px 8px;
|
||||||
|
margin-left: 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: background 0.2s, border-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-link:hover {
|
.btn-rules:hover {
|
||||||
color: #fbbf24;
|
background: rgba(244, 164, 96, 0.35);
|
||||||
|
border-color: #ffb366;
|
||||||
|
color: #ffc880;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Select option styling in advanced options */
|
/* Select option styling in advanced options */
|
||||||
|
|||||||
BIN
server/games.db
BIN
server/games.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user