// 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); // Create a basic game area const gameArea = this.add.rectangle(800, 450, 1400, 700, 0x3a7c2d); gameArea.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' }; // Create a 10-card deck with numbers 1-10 this.gameState.playerDeck = []; for (let i = 1; i <= 10; i++) { this.gameState.playerDeck.push({ id: i, number: i, type: 'creature', // placeholder type attack: Math.floor(Math.random() * 5) + 1, // random attack value health: Math.floor(Math.random() * 5) + 1 // random health value }); } // 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); // 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', () => { // 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); } }); hitArea.on('pointerout', () => { // Return the card to its original position if (card.sprite && !card.isLocked) { card.sprite.setY(originalY); } if (card.text && !card.isLocked) { card.text.setY(originalY - 20); } }); // Add click event to lock the card in place hitArea.on('pointerdown', () => { // 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 (center of screen) */ function moveCardToField(card) { // For now, we'll just remove it from hand and add it to field // In a real implementation this would involve more complex logic const index = this.gameState.playerHand.indexOf(card); if (index > -1) { this.gameState.playerHand.splice(index, 1); this.gameState.playerField.push(card); } // Remove the card from display if (card.sprite) { card.sprite.destroy(); } if (card.text) { card.text.destroy(); } // Re-display the player's hand to update positions of remaining cards // 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); } /** * 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 };