User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'effectMsg.style.fill = "#ff3333";' Line Number: 965
Code edit (1 edits merged)
Please save this source code
User prompt
Forge of Champions: Tactical Duels
User prompt
Similar to flesh and blood
Initial prompt
we are making a turn-based card game
/****
* 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
self.background = self.attachAsset('cardFront', {
anchorX: 0.5,
anchorY: 0.5
});
// Card title
self.titleText = new Text2(self.name, {
size: 24,
fill: 0x000000
});
self.titleText.anchor.set(0.5, 0);
self.titleText.y = -150;
self.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: 120,
tint: 0x666666
});
self.healthBar = self.attachAsset('healthBar', {
anchorX: 0,
anchorY: 0.5,
x: -100,
y: 120
});
// Health text
self.healthText = new Text2(self.health + "/" + self.maxHealth, {
size: 22,
fill: 0xFFFFFF
});
self.healthText.anchor.set(0.5, 0.5);
self.healthText.y = 120;
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: -100,
y: 150
});
// 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);
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 - 500;
game.addChild(playerHero);
enemyHero = new Hero(false);
enemyHero.x = GAME_WIDTH / 2;
enemyHero.y = 500;
game.addChild(enemyHero);
// 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("Discard", 200, 80);
discardCardButton.x = GAME_WIDTH / 2 + 120;
discardCardButton.y = GAME_HEIGHT - 200;
discardCardButton.alpha = 0;
discardCardButton.onClick = function () {
if (selectedCard) {
discardForResources(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 cards for player
for (var i = 0; i < maxHandSize; i++) {
drawCard(true);
}
// Draw cards for enemy
for (var i = 0; i < maxHandSize; 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();
// Draw a card if possible
if (playerHand.length < maxHandSize) {
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();
// Draw a card
if (enemyHand.length < maxHandSize) {
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: 'discard',
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 === 'discard') {
// 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);
// 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 discardForResources(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();
}
});
}
});
}
// 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.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";
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'); ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,1022 @@
-/****
+/****
+* 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
+ self.background = self.attachAsset('cardFront', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Card title
+ self.titleText = new Text2(self.name, {
+ size: 24,
+ fill: 0x000000
+ });
+ self.titleText.anchor.set(0.5, 0);
+ self.titleText.y = -150;
+ self.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: 120,
+ tint: 0x666666
+ });
+ self.healthBar = self.attachAsset('healthBar', {
+ anchorX: 0,
+ anchorY: 0.5,
+ x: -100,
+ y: 120
+ });
+ // Health text
+ self.healthText = new Text2(self.health + "/" + self.maxHealth, {
+ size: 22,
+ fill: 0xFFFFFF
+ });
+ self.healthText.anchor.set(0.5, 0.5);
+ self.healthText.y = 120;
+ 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: -100,
+ y: 150
+ });
+ // 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);
+ 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: 0x000000
-});
\ No newline at end of file
+ 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 - 500;
+ game.addChild(playerHero);
+ enemyHero = new Hero(false);
+ enemyHero.x = GAME_WIDTH / 2;
+ enemyHero.y = 500;
+ game.addChild(enemyHero);
+ // 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("Discard", 200, 80);
+ discardCardButton.x = GAME_WIDTH / 2 + 120;
+ discardCardButton.y = GAME_HEIGHT - 200;
+ discardCardButton.alpha = 0;
+ discardCardButton.onClick = function () {
+ if (selectedCard) {
+ discardForResources(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 cards for player
+ for (var i = 0; i < maxHandSize; i++) {
+ drawCard(true);
+ }
+ // Draw cards for enemy
+ for (var i = 0; i < maxHandSize; 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();
+ // Draw a card if possible
+ if (playerHand.length < maxHandSize) {
+ 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();
+ // Draw a card
+ if (enemyHand.length < maxHandSize) {
+ 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: 'discard',
+ 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 === 'discard') {
+ // 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);
+ // 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 discardForResources(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();
+ }
+ });
+ }
+ });
+}
+// 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.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";
+ 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');
\ No newline at end of file