/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, gamesPlayed: 0 }); /**** * Classes ****/ // Button class for game controls var Button = Container.expand(function (text, width, height) { var self = Container.call(this); self.isEnabled = true; // Button background self.background = self.attachAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5, width: width || 200, height: height || 80 }); // Button text self.text = new Text2(text || "Button", { size: 28, fill: 0xFFFFFF }); self.text.anchor.set(0.5, 0.5); self.addChild(self.text); // Interaction handlers self.down = function (x, y, obj) { if (self.isEnabled) { self.background.alpha = 0.7; } }; self.up = function (x, y, obj) { if (self.isEnabled) { self.background.alpha = 1; if (typeof self.onClick === 'function') { self.onClick(); } } }; // Enable/disable the button self.setEnabled = function (enabled) { self.isEnabled = enabled; self.alpha = enabled ? 1 : 0.5; }; return self; }); // Card class to represent game cards var Card = Container.expand(function (type, value, name, description) { var self = Container.call(this); // Card properties self.type = type || 'attack'; // attack, defense, utility self.value = value || 1; // primary value (attack/defense/resource) self.name = name || 'Basic Card'; self.description = description || 'A basic card'; self.isPlayable = true; self.isSelected = false; // Visual elements var cardFrontId = ''; switch (self.name) { case 'Fierce Strike': cardFrontId = 'fierceStrikeFront'; break; case 'Quick Slash': cardFrontId = 'quickSlashFront'; break; case 'Heavy Blow': cardFrontId = 'heavyBlowFront'; break; case 'Crushing Attack': cardFrontId = 'crushingAttackFront'; break; case 'Jab': cardFrontId = 'jabFront'; break; case 'Block': cardFrontId = 'blockFront'; break; case 'Shield Up': cardFrontId = 'shieldUpFront'; break; case 'Deflect': cardFrontId = 'deflectFront'; break; case 'Parry': cardFrontId = 'parryFront'; break; case 'Focus': cardFrontId = 'focusFront'; break; case 'Strategize': cardFrontId = 'strategizeFront'; break; case 'Second Wind': cardFrontId = 'secondWindFront'; break; default: cardFrontId = 'cardFront'; } self.background = self.attachAsset(cardFrontId, { anchorX: 0.5, anchorY: 0.5 }); // Image area self.imageArea = new Container(); self.imageArea.width = 250; self.imageArea.height = 200; self.imageArea.y = -75; self.addChild(self.imageArea); // Text area self.textArea = new Container(); self.textArea.width = 250; self.textArea.height = 150; self.textArea.y = 75; self.textArea.tint = 0xFFFFFF; // White background for text area self.addChild(self.textArea); // Card title self.titleText = new Text2(self.name, { size: 24, fill: 0x000000 }); self.titleText.anchor.set(0.5, 0); self.titleText.y = -150; self.textArea.addChild(self.titleText); // Card value and type var typeColors = { attack: '#ff3333', defense: '#3399ff', utility: '#33cc33' }; self.valueText = new Text2(self.value.toString(), { size: 40, fill: typeColors[self.type] || "#000000" }); self.valueText.anchor.set(0.5, 0.5); self.valueText.y = -70; self.addChild(self.valueText); // Card type icon var iconType = self.type === 'attack' ? 'attackIcon' : self.type === 'defense' ? 'defenseIcon' : 'resourceIcon'; self.typeIcon = self.attachAsset(iconType, { anchorX: 0.5, anchorY: 0.5, x: 0, y: -20 }); // Description text self.descText = new Text2(self.description, { size: 18, fill: 0x333333 }); self.descText.anchor.set(0.5, 0); self.descText.y = 20; self.descText.x = 0; // Limit text width if (self.descText.width > 230) { self.descText.scale.set(230 / self.descText.width); } self.addChild(self.descText); // Resource value (all cards can be discarded for resources) self.resourceValue = Math.max(1, Math.floor(self.value / 2)); self.resourceText = new Text2("R: " + self.resourceValue, { size: 24, fill: 0xFFCC00 }); self.resourceText.anchor.set(0.5, 1); self.resourceText.y = 140; self.addChild(self.resourceText); // Highlight for selection self.highlight = self.attachAsset('cardHighlight', { anchorX: 0.5, anchorY: 0.5 }); self.highlight.alpha = 0; // Card interaction events self.down = function (x, y, obj) { if (self.isPlayable) { self.isSelected = true; self.highlight.alpha = 0.5; } }; self.up = function (x, y, obj) { if (!self.isPlayable) { return; } // Call the game's card played handler - implemented in game.cardPlayed var gameCoords = game.toLocal(self.parent.toGlobal(self.position)); game.cardSelected(self, gameCoords.x, gameCoords.y); }; // Toggle selection state self.setSelected = function (selected) { self.isSelected = selected; self.highlight.alpha = selected ? 0.5 : 0; }; // Set card playability self.setPlayable = function (playable) { self.isPlayable = playable; self.alpha = playable ? 1 : 0.6; }; return self; }); // Hero class representing player or enemy champion var Hero = Container.expand(function (isPlayer) { var self = Container.call(this); // Hero properties self.isPlayer = isPlayer; self.health = 20; self.maxHealth = 20; self.resources = 0; self.maxResources = 10; self.attack = 0; self.defense = 0; self.name = isPlayer ? "Player Hero" : "Enemy Hero"; // Visual representation self.avatar = self.attachAsset(isPlayer ? 'heroAvatar' : 'enemyAvatar', { anchorX: 0.5, anchorY: 0.5 }); // Name display self.nameText = new Text2(self.name, { size: 28, fill: 0xFFFFFF }); self.nameText.anchor.set(0.5, 0); self.nameText.y = -130; self.addChild(self.nameText); // Health bar self.healthBarBg = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, y: 180, tint: 0x666666 }); self.healthBar = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5, x: -150, y: 200, scaleX: 2, // Further increase the width of the health bar tint: 0x00ff00 // Apply a fantasy-themed green tint }); self.healthText = new Text2(self.health + "/" + self.maxHealth, { size: 22, fill: 0xFFFFFF }); self.healthText.anchor.set(0.5, 0.5); self.healthText.y = 200; self.addChild(self.healthText); // Resource bar (only for player) if (isPlayer) { self.resourceBarBg = self.attachAsset('resourceBar', { anchorX: 0.5, anchorY: 0.5, y: 150, tint: 0x666666 }); self.resourceBar = self.attachAsset('resourceBar', { anchorX: 0, anchorY: 0.5, x: -150, y: 150, scaleX: 2, // Further increase the width of the resource bar tint: 0xFFD700 // Apply a fantasy-themed gold tint }); // Resource text self.resourceText = new Text2(self.resources + "/" + self.maxResources, { size: 22, fill: 0xFFFFFF }); self.resourceText.anchor.set(0.5, 0.5); self.resourceText.y = 150; self.addChild(self.resourceText); } // Update health display self.updateHealth = function (newHealth) { self.health = Math.max(0, Math.min(self.maxHealth, newHealth)); self.healthText.setText(self.health + "/" + self.maxHealth); // Update health bar var healthRatio = self.health / self.maxHealth; self.healthBar.width = 200 * healthRatio; self.healthBar.x = -100; // Flash red if damage taken if (newHealth < self.health) { LK.effects.flashObject(self.avatar, 0xff0000, 500); } return self.health; }; // Update resource display (player only) self.updateResources = function (newResources) { if (!self.isPlayer) { return; } self.resources = Math.max(0, Math.min(self.maxResources, newResources)); self.resourceText.setText(self.resources + "/" + self.maxResources); // Update resource bar var resourceRatio = self.resources / self.maxResources; self.resourceBar.width = 200 * resourceRatio; }; // Take damage accounting for defense self.takeDamage = function (amount) { var actualDamage = Math.max(0, amount - (self.defense + 2)); // Always block two damage self.defense = Math.max(0, self.defense - amount); if (actualDamage > 0) { self.updateHealth(self.health - actualDamage); LK.getSound('damage').play(); // Flash effect for damage LK.effects.flashObject(self, 0xff0000, 300); } return actualDamage; }; // Add attack power self.addAttack = function (amount) { self.attack += amount; return self.attack; }; // Add defense self.addDefense = function (amount) { self.defense += amount; return self.defense; }; // Reset turn stats self.resetTurnStats = function () { self.attack = 0; self.defense = 0; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ // Game state var playerHand = []; var playerDeck = []; var enemyDeck = []; var enemyHand = []; var discardPile = []; var isPlayerTurn = true; var gameStarted = false; var selectedCard = null; var draggedCard = null; var turnCounter = 0; var playZone = null; var maxHandSize = 5; var roundActions = []; // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var CARD_WIDTH = 250; var CARD_HEIGHT = 350; var HAND_Y = 2300; var PLAY_ZONE_Y = 1500; // Game entities var playerHero; var enemyHero; var endTurnButton; var playCardButton; var discardCardButton; var actionText; var turnIndicator; // Initialize game elements function initializeGame() { // Create heroes playerHero = new Hero(true); playerHero.x = GAME_WIDTH / 2; playerHero.y = GAME_HEIGHT - 200; game.addChild(playerHero); enemyHero = new Hero(false); enemyHero.x = GAME_WIDTH / 2; enemyHero.y = 500; game.addChild(enemyHero); // Create resource indicator resourceIndicator = new Text2("Resources: " + playerHero.resources, { size: 30, fill: 0xFFCC00 }); resourceIndicator.anchor.set(0.5, 0.5); resourceIndicator.x = GAME_WIDTH - 150; resourceIndicator.y = GAME_HEIGHT / 2; game.addChild(resourceIndicator); // Create card templates createCardTemplates(); // Create play zone playZone = new Container(); playZone.x = GAME_WIDTH / 2; playZone.y = PLAY_ZONE_Y; game.addChild(playZone); // Create UI elements createUIElements(); // Shuffle and deal initial hands initializeDecks(); shuffleDeck(playerDeck); shuffleDeck(enemyDeck); dealInitialHands(); // Setup turn indicator turnIndicator = LK.getAsset('turnIndicator', { anchorX: 0.5, anchorY: 0.5 }); turnIndicator.x = GAME_WIDTH / 2; turnIndicator.y = GAME_HEIGHT / 2 - 300; turnIndicator.alpha = 0.7; game.addChild(turnIndicator); // Add action text actionText = new Text2("Your Turn", { size: 40, fill: 0xFFFFFF }); actionText.anchor.set(0.5, 0.5); actionText.x = GAME_WIDTH / 2; actionText.y = GAME_HEIGHT / 2 - 300; game.addChild(actionText); // Start with player turn startPlayerTurn(); // Play background music LK.playMusic('battleMusic'); gameStarted = true; } // Create UI buttons function createUIElements() { // End turn button endTurnButton = new Button("End Turn", 250, 80); endTurnButton.x = GAME_WIDTH - 200; endTurnButton.y = GAME_HEIGHT - 200; endTurnButton.onClick = function () { if (isPlayerTurn) { endPlayerTurn(); } }; game.addChild(endTurnButton); // Play card button (appears when a card is selected) playCardButton = new Button("Play Card", 200, 80); playCardButton.x = GAME_WIDTH / 2 - 120; playCardButton.y = GAME_HEIGHT - 200; playCardButton.alpha = 0; playCardButton.onClick = function () { if (selectedCard) { playCard(selectedCard); } }; game.addChild(playCardButton); // Discard for resources button discardCardButton = new Button("Pitch", 200, 80); discardCardButton.x = GAME_WIDTH / 2 + 120; discardCardButton.y = GAME_HEIGHT - 200; discardCardButton.alpha = 0; discardCardButton.onClick = function () { if (selectedCard) { pitchForResources(selectedCard); } }; game.addChild(discardCardButton); } // Create card templates function createCardTemplates() { cardTemplates = [ // Attack cards { type: 'attack', value: 3, name: 'Fierce Strike', description: 'Deal 3 damage to opponent' }, { type: 'attack', value: 2, name: 'Quick Slash', description: 'Deal 2 damage to opponent' }, { type: 'attack', value: 4, name: 'Heavy Blow', description: 'Deal 4 damage to opponent' }, { type: 'attack', value: 5, name: 'Crushing Attack', description: 'Deal 5 damage to opponent' }, { type: 'attack', value: 1, name: 'Jab', description: 'Deal 1 damage to opponent' }, // Defense cards { type: 'defense', value: 2, name: 'Block', description: 'Reduce damage by 2' }, { type: 'defense', value: 3, name: 'Shield Up', description: 'Reduce damage by 3' }, { type: 'defense', value: 4, name: 'Deflect', description: 'Reduce damage by 4' }, { type: 'defense', value: 1, name: 'Parry', description: 'Reduce damage by 1' }, // Utility cards { type: 'utility', value: 2, name: 'Focus', description: 'Draw 1 card' }, { type: 'utility', value: 3, name: 'Strategize', description: 'Gain 2 resources' }, { type: 'utility', value: 4, name: 'Second Wind', description: 'Restore 2 health' }]; } // Initialize player and enemy decks function initializeDecks() { playerDeck = []; enemyDeck = []; // Add 30 cards to each deck for (var i = 0; i < 30; i++) { var templateIndex = i % cardTemplates.length; var cardTemplate = cardTemplates[templateIndex]; // Create player card var playerCard = new Card(cardTemplate.type, cardTemplate.value, cardTemplate.name, cardTemplate.description); playerDeck.push(playerCard); // Create enemy card var enemyCard = new Card(cardTemplate.type, cardTemplate.value, cardTemplate.name, cardTemplate.description); enemyDeck.push(enemyCard); } } // Shuffle a deck function shuffleDeck(deck) { for (var i = deck.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = deck[i]; deck[i] = deck[j]; deck[j] = temp; } } // Deal initial hands function dealInitialHands() { // Draw exactly 4 cards for player for (var i = 0; i < 4; i++) { drawCard(true); } // Draw exactly 4 cards for enemy for (var i = 0; i < 4; i++) { drawCard(false); } // Display player hand updateHandDisplay(); } // Draw a card for player or enemy function drawCard(isPlayer) { var deck = isPlayer ? playerDeck : enemyDeck; var hand = isPlayer ? playerHand : enemyHand; if (deck.length > 0 && hand.length < maxHandSize) { var card = deck.pop(); hand.push(card); if (isPlayer) { updateHandDisplay(); } return card; } return null; } // Update the display of cards in player's hand function updateHandDisplay() { // Remove all existing cards from display for (var i = 0; i < playerHand.length; i++) { if (playerHand[i].parent) { playerHand[i].parent.removeChild(playerHand[i]); } } // Calculate card positioning var totalWidth = playerHand.length * CARD_WIDTH * 0.7; var startX = (GAME_WIDTH - totalWidth) / 2; // Add cards to display for (var i = 0; i < playerHand.length; i++) { var card = playerHand[i]; card.x = startX + i * (CARD_WIDTH * 0.7); card.y = HAND_Y; card.setPlayable(isPlayerTurn); game.addChild(card); } } // Start player turn function startPlayerTurn() { isPlayerTurn = true; turnCounter++; // Reset state playerHero.resetTurnStats(); selectedCard = null; hideCardButtons(); // Ensure player has at least 4 cards at the start of their turn while (playerHand.length < 4) { drawCard(true); } // Update player resources playerHero.updateResources(Math.min(playerHero.maxResources, playerHero.resources + 2)); // Update UI endTurnButton.setEnabled(true); actionText.setText("Your Turn"); // Update player hand cards to be playable for (var i = 0; i < playerHand.length; i++) { playerHand[i].setPlayable(true); } // Position the turn indicator near player tween(turnIndicator, { x: playerHero.x, y: playerHero.y - 250 }, { duration: 500, easing: tween.easeOut }); // Log turn start console.log("Player turn " + turnCounter + " started"); } // End player turn and start enemy turn function endPlayerTurn() { isPlayerTurn = false; // Update UI endTurnButton.setEnabled(false); actionText.setText("Enemy Turn"); hideCardButtons(); // Update player hand cards to not be playable for (var i = 0; i < playerHand.length; i++) { playerHand[i].setPlayable(false); playerHand[i].setSelected(false); } // Position turn indicator near enemy tween(turnIndicator, { x: enemyHero.x, y: enemyHero.y + 250 }, { duration: 500, easing: tween.easeOut }); // Execute enemy turn with a delay LK.setTimeout(function () { executeEnemyTurn(); }, 1000); } // Execute enemy AI turn function executeEnemyTurn() { // Reset enemy stats enemyHero.resetTurnStats(); // Ensure enemy has at least 4 cards at the start of their turn while (enemyHand.length < 4) { drawCard(false); } // Update enemy resources (AI starts with some resources) enemyHero.resources = Math.min(enemyHero.maxResources, enemyHero.resources + 2); // AI decision making var actions = planEnemyActions(); executeEnemyActions(actions); } // Plan enemy actions function planEnemyActions() { var actions = []; var availableResources = enemyHero.resources; // Sort cards by value enemyHand.sort(function (a, b) { // Prioritize attack cards if (a.type === 'attack' && b.type !== 'attack') { return -1; } if (a.type !== 'attack' && b.type === 'attack') { return 1; } // Then by value return b.value - a.value; }); // First, check if we need to defend if (playerHero.attack > 0) { // Find defense cards var defenseCards = enemyHand.filter(function (card) { return card.type === 'defense'; }); // Use defense if available if (defenseCards.length > 0) { actions.push({ type: 'play', card: defenseCards[0] }); availableResources -= defenseCards[0].value; // Remove this card from consideration var index = enemyHand.indexOf(defenseCards[0]); if (index !== -1) { enemyHand.splice(index, 1); } } } // Then, try to play attack cards var attackCards = enemyHand.filter(function (card) { return card.type === 'attack' && card.value <= availableResources; }); for (var i = 0; i < attackCards.length; i++) { if (availableResources >= attackCards[i].value) { actions.push({ type: 'play', card: attackCards[i] }); availableResources -= attackCards[i].value; // Remove this card from consideration var index = enemyHand.indexOf(attackCards[i]); if (index !== -1) { enemyHand.splice(index, 1); } } } // If we haven't used any cards but have some, discard one for resources if (actions.length === 0 && enemyHand.length > 0) { // Sort by resource value enemyHand.sort(function (a, b) { return b.resourceValue - a.resourceValue; }); actions.push({ type: 'pitch', card: enemyHand[0] }); // Remove this card from consideration var index = enemyHand.indexOf(enemyHand[0]); if (index !== -1) { enemyHand.splice(index, 1); } } return actions; } // Execute a list of enemy actions with delays function executeEnemyActions(actions) { if (actions.length === 0) { // End enemy turn if no more actions LK.setTimeout(startPlayerTurn, 1000); return; } var action = actions.shift(); var card = action.card; LK.setTimeout(function () { if (action.type === 'play') { // Create a visual representation of the card var enemyCard = new Card(card.type, card.value, card.name, card.description); enemyCard.x = enemyHero.x; enemyCard.y = enemyHero.y + 100; game.addChild(enemyCard); // Animate it to play area tween(enemyCard, { x: GAME_WIDTH / 2, y: PLAY_ZONE_Y - 200 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Apply card effect applyCardEffect(card, false); // Fade out card tween(enemyCard, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { game.removeChild(enemyCard); // Continue with next action executeEnemyActions(actions); } }); } }); } else if (action.type === 'pitch') { // Create a visual representation of the card var enemyCard = new Card(card.type, card.value, card.name, card.description); enemyCard.x = enemyHero.x; enemyCard.y = enemyHero.y + 100; game.addChild(enemyCard); // Animate discard tween(enemyCard, { x: GAME_WIDTH / 2 + 400, y: PLAY_ZONE_Y, rotation: Math.PI / 4 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Add resources enemyHero.resources += card.resourceValue; // Show resource gain var resourceMsg = new Text2("+" + card.resourceValue + " R", { size: 40, fill: 0xFFCC00 }); resourceMsg.anchor.set(0.5, 0.5); resourceMsg.x = enemyHero.x; resourceMsg.y = enemyHero.y - 50; game.addChild(resourceMsg); // Fade out card tween(enemyCard, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { game.removeChild(enemyCard); // Fade out resource message tween(resourceMsg, { alpha: 0, y: resourceMsg.y - 50 }, { duration: 500, onFinish: function onFinish() { game.removeChild(resourceMsg); // Continue with next action executeEnemyActions(actions); } }); } }); } }); } }, 1000); } // Handle card selected by player game.cardSelected = function (card, x, y) { // Deselect previous card if (selectedCard && selectedCard !== card) { selectedCard.setSelected(false); } // Toggle selection if (selectedCard === card) { selectedCard.setSelected(false); selectedCard = null; hideCardButtons(); } else { selectedCard = card; showCardButtons(); } }; // Show action buttons when a card is selected function showCardButtons() { if (!selectedCard) { return; } // Check if the card can be played (enough resources) var canPlay = playerHero.resources >= selectedCard.value; playCardButton.setEnabled(canPlay); // Show buttons tween(playCardButton, { alpha: 1 }, { duration: 200 }); tween(discardCardButton, { alpha: 1 }, { duration: 200 }); } // Hide action buttons function hideCardButtons() { tween(playCardButton, { alpha: 0 }, { duration: 200 }); tween(discardCardButton, { alpha: 0 }, { duration: 200 }); } // Play selected card function playCard(card) { if (!isPlayerTurn || playerHero.resources < card.value) { return; } // Subtract resources playerHero.updateResources(playerHero.resources - card.value); // Remove from hand var index = playerHand.indexOf(card); if (index !== -1) { playerHand.splice(index, 1); } // Play sound LK.getSound('cardPlay').play(); // Move card to play area var originalX = card.x; var originalY = card.y; tween(card, { x: GAME_WIDTH / 2, y: PLAY_ZONE_Y }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Apply card effect applyCardEffect(card, true); // Allow opponent to block with cards if (card.type === 'attack') { // Check if opponent has defense cards var defenseCards = enemyHand.filter(function (card) { return card.type === 'defense'; }); // If defense cards are available, let opponent choose to block if (defenseCards.length > 0) { // For simplicity, automatically choose the first defense card var chosenDefenseCard = defenseCards[0]; applyCardEffect(chosenDefenseCard, false); // Remove the chosen defense card from the enemy's hand var index = enemyHand.indexOf(chosenDefenseCard); if (index !== -1) { enemyHand.splice(index, 1); } } } // Fade out card tween(card, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { // Remove card game.removeChild(card); // Reset selection selectedCard = null; hideCardButtons(); // Update hand display updateHandDisplay(); } }); } }); } // Discard card for resources function pitchForResources(card) { if (!isPlayerTurn) { return; } // Add resources playerHero.updateResources(playerHero.resources + card.resourceValue); // Remove from hand var index = playerHand.indexOf(card); if (index !== -1) { playerHand.splice(index, 1); } // Play sound LK.getSound('cardPlay').play(); // Move card to discard area tween(card, { x: GAME_WIDTH / 2 + 400, y: PLAY_ZONE_Y, rotation: Math.PI / 4 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Show resource gain var resourceMsg = new Text2("+" + card.resourceValue + " Resources", { size: 40, fill: 0xFFCC00 }); resourceMsg.anchor.set(0.5, 0.5); resourceMsg.x = playerHero.x; resourceMsg.y = playerHero.y - 50; game.addChild(resourceMsg); // Fade out card tween(card, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { game.removeChild(card); // Fade out resource message tween(resourceMsg, { alpha: 0, y: resourceMsg.y - 50 }, { duration: 500, onFinish: function onFinish() { game.removeChild(resourceMsg); } }); // Reset selection selectedCard = null; hideCardButtons(); // Update hand display updateHandDisplay(); // Update resource indicator resourceIndicator.setText("Resources: " + playerHero.resources); } }); } }); } // Apply card effect based on type function applyCardEffect(card, isPlayer) { var hero = isPlayer ? playerHero : enemyHero; var opponent = isPlayer ? enemyHero : playerHero; // Create effect text var effectMsg = new Text2("", { size: 40, fill: 0xFFFFFF }); effectMsg.style = { fill: 0xFFFFFF }; // Ensure style property is defined effectMsg.anchor.set(0.5, 0.5); effectMsg.x = isPlayer ? playerHero.x : enemyHero.x; effectMsg.y = isPlayer ? playerHero.y - 50 : enemyHero.y - 50; switch (card.type) { case 'attack': // Add attack power hero.addAttack(card.value); // Deal damage immediately var damageDone = opponent.takeDamage(card.value); // Play sound LK.getSound('attack').play(); // Show damage text effectMsg.setText("-" + damageDone); effectMsg.style.fill = "#ff3333"; // Enhance damage animation size and duration tween(effectMsg, { scaleX: 1.5, scaleY: 1.5, alpha: 0, y: effectMsg.y - 50 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(effectMsg); } }); break; case 'defense': // Add defense hero.addDefense(card.value); // Play sound LK.getSound('defend').play(); // Show defense text effectMsg.setText("+" + card.value + " DEF"); effectMsg.style.fill = "#3399ff"; break; case 'utility': // Handle utility effects if (card.name.includes("Draw")) { // Draw card drawCard(isPlayer); effectMsg.setText("Draw Card"); effectMsg.style.fill = "#33cc33"; } else if (card.name.includes("Gain")) { // Add resources var resourceGain = Math.floor(card.value / 2); hero.updateResources(hero.resources + resourceGain); effectMsg.setText("+" + resourceGain + " Resources"); effectMsg.style.fill = "#ffcc00"; } else if (card.name.includes("Restore")) { // Heal var healAmount = Math.floor(card.value / 2); hero.updateHealth(hero.health + healAmount); effectMsg.setText("+" + healAmount + " HP"); effectMsg.style.fill = "#33cc33"; } break; } // Add effect message to game game.addChild(effectMsg); // Animate and remove effect message tween(effectMsg, { alpha: 0, y: effectMsg.y - 50 }, { duration: 1000, onFinish: function onFinish() { game.removeChild(effectMsg); } }); // Check for game over if (opponent.health <= 0) { LK.setTimeout(function () { if (isPlayer) { // Player won storage.gamesPlayed = (storage.gamesPlayed || 0) + 1; storage.highScore = Math.max(storage.highScore || 0, playerHero.health); LK.setScore(playerHero.health); LK.showYouWin(); } else { // Enemy won storage.gamesPlayed = (storage.gamesPlayed || 0) + 1; LK.setScore(0); LK.showGameOver(); } }, 1000); } } // Game update loop game.update = function () { if (!gameStarted) { initializeGame(); } }; // Handle touch/mouse movement game.move = function (x, y, obj) { // We could implement card dragging here if needed }; // Start the game LK.playMusic('battleMusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
gamesPlayed: 0
});
/****
* Classes
****/
// Button class for game controls
var Button = Container.expand(function (text, width, height) {
var self = Container.call(this);
self.isEnabled = true;
// Button background
self.background = self.attachAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5,
width: width || 200,
height: height || 80
});
// Button text
self.text = new Text2(text || "Button", {
size: 28,
fill: 0xFFFFFF
});
self.text.anchor.set(0.5, 0.5);
self.addChild(self.text);
// Interaction handlers
self.down = function (x, y, obj) {
if (self.isEnabled) {
self.background.alpha = 0.7;
}
};
self.up = function (x, y, obj) {
if (self.isEnabled) {
self.background.alpha = 1;
if (typeof self.onClick === 'function') {
self.onClick();
}
}
};
// Enable/disable the button
self.setEnabled = function (enabled) {
self.isEnabled = enabled;
self.alpha = enabled ? 1 : 0.5;
};
return self;
});
// Card class to represent game cards
var Card = Container.expand(function (type, value, name, description) {
var self = Container.call(this);
// Card properties
self.type = type || 'attack'; // attack, defense, utility
self.value = value || 1; // primary value (attack/defense/resource)
self.name = name || 'Basic Card';
self.description = description || 'A basic card';
self.isPlayable = true;
self.isSelected = false;
// Visual elements
var cardFrontId = '';
switch (self.name) {
case 'Fierce Strike':
cardFrontId = 'fierceStrikeFront';
break;
case 'Quick Slash':
cardFrontId = 'quickSlashFront';
break;
case 'Heavy Blow':
cardFrontId = 'heavyBlowFront';
break;
case 'Crushing Attack':
cardFrontId = 'crushingAttackFront';
break;
case 'Jab':
cardFrontId = 'jabFront';
break;
case 'Block':
cardFrontId = 'blockFront';
break;
case 'Shield Up':
cardFrontId = 'shieldUpFront';
break;
case 'Deflect':
cardFrontId = 'deflectFront';
break;
case 'Parry':
cardFrontId = 'parryFront';
break;
case 'Focus':
cardFrontId = 'focusFront';
break;
case 'Strategize':
cardFrontId = 'strategizeFront';
break;
case 'Second Wind':
cardFrontId = 'secondWindFront';
break;
default:
cardFrontId = 'cardFront';
}
self.background = self.attachAsset(cardFrontId, {
anchorX: 0.5,
anchorY: 0.5
});
// Image area
self.imageArea = new Container();
self.imageArea.width = 250;
self.imageArea.height = 200;
self.imageArea.y = -75;
self.addChild(self.imageArea);
// Text area
self.textArea = new Container();
self.textArea.width = 250;
self.textArea.height = 150;
self.textArea.y = 75;
self.textArea.tint = 0xFFFFFF; // White background for text area
self.addChild(self.textArea);
// Card title
self.titleText = new Text2(self.name, {
size: 24,
fill: 0x000000
});
self.titleText.anchor.set(0.5, 0);
self.titleText.y = -150;
self.textArea.addChild(self.titleText);
// Card value and type
var typeColors = {
attack: '#ff3333',
defense: '#3399ff',
utility: '#33cc33'
};
self.valueText = new Text2(self.value.toString(), {
size: 40,
fill: typeColors[self.type] || "#000000"
});
self.valueText.anchor.set(0.5, 0.5);
self.valueText.y = -70;
self.addChild(self.valueText);
// Card type icon
var iconType = self.type === 'attack' ? 'attackIcon' : self.type === 'defense' ? 'defenseIcon' : 'resourceIcon';
self.typeIcon = self.attachAsset(iconType, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -20
});
// Description text
self.descText = new Text2(self.description, {
size: 18,
fill: 0x333333
});
self.descText.anchor.set(0.5, 0);
self.descText.y = 20;
self.descText.x = 0;
// Limit text width
if (self.descText.width > 230) {
self.descText.scale.set(230 / self.descText.width);
}
self.addChild(self.descText);
// Resource value (all cards can be discarded for resources)
self.resourceValue = Math.max(1, Math.floor(self.value / 2));
self.resourceText = new Text2("R: " + self.resourceValue, {
size: 24,
fill: 0xFFCC00
});
self.resourceText.anchor.set(0.5, 1);
self.resourceText.y = 140;
self.addChild(self.resourceText);
// Highlight for selection
self.highlight = self.attachAsset('cardHighlight', {
anchorX: 0.5,
anchorY: 0.5
});
self.highlight.alpha = 0;
// Card interaction events
self.down = function (x, y, obj) {
if (self.isPlayable) {
self.isSelected = true;
self.highlight.alpha = 0.5;
}
};
self.up = function (x, y, obj) {
if (!self.isPlayable) {
return;
}
// Call the game's card played handler - implemented in game.cardPlayed
var gameCoords = game.toLocal(self.parent.toGlobal(self.position));
game.cardSelected(self, gameCoords.x, gameCoords.y);
};
// Toggle selection state
self.setSelected = function (selected) {
self.isSelected = selected;
self.highlight.alpha = selected ? 0.5 : 0;
};
// Set card playability
self.setPlayable = function (playable) {
self.isPlayable = playable;
self.alpha = playable ? 1 : 0.6;
};
return self;
});
// Hero class representing player or enemy champion
var Hero = Container.expand(function (isPlayer) {
var self = Container.call(this);
// Hero properties
self.isPlayer = isPlayer;
self.health = 20;
self.maxHealth = 20;
self.resources = 0;
self.maxResources = 10;
self.attack = 0;
self.defense = 0;
self.name = isPlayer ? "Player Hero" : "Enemy Hero";
// Visual representation
self.avatar = self.attachAsset(isPlayer ? 'heroAvatar' : 'enemyAvatar', {
anchorX: 0.5,
anchorY: 0.5
});
// Name display
self.nameText = new Text2(self.name, {
size: 28,
fill: 0xFFFFFF
});
self.nameText.anchor.set(0.5, 0);
self.nameText.y = -130;
self.addChild(self.nameText);
// Health bar
self.healthBarBg = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
y: 180,
tint: 0x666666
});
self.healthBar = self.attachAsset('healthBar', {
anchorX: 0,
anchorY: 0.5,
x: -150,
y: 200,
scaleX: 2,
// Further increase the width of the health bar
tint: 0x00ff00 // Apply a fantasy-themed green tint
});
self.healthText = new Text2(self.health + "/" + self.maxHealth, {
size: 22,
fill: 0xFFFFFF
});
self.healthText.anchor.set(0.5, 0.5);
self.healthText.y = 200;
self.addChild(self.healthText);
// Resource bar (only for player)
if (isPlayer) {
self.resourceBarBg = self.attachAsset('resourceBar', {
anchorX: 0.5,
anchorY: 0.5,
y: 150,
tint: 0x666666
});
self.resourceBar = self.attachAsset('resourceBar', {
anchorX: 0,
anchorY: 0.5,
x: -150,
y: 150,
scaleX: 2,
// Further increase the width of the resource bar
tint: 0xFFD700 // Apply a fantasy-themed gold tint
});
// Resource text
self.resourceText = new Text2(self.resources + "/" + self.maxResources, {
size: 22,
fill: 0xFFFFFF
});
self.resourceText.anchor.set(0.5, 0.5);
self.resourceText.y = 150;
self.addChild(self.resourceText);
}
// Update health display
self.updateHealth = function (newHealth) {
self.health = Math.max(0, Math.min(self.maxHealth, newHealth));
self.healthText.setText(self.health + "/" + self.maxHealth);
// Update health bar
var healthRatio = self.health / self.maxHealth;
self.healthBar.width = 200 * healthRatio;
self.healthBar.x = -100;
// Flash red if damage taken
if (newHealth < self.health) {
LK.effects.flashObject(self.avatar, 0xff0000, 500);
}
return self.health;
};
// Update resource display (player only)
self.updateResources = function (newResources) {
if (!self.isPlayer) {
return;
}
self.resources = Math.max(0, Math.min(self.maxResources, newResources));
self.resourceText.setText(self.resources + "/" + self.maxResources);
// Update resource bar
var resourceRatio = self.resources / self.maxResources;
self.resourceBar.width = 200 * resourceRatio;
};
// Take damage accounting for defense
self.takeDamage = function (amount) {
var actualDamage = Math.max(0, amount - (self.defense + 2)); // Always block two damage
self.defense = Math.max(0, self.defense - amount);
if (actualDamage > 0) {
self.updateHealth(self.health - actualDamage);
LK.getSound('damage').play();
// Flash effect for damage
LK.effects.flashObject(self, 0xff0000, 300);
}
return actualDamage;
};
// Add attack power
self.addAttack = function (amount) {
self.attack += amount;
return self.attack;
};
// Add defense
self.addDefense = function (amount) {
self.defense += amount;
return self.defense;
};
// Reset turn stats
self.resetTurnStats = function () {
self.attack = 0;
self.defense = 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state
var playerHand = [];
var playerDeck = [];
var enemyDeck = [];
var enemyHand = [];
var discardPile = [];
var isPlayerTurn = true;
var gameStarted = false;
var selectedCard = null;
var draggedCard = null;
var turnCounter = 0;
var playZone = null;
var maxHandSize = 5;
var roundActions = [];
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var CARD_WIDTH = 250;
var CARD_HEIGHT = 350;
var HAND_Y = 2300;
var PLAY_ZONE_Y = 1500;
// Game entities
var playerHero;
var enemyHero;
var endTurnButton;
var playCardButton;
var discardCardButton;
var actionText;
var turnIndicator;
// Initialize game elements
function initializeGame() {
// Create heroes
playerHero = new Hero(true);
playerHero.x = GAME_WIDTH / 2;
playerHero.y = GAME_HEIGHT - 200;
game.addChild(playerHero);
enemyHero = new Hero(false);
enemyHero.x = GAME_WIDTH / 2;
enemyHero.y = 500;
game.addChild(enemyHero);
// Create resource indicator
resourceIndicator = new Text2("Resources: " + playerHero.resources, {
size: 30,
fill: 0xFFCC00
});
resourceIndicator.anchor.set(0.5, 0.5);
resourceIndicator.x = GAME_WIDTH - 150;
resourceIndicator.y = GAME_HEIGHT / 2;
game.addChild(resourceIndicator);
// Create card templates
createCardTemplates();
// Create play zone
playZone = new Container();
playZone.x = GAME_WIDTH / 2;
playZone.y = PLAY_ZONE_Y;
game.addChild(playZone);
// Create UI elements
createUIElements();
// Shuffle and deal initial hands
initializeDecks();
shuffleDeck(playerDeck);
shuffleDeck(enemyDeck);
dealInitialHands();
// Setup turn indicator
turnIndicator = LK.getAsset('turnIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
turnIndicator.x = GAME_WIDTH / 2;
turnIndicator.y = GAME_HEIGHT / 2 - 300;
turnIndicator.alpha = 0.7;
game.addChild(turnIndicator);
// Add action text
actionText = new Text2("Your Turn", {
size: 40,
fill: 0xFFFFFF
});
actionText.anchor.set(0.5, 0.5);
actionText.x = GAME_WIDTH / 2;
actionText.y = GAME_HEIGHT / 2 - 300;
game.addChild(actionText);
// Start with player turn
startPlayerTurn();
// Play background music
LK.playMusic('battleMusic');
gameStarted = true;
}
// Create UI buttons
function createUIElements() {
// End turn button
endTurnButton = new Button("End Turn", 250, 80);
endTurnButton.x = GAME_WIDTH - 200;
endTurnButton.y = GAME_HEIGHT - 200;
endTurnButton.onClick = function () {
if (isPlayerTurn) {
endPlayerTurn();
}
};
game.addChild(endTurnButton);
// Play card button (appears when a card is selected)
playCardButton = new Button("Play Card", 200, 80);
playCardButton.x = GAME_WIDTH / 2 - 120;
playCardButton.y = GAME_HEIGHT - 200;
playCardButton.alpha = 0;
playCardButton.onClick = function () {
if (selectedCard) {
playCard(selectedCard);
}
};
game.addChild(playCardButton);
// Discard for resources button
discardCardButton = new Button("Pitch", 200, 80);
discardCardButton.x = GAME_WIDTH / 2 + 120;
discardCardButton.y = GAME_HEIGHT - 200;
discardCardButton.alpha = 0;
discardCardButton.onClick = function () {
if (selectedCard) {
pitchForResources(selectedCard);
}
};
game.addChild(discardCardButton);
}
// Create card templates
function createCardTemplates() {
cardTemplates = [
// Attack cards
{
type: 'attack',
value: 3,
name: 'Fierce Strike',
description: 'Deal 3 damage to opponent'
}, {
type: 'attack',
value: 2,
name: 'Quick Slash',
description: 'Deal 2 damage to opponent'
}, {
type: 'attack',
value: 4,
name: 'Heavy Blow',
description: 'Deal 4 damage to opponent'
}, {
type: 'attack',
value: 5,
name: 'Crushing Attack',
description: 'Deal 5 damage to opponent'
}, {
type: 'attack',
value: 1,
name: 'Jab',
description: 'Deal 1 damage to opponent'
},
// Defense cards
{
type: 'defense',
value: 2,
name: 'Block',
description: 'Reduce damage by 2'
}, {
type: 'defense',
value: 3,
name: 'Shield Up',
description: 'Reduce damage by 3'
}, {
type: 'defense',
value: 4,
name: 'Deflect',
description: 'Reduce damage by 4'
}, {
type: 'defense',
value: 1,
name: 'Parry',
description: 'Reduce damage by 1'
},
// Utility cards
{
type: 'utility',
value: 2,
name: 'Focus',
description: 'Draw 1 card'
}, {
type: 'utility',
value: 3,
name: 'Strategize',
description: 'Gain 2 resources'
}, {
type: 'utility',
value: 4,
name: 'Second Wind',
description: 'Restore 2 health'
}];
}
// Initialize player and enemy decks
function initializeDecks() {
playerDeck = [];
enemyDeck = [];
// Add 30 cards to each deck
for (var i = 0; i < 30; i++) {
var templateIndex = i % cardTemplates.length;
var cardTemplate = cardTemplates[templateIndex];
// Create player card
var playerCard = new Card(cardTemplate.type, cardTemplate.value, cardTemplate.name, cardTemplate.description);
playerDeck.push(playerCard);
// Create enemy card
var enemyCard = new Card(cardTemplate.type, cardTemplate.value, cardTemplate.name, cardTemplate.description);
enemyDeck.push(enemyCard);
}
}
// Shuffle a deck
function shuffleDeck(deck) {
for (var i = deck.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = deck[i];
deck[i] = deck[j];
deck[j] = temp;
}
}
// Deal initial hands
function dealInitialHands() {
// Draw exactly 4 cards for player
for (var i = 0; i < 4; i++) {
drawCard(true);
}
// Draw exactly 4 cards for enemy
for (var i = 0; i < 4; i++) {
drawCard(false);
}
// Display player hand
updateHandDisplay();
}
// Draw a card for player or enemy
function drawCard(isPlayer) {
var deck = isPlayer ? playerDeck : enemyDeck;
var hand = isPlayer ? playerHand : enemyHand;
if (deck.length > 0 && hand.length < maxHandSize) {
var card = deck.pop();
hand.push(card);
if (isPlayer) {
updateHandDisplay();
}
return card;
}
return null;
}
// Update the display of cards in player's hand
function updateHandDisplay() {
// Remove all existing cards from display
for (var i = 0; i < playerHand.length; i++) {
if (playerHand[i].parent) {
playerHand[i].parent.removeChild(playerHand[i]);
}
}
// Calculate card positioning
var totalWidth = playerHand.length * CARD_WIDTH * 0.7;
var startX = (GAME_WIDTH - totalWidth) / 2;
// Add cards to display
for (var i = 0; i < playerHand.length; i++) {
var card = playerHand[i];
card.x = startX + i * (CARD_WIDTH * 0.7);
card.y = HAND_Y;
card.setPlayable(isPlayerTurn);
game.addChild(card);
}
}
// Start player turn
function startPlayerTurn() {
isPlayerTurn = true;
turnCounter++;
// Reset state
playerHero.resetTurnStats();
selectedCard = null;
hideCardButtons();
// Ensure player has at least 4 cards at the start of their turn
while (playerHand.length < 4) {
drawCard(true);
}
// Update player resources
playerHero.updateResources(Math.min(playerHero.maxResources, playerHero.resources + 2));
// Update UI
endTurnButton.setEnabled(true);
actionText.setText("Your Turn");
// Update player hand cards to be playable
for (var i = 0; i < playerHand.length; i++) {
playerHand[i].setPlayable(true);
}
// Position the turn indicator near player
tween(turnIndicator, {
x: playerHero.x,
y: playerHero.y - 250
}, {
duration: 500,
easing: tween.easeOut
});
// Log turn start
console.log("Player turn " + turnCounter + " started");
}
// End player turn and start enemy turn
function endPlayerTurn() {
isPlayerTurn = false;
// Update UI
endTurnButton.setEnabled(false);
actionText.setText("Enemy Turn");
hideCardButtons();
// Update player hand cards to not be playable
for (var i = 0; i < playerHand.length; i++) {
playerHand[i].setPlayable(false);
playerHand[i].setSelected(false);
}
// Position turn indicator near enemy
tween(turnIndicator, {
x: enemyHero.x,
y: enemyHero.y + 250
}, {
duration: 500,
easing: tween.easeOut
});
// Execute enemy turn with a delay
LK.setTimeout(function () {
executeEnemyTurn();
}, 1000);
}
// Execute enemy AI turn
function executeEnemyTurn() {
// Reset enemy stats
enemyHero.resetTurnStats();
// Ensure enemy has at least 4 cards at the start of their turn
while (enemyHand.length < 4) {
drawCard(false);
}
// Update enemy resources (AI starts with some resources)
enemyHero.resources = Math.min(enemyHero.maxResources, enemyHero.resources + 2);
// AI decision making
var actions = planEnemyActions();
executeEnemyActions(actions);
}
// Plan enemy actions
function planEnemyActions() {
var actions = [];
var availableResources = enemyHero.resources;
// Sort cards by value
enemyHand.sort(function (a, b) {
// Prioritize attack cards
if (a.type === 'attack' && b.type !== 'attack') {
return -1;
}
if (a.type !== 'attack' && b.type === 'attack') {
return 1;
}
// Then by value
return b.value - a.value;
});
// First, check if we need to defend
if (playerHero.attack > 0) {
// Find defense cards
var defenseCards = enemyHand.filter(function (card) {
return card.type === 'defense';
});
// Use defense if available
if (defenseCards.length > 0) {
actions.push({
type: 'play',
card: defenseCards[0]
});
availableResources -= defenseCards[0].value;
// Remove this card from consideration
var index = enemyHand.indexOf(defenseCards[0]);
if (index !== -1) {
enemyHand.splice(index, 1);
}
}
}
// Then, try to play attack cards
var attackCards = enemyHand.filter(function (card) {
return card.type === 'attack' && card.value <= availableResources;
});
for (var i = 0; i < attackCards.length; i++) {
if (availableResources >= attackCards[i].value) {
actions.push({
type: 'play',
card: attackCards[i]
});
availableResources -= attackCards[i].value;
// Remove this card from consideration
var index = enemyHand.indexOf(attackCards[i]);
if (index !== -1) {
enemyHand.splice(index, 1);
}
}
}
// If we haven't used any cards but have some, discard one for resources
if (actions.length === 0 && enemyHand.length > 0) {
// Sort by resource value
enemyHand.sort(function (a, b) {
return b.resourceValue - a.resourceValue;
});
actions.push({
type: 'pitch',
card: enemyHand[0]
});
// Remove this card from consideration
var index = enemyHand.indexOf(enemyHand[0]);
if (index !== -1) {
enemyHand.splice(index, 1);
}
}
return actions;
}
// Execute a list of enemy actions with delays
function executeEnemyActions(actions) {
if (actions.length === 0) {
// End enemy turn if no more actions
LK.setTimeout(startPlayerTurn, 1000);
return;
}
var action = actions.shift();
var card = action.card;
LK.setTimeout(function () {
if (action.type === 'play') {
// Create a visual representation of the card
var enemyCard = new Card(card.type, card.value, card.name, card.description);
enemyCard.x = enemyHero.x;
enemyCard.y = enemyHero.y + 100;
game.addChild(enemyCard);
// Animate it to play area
tween(enemyCard, {
x: GAME_WIDTH / 2,
y: PLAY_ZONE_Y - 200
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Apply card effect
applyCardEffect(card, false);
// Fade out card
tween(enemyCard, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
game.removeChild(enemyCard);
// Continue with next action
executeEnemyActions(actions);
}
});
}
});
} else if (action.type === 'pitch') {
// Create a visual representation of the card
var enemyCard = new Card(card.type, card.value, card.name, card.description);
enemyCard.x = enemyHero.x;
enemyCard.y = enemyHero.y + 100;
game.addChild(enemyCard);
// Animate discard
tween(enemyCard, {
x: GAME_WIDTH / 2 + 400,
y: PLAY_ZONE_Y,
rotation: Math.PI / 4
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Add resources
enemyHero.resources += card.resourceValue;
// Show resource gain
var resourceMsg = new Text2("+" + card.resourceValue + " R", {
size: 40,
fill: 0xFFCC00
});
resourceMsg.anchor.set(0.5, 0.5);
resourceMsg.x = enemyHero.x;
resourceMsg.y = enemyHero.y - 50;
game.addChild(resourceMsg);
// Fade out card
tween(enemyCard, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
game.removeChild(enemyCard);
// Fade out resource message
tween(resourceMsg, {
alpha: 0,
y: resourceMsg.y - 50
}, {
duration: 500,
onFinish: function onFinish() {
game.removeChild(resourceMsg);
// Continue with next action
executeEnemyActions(actions);
}
});
}
});
}
});
}
}, 1000);
}
// Handle card selected by player
game.cardSelected = function (card, x, y) {
// Deselect previous card
if (selectedCard && selectedCard !== card) {
selectedCard.setSelected(false);
}
// Toggle selection
if (selectedCard === card) {
selectedCard.setSelected(false);
selectedCard = null;
hideCardButtons();
} else {
selectedCard = card;
showCardButtons();
}
};
// Show action buttons when a card is selected
function showCardButtons() {
if (!selectedCard) {
return;
}
// Check if the card can be played (enough resources)
var canPlay = playerHero.resources >= selectedCard.value;
playCardButton.setEnabled(canPlay);
// Show buttons
tween(playCardButton, {
alpha: 1
}, {
duration: 200
});
tween(discardCardButton, {
alpha: 1
}, {
duration: 200
});
}
// Hide action buttons
function hideCardButtons() {
tween(playCardButton, {
alpha: 0
}, {
duration: 200
});
tween(discardCardButton, {
alpha: 0
}, {
duration: 200
});
}
// Play selected card
function playCard(card) {
if (!isPlayerTurn || playerHero.resources < card.value) {
return;
}
// Subtract resources
playerHero.updateResources(playerHero.resources - card.value);
// Remove from hand
var index = playerHand.indexOf(card);
if (index !== -1) {
playerHand.splice(index, 1);
}
// Play sound
LK.getSound('cardPlay').play();
// Move card to play area
var originalX = card.x;
var originalY = card.y;
tween(card, {
x: GAME_WIDTH / 2,
y: PLAY_ZONE_Y
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Apply card effect
applyCardEffect(card, true);
// Allow opponent to block with cards
if (card.type === 'attack') {
// Check if opponent has defense cards
var defenseCards = enemyHand.filter(function (card) {
return card.type === 'defense';
});
// If defense cards are available, let opponent choose to block
if (defenseCards.length > 0) {
// For simplicity, automatically choose the first defense card
var chosenDefenseCard = defenseCards[0];
applyCardEffect(chosenDefenseCard, false);
// Remove the chosen defense card from the enemy's hand
var index = enemyHand.indexOf(chosenDefenseCard);
if (index !== -1) {
enemyHand.splice(index, 1);
}
}
}
// Fade out card
tween(card, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
// Remove card
game.removeChild(card);
// Reset selection
selectedCard = null;
hideCardButtons();
// Update hand display
updateHandDisplay();
}
});
}
});
}
// Discard card for resources
function pitchForResources(card) {
if (!isPlayerTurn) {
return;
}
// Add resources
playerHero.updateResources(playerHero.resources + card.resourceValue);
// Remove from hand
var index = playerHand.indexOf(card);
if (index !== -1) {
playerHand.splice(index, 1);
}
// Play sound
LK.getSound('cardPlay').play();
// Move card to discard area
tween(card, {
x: GAME_WIDTH / 2 + 400,
y: PLAY_ZONE_Y,
rotation: Math.PI / 4
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show resource gain
var resourceMsg = new Text2("+" + card.resourceValue + " Resources", {
size: 40,
fill: 0xFFCC00
});
resourceMsg.anchor.set(0.5, 0.5);
resourceMsg.x = playerHero.x;
resourceMsg.y = playerHero.y - 50;
game.addChild(resourceMsg);
// Fade out card
tween(card, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
game.removeChild(card);
// Fade out resource message
tween(resourceMsg, {
alpha: 0,
y: resourceMsg.y - 50
}, {
duration: 500,
onFinish: function onFinish() {
game.removeChild(resourceMsg);
}
});
// Reset selection
selectedCard = null;
hideCardButtons();
// Update hand display
updateHandDisplay();
// Update resource indicator
resourceIndicator.setText("Resources: " + playerHero.resources);
}
});
}
});
}
// Apply card effect based on type
function applyCardEffect(card, isPlayer) {
var hero = isPlayer ? playerHero : enemyHero;
var opponent = isPlayer ? enemyHero : playerHero;
// Create effect text
var effectMsg = new Text2("", {
size: 40,
fill: 0xFFFFFF
});
effectMsg.style = {
fill: 0xFFFFFF
}; // Ensure style property is defined
effectMsg.anchor.set(0.5, 0.5);
effectMsg.x = isPlayer ? playerHero.x : enemyHero.x;
effectMsg.y = isPlayer ? playerHero.y - 50 : enemyHero.y - 50;
switch (card.type) {
case 'attack':
// Add attack power
hero.addAttack(card.value);
// Deal damage immediately
var damageDone = opponent.takeDamage(card.value);
// Play sound
LK.getSound('attack').play();
// Show damage text
effectMsg.setText("-" + damageDone);
effectMsg.style.fill = "#ff3333";
// Enhance damage animation size and duration
tween(effectMsg, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0,
y: effectMsg.y - 50
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(effectMsg);
}
});
break;
case 'defense':
// Add defense
hero.addDefense(card.value);
// Play sound
LK.getSound('defend').play();
// Show defense text
effectMsg.setText("+" + card.value + " DEF");
effectMsg.style.fill = "#3399ff";
break;
case 'utility':
// Handle utility effects
if (card.name.includes("Draw")) {
// Draw card
drawCard(isPlayer);
effectMsg.setText("Draw Card");
effectMsg.style.fill = "#33cc33";
} else if (card.name.includes("Gain")) {
// Add resources
var resourceGain = Math.floor(card.value / 2);
hero.updateResources(hero.resources + resourceGain);
effectMsg.setText("+" + resourceGain + " Resources");
effectMsg.style.fill = "#ffcc00";
} else if (card.name.includes("Restore")) {
// Heal
var healAmount = Math.floor(card.value / 2);
hero.updateHealth(hero.health + healAmount);
effectMsg.setText("+" + healAmount + " HP");
effectMsg.style.fill = "#33cc33";
}
break;
}
// Add effect message to game
game.addChild(effectMsg);
// Animate and remove effect message
tween(effectMsg, {
alpha: 0,
y: effectMsg.y - 50
}, {
duration: 1000,
onFinish: function onFinish() {
game.removeChild(effectMsg);
}
});
// Check for game over
if (opponent.health <= 0) {
LK.setTimeout(function () {
if (isPlayer) {
// Player won
storage.gamesPlayed = (storage.gamesPlayed || 0) + 1;
storage.highScore = Math.max(storage.highScore || 0, playerHero.health);
LK.setScore(playerHero.health);
LK.showYouWin();
} else {
// Enemy won
storage.gamesPlayed = (storage.gamesPlayed || 0) + 1;
LK.setScore(0);
LK.showGameOver();
}
}, 1000);
}
}
// Game update loop
game.update = function () {
if (!gameStarted) {
initializeGame();
}
};
// Handle touch/mouse movement
game.move = function (x, y, obj) {
// We could implement card dragging here if needed
};
// Start the game
LK.playMusic('battleMusic');