276 lines
8.4 KiB
Vue
276 lines
8.4 KiB
Vue
<template>
|
|
<div class="min-h-screen bg-gradient-to-br from-green-700 via-green-800 to-green-900 p-8">
|
|
<div class="max-w-6xl mx-auto">
|
|
<!-- Title -->
|
|
<h1 class="text-white text-3xl font-bold mb-8 text-center">Card Dealing Test Page</h1>
|
|
|
|
<!-- Control Panel -->
|
|
<div class="bg-gray-900/80 rounded-lg p-6 mb-8">
|
|
<h2 class="text-white text-xl font-semibold mb-4">Controls</h2>
|
|
<div class="flex flex-wrap gap-3 mb-4">
|
|
<button
|
|
@click="startDealing"
|
|
:disabled="isDealing"
|
|
class="px-4 py-2 bg-green-600 hover:bg-green-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white rounded-lg font-medium transition-colors"
|
|
>
|
|
Start Dealing
|
|
</button>
|
|
<button
|
|
@click="dealSingleCard"
|
|
:disabled="isDealing || cardsRemaining === 0"
|
|
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white rounded-lg font-medium transition-colors"
|
|
>
|
|
Deal One Card
|
|
</button>
|
|
<button
|
|
@click="reset"
|
|
class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg font-medium transition-colors"
|
|
>
|
|
Reset
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Current State Display -->
|
|
<div class="grid grid-cols-3 gap-4 text-gray-300 text-sm">
|
|
<div>
|
|
<p><strong>Player Hand:</strong> {{ playerHand.length }} cards</p>
|
|
<p><strong>Opponent Hand:</strong> {{ opponentHand.length }} cards</p>
|
|
</div>
|
|
<div>
|
|
<p><strong>Cards Remaining:</strong> {{ cardsRemaining }}</p>
|
|
<p><strong>Dealing:</strong> {{ isDealing ? 'Yes' : 'No' }}</p>
|
|
</div>
|
|
<div>
|
|
<p><strong>Next Recipient:</strong> {{ nextRecipient }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Game Board Layout -->
|
|
<div class="grid grid-rows-[auto_1fr_auto] gap-8">
|
|
<!-- Opponent Hand (Top) -->
|
|
<div class="flex justify-center">
|
|
<div class="bg-gray-800/30 rounded-lg p-4">
|
|
<h3 class="text-white text-sm font-semibold mb-2 text-center">Opponent Hand</h3>
|
|
<PlayerHand
|
|
ref="opponentHandRef"
|
|
:cards="opponentHand"
|
|
:face-down="true"
|
|
:max-cards="3"
|
|
:playable-cards="[]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Center Area (Deck) -->
|
|
<div class="flex items-center justify-center">
|
|
<div class="bg-gray-800/30 rounded-lg p-6">
|
|
<h3 class="text-white text-sm font-semibold mb-4 text-center">Deck</h3>
|
|
<DeckArea
|
|
ref="deckRef"
|
|
:trump-card="trumpCard"
|
|
:cards-remaining="cardsRemaining"
|
|
:is-empty="cardsRemaining === 0"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Player Hand (Bottom) -->
|
|
<div class="flex justify-center">
|
|
<div class="bg-gray-800/30 rounded-lg p-4">
|
|
<h3 class="text-white text-sm font-semibold mb-2 text-center">Your Hand</h3>
|
|
<PlayerHand
|
|
ref="playerHandRef"
|
|
:cards="playerHand"
|
|
:face-down="false"
|
|
:max-cards="3"
|
|
:playable-cards="[]"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Instructions -->
|
|
<div class="bg-gray-900/80 rounded-lg p-6 mt-8 text-gray-300">
|
|
<h3 class="text-white font-semibold mb-2">Instructions:</h3>
|
|
<ul class="list-disc list-inside space-y-1 text-sm">
|
|
<li><strong>Start Dealing:</strong> Automatically deals 6 cards (3 to each player) with 200ms delay</li>
|
|
<li><strong>Deal One Card:</strong> Manually deal a single card to the next recipient</li>
|
|
<li><strong>Reset:</strong> Clear all hands and reset the deck to 40 cards</li>
|
|
<li>Watch the FlyingCard animation as cards move from deck to hands</li>
|
|
<li>Notice the deck counter decreasing and the pulse effect</li>
|
|
<li>Cards appear in hands with a fade-in animation</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Flying Cards Container -->
|
|
<FlyingCard
|
|
v-for="flyingCard in flyingCards"
|
|
:key="flyingCard.id"
|
|
:card="flyingCard.card"
|
|
:start-position="flyingCard.startPosition"
|
|
:end-position="flyingCard.endPosition"
|
|
:duration="500"
|
|
:face-down="flyingCard.faceDown"
|
|
:delay="flyingCard.delay"
|
|
@complete="onFlyingCardComplete(flyingCard)"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, nextTick } from 'vue'
|
|
import PlayerHand from '@/components/game/PlayerHand.vue'
|
|
import DeckArea from '@/components/game/DeckArea.vue'
|
|
import FlyingCard from '@/components/game/FlyingCard.vue'
|
|
|
|
// Refs for components
|
|
const playerHandRef = ref(null)
|
|
const opponentHandRef = ref(null)
|
|
const deckRef = ref(null)
|
|
|
|
// Game state
|
|
const playerHand = ref([])
|
|
const opponentHand = ref([])
|
|
const cardsRemaining = ref(40)
|
|
const isDealing = ref(false)
|
|
const flyingCards = ref([])
|
|
let flyingCardIdCounter = 0
|
|
|
|
// Trump card (fixed for testing)
|
|
const trumpCard = ref({ suit: 'o', rank: 7 })
|
|
|
|
// Sample deck
|
|
const sampleCards = [
|
|
{ suit: 'c', rank: 1 },
|
|
{ suit: 'c', rank: 2 },
|
|
{ suit: 'c', rank: 3 },
|
|
{ suit: 'c', rank: 4 },
|
|
{ suit: 'c', rank: 5 },
|
|
{ suit: 'c', rank: 6 },
|
|
{ suit: 'c', rank: 7 },
|
|
{ suit: 'c', rank: 11 },
|
|
{ suit: 'c', rank: 12 },
|
|
{ suit: 'c', rank: 13 },
|
|
{ suit: 'e', rank: 1 },
|
|
{ suit: 'e', rank: 2 },
|
|
{ suit: 'e', rank: 3 },
|
|
{ suit: 'e', rank: 4 },
|
|
{ suit: 'e', rank: 5 },
|
|
{ suit: 'e', rank: 6 },
|
|
{ suit: 'e', rank: 7 },
|
|
{ suit: 'e', rank: 11 },
|
|
{ suit: 'e', rank: 12 },
|
|
{ suit: 'e', rank: 13 },
|
|
{ suit: 'o', rank: 1 },
|
|
{ suit: 'o', rank: 2 },
|
|
{ suit: 'o', rank: 3 },
|
|
{ suit: 'o', rank: 4 },
|
|
{ suit: 'o', rank: 5 },
|
|
{ suit: 'o', rank: 6 },
|
|
{ suit: 'p', rank: 1 },
|
|
{ suit: 'p', rank: 2 },
|
|
{ suit: 'p', rank: 3 },
|
|
{ suit: 'p', rank: 4 },
|
|
{ suit: 'p', rank: 5 },
|
|
{ suit: 'p', rank: 6 },
|
|
{ suit: 'p', rank: 7 },
|
|
{ suit: 'p', rank: 11 },
|
|
{ suit: 'p', rank: 12 },
|
|
{ suit: 'p', rank: 13 },
|
|
]
|
|
|
|
// Computed
|
|
const nextRecipient = computed(() => {
|
|
const totalDealt = playerHand.value.length + opponentHand.value.length
|
|
return totalDealt % 2 === 0 ? 'Player' : 'Opponent'
|
|
})
|
|
|
|
// Get random card
|
|
const getRandomCard = () => {
|
|
return sampleCards[Math.floor(Math.random() * sampleCards.length)]
|
|
}
|
|
|
|
// Get position of an element
|
|
const getElementPosition = (el) => {
|
|
if (!el) return { x: 0, y: 0 }
|
|
const rect = el.$el?.getBoundingClientRect() || el.getBoundingClientRect()
|
|
return {
|
|
x: rect.left + rect.width / 2 - 64, // Center, minus half card width
|
|
y: rect.top + rect.height / 2 - 89, // Center, minus half card height
|
|
}
|
|
}
|
|
|
|
// Deal a single card
|
|
const dealSingleCard = async () => {
|
|
if (cardsRemaining.value === 0) return
|
|
|
|
await nextTick()
|
|
|
|
// Determine recipient
|
|
const totalDealt = playerHand.value.length + opponentHand.value.length
|
|
const isPlayer = totalDealt % 2 === 0
|
|
|
|
// Get positions
|
|
const startPos = getElementPosition(deckRef.value)
|
|
const endPos = getElementPosition(isPlayer ? playerHandRef.value : opponentHandRef.value)
|
|
|
|
// Create flying card
|
|
const card = getRandomCard()
|
|
const flyingCard = {
|
|
id: flyingCardIdCounter++,
|
|
card,
|
|
startPosition: startPos,
|
|
endPosition: endPos,
|
|
faceDown: !isPlayer, // Face down for opponent
|
|
delay: 0,
|
|
recipient: isPlayer ? 'player' : 'opponent',
|
|
}
|
|
|
|
flyingCards.value.push(flyingCard)
|
|
cardsRemaining.value--
|
|
}
|
|
|
|
// Flying card animation complete
|
|
const onFlyingCardComplete = (flyingCard) => {
|
|
// Add card to appropriate hand
|
|
if (flyingCard.recipient === 'player') {
|
|
playerHand.value.push(flyingCard.card)
|
|
} else {
|
|
opponentHand.value.push(flyingCard.card)
|
|
}
|
|
|
|
// Remove flying card
|
|
flyingCards.value = flyingCards.value.filter((fc) => fc.id !== flyingCard.id)
|
|
}
|
|
|
|
// Start automatic dealing sequence
|
|
const startDealing = async () => {
|
|
if (isDealing.value) return
|
|
|
|
isDealing.value = true
|
|
|
|
// Deal 6 cards (3 to each player)
|
|
for (let i = 0; i < 6; i++) {
|
|
if (cardsRemaining.value === 0) break
|
|
|
|
await dealSingleCard()
|
|
|
|
// Wait 200ms before next card
|
|
await new Promise((resolve) => setTimeout(resolve, 700))
|
|
}
|
|
|
|
isDealing.value = false
|
|
}
|
|
|
|
// Reset everything
|
|
const reset = () => {
|
|
playerHand.value = []
|
|
opponentHand.value = []
|
|
cardsRemaining.value = 40
|
|
flyingCards.value = []
|
|
isDealing.value = false
|
|
}
|
|
</script>
|