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.
This commit is contained in:
Brian Fertig 2025-08-13 16:20:30 -06:00
parent a07d64e9b1
commit a4931d491a
1 changed files with 142 additions and 1 deletions

View File

@ -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
*/