/****
* 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');