// Phaser is loaded from CDN, so we don't need to import it as a module /** * Main game configuration and initialization */ const config = { type: Phaser.AUTO, width: 1600, height: 900, parent: 'game-container', scene: { preload: preload, create: create, update: update }, scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH }, physics: { default: 'arcade', arcade: { gravity: { y: 0 }, debug: false } } }; /** * Game instance */ let game; /** * Preload function - load all assets before the game starts */ function preload() { console.log('Preloading game assets...'); // Create a simple loading bar const progressBar = this.add.graphics(); const progressBox = this.add.graphics(); progressBox.fillStyle(0x242424, 0.8); progressBox.fillRect(700, 430, 200, 50); const width = this.cameras.main.width; const height = this.cameras.main.height; const loadingText = this.make.text({ x: width / 2, y: height / 2 - 50, text: 'Loading...', style: { font: '20px monospace', fill: '#ffffff' } }); loadingText.setOrigin(0.5, 0.5); const percentText = this.make.text({ x: width / 2, y: height / 2 - 5, text: '0%', style: { font: '18px monospace', fill: '#ffffff' } }); percentText.setOrigin(0.5, 0.5); const assetText = this.make.text({ x: width / 2, y: height / 2 + 50, text: '', style: { font: '18px monospace', fill: '#ffffff' } }); assetText.setOrigin(0.5, 0.5); // Update loading progress this.load.on('progress', (value) => { percentText.setText(parseInt(value * 100) + '%'); progressBar.clear(); progressBar.fillStyle(0xffffff, 1); progressBar.fillRect(700, 430, 200 * value, 50); }); this.load.on('fileprogress', (file) => { assetText.setText('Loading asset: ' + file.key); }); this.load.on('complete', () => { progressBar.destroy(); progressBox.destroy(); loadingText.destroy(); percentText.destroy(); assetText.destroy(); }); } /** * Create function - setup the game world and initial state */ function create() { console.log('Creating game scene...'); // Set background color this.cameras.main.setBackgroundColor('#2d5a8c'); // Add a simple title text const title = this.add.text(800, 100, 'Deck Building Card Battle', { fontSize: '32px', fill: '#ffffff', align: 'center' }); title.setOrigin(0.5); // Add player and opponent rows for cards in center of screen // Player row at bottom (closest to player) const playerRow = this.add.rectangle(800, 600, 1400, 120, 0x3a7c2d); playerRow.setOrigin(0.5); // Opponent row at top (closest to opponent) const opponentRow = this.add.rectangle(800, 300, 1400, 120, 0x3a7c2d); opponentRow.setOrigin(0.5); // Add some instructions const instructions = this.add.text(800, 850, 'Use mouse to play cards and build your deck', { fontSize: '16px', fill: '#ffffff', align: 'center' }); instructions.setOrigin(0.5); // Initialize game state this.gameState = { playerDeck: [], opponentDeck: [], playerHand: [], opponentHand: [], playerField: [], opponentField: [], selectedCard: null, turnPhase: 'draw', currentPlayer: 'player' }; // Assign the first deck (deck1) to the player this.gameState.playerDeck = [...window.deck1]; // Shuffle the deck using Fisher-Yates algorithm (using Phaser's scope) shuffleDeck(this.gameState.playerDeck); // Deal 3 cards to player's hand this.gameState.playerHand = this.gameState.playerDeck.splice(0, 3); // Display the player's hand at the bottom of the screen displayPlayerHand.call(this); console.log('Game scene created successfully'); } /** * Shuffle an array using Fisher-Yates algorithm */ function shuffleDeck(deck) { for (let i = deck.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [deck[i], deck[j]] = [deck[j], deck[i]]; } } /** * Display the player's hand at the bottom of the screen */ function displayPlayerHand() { const cardWidth = 100; const cardHeight = 140; const spacing = 20; // Calculate total width needed for all cards const totalWidth = (this.gameState.playerHand.length * cardWidth) + ((this.gameState.playerHand.length - 1) * spacing); // Starting x position to center the hand const startX = (1600 - totalWidth) / 2; // Position cards at the bottom of the screen const yPosition = 850; this.gameState.playerHand.forEach((card, index) => { const xPosition = startX + (index * (cardWidth + spacing)); // Create a simple card placeholder with number const cardSprite = this.add.rectangle(xPosition, yPosition, cardWidth, cardHeight, 0x8B4513); cardSprite.setOrigin(0.5); // Add the number to the card const numberText = this.add.text(xPosition, yPosition - 20, `${card.number}`, { fontSize: '24px', fill: '#ffffff' }); numberText.setOrigin(0.5); // Display attack in upper left corner const attackText = this.add.text(xPosition - 40, yPosition - 50, `A:${card.attack}`, { fontSize: '16px', fill: '#ffffff' }); attackText.setOrigin(0.5); // Display shield in lower left corner const shieldText = this.add.text(xPosition - 40, yPosition + 50, `S:${card.shield}`, { fontSize: '16px', fill: '#ffffff' }); shieldText.setOrigin(0.5); // Display health in lower right corner const healthText = this.add.text(xPosition + 40, yPosition + 50, `H:${card.health}`, { fontSize: '16px', fill: '#ffffff' }); healthText.setOrigin(0.5); // Store references to stat texts with sprite reference card.attackText = attackText; card.shieldText = shieldText; card.healthText = healthText; // Store card data with sprite reference card.sprite = cardSprite; card.text = numberText; // Add hover events for the card addInteractiveCard.call(this, card, xPosition, yPosition, cardWidth, cardHeight); }); } /** * Make a single card interactive with hover effects */ function addInteractiveCard(card, xPosition, yPosition, width, height) { const originalY = yPosition; // Create a transparent hit area for the card that matches its dimensions const hitArea = this.add.rectangle(xPosition, yPosition + 70, width - 10, height - 10); hitArea.setOrigin(0.5); hitArea.setInteractive(); // Add event listeners for hover effects hitArea.on('pointerover', () => { // Only allow hover effects if the card is in hand (not already on field) const isInHand = this.gameState.playerHand.includes(card); if (isInHand && !card.isLocked) { // Raise the card up to full view (move it up by 30 pixels) if (card.sprite) { card.sprite.setY(originalY - 30); } if (card.text) { card.text.setY(originalY - 50); } // Move stat texts with the card if (card.attackText) { card.attackText.setY(originalY - 80); } if (card.shieldText) { card.shieldText.setY(originalY + 20); } if (card.healthText) { card.healthText.setY(originalY + 20); } } }); hitArea.on('pointerout', () => { // Only allow hover effects if the card is in hand (not already on field) const isInHand = this.gameState.playerHand.includes(card); if (isInHand && !card.isLocked) { // Return the card to its original position if (card.sprite) { card.sprite.setY(originalY); } if (card.text) { card.text.setY(originalY - 20); } // Return stat texts to original positions if (card.attackText) { card.attackText.setY(originalY - 50); } if (card.shieldText) { card.shieldText.setY(originalY + 50); } if (card.healthText) { card.healthText.setY(originalY + 50); } } }); // Add click event to lock the card in place hitArea.on('pointerdown', () => { // Only allow clicking if the card is in hand (not already on field) const isInHand = this.gameState.playerHand.includes(card); if (isInHand) { // Lock the card in its current position card.isLocked = true; // Remove hover effects while locked if (card.sprite) { card.sprite.setY(originalY - 30); // Keep it raised up when locked } if (card.text) { card.text.setY(originalY - 50); } // Create Play and Cancel buttons above the card createCardButtons.call(this, card, xPosition, originalY - 30); } }); } /** * Update function - called every frame */ function update() { // Game logic updates go here } /** * Create Play and Cancel buttons for a locked card */ function createCardButtons(card, xPosition, yPosition) { const buttonWidth = 60; const buttonHeight = 30; const spacing = 10; // Position the buttons above the card (at yPosition - 40) const playButtonX = xPosition - buttonWidth - spacing / 2; // Left of card const cancelButtonX = xPosition + buttonWidth + spacing / 2; // Right of card const buttonY = yPosition - 40; // Create Play button const playButton = this.add.rectangle(playButtonX, buttonY, buttonWidth, buttonHeight, 0x00ff00); // Green color playButton.setOrigin(0.5); playButton.setInteractive(); // Add text to the Play button const playText = this.add.text(playButtonX, buttonY, 'Play', { fontSize: '14px', fill: '#ffffff' }); playText.setOrigin(0.5); // Create Cancel button const cancelButton = this.add.rectangle(cancelButtonX, buttonY, buttonWidth, buttonHeight, 0xff0000); // Red color cancelButton.setOrigin(0.5); cancelButton.setInteractive(); // Add text to the Cancel button const cancelText = this.add.text(cancelButtonX, buttonY, 'Cancel', { fontSize: '14px', fill: '#ffffff' }); cancelText.setOrigin(0.5); // Store references to buttons in the card object card.playButton = playButton; card.cancelButton = cancelButton; card.playButtonText = playText; card.cancelButtonText = cancelText; // Add click events for the buttons playButton.on('pointerdown', () => { // Move the card to the player's field (center of screen) moveCardToField.call(this, card); // Remove buttons and unlock the card removeCardButtons(card); }); cancelButton.on('pointerdown', () => { // Return card to original position in hand returnCardToHand.call(this, card); // Remove buttons and unlock the card removeCardButtons(card); }); } /** * Move a card to the player's field (player row on far right) */ function moveCardToField(card) { // Remove it from hand and add it to field const index = this.gameState.playerHand.indexOf(card); if (index > -1) { this.gameState.playerHand.splice(index, 1); this.gameState.playerField.push(card); } // Calculate starting position for the animation (right side of screen) const startX = 1600 + 50; // Start just off-screen to the right const startY = 600; // Player field y-position (bottom row) // Calculate target x position based on how many cards are already in the field // Cards stack from right to left with each card being 120px wide + 20px spacing const cardWidth = 100; const spacing = 20; const currentFieldCount = this.gameState.playerField.length - 1; // -1 because we just added the new card const targetX = 1600 - (cardWidth + spacing) * currentFieldCount - (cardWidth / 2); // Position from right edge // Animate the card moving from right to left towards its final position in player field this.tweens.add({ targets: [card.sprite, card.text, card.attackText, card.shieldText, card.healthText], x: targetX, y: startY, duration: 500, // 500ms animation time ease: 'Power2' }); // 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 this.gameState.playerHand.forEach(handCard => { if (handCard.sprite) { handCard.sprite.destroy(); } if (handCard.text) { handCard.text.destroy(); } }); // Redisplay the updated player's hand at the bottom of the screen displayPlayerHand.call(this); }, 500); // Wait for animation to complete before updating hand } /** * Return a locked card to its original position in the player's hand */ function returnCardToHand(card) { // Reset the locked state card.isLocked = false; // Remove buttons and text references from the card object if (card.playButton) { card.playButton.destroy(); card.playButtonText.destroy(); } if (card.cancelButton) { card.cancelButton.destroy(); card.cancelButtonText.destroy(); } // Reset positions to original hand position const yPosition = 850; // Original hand position at bottom of screen const originalY = yPosition; if (card.sprite) { card.sprite.setY(originalY); } if (card.text) { card.text.setY(originalY - 20); } } /** * Remove buttons from a locked card */ function removeCardButtons(card) { // Reset the locked state card.isLocked = false; // Destroy button elements if they exist if (card.playButton) { card.playButton.destroy(); card.playButtonText.destroy(); delete card.playButton; delete card.playButtonText; } if (card.cancelButton) { card.cancelButton.destroy(); card.cancelButtonText.destroy(); delete card.cancelButton; delete card.cancelButtonText; } } /** * Initialize the game when the page loads */ document.addEventListener('DOMContentLoaded', () => { console.log('Initializing Phaser 3 card battle game...'); // Create the game instance game = new Phaser.Game(config); console.log('Phaser 3 card battle game initialized'); }); // Export for potential module usage (not needed when using CDN) // export { config, game };