User prompt
Please fix the bug: 'Timeout.tick error: Cannot read properties of null (reading 'setFaceUp')' in or related to this line: 'card2.setFaceUp(true);' Line Number: 498
User prompt
Add 2 options to the part that says start game, play 3 hands, play 5 hands, play 3 hands in 3 hands, play 5 hands in 5 hands, the current state of the game is to play 1 hand anyway
User prompt
There is a problem in the transition between stages. I give you a sequence of locations. Always come to the locations in that order. Stage 1: Castle, Cave, Forest / Stage 2: Cave, Castle, Forest / Stage 3: Castle, Forest, Cave / Stage 4: Forest, Cave, Castle
User prompt
There are 3 location cards in total, forest, castle, cave. These should be reduced, when they run out, 3 more should be filled and randomly come. When they run out, they should be reduced and randomly come. In other words, all 3 of these locations should come in every 3 rounds.
User prompt
When we get 3 cards in our hand, the places should be the same, there should be 3 places, these should be reduced, and they should be filled and reduced again at each stage.
User prompt
The game consists of 4 stages and 3 rounds, redistribute the cards after every 3 rounds, accumulate points for 4 rounds, and at the end, the player with the highest score wins.
User prompt
Let's play the whole game 3 times. The one with the most points at the end of the 3 games wins. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
When all 3 cards are in the middle, the sound effect called "battle" is played while the score is being calculated in each round.
User prompt
When all 3 conditions are present, play the sound called "war" while calculating the score.
User prompt
Please fix the bug: 'Uncaught TypeError: LK.getMusic is not a function' in or related to this line: 'LK.getMusic('card-playing-sound').play();' Line Number: 390
User prompt
card-plaing-sound , Play this sound once every time I play a card
User prompt
Make sure background-song music is playing as long as the game is running
User prompt
Let me drag the cards to the middle
User prompt
LocationBG should be placed higher
User prompt
human-warrior, elf-archer, orc-wizard write an extra +1 point to these three cards in each location
User prompt
"If there is a tie, the game should restart from the beginning."
User prompt
"The game's AI should also play logically by choosing the card in its hand that will score the most points based on the current location.
User prompt
Look, update the way you give points, I'm telling you the point giving system: According to the places: In the cave place, those who have the word wizard in their name get +1 and orcs get +1, so orc-wizard get +2 in this inference. In the castle, those who have the word warrior in their name get +1 and those who have the word human in their name get +1, so human-warrior get +2 in this inference. In the forest, those who have the word archer in their name get +1, those who have the word elf in their name get +1, so elf-archer get +2 in this inference. Arrange the points according to this information
User prompt
Just now, in the forest area, I played human-archer and gained +2. In the forest, elves gain +1 and archers gain +1. And as a human-archer, I should have gained only +1 but I gained +2. Fix this.
User prompt
While trying the game just now, I got +1 even though I played the orc-wizard card in the cave area. Orcs get +1 and wizards get +1 in the cave. Since the orc and wizard counter both, I should have had a total of +2 points.
User prompt
Click "start game" to open the place
User prompt
Please fix the bug: 'Timeout.tick error: Cannot read properties of null (reading 'bonuses')' in or related to this line: 'for (var i = 0; i < roundLocation.bonuses.length; i++) {' Line Number: 467
User prompt
Please fix the bug: 'Timeout.tick error: Cannot read properties of null (reading 'bonus')' in or related to this line: 'var bonus = roundLocation.bonus;' Line Number: 463
User prompt
When the venue card starts the game, it should be opened and we will throw cards according to the opened venue, when the first round is over, a new venue will be opened and we will throw cards according to that venue, when the second round is over, a new venue will be opened and we will throw cards according to that venue, in other words, let's start the game by knowing the venue, make this change, I think the m button is blocking this, first do what is necessary to prevent it from blocking, if necessary, cancel the functions of the m button
User prompt
The game start button should be in the middle of the screen, it is not visible right now, make it visible
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Card class: represents a single card (type, subtype, owner, value, etc) var Card = Container.expand(function () { var self = Container.call(this); // Card properties (set after creation) self.cardType = null; // 'human', 'elf', 'orc' self.subType = null; // 'warrior', 'archer', 'wizard' self.owner = null; // 'player', 'bot1', 'bot2' self.value = 0; // base value (for future expansion) self.isFaceUp = false; self.cardIndex = -1; // 0-8, for unique identification // Card visuals // Card art asset (will be set in setCard) var cardArt = null; // Set card data and visuals self.setCard = function (cardType, subType, owner, cardIndex) { self.cardType = cardType; self.subType = subType; self.owner = owner; self.cardIndex = cardIndex; // Remove previous cardArt if any if (cardArt) { cardArt.destroy(); cardArt = null; } // Determine which asset to use: for bots, use 'cardBg' until played, then real asset var assetId; if ((owner === 'bot1' || owner === 'bot2') && !self.isFaceUp) { assetId = 'cardBg'; } else { assetId = cardType + '-' + subType; } cardArt = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, width: 320, height: 480 }); // Always on top for click/tap detection self.setChildIndex(cardArt, self.children.length - 1); }; // Show/hide card face self.setFaceUp = function (isUp) { self.isFaceUp = isUp; // If bot card and being turned face up, swap asset to real card art if ((self.owner === 'bot1' || self.owner === 'bot2') && isUp) { if (cardArt) { cardArt.destroy(); cardArt = null; } var assetId = self.cardType + '-' + self.subType; cardArt = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, width: 320, height: 480 }); self.setChildIndex(cardArt, self.children.length - 1); } else if (cardArt) { cardArt.alpha = isUp ? 1 : 0.2; } }; // For click/tap detection self.down = function (x, y, obj) { // Allow player to play a card by tapping a card in their hand if (self.owner === 'player' && self.isFaceUp && canPlayCard && !roundCards.player) { playPlayerCard(self); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x22223a }); /**** * Game Code ****/ // --- Game Data --- var cardTypes = ['human', 'elf', 'orc']; var subTypes = ['warrior', 'archer', 'wizard']; // All 9 unique cards var allCards = []; for (var i = 0; i < cardTypes.length; i++) { for (var j = 0; j < subTypes.length; j++) { allCards.push({ cardType: cardTypes[i], subType: subTypes[j], cardIndex: i * 3 + j }); } } // Location data // Each location has a name, color, and a list of cardType+subType combos that get +1 point var locations = [{ name: 'Castle', color: 0x607d8b, bonuses: [{ cardType: 'human', subType: 'archer' }, { cardType: 'human', subType: 'mage' }, { cardType: 'human', subType: 'warrior' }, { cardType: 'elf', subType: 'warrior' }, { cardType: 'orc', subType: 'warrior' }] }, { name: 'Forest', color: 0x4caf50, bonuses: [{ cardType: 'elf', subType: 'archer' }, { cardType: 'elf', subType: 'mage' }, { cardType: 'elf', subType: 'archer' }, // duplicate, but harmless { cardType: 'human', subType: 'archer' }, { cardType: 'orc', subType: 'archer' }] }, { name: 'Cave', color: 0x795548, bonuses: [{ cardType: 'orc', subType: 'mage' }, { cardType: 'orc', subType: 'warrior' }, { cardType: 'orc', subType: 'archer' }, { cardType: 'human', subType: 'mage' }, { cardType: 'elf', subType: 'mage' }] }]; // --- Game State --- var playerHand = []; var bot1Hand = []; var bot2Hand = []; var playerScore = 0; var bot1Score = 0; var bot2Score = 0; var round = 1; var maxRounds = 3; var canPlayCard = false; var roundCards = { player: null, bot1: null, bot2: null }; var roundLocation = null; var allCardObjs = []; // All Card instances for cleanup var locationNode = null; var infoText = null; var scoreText = null; var roundText = null; // --- GUI Setup --- // Info text (center top) infoText = new Text2('Triad Arena: Card Clash', { size: 90, fill: "#fff" }); infoText.anchor.set(0.5, 0); LK.gui.top.addChild(infoText); // Score text (center bottom) scoreText = new Text2('', { size: 70, fill: "#fff" }); scoreText.anchor.set(0.5, 1); LK.gui.bottom.addChild(scoreText); // Round text (center) roundText = new Text2('', { size: 80, fill: "#fff" }); roundText.anchor.set(0.5, 0.5); LK.gui.center.addChild(roundText); // --- Helper Functions --- function shuffle(arr) { // Fisher-Yates shuffle for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var t = arr[i]; arr[i] = arr[j]; arr[j] = t; } return arr; } function dealHands() { var deck = allCards.slice(); shuffle(deck); playerHand = deck.slice(0, 3); bot1Hand = deck.slice(3, 6); bot2Hand = deck.slice(6, 9); } function updateScoreText() { scoreText.setText("You: " + playerScore + " Bot1: " + bot1Score + " Bot2: " + bot2Score); } function updateRoundText(msg) { if (msg) { roundText.setText(msg); } else { roundText.setText("Round " + round + " / " + maxRounds); } } function clearBoard() { for (var i = 0; i < allCardObjs.length; i++) { allCardObjs[i].destroy(); } allCardObjs = []; if (locationNode) { locationNode.destroy(); locationNode = null; } } function showLocation(location) { // Remove previous background if any if (game._locationBgImage) { game._locationBgImage.destroy(); game._locationBgImage = null; } // Determine background asset id by location name (lowercase) var bgAssetId = ''; if (location.name.toLowerCase() === 'castle') { bgAssetId = 'castle'; } else if (location.name.toLowerCase() === 'forest') { bgAssetId = 'forest'; } else if (location.name.toLowerCase() === 'cave') { bgAssetId = 'cave'; } // Add background image covering the whole game area if (bgAssetId) { var bgImg = LK.getAsset(bgAssetId, { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChildAt(bgImg, 0); // Add at bottom game._locationBgImage = bgImg; } // Show location in center locationNode = LK.getAsset('locationBg', { width: 600, height: 200, color: location.color, shape: 'box', anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 350 }); game.addChild(locationNode); var locText = new Text2(location.name, { size: 90, fill: "#fff" }); locText.anchor.set(0.5, 0.5); locText.x = 0; locText.y = 0; locationNode.addChild(locText); // Bonus info (now in top left, not in center) var bonusStr = "Bonuses:"; for (var i = 0; i < location.bonuses.length; i++) { var b = location.bonuses[i]; bonusStr += "\n" + b.cardType.charAt(0).toUpperCase() + b.cardType.slice(1) + "-" + b.subType.charAt(0).toUpperCase() + b.subType.slice(1); } // Remove previous bonusText if any if (game._bonusText) { game._bonusText.destroy(); game._bonusText = null; } var bonusText = new Text2(bonusStr, { size: 50, fill: "#fff" }); bonusText.anchor.set(0, 0); // Top left anchor bonusText.x = 110; // Leave 10px margin from top left (avoid menu) bonusText.y = 10; LK.gui.topLeft.addChild(bonusText); game._bonusText = bonusText; } function layoutHands() { // Player hand (bottom) var spacing = 400; var startX = 2048 / 2 - spacing; var y = 2732 - 400; for (var i = 0; i < playerHand.length; i++) { var c = new Card(); c.setCard(playerHand[i].cardType, playerHand[i].subType, 'player', playerHand[i].cardIndex); c.x = startX + i * spacing; c.y = y; // Show player's hand face up so the player can see their cards c.setFaceUp(true); game.addChild(c); allCardObjs.push(c); } // Bot1 hand (left) var x1 = 400; var spacingY1 = 400; var startY1 = 2732 / 2 - spacingY1; for (var i = 0; i < bot1Hand.length; i++) { var c = new Card(); c.setCard(bot1Hand[i].cardType, bot1Hand[i].subType, 'bot1', bot1Hand[i].cardIndex); c.x = x1; c.y = startY1 + i * spacingY1; c.setFaceUp(false); c.rotation = Math.PI / 2; // Rotate 90 degrees for left bot game.addChild(c); allCardObjs.push(c); } // Bot2 hand (right) var x2 = 2048 - 400; var spacingY2 = 400; var startY2 = 2732 / 2 - spacingY2; for (var i = 0; i < bot2Hand.length; i++) { var c = new Card(); c.setCard(bot2Hand[i].cardType, bot2Hand[i].subType, 'bot2', bot2Hand[i].cardIndex); c.x = x2; c.y = startY2 + i * spacingY2; c.setFaceUp(false); c.rotation = Math.PI / 2; // Rotate 90 degrees for right bot game.addChild(c); allCardObjs.push(c); } } function playPlayerCard(cardObj) { if (!canPlayCard || roundCards.player) return; canPlayCard = false; roundCards.player = cardObj; cardObj.setFaceUp(true); // Animate to center tween(cardObj, { x: 2048 / 2 - 300, y: 2732 / 2 + 250 }, { duration: 400, easing: tween.cubicOut }); // Remove from hand for (var i = 0; i < playerHand.length; i++) { if (playerHand[i].cardIndex === cardObj.cardIndex) { playerHand.splice(i, 1); break; } } // Bots play after short delay LK.setTimeout(function () { playBotCards(); }, 500); } function playBotCards() { // Bot1: pick random card var idx1 = Math.floor(Math.random() * bot1Hand.length); var card1 = null; for (var i = 0; i < allCardObjs.length; i++) { if (allCardObjs[i].owner === 'bot1' && allCardObjs[i].cardIndex === bot1Hand[idx1].cardIndex) { card1 = allCardObjs[i]; break; } } roundCards.bot1 = card1; card1.setFaceUp(true); // Rotate bot1 card 90 degrees when played to the ground card1.rotation = Math.PI / 2; tween(card1, { x: 2048 / 2, y: 2732 / 2 - 250 }, { duration: 400, easing: tween.cubicOut }); bot1Hand.splice(idx1, 1); // Bot2: pick random card var idx2 = Math.floor(Math.random() * bot2Hand.length); var card2 = null; for (var i = 0; i < allCardObjs.length; i++) { if (allCardObjs[i].owner === 'bot2' && allCardObjs[i].cardIndex === bot2Hand[idx2].cardIndex) { card2 = allCardObjs[i]; break; } } roundCards.bot2 = card2; card2.setFaceUp(true); // Rotate bot2 card 90 degrees when played to the ground card2.rotation = Math.PI / 2; tween(card2, { x: 2048 / 2 + 300, y: 2732 / 2 + 250 }, { duration: 400, easing: tween.cubicOut }); bot2Hand.splice(idx2, 1); // Reveal location after short delay LK.setTimeout(function () { revealLocationAndScore(); }, 700); } function revealLocationAndScore() { // Pick random location from availableLocations, ensuring no repeats if (availableLocations.length === 0) { // All locations used, reset (should not happen in 3 rounds) availableLocations = locations.slice(); } var idx = Math.floor(Math.random() * availableLocations.length); roundLocation = availableLocations[idx]; availableLocations.splice(idx, 1); // Remove used location showLocation(roundLocation); // Calculate points var pts = { player: 0, bot1: 0, bot2: 0 }; var bonus = roundLocation.bonus; // Each card: 1 point base, +1 if matches a bonus combo for this location function calcCardPoints(card) { var p = 1; for (var i = 0; i < roundLocation.bonuses.length; i++) { var b = roundLocation.bonuses[i]; if (card.cardType === b.cardType && card.subType === b.subType) { p += 1; break; } } return p; } pts.player = calcCardPoints(roundCards.player); pts.bot1 = calcCardPoints(roundCards.bot1); pts.bot2 = calcCardPoints(roundCards.bot2); // Animate cards (flash winner) var maxPts = Math.max(pts.player, pts.bot1, pts.bot2); if (pts.player === maxPts) { LK.effects.flashObject(roundCards.player, 0xffff00, 700); } if (pts.bot1 === maxPts) { LK.effects.flashObject(roundCards.bot1, 0xffff00, 700); } if (pts.bot2 === maxPts) { LK.effects.flashObject(roundCards.bot2, 0xffff00, 700); } // Update scores playerScore += pts.player; bot1Score += pts.bot1; bot2Score += pts.bot2; updateScoreText(); // Show round result var msg = "You: +" + pts.player + " Bot1: +" + pts.bot1 + " Bot2: +" + pts.bot2; updateRoundText(msg); // Next round or end after delay LK.setTimeout(function () { round++; if (round > maxRounds) { endGame(); } else { startRound(); } }, 1500); } function startRound() { clearBoard(); roundCards = { player: null, bot1: null, bot2: null }; roundLocation = null; canPlayCard = true; updateScoreText(); updateRoundText(); layoutHands(); infoText.setText("Pick a card to play!"); } function endGame() { clearBoard(); canPlayCard = false; var winner = ''; if (playerScore > bot1Score && playerScore > bot2Score) { winner = "You win!"; LK.showYouWin(); } else if (playerScore === bot1Score && playerScore === bot2Score) { winner = "It's a 3-way tie!"; LK.showGameOver(); } else if (playerScore === bot1Score && playerScore > bot2Score) { winner = "Tie with Bot1!"; LK.showGameOver(); } else if (playerScore === bot2Score && playerScore > bot1Score) { winner = "Tie with Bot2!"; LK.showGameOver(); } else if (bot1Score > playerScore && bot1Score > bot2Score) { winner = "Bot1 wins!"; LK.showGameOver(); } else if (bot2Score > playerScore && bot2Score > bot1Score) { winner = "Bot2 wins!"; LK.showGameOver(); } else { winner = "Game Over!"; LK.showGameOver(); } infoText.setText(winner); updateRoundText(''); } // --- Game Start --- // Track which locations have been used this game var availableLocations = []; // Game start button var startButton = null; function showStartButton() { if (startButton) { startButton.destroy(); startButton = null; } startButton = new Text2('Start Game', { size: 120, fill: "#fff" }); startButton.anchor.set(0.5, 0.5); startButton.x = 2048 / 2; startButton.y = 2732 / 2; startButton.interactive = true; startButton.buttonMode = true; startButton.alpha = 1; startButton.visible = true; startButton.down = function (x, y, obj) { startButton.visible = false; startButton.alpha = 0; newGame(); }; game.addChild(startButton); } // Only call newGame from the start button now function newGame() { playerScore = 0; bot1Score = 0; bot2Score = 0; round = 1; canPlayCard = false; roundCards = { player: null, bot1: null, bot2: null }; roundLocation = null; clearBoard(); dealHands(); updateScoreText(); updateRoundText(); infoText.setText("Pick a card to play!"); layoutHands(); canPlayCard = true; // Reset available locations for the new game availableLocations = locations.slice(); // Show M button for location reveal showMButton(); } // Show the start button on load showStartButton(); // M button for revealing location var mButton = null; function showMButton() { if (mButton) { mButton.destroy(); mButton = null; } mButton = new Text2('M', { size: 120, fill: "#fff" }); mButton.anchor.set(0.5, 0.5); // Place at visible center of the screen mButton.x = 2048 / 2; mButton.y = 2732 / 2; mButton.interactive = true; mButton.buttonMode = true; mButton.alpha = 1; mButton.visible = true; mButton.down = function (x, y, obj) { // Only allow if all cards are played and location not yet revealed if (roundCards.player && roundCards.bot1 && roundCards.bot2 && !roundLocation) { mButton.visible = false; mButton.alpha = 0; revealLocationAndScore(); } }; LK.gui.top.addChild(mButton); } // Override revealLocationAndScore to ensure unique locations per round // Do not auto-start game; only show start button on load // --- Game move handler (for drag, not used here) --- game.move = function (x, y, obj) { // No drag in this game };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Card class: represents a single card (type, subtype, owner, value, etc)
var Card = Container.expand(function () {
var self = Container.call(this);
// Card properties (set after creation)
self.cardType = null; // 'human', 'elf', 'orc'
self.subType = null; // 'warrior', 'archer', 'wizard'
self.owner = null; // 'player', 'bot1', 'bot2'
self.value = 0; // base value (for future expansion)
self.isFaceUp = false;
self.cardIndex = -1; // 0-8, for unique identification
// Card visuals
// Card art asset (will be set in setCard)
var cardArt = null;
// Set card data and visuals
self.setCard = function (cardType, subType, owner, cardIndex) {
self.cardType = cardType;
self.subType = subType;
self.owner = owner;
self.cardIndex = cardIndex;
// Remove previous cardArt if any
if (cardArt) {
cardArt.destroy();
cardArt = null;
}
// Determine which asset to use: for bots, use 'cardBg' until played, then real asset
var assetId;
if ((owner === 'bot1' || owner === 'bot2') && !self.isFaceUp) {
assetId = 'cardBg';
} else {
assetId = cardType + '-' + subType;
}
cardArt = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 480
});
// Always on top for click/tap detection
self.setChildIndex(cardArt, self.children.length - 1);
};
// Show/hide card face
self.setFaceUp = function (isUp) {
self.isFaceUp = isUp;
// If bot card and being turned face up, swap asset to real card art
if ((self.owner === 'bot1' || self.owner === 'bot2') && isUp) {
if (cardArt) {
cardArt.destroy();
cardArt = null;
}
var assetId = self.cardType + '-' + self.subType;
cardArt = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 480
});
self.setChildIndex(cardArt, self.children.length - 1);
} else if (cardArt) {
cardArt.alpha = isUp ? 1 : 0.2;
}
};
// For click/tap detection
self.down = function (x, y, obj) {
// Allow player to play a card by tapping a card in their hand
if (self.owner === 'player' && self.isFaceUp && canPlayCard && !roundCards.player) {
playPlayerCard(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x22223a
});
/****
* Game Code
****/
// --- Game Data ---
var cardTypes = ['human', 'elf', 'orc'];
var subTypes = ['warrior', 'archer', 'wizard'];
// All 9 unique cards
var allCards = [];
for (var i = 0; i < cardTypes.length; i++) {
for (var j = 0; j < subTypes.length; j++) {
allCards.push({
cardType: cardTypes[i],
subType: subTypes[j],
cardIndex: i * 3 + j
});
}
}
// Location data
// Each location has a name, color, and a list of cardType+subType combos that get +1 point
var locations = [{
name: 'Castle',
color: 0x607d8b,
bonuses: [{
cardType: 'human',
subType: 'archer'
}, {
cardType: 'human',
subType: 'mage'
}, {
cardType: 'human',
subType: 'warrior'
}, {
cardType: 'elf',
subType: 'warrior'
}, {
cardType: 'orc',
subType: 'warrior'
}]
}, {
name: 'Forest',
color: 0x4caf50,
bonuses: [{
cardType: 'elf',
subType: 'archer'
}, {
cardType: 'elf',
subType: 'mage'
}, {
cardType: 'elf',
subType: 'archer'
},
// duplicate, but harmless
{
cardType: 'human',
subType: 'archer'
}, {
cardType: 'orc',
subType: 'archer'
}]
}, {
name: 'Cave',
color: 0x795548,
bonuses: [{
cardType: 'orc',
subType: 'mage'
}, {
cardType: 'orc',
subType: 'warrior'
}, {
cardType: 'orc',
subType: 'archer'
}, {
cardType: 'human',
subType: 'mage'
}, {
cardType: 'elf',
subType: 'mage'
}]
}];
// --- Game State ---
var playerHand = [];
var bot1Hand = [];
var bot2Hand = [];
var playerScore = 0;
var bot1Score = 0;
var bot2Score = 0;
var round = 1;
var maxRounds = 3;
var canPlayCard = false;
var roundCards = {
player: null,
bot1: null,
bot2: null
};
var roundLocation = null;
var allCardObjs = []; // All Card instances for cleanup
var locationNode = null;
var infoText = null;
var scoreText = null;
var roundText = null;
// --- GUI Setup ---
// Info text (center top)
infoText = new Text2('Triad Arena: Card Clash', {
size: 90,
fill: "#fff"
});
infoText.anchor.set(0.5, 0);
LK.gui.top.addChild(infoText);
// Score text (center bottom)
scoreText = new Text2('', {
size: 70,
fill: "#fff"
});
scoreText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(scoreText);
// Round text (center)
roundText = new Text2('', {
size: 80,
fill: "#fff"
});
roundText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(roundText);
// --- Helper Functions ---
function shuffle(arr) {
// Fisher-Yates shuffle
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
return arr;
}
function dealHands() {
var deck = allCards.slice();
shuffle(deck);
playerHand = deck.slice(0, 3);
bot1Hand = deck.slice(3, 6);
bot2Hand = deck.slice(6, 9);
}
function updateScoreText() {
scoreText.setText("You: " + playerScore + " Bot1: " + bot1Score + " Bot2: " + bot2Score);
}
function updateRoundText(msg) {
if (msg) {
roundText.setText(msg);
} else {
roundText.setText("Round " + round + " / " + maxRounds);
}
}
function clearBoard() {
for (var i = 0; i < allCardObjs.length; i++) {
allCardObjs[i].destroy();
}
allCardObjs = [];
if (locationNode) {
locationNode.destroy();
locationNode = null;
}
}
function showLocation(location) {
// Remove previous background if any
if (game._locationBgImage) {
game._locationBgImage.destroy();
game._locationBgImage = null;
}
// Determine background asset id by location name (lowercase)
var bgAssetId = '';
if (location.name.toLowerCase() === 'castle') {
bgAssetId = 'castle';
} else if (location.name.toLowerCase() === 'forest') {
bgAssetId = 'forest';
} else if (location.name.toLowerCase() === 'cave') {
bgAssetId = 'cave';
}
// Add background image covering the whole game area
if (bgAssetId) {
var bgImg = LK.getAsset(bgAssetId, {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChildAt(bgImg, 0); // Add at bottom
game._locationBgImage = bgImg;
}
// Show location in center
locationNode = LK.getAsset('locationBg', {
width: 600,
height: 200,
color: location.color,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 - 350
});
game.addChild(locationNode);
var locText = new Text2(location.name, {
size: 90,
fill: "#fff"
});
locText.anchor.set(0.5, 0.5);
locText.x = 0;
locText.y = 0;
locationNode.addChild(locText);
// Bonus info (now in top left, not in center)
var bonusStr = "Bonuses:";
for (var i = 0; i < location.bonuses.length; i++) {
var b = location.bonuses[i];
bonusStr += "\n" + b.cardType.charAt(0).toUpperCase() + b.cardType.slice(1) + "-" + b.subType.charAt(0).toUpperCase() + b.subType.slice(1);
}
// Remove previous bonusText if any
if (game._bonusText) {
game._bonusText.destroy();
game._bonusText = null;
}
var bonusText = new Text2(bonusStr, {
size: 50,
fill: "#fff"
});
bonusText.anchor.set(0, 0); // Top left anchor
bonusText.x = 110; // Leave 10px margin from top left (avoid menu)
bonusText.y = 10;
LK.gui.topLeft.addChild(bonusText);
game._bonusText = bonusText;
}
function layoutHands() {
// Player hand (bottom)
var spacing = 400;
var startX = 2048 / 2 - spacing;
var y = 2732 - 400;
for (var i = 0; i < playerHand.length; i++) {
var c = new Card();
c.setCard(playerHand[i].cardType, playerHand[i].subType, 'player', playerHand[i].cardIndex);
c.x = startX + i * spacing;
c.y = y;
// Show player's hand face up so the player can see their cards
c.setFaceUp(true);
game.addChild(c);
allCardObjs.push(c);
}
// Bot1 hand (left)
var x1 = 400;
var spacingY1 = 400;
var startY1 = 2732 / 2 - spacingY1;
for (var i = 0; i < bot1Hand.length; i++) {
var c = new Card();
c.setCard(bot1Hand[i].cardType, bot1Hand[i].subType, 'bot1', bot1Hand[i].cardIndex);
c.x = x1;
c.y = startY1 + i * spacingY1;
c.setFaceUp(false);
c.rotation = Math.PI / 2; // Rotate 90 degrees for left bot
game.addChild(c);
allCardObjs.push(c);
}
// Bot2 hand (right)
var x2 = 2048 - 400;
var spacingY2 = 400;
var startY2 = 2732 / 2 - spacingY2;
for (var i = 0; i < bot2Hand.length; i++) {
var c = new Card();
c.setCard(bot2Hand[i].cardType, bot2Hand[i].subType, 'bot2', bot2Hand[i].cardIndex);
c.x = x2;
c.y = startY2 + i * spacingY2;
c.setFaceUp(false);
c.rotation = Math.PI / 2; // Rotate 90 degrees for right bot
game.addChild(c);
allCardObjs.push(c);
}
}
function playPlayerCard(cardObj) {
if (!canPlayCard || roundCards.player) return;
canPlayCard = false;
roundCards.player = cardObj;
cardObj.setFaceUp(true);
// Animate to center
tween(cardObj, {
x: 2048 / 2 - 300,
y: 2732 / 2 + 250
}, {
duration: 400,
easing: tween.cubicOut
});
// Remove from hand
for (var i = 0; i < playerHand.length; i++) {
if (playerHand[i].cardIndex === cardObj.cardIndex) {
playerHand.splice(i, 1);
break;
}
}
// Bots play after short delay
LK.setTimeout(function () {
playBotCards();
}, 500);
}
function playBotCards() {
// Bot1: pick random card
var idx1 = Math.floor(Math.random() * bot1Hand.length);
var card1 = null;
for (var i = 0; i < allCardObjs.length; i++) {
if (allCardObjs[i].owner === 'bot1' && allCardObjs[i].cardIndex === bot1Hand[idx1].cardIndex) {
card1 = allCardObjs[i];
break;
}
}
roundCards.bot1 = card1;
card1.setFaceUp(true);
// Rotate bot1 card 90 degrees when played to the ground
card1.rotation = Math.PI / 2;
tween(card1, {
x: 2048 / 2,
y: 2732 / 2 - 250
}, {
duration: 400,
easing: tween.cubicOut
});
bot1Hand.splice(idx1, 1);
// Bot2: pick random card
var idx2 = Math.floor(Math.random() * bot2Hand.length);
var card2 = null;
for (var i = 0; i < allCardObjs.length; i++) {
if (allCardObjs[i].owner === 'bot2' && allCardObjs[i].cardIndex === bot2Hand[idx2].cardIndex) {
card2 = allCardObjs[i];
break;
}
}
roundCards.bot2 = card2;
card2.setFaceUp(true);
// Rotate bot2 card 90 degrees when played to the ground
card2.rotation = Math.PI / 2;
tween(card2, {
x: 2048 / 2 + 300,
y: 2732 / 2 + 250
}, {
duration: 400,
easing: tween.cubicOut
});
bot2Hand.splice(idx2, 1);
// Reveal location after short delay
LK.setTimeout(function () {
revealLocationAndScore();
}, 700);
}
function revealLocationAndScore() {
// Pick random location from availableLocations, ensuring no repeats
if (availableLocations.length === 0) {
// All locations used, reset (should not happen in 3 rounds)
availableLocations = locations.slice();
}
var idx = Math.floor(Math.random() * availableLocations.length);
roundLocation = availableLocations[idx];
availableLocations.splice(idx, 1); // Remove used location
showLocation(roundLocation);
// Calculate points
var pts = {
player: 0,
bot1: 0,
bot2: 0
};
var bonus = roundLocation.bonus;
// Each card: 1 point base, +1 if matches a bonus combo for this location
function calcCardPoints(card) {
var p = 1;
for (var i = 0; i < roundLocation.bonuses.length; i++) {
var b = roundLocation.bonuses[i];
if (card.cardType === b.cardType && card.subType === b.subType) {
p += 1;
break;
}
}
return p;
}
pts.player = calcCardPoints(roundCards.player);
pts.bot1 = calcCardPoints(roundCards.bot1);
pts.bot2 = calcCardPoints(roundCards.bot2);
// Animate cards (flash winner)
var maxPts = Math.max(pts.player, pts.bot1, pts.bot2);
if (pts.player === maxPts) {
LK.effects.flashObject(roundCards.player, 0xffff00, 700);
}
if (pts.bot1 === maxPts) {
LK.effects.flashObject(roundCards.bot1, 0xffff00, 700);
}
if (pts.bot2 === maxPts) {
LK.effects.flashObject(roundCards.bot2, 0xffff00, 700);
}
// Update scores
playerScore += pts.player;
bot1Score += pts.bot1;
bot2Score += pts.bot2;
updateScoreText();
// Show round result
var msg = "You: +" + pts.player + " Bot1: +" + pts.bot1 + " Bot2: +" + pts.bot2;
updateRoundText(msg);
// Next round or end after delay
LK.setTimeout(function () {
round++;
if (round > maxRounds) {
endGame();
} else {
startRound();
}
}, 1500);
}
function startRound() {
clearBoard();
roundCards = {
player: null,
bot1: null,
bot2: null
};
roundLocation = null;
canPlayCard = true;
updateScoreText();
updateRoundText();
layoutHands();
infoText.setText("Pick a card to play!");
}
function endGame() {
clearBoard();
canPlayCard = false;
var winner = '';
if (playerScore > bot1Score && playerScore > bot2Score) {
winner = "You win!";
LK.showYouWin();
} else if (playerScore === bot1Score && playerScore === bot2Score) {
winner = "It's a 3-way tie!";
LK.showGameOver();
} else if (playerScore === bot1Score && playerScore > bot2Score) {
winner = "Tie with Bot1!";
LK.showGameOver();
} else if (playerScore === bot2Score && playerScore > bot1Score) {
winner = "Tie with Bot2!";
LK.showGameOver();
} else if (bot1Score > playerScore && bot1Score > bot2Score) {
winner = "Bot1 wins!";
LK.showGameOver();
} else if (bot2Score > playerScore && bot2Score > bot1Score) {
winner = "Bot2 wins!";
LK.showGameOver();
} else {
winner = "Game Over!";
LK.showGameOver();
}
infoText.setText(winner);
updateRoundText('');
}
// --- Game Start ---
// Track which locations have been used this game
var availableLocations = [];
// Game start button
var startButton = null;
function showStartButton() {
if (startButton) {
startButton.destroy();
startButton = null;
}
startButton = new Text2('Start Game', {
size: 120,
fill: "#fff"
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 2048 / 2;
startButton.y = 2732 / 2;
startButton.interactive = true;
startButton.buttonMode = true;
startButton.alpha = 1;
startButton.visible = true;
startButton.down = function (x, y, obj) {
startButton.visible = false;
startButton.alpha = 0;
newGame();
};
game.addChild(startButton);
}
// Only call newGame from the start button now
function newGame() {
playerScore = 0;
bot1Score = 0;
bot2Score = 0;
round = 1;
canPlayCard = false;
roundCards = {
player: null,
bot1: null,
bot2: null
};
roundLocation = null;
clearBoard();
dealHands();
updateScoreText();
updateRoundText();
infoText.setText("Pick a card to play!");
layoutHands();
canPlayCard = true;
// Reset available locations for the new game
availableLocations = locations.slice();
// Show M button for location reveal
showMButton();
}
// Show the start button on load
showStartButton();
// M button for revealing location
var mButton = null;
function showMButton() {
if (mButton) {
mButton.destroy();
mButton = null;
}
mButton = new Text2('M', {
size: 120,
fill: "#fff"
});
mButton.anchor.set(0.5, 0.5);
// Place at visible center of the screen
mButton.x = 2048 / 2;
mButton.y = 2732 / 2;
mButton.interactive = true;
mButton.buttonMode = true;
mButton.alpha = 1;
mButton.visible = true;
mButton.down = function (x, y, obj) {
// Only allow if all cards are played and location not yet revealed
if (roundCards.player && roundCards.bot1 && roundCards.bot2 && !roundLocation) {
mButton.visible = false;
mButton.alpha = 0;
revealLocationAndScore();
}
};
LK.gui.top.addChild(mButton);
}
// Override revealLocationAndScore to ensure unique locations per round
// Do not auto-start game; only show start button on load
// --- Game move handler (for drag, not used here) ---
game.move = function (x, y, obj) {
// No drag in this game
};