From a4931d491a74fb954db5beb8614be790aec574c6 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Wed, 13 Aug 2025 16:20:30 -0600 Subject: [PATCH] feat: Implement opponent AI with CPU card playing logic and enhanced hand display This commit adds full opponent functionality to the game including: - Shuffling and dealing cards to both player and CPU decks - Displaying opponent's hand face-down at top of screen - Implementing CPU turn logic that automatically plays random cards - Adding animations for CPU card placement on field with stats display - Switching turns between player and CPU after card plays The changes enable a complete two-player gameplay experience where the CPU automatically responds to player actions, creating a more engaging game flow. --- src/main.js | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/src/main.js b/src/main.js index 60667ab..b6fd3fc 100644 --- a/src/main.js +++ b/src/main.js @@ -149,15 +149,25 @@ function create() { // Assign the first deck (deck1) to the player this.gameState.playerDeck = [...window.deck1]; - // Shuffle the deck using Fisher-Yates algorithm (using Phaser's scope) + // Assign deck2 to the opponent + this.gameState.opponentDeck = [...window.deck2]; + + // Shuffle both decks using Fisher-Yates algorithm (using Phaser's scope) shuffleDeck(this.gameState.playerDeck); + shuffleDeck(this.gameState.opponentDeck); // Deal 3 cards to player's hand this.gameState.playerHand = this.gameState.playerDeck.splice(0, 3); + // Deal 3 cards to opponent's hand + this.gameState.opponentHand = this.gameState.opponentDeck.splice(0, 3); + // Display the player's hand at the bottom of the screen displayPlayerHand.call(this); + // Display the opponent's hand at the top of the screen (face down) + displayOpponentHand.call(this); + console.log('Game scene created successfully'); } @@ -237,6 +247,44 @@ function displayPlayerHand() { }); } +/** + * Display the opponent's hand at the top of the screen (face down) + */ +function displayOpponentHand() { + const cardWidth = 100; + const cardHeight = 140; + const spacing = 20; + + // Calculate total width needed for all cards + const totalWidth = (this.gameState.opponentHand.length * cardWidth) + ((this.gameState.opponentHand.length - 1) * spacing); + + // Starting x position to center the hand + const startX = (1600 - totalWidth) / 2; + + // Position cards at the top of the screen (opponent's row) + const yPosition = 350; // Slightly below opponentRow rectangle (which is at y=300) + + this.gameState.opponentHand.forEach((card, index) => { + const xPosition = startX + (index * (cardWidth + spacing)); + + // Create a simple card placeholder - face down (gray color) + const cardSprite = this.add.rectangle(xPosition, yPosition, cardWidth, cardHeight, 0x808080); // Gray color for face-down cards + cardSprite.setOrigin(0.5); + + // Add the number to the card (face down - show only number) + const numberText = this.add.text(xPosition, yPosition - 20, `${card.number}`, { + fontSize: '24px', + fill: '#ffffff' + }); + numberText.setOrigin(0.5); + + // For face-down cards, we don't display attack/shield/health values + // Store references to sprite with no stat texts for opponent's hand + card.sprite = cardSprite; + card.text = numberText; + }); +} + /** * Make a single card interactive with hover effects */ @@ -418,6 +466,9 @@ function moveCardToField(card) { ease: 'Power2' }); + // Set the current player to CPU after playing a card + this.gameState.currentPlayer = 'cpu'; + // Re-display the player's hand to update positions of remaining cards after animation completes setTimeout(() => { // Clear existing hand display first by removing all sprites and texts @@ -433,6 +484,11 @@ function moveCardToField(card) { // Redisplay the updated player's hand at the bottom of the screen displayPlayerHand.call(this); }, 500); // Wait for animation to complete before updating hand + + // After a short delay, have CPU play a card automatically (if available) + setTimeout(() => { + cpuPlayCard.call(this); + }, 1000); // Small delay before CPU plays card } /** @@ -486,6 +542,91 @@ function removeCardButtons(card) { } } +/** + * CPU plays a random card from its hand + */ +function cpuPlayCard() { + // Only play a card if it's the CPU's turn and they have cards in hand + if (this.gameState.currentPlayer !== 'cpu' || this.gameState.opponentHand.length === 0) { + return; + } + + // Select a random card from opponent's hand + const randomIndex = Math.floor(Math.random() * this.gameState.opponentHand.length); + const card = this.gameState.opponentHand[randomIndex]; + + // Remove it from hand and add it to field + this.gameState.opponentHand.splice(randomIndex, 1); + this.gameState.opponentField.push(card); + + // Calculate starting position for the animation (left side of screen) + const startX = -50; // Start just off-screen to the left + const startY = 300; // Opponent field y-position (top row) + + // Calculate target x position based on how many cards are already in the field + // Cards stack from left to right with each card being 120px wide + 20px spacing + const cardWidth = 100; + const spacing = 20; + const currentFieldCount = this.gameState.opponentField.length - 1; // -1 because we just added the new card + const targetX = (cardWidth + spacing) * currentFieldCount + (cardWidth / 2); // Position from left edge + + // For CPU cards in field, show full stats (not face-down) + const cpuCardSprite = this.add.rectangle(targetX, startY, cardWidth, 140, 0x8B4513); // Brown color for card background + cpuCardSprite.setOrigin(0.5); + + // Add the number to the CPU's played card (showing full stats) + const numberText = this.add.text(targetX, startY - 20, `${card.number}`, { + fontSize: '24px', + fill: '#ffffff' + }); + numberText.setOrigin(0.5); + + // Display attack in upper left corner for CPU card + const attackText = this.add.text(targetX - 40, startY - 50, `A:${card.attack}`, { + fontSize: '16px', + fill: '#ffffff' + }); + attackText.setOrigin(0.5); + + // Display shield in lower left corner for CPU card + const shieldText = this.add.text(targetX - 40, startY + 50, `S:${card.shield}`, { + fontSize: '16px', + fill: '#ffffff' + }); + shieldText.setOrigin(0.5); + + // Display health in lower right corner for CPU card + const healthText = this.add.text(targetX + 40, startY + 50, `H:${card.health}`, { + fontSize: '16px', + fill: '#ffffff' + }); + healthText.setOrigin(0.5); + + // Animate the card moving from left to right towards its final position in opponent field + this.tweens.add({ + targets: [cpuCardSprite, numberText, attackText, shieldText, healthText], + x: targetX, + y: startY, + duration: 500, // 500ms animation time + ease: 'Power2' + }); + + // Update the card's sprite and text references to point to the new elements for CPU field display + card.sprite = cpuCardSprite; + card.text = numberText; + card.attackText = attackText; + card.shieldText = shieldText; + card.healthText = healthText; + + // After a short delay, return to player's turn (or end game if needed) + setTimeout(() => { + this.gameState.currentPlayer = 'player'; + + // Redisplay the opponent's hand at the top of the screen - now only cards that remain in hand + displayOpponentHand.call(this); + }, 1000); // Wait for animation to complete before returning to player turn +} + /** * Initialize the game when the page loads */