User prompt
at the bot choose screen bot boxes are wrong object can you made them one colour
User prompt
Can you add a menu to chose how many bot we want? 1-2-3 bot for example
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'shape')' in or related to this line: 'var boardBg = LK.getAsset(LK.init.shape('deskBg', {' Line Number: 109
User prompt
Background is wrong add a brown desk background
User prompt
Can you add a board backgroun
Code edit (1 edits merged)
Please save this source code
User prompt
Love Letter Duel: Me vs Bot
Initial prompt
Made me a me vs bot love letter game.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Card class: represents a card in hand or on table var Card = Container.expand(function () { var self = Container.call(this); // Card data: {id, name, value, desc} self.cardData = null; self.isFaceUp = true; self.owner = null; // 'player' or 'bot' self.index = 0; // hand index // Card graphics self.cardAsset = null; self.textName = null; self.textValue = null; // Set card data and visuals self.setCard = function (cardData, faceUp) { self.cardData = cardData; self.isFaceUp = faceUp; self.removeChildren(); var assetId = faceUp ? 'card' + cardData.value : 'cardBack'; self.cardAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); if (faceUp) { self.textName = new Text2(cardData.name, { size: 48, fill: 0x333333 }); self.textName.anchor.set(0.5, 0); self.textName.x = 0; self.textName.y = -180; self.addChild(self.textName); self.textValue = new Text2(cardData.value + '', { size: 64, fill: 0x000000 }); self.textValue.anchor.set(0.5, 0.5); self.textValue.x = 0; self.textValue.y = 0; self.addChild(self.textValue); } }; // Flip card face up/down self.flip = function (faceUp) { if (self.cardData) { self.setCard(self.cardData, faceUp); } }; return self; }); // Simple popup for messages var Popup = Container.expand(function () { var self = Container.call(this); self.bg = self.attachAsset('cardBack', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1 }); self.bg.tint = 0x222222; self.bg.alpha = 0.95; self.text = new Text2('', { size: 72, fill: "#fff" }); self.text.anchor.set(0.5, 0.5); self.text.x = 0; self.text.y = 0; self.addChild(self.text); self.setText = function (str) { self.text.setText(str); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d2d44 }); /**** * Game Code ****/ // Add brown desk background using a colored box var boardBg = LK.getAsset('deskBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(boardBg); // Simple heart for score // Back of card // Princess // Countess // King // Prince // Handmaid // Baron // Priest // Guard // Card assets (simple colored boxes for each card type) // Card definitions (Love Letter 8-card deck) var CARD_DECK = [ // id, name, value, desc, count { id: 'guard', name: 'Guard', value: 1, desc: 'Guess a card', count: 5 }, { id: 'priest', name: 'Priest', value: 2, desc: 'See hand', count: 2 }, { id: 'baron', name: 'Baron', value: 3, desc: 'Compare hands', count: 2 }, { id: 'handmaid', name: 'Handmaid', value: 4, desc: 'Immunity', count: 2 }, { id: 'prince', name: 'Prince', value: 5, desc: 'Discard hand', count: 2 }, { id: 'king', name: 'King', value: 6, desc: 'Trade hands', count: 1 }, { id: 'countess', name: 'Countess', value: 7, desc: 'Must play if with King/Prince', count: 1 }, { id: 'princess', name: 'Princess', value: 8, desc: 'Lose if discarded', count: 1 }]; // Build deck array function buildDeck() { var deck = []; for (var i = 0; i < CARD_DECK.length; ++i) { for (var j = 0; j < CARD_DECK[i].count; ++j) { deck.push({ id: CARD_DECK[i].id, name: CARD_DECK[i].name, value: CARD_DECK[i].value, desc: CARD_DECK[i].desc }); } } return deck; } // Shuffle deck function shuffle(deck) { for (var i = deck.length - 1; i > 0; --i) { var j = Math.floor(Math.random() * (i + 1)); var t = deck[i]; deck[i] = deck[j]; deck[j] = t; } } // Game state var playerHand = []; var botHand = []; var deck = []; var discardPile = []; var playerProtected = false; var botProtected = false; var playerOut = false; var botOut = false; var playerScore = 0; var botScore = 0; var roundOver = false; var currentTurn = 'player'; // 'player' or 'bot' var popup = null; var playerCardNodes = []; var botCardNodes = []; var discardNodes = []; var heartsPlayer = []; var heartsBot = []; var guiText = null; var roundTarget = 3; // First to 3 wins // Card positions var playerHandY = 2732 - 350; var botHandY = 350; var handX = 2048 / 2; var handSpacing = 400; // GUI setup function setupGUI() { // Remove old if (guiText) LK.gui.top.removeChild(guiText); guiText = new Text2('', { size: 64, fill: "#fff" }); guiText.anchor.set(0.5, 0); LK.gui.top.addChild(guiText); // Hearts for score for (var i = 0; i < heartsPlayer.length; ++i) LK.gui.top.removeChild(heartsPlayer[i]); for (var i = 0; i < heartsBot.length; ++i) LK.gui.top.removeChild(heartsBot[i]); heartsPlayer = []; heartsBot = []; for (var i = 0; i < roundTarget; ++i) { var h1 = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); h1.x = 2048 / 2 - 200 + i * 60; h1.y = 120; LK.gui.top.addChild(h1); heartsPlayer.push(h1); var h2 = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); h2.x = 2048 / 2 - 200 + i * 60; h2.y = 60; LK.gui.top.addChild(h2); heartsBot.push(h2); } } // Update GUI function updateGUI() { guiText.setText("You: " + playerScore + " Bot: " + botScore + " First to " + roundTarget); for (var i = 0; i < roundTarget; ++i) { heartsPlayer[i].alpha = i < playerScore ? 1 : 0.3; heartsBot[i].alpha = i < botScore ? 1 : 0.3; } } // Start a new round function startRound() { // Reset state deck = buildDeck(); shuffle(deck); discardPile = []; playerHand = []; botHand = []; playerProtected = false; botProtected = false; playerOut = false; botOut = false; roundOver = false; currentTurn = 'player'; // Remove old cards for (var i = 0; i < playerCardNodes.length; ++i) game.removeChild(playerCardNodes[i]); for (var i = 0; i < botCardNodes.length; ++i) game.removeChild(botCardNodes[i]); for (var i = 0; i < discardNodes.length; ++i) game.removeChild(discardNodes[i]); playerCardNodes = []; botCardNodes = []; discardNodes = []; // Remove popup if (popup) { game.removeChild(popup); popup = null; } // Remove one card face down (not used in 2p, but for deduction) deck.pop(); // Deal 1 card to each playerHand.push(deck.pop()); botHand.push(deck.pop()); // Draw 1 more for player to start playerDraw(); // Show hands renderHands(); // Update GUI updateGUI(); // Show message showPopup("Your turn!", 1200, function () { // Player's turn currentTurn = 'player'; enablePlayerPlay(); }); } // Draw a card for player function playerDraw() { if (deck.length > 0) { playerHand.push(deck.pop()); } } // Draw a card for bot function botDraw() { if (deck.length > 0) { botHand.push(deck.pop()); } } // Render hands function renderHands() { // Remove old for (var i = 0; i < playerCardNodes.length; ++i) game.removeChild(playerCardNodes[i]); for (var i = 0; i < botCardNodes.length; ++i) game.removeChild(botCardNodes[i]); playerCardNodes = []; botCardNodes = []; // Player hand (always face up) for (var i = 0; i < playerHand.length; ++i) { var c = new Card(); c.setCard(playerHand[i], true); c.owner = 'player'; c.index = i; c.x = handX + (i - 0.5) * handSpacing; c.y = playerHandY; c.scaleX = c.scaleY = 1.1; game.addChild(c); playerCardNodes.push(c); } // Bot hand (always 1 card, face down) for (var i = 0; i < botHand.length; ++i) { var c = new Card(); c.setCard(botHand[i], false); c.owner = 'bot'; c.index = i; c.x = handX + (i - 0.5) * handSpacing; c.y = botHandY; c.scaleX = c.scaleY = 1.1; game.addChild(c); botCardNodes.push(c); } // Discard pile for (var i = 0; i < discardNodes.length; ++i) game.removeChild(discardNodes[i]); discardNodes = []; for (var i = 0; i < discardPile.length; ++i) { var c = new Card(); c.setCard(discardPile[i], true); c.x = 2048 - 200; c.y = 2732 / 2 + (i - 2) * 40; c.scaleX = c.scaleY = 0.7; game.addChild(c); discardNodes.push(c); } } // Show popup message function showPopup(msg, duration, cb) { if (popup) { game.removeChild(popup); popup = null; } popup = new Popup(); popup.setText(msg); popup.x = 2048 / 2; popup.y = 2732 / 2; game.addChild(popup); if (duration) { LK.setTimeout(function () { if (popup) { game.removeChild(popup); popup = null; } if (cb) cb(); }, duration); } } // Enable player to play a card function enablePlayerPlay() { // Only allow if not out if (playerOut || roundOver) return; // Add .down event to player's cards for (var i = 0; i < playerCardNodes.length; ++i) { (function (idx) { playerCardNodes[idx].down = function (x, y, obj) { // Play this card playerPlayCard(idx); }; })(i); } } // Disable player input function disablePlayerPlay() { for (var i = 0; i < playerCardNodes.length; ++i) { playerCardNodes[i].down = undefined; } } // Player plays a card function playerPlayCard(idx) { if (playerOut || roundOver) return; disablePlayerPlay(); // Play card at idx var card = playerHand[idx]; var otherIdx = idx === 0 ? 1 : 0; var keepCard = playerHand[otherIdx]; // Countess rule: must play Countess if holding King or Prince if (card.id !== 'countess' && keepCard && keepCard.id === 'countess' && (card.id === 'king' || card.id === 'prince')) { showPopup("You must play Countess!", 1200, function () { enablePlayerPlay(); }); return; } // Remove card from hand var played = playerHand.splice(idx, 1)[0]; discardPile.push(played); // Animate card to discard renderHands(); // Card effect resolveCardEffect('player', played, function () { // If not out, keep card in hand if (!playerOut && keepCard) { playerHand = [keepCard]; } renderHands(); // Next: bot's turn if (!roundOver) { LK.setTimeout(function () { botTurn(); }, 900); } }); } // Bot's turn function botTurn() { if (botOut || roundOver) return; // Draw botDraw(); renderHands(); // Choose card to play var idx = botChooseCard(); var card = botHand[idx]; var otherIdx = idx === 0 ? 1 : 0; var keepCard = botHand[otherIdx]; // Countess rule if (card.id !== 'countess' && keepCard && keepCard.id === 'countess' && (card.id === 'king' || card.id === 'prince')) { idx = otherIdx; card = botHand[idx]; otherIdx = idx === 0 ? 1 : 0; keepCard = botHand[otherIdx]; } // Remove card from hand var played = botHand.splice(idx, 1)[0]; discardPile.push(played); // Animate card to discard renderHands(); // Card effect resolveCardEffect('bot', played, function () { // If not out, keep card in hand if (!botOut && keepCard) { botHand = [keepCard]; } renderHands(); // Next: player's turn if (!roundOver) { LK.setTimeout(function () { playerDraw(); renderHands(); enablePlayerPlay(); }, 900); } }); } // Bot chooses which card to play (simple AI) function botChooseCard() { // If must play Countess if (botHand.length == 2) { var c0 = botHand[0], c1 = botHand[1]; if (c0.id === 'countess' && (c1.id === 'king' || c1.id === 'prince')) return 0; if (c1.id === 'countess' && (c0.id === 'king' || c0.id === 'prince')) return 1; } // Prefer not to play Princess for (var i = 0; i < botHand.length; ++i) { if (botHand[i].id !== 'princess') return i; } // Otherwise, play first return 0; } // Card effect resolution function resolveCardEffect(who, card, cb) { // who: 'player' or 'bot' // card: card object // cb: callback after effect // Helper: get opponent function getOpponent() { return who === 'player' ? 'bot' : 'player'; } function getHand(who) { return who === 'player' ? playerHand : botHand; } function setProtected(who, val) { if (who === 'player') playerProtected = val;else botProtected = val; } function isProtected(who) { return who === 'player' ? playerProtected : botProtected; } function setOut(who) { if (who === 'player') playerOut = true;else botOut = true; } // Card effects if (card.id === 'guard') { // Guess a card (not Guard) if (who === 'player') { // Show options to guess showGuardGuess(function (guessId) { if (botProtected) { showPopup("Bot is protected!", 1000, cb); } else if (botHand[0].id === guessId) { showPopup("Correct! Bot had " + botHand[0].name + ".", 1200, function () { setOut('bot'); endRound(); cb(); }); return; } else { showPopup("Wrong guess.", 1000, cb); } }); return; } else { // Bot guesses randomly (not Guard) if (playerProtected) { showPopup("You are protected!", 1000, cb); } else { // Guess random card (not Guard) var guessable = ['priest', 'baron', 'handmaid', 'prince', 'king', 'countess', 'princess']; var guessId = guessable[Math.floor(Math.random() * guessable.length)]; if (playerHand[0].id === guessId) { showPopup("Bot guessed " + CARD_DECK.filter(function (c) { return c.id === guessId; })[0].name + " and was right!", 1200, function () { setOut('player'); endRound(); cb(); }); return; } else { showPopup("Bot guessed " + CARD_DECK.filter(function (c) { return c.id === guessId; })[0].name + " and was wrong.", 1000, cb); } } } } else if (card.id === 'priest') { // See opponent's hand if (who === 'player') { if (botProtected) { showPopup("Bot is protected!", 1000, cb); } else { showPopup("Bot has " + botHand[0].name + ".", 1500, cb); } } else { if (playerProtected) { showPopup("You are protected!", 1000, cb); } else { showPopup("Bot looks at your hand.", 1000, cb); } } } else if (card.id === 'baron') { // Compare hands, lower is out if (who === 'player') { if (botProtected) { showPopup("Bot is protected!", 1000, cb); } else { var p = playerHand[0].value, b = botHand[0].value; if (p > b) { showPopup("You win! (" + playerHand[0].name + " > " + botHand[0].name + ")", 1200, function () { setOut('bot'); endRound(); cb(); }); return; } else if (b > p) { showPopup("You lose! (" + playerHand[0].name + " < " + botHand[0].name + ")", 1200, function () { setOut('player'); endRound(); cb(); }); return; } else { showPopup("Tie! (" + playerHand[0].name + " = " + botHand[0].name + ")", 1000, cb); } } } else { if (playerProtected) { showPopup("You are protected!", 1000, cb); } else { var p = playerHand[0].value, b = botHand[0].value; if (b > p) { showPopup("Bot wins! (" + botHand[0].name + " > " + playerHand[0].name + ")", 1200, function () { setOut('player'); endRound(); cb(); }); return; } else if (p > b) { showPopup("Bot loses! (" + botHand[0].name + " < " + playerHand[0].name + ")", 1200, function () { setOut('bot'); endRound(); cb(); }); return; } else { showPopup("Tie! (" + playerHand[0].name + " = " + botHand[0].name + ")", 1000, cb); } } } } else if (card.id === 'handmaid') { // Immunity until next turn setProtected(who, true); showPopup((who === 'player' ? "You" : "Bot") + " are protected until next turn.", 1000, cb); } else if (card.id === 'prince') { // Choose a player to discard hand if (who === 'player') { // If bot is protected, must target self if (botProtected && !playerProtected) { showPopup("Bot is protected. You discard your hand.", 1000, function () { princeDiscard('player', cb); }); } else if (playerProtected && !botProtected) { showPopup("You are protected. Bot discards hand.", 1000, function () { princeDiscard('bot', cb); }); } else if (playerProtected && botProtected) { showPopup("Both protected. Nothing happens.", 1000, cb); } else { // Choose target showPrinceTarget(function (target) { princeDiscard(target, cb); }); return; } } else { // Bot: prefer to target player if not protected if (!playerProtected) { showPopup("Bot makes you discard your hand.", 1000, function () { princeDiscard('player', cb); }); } else if (!botProtected) { showPopup("Bot discards its own hand.", 1000, function () { princeDiscard('bot', cb); }); } else { showPopup("Both protected. Nothing happens.", 1000, cb); } } } else if (card.id === 'king') { // Trade hands if (who === 'player') { if (botProtected) { showPopup("Bot is protected!", 1000, cb); } else { var tmp = playerHand[0]; playerHand[0] = botHand[0]; botHand[0] = tmp; showPopup("You swapped hands!", 1000, cb); } } else { if (playerProtected) { showPopup("You are protected!", 1000, cb); } else { var tmp = playerHand[0]; playerHand[0] = botHand[0]; botHand[0] = tmp; showPopup("Bot swapped hands!", 1000, cb); } } } else if (card.id === 'countess') { // No effect showPopup((who === 'player' ? "You" : "Bot") + " played Countess.", 1000, cb); } else if (card.id === 'princess') { // If discarded, out showPopup((who === 'player' ? "You" : "Bot") + " discarded the Princess and is out!", 1200, function () { setOut(who); endRound(); cb(); }); return; } else { showPopup("No effect.", 1000, cb); } } // Guard guess UI function showGuardGuess(cb) { // Show options for player to guess (not Guard) var opts = ['priest', 'baron', 'handmaid', 'prince', 'king', 'countess', 'princess']; var buttons = []; var y0 = 2732 / 2 - 120; for (var i = 0; i < opts.length; ++i) { (function (idx) { var c = new Card(); var cardData = CARD_DECK.filter(function (cd) { return cd.id === opts[idx]; })[0]; c.setCard(cardData, true); c.x = 2048 / 2 - (opts.length / 2 - idx) * 180; c.y = y0; c.scaleX = c.scaleY = 0.7; c.down = function (x, y, obj) { // Remove all for (var j = 0; j < buttons.length; ++j) game.removeChild(buttons[j]); cb(opts[idx]); }; game.addChild(c); buttons.push(c); })(i); } } // Prince target UI function showPrinceTarget(cb) { var buttons = []; var y0 = 2732 / 2 - 120; // Player var c1 = new Card(); c1.setCard(playerHand[0], true); c1.x = 2048 / 2 - 120; c1.y = y0; c1.scaleX = c1.scaleY = 0.8; c1.down = function (x, y, obj) { for (var j = 0; j < buttons.length; ++j) game.removeChild(buttons[j]); cb('player'); }; game.addChild(c1); buttons.push(c1); // Bot (face down) var c2 = new Card(); c2.setCard(botHand[0], false); c2.x = 2048 / 2 + 120; c2.y = y0; c2.scaleX = c2.scaleY = 0.8; c2.down = function (x, y, obj) { for (var j = 0; j < buttons.length; ++j) game.removeChild(buttons[j]); cb('bot'); }; game.addChild(c2); buttons.push(c2); } // Prince discard effect function princeDiscard(who, cb) { if (who === 'player') { var card = playerHand[0]; discardPile.push(card); if (card.id === 'princess') { showPopup("You discarded the Princess and are out!", 1200, function () { playerOut = true; endRound(); cb(); }); return; } else { // Draw new card if (deck.length > 0) { playerHand[0] = deck.pop(); showPopup("You drew a new card.", 1000, cb); } else { playerHand = []; showPopup("No cards left to draw.", 1000, cb); } } } else { var card = botHand[0]; discardPile.push(card); if (card.id === 'princess') { showPopup("Bot discarded the Princess and is out!", 1200, function () { botOut = true; endRound(); cb(); }); return; } else { // Draw new card if (deck.length > 0) { botHand[0] = deck.pop(); showPopup("Bot drew a new card.", 1000, cb); } else { botHand = []; showPopup("No cards left to draw.", 1000, cb); } } } } // End round: check for winner function endRound() { roundOver = true; disablePlayerPlay(); // Reveal bot's hand for (var i = 0; i < botCardNodes.length; ++i) { botCardNodes[i].flip(true); } // Who wins? var winner = null; if (playerOut && botOut) { winner = null; } else if (playerOut) { winner = 'bot'; } else if (botOut) { winner = 'player'; } else if (deck.length === 0) { // Compare hands if (playerHand[0].value > botHand[0].value) winner = 'player';else if (botHand[0].value > playerHand[0].value) winner = 'bot';else winner = null; } // Update score if (winner === 'player') { playerScore += 1; updateGUI(); showPopup("You win the round!", 1800, function () { checkGameEnd(); }); } else if (winner === 'bot') { botScore += 1; updateGUI(); showPopup("Bot wins the round!", 1800, function () { checkGameEnd(); }); } else { showPopup("Round is a tie!", 1500, function () { checkGameEnd(); }); } } // Check for game end function checkGameEnd() { if (playerScore >= roundTarget) { LK.setScore(playerScore); LK.showYouWin(); } else if (botScore >= roundTarget) { LK.setScore(playerScore); LK.showGameOver(); } else { // Start next round startRound(); } } // On every update, clear protection if needed game.update = function () { // Remove protection at start of player's/bot's turn if (currentTurn === 'player' && playerProtected) playerProtected = false; if (currentTurn === 'bot' && botProtected) botProtected = false; }; // Number of bots (default 1) var numBots = 1; // Menu to choose number of bots (1-3) function showBotCountMenu() { // Remove any previous menu if (typeof botMenuButtons !== "undefined") { for (var i = 0; i < botMenuButtons.length; ++i) { game.removeChild(botMenuButtons[i]); } } botMenuButtons = []; var menuText = new Text2("Choose number of bots", { size: 80, fill: "#fff" }); menuText.anchor.set(0.5, 0.5); menuText.x = 2048 / 2; menuText.y = 900; game.addChild(menuText); botMenuButtons.push(menuText); var y = 1200; for (var i = 1; i <= 3; ++i) { (function (botCount) { var btn = new Container(); var bg = LK.getAsset({ anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 0.7 }, {}); btn.addChild(bg); var txt = new Text2(botCount + " Bot" + (botCount > 1 ? "s" : ""), { size: 64, fill: "#222" }); txt.anchor.set(0.5, 0.5); txt.x = 0; txt.y = 0; btn.addChild(txt); btn.x = 2048 / 2 + (botCount - 2) * 350; btn.y = y; btn.down = function (x, y, obj) { numBots = botCount; // Remove menu for (var j = 0; j < botMenuButtons.length; ++j) { game.removeChild(botMenuButtons[j]); } // Start game setupGUI(); updateGUI(); startRound(); }; game.addChild(btn); botMenuButtons.push(btn); })(i); } } // Show menu at game start showBotCountMenu();
===================================================================
--- original.js
+++ change.js
@@ -871,14 +871,14 @@
var y = 1200;
for (var i = 1; i <= 3; ++i) {
(function (botCount) {
var btn = new Container();
- var bg = LK.getAsset('cardBack', {
+ var bg = LK.getAsset({
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.7
- });
+ }, {});
btn.addChild(bg);
var txt = new Text2(botCount + " Bot" + (botCount > 1 ? "s" : ""), {
size: 64,
fill: "#222"