Compare commits

...

2 Commits

Author SHA1 Message Date
Brian Fertig 8e8bc514ad feat: Implement card data management with multiple decks
- Added card-data.js file to define four different decks (Player default, Warrior, Mage, Archer)
- Each deck contains 10 creature cards with randomized attack, shield, and health values
- Updated index.html to load the new card data script before main.js
- Modified main.js to initialize player deck using predefined deck1 instead of generating random cards
- Removed old deck generation code from main.js
2025-08-12 20:25:55 -06:00
Brian Fertig 282c30eba4 feat: Add card locking mechanism with play/cancel buttons and hand management
- Implemented card locking functionality that prevents cards from being dragged after clicking
- Added visual feedback for locked cards (raised position)
- Created Play and Cancel buttons above locked cards to confirm or revert actions
- Implemented game logic for moving cards to player field and returning them to hand
- Enhanced user interaction with proper button handling and state management
- Updated card display system to handle repositioning of remaining hand cards after interactions
2025-08-12 19:40:02 -06:00
3 changed files with 236 additions and 15 deletions

View File

@ -4,6 +4,8 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Phaser 3 Card Battle Game</title> <title>Phaser 3 Card Battle Game</title>
<!-- Prevent favicon.ico request that causes errors -->
<link rel="icon" href="data:;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChgGA60e6kgAAAABJRU5ErkJggg==" />
<style> <style>
body { body {
margin: 0; margin: 0;
@ -27,7 +29,10 @@
<!-- Load Phaser 3 from CDN --> <!-- Load Phaser 3 from CDN -->
<script src="https://cdn.jsdelivr.net/npm/phaser@3.70.0/dist/phaser.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/phaser@3.70.0/dist/phaser.min.js"></script>
<!-- Card data scripts -->
<script src="src/card-data.js"></script>
<!-- Game script --> <!-- Game script -->
<script src="src/main.js" type="module"></script> <script src="src/main.js"></script>
</body> </body>
</html> </html>

61
src/card-data.js Normal file
View File

@ -0,0 +1,61 @@
// Card data definitions for different decks
// Each deck contains 10 cards with attack, shield, health and required-army-size attributes
// Deck 1 - Player's default deck (will be assigned to player)
window.deck1 = [
{ id: 1, number: 1, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 2, number: 2, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 3, number: 3, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 4, number: 4, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 5, number: 5, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 6, number: 6, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 7, number: 7, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 8, number: 8, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 9, number: 9, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 10, number: 10, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 }
];
// Deck 2 - Warrior deck
window.deck2 = [
{ id: 1, number: 1, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 2, number: 2, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 3, number: 3, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 4, number: 4, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 5, number: 5, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 6, number: 6, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 7, number: 7, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 8, number: 8, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 9, number: 9, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 10, number: 10, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 }
];
// Deck 3 - Mage deck
window.deck3 = [
{ id: 1, number: 1, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 2, number: 2, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 3, number: 3, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 4, number: 4, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 5, number: 5, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 6, number: 6, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 7, number: 7, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 8, number: 8, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 9, number: 9, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 10, number: 10, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 }
];
// Deck 4 - Archer deck
window.deck4 = [
{ id: 1, number: 1, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 2, number: 2, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 3, number: 3, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 4, number: 4, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 5, number: 5, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 6, number: 6, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 7, number: 7, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 8, number: 8, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 9, number: 9, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 },
{ id: 10, number: 10, type: 'creature', attack: Math.floor(Math.random() * 5) + 1, shield: Math.floor(Math.random() * 5) + 1, health: Math.floor(Math.random() * 5) + 1, requiredArmySize: 1 }
];
// Make all decks available globally for the game
window.allDecks = [window.deck1, window.deck2, window.deck3, window.deck4];

View File

@ -141,17 +141,8 @@ function create() {
currentPlayer: 'player' currentPlayer: 'player'
}; };
// Create a 10-card deck with numbers 1-10 // Assign the first deck (deck1) to the player
this.gameState.playerDeck = []; this.gameState.playerDeck = [...window.deck1];
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) // Shuffle the deck using Fisher-Yates algorithm (using Phaser's scope)
shuffleDeck(this.gameState.playerDeck); shuffleDeck(this.gameState.playerDeck);
@ -239,13 +230,30 @@ function addInteractiveCard(card, xPosition, yPosition, width, height) {
hitArea.on('pointerout', () => { hitArea.on('pointerout', () => {
// Return the card to its original position // Return the card to its original position
if (card.sprite) { if (card.sprite && !card.isLocked) {
card.sprite.setY(originalY); card.sprite.setY(originalY);
} }
if (card.text) { if (card.text && !card.isLocked) {
card.text.setY(originalY - 20); 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);
});
} }
/** /**
@ -255,6 +263,153 @@ function update() {
// Game logic updates go here // 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 * Initialize the game when the page loads
*/ */