User prompt
On the start screen, I also want to let players choose the number of rounds to be played (1, 3, or 5 rounds) while selecting the target limit. I want to increase the size of the cards.
Code edit (1 edits merged)
Please save this source code
User prompt
Over the Limit
Initial prompt
I want to create a card game where each card has a numerical value, and if the total value exceeds a predetermined amount, one side loses. For example, I have the cards 4, 6, 7, and 9 in my hand. The opponent has the cards 3, 5, 6, and 8. I play the 4 and 6 cards. The opponent plays the 3 and 5 cards. Whoever exceeds 30 loses. If I play the 7, the opponent loses. Each player can have 4 cards in their hand, and the card numbers should range from 1 to 10. There should be 5 copies of each number, for a total of 50 cards, and there should be two additional 0 cards. At the beginning, I want the players to choose the maximum total (the limit not to be exceeded) on a screen; there should be 3 options: 30, 50, or 80. The game continues until the chosen number is exceeded.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Card = Container.expand(function (cardData) { var self = Container.call(this); // Add null check for cardData parameter if (!cardData) { cardData = { value: 0, color: 'red' }; } self.value = cardData.value || 0; self.color = cardData.color || 'red'; self.isSelected = false; // Add shadow beneath the card var cardShadow = self.attachAsset('cardShadow', { anchorX: 0.5, anchorY: 0.5, x: 5, y: 5, scaleX: 0.8, scaleY: 0.3 }); cardShadow.alpha = 0.3; var cardGraphics = self.attachAsset('card' + self.value, { anchorX: 0.5, anchorY: 0.5 }); var cardText = new Text2(self.value.toString(), { size: 180, fill: 0xFFFFFF }); cardText.anchor.set(0.5, 0.5); cardText.alpha = 0; self.addChild(cardText); self.setSelected = function (selected) { self.isSelected = selected; if (selected) { cardGraphics.tint = 0xf1c40f; self.y -= 20; } else { cardGraphics.tint = 0xffffff; self.y += 20; } }; self.down = function (x, y, obj) { if (currentGameState === 'playing' && currentPlayer === 0) { self.setSelected(!self.isSelected); // Auto-play cards when selection changes LK.setTimeout(function () { playSelectedCards(); }, 100); } }; return self; }); var LimitButton = Container.expand(function (limit) { var self = Container.call(this); self.limit = limit; self.isSelected = false; var buttonGraphics = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2(limit.toString(), { size: 100, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.setSelected = function (selected) { self.isSelected = selected; buttonGraphics.removeChild(buttonGraphics.children[0]); if (selected) { buttonGraphics.addChild(LK.getAsset('selectedButton', { anchorX: 0.5, anchorY: 0.5 })); } else { buttonGraphics.addChild(LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5 })); } }; self.down = function (x, y, obj) { if (currentGameState === 'setup') { LK.getSound('buttonClick').play(); selectLimit(self.limit); } }; return self; }); var RoundButton = Container.expand(function (rounds) { var self = Container.call(this); self.rounds = rounds; self.isSelected = false; var buttonGraphics = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2(rounds + ' Round' + (rounds > 1 ? 's' : ''), { size: 85, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.setSelected = function (selected) { self.isSelected = selected; buttonGraphics.removeChild(buttonGraphics.children[0]); if (selected) { buttonGraphics.addChild(LK.getAsset('selectedButton', { anchorX: 0.5, anchorY: 0.5 })); } else { buttonGraphics.addChild(LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5 })); } }; self.down = function (x, y, obj) { if (currentGameState === 'setup') { LK.getSound('buttonClick').play(); selectRounds(self.rounds); } }; return self; }); var RulesButton = Container.expand(function () { var self = Container.call(this); var buttonGraphics = self.attachAsset('rulesButton', { anchorX: 0.5, anchorY: 0.5 }); var buttonText = new Text2('Rules', { size: 60, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); self.down = function (x, y, obj) { LK.getSound('buttonClick').play(); showRules(); }; return self; }); var SpeakerButton = Container.expand(function () { var self = Container.call(this); // Get music state from storage, default to true (music on) self.musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true; // Create speaker icon using a round shape var speakerIcon = self.attachAsset('thinkingDot', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 4 }); speakerIcon.tint = 0x3498db; // Blue tint // Create speaker text var speakerText = new Text2('♪', { size: 60, fill: 0xFFFFFF }); speakerText.anchor.set(0.5, 0.5); self.addChild(speakerText); // Create cross overlay for muted state var crossOverlay = new Text2('✕', { size: 80, fill: 0xe74c3c }); crossOverlay.anchor.set(0.5, 0.5); crossOverlay.x = 10; crossOverlay.y = -10; self.addChild(crossOverlay); // Update visual state self.updateVisual = function () { if (self.musicEnabled) { crossOverlay.alpha = 0; speakerIcon.tint = 0x3498db; // Blue when on } else { crossOverlay.alpha = 1; speakerIcon.tint = 0x7f8c8d; // Gray when off } }; // Initial visual state self.updateVisual(); // Toggle music when clicked self.down = function (x, y, obj) { LK.getSound('buttonClick').play(); self.musicEnabled = !self.musicEnabled; // Save to storage storage.musicEnabled = self.musicEnabled; // Update music playback if (self.musicEnabled) { // Play appropriate music based on game mode if (currentGameState === 'playing' && isArcadeMode) { LK.playMusic('arcade'); } else { LK.playMusic('startup'); } } else { LK.stopMusic(); } // Update visual self.updateVisual(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x27ae60 }); /**** * Game Code ****/ // Game state variables var currentGameState = 'setup'; // 'setup', 'playing', 'gameOver' var selectedPlayerCount = 2; // Fixed to 2-player mode var selectedLimit = 0; var selectedRounds = 0; var maxRounds = 0; var currentTotal = 0; var currentPlayer = 0; // 0 = player, 1 = AI var playerHand = []; var aiHand = []; var deck = []; var playerScore = 0; var aiScore = 0; var aiThinkingTimeout = null; var isArcadeMode = false; var arcadeRoundCount = 0; var arcadeTotalScore = 0; var aiCardPositions = [{ x: -675, y: -20, rotation: 0.12 }, { x: -225, y: 10, rotation: 0.05 }, { x: 225, y: -15, rotation: -0.08 }, { x: 675, y: 5, rotation: -0.12 }]; // UI elements var limitButtons = []; var roundButtons = []; var playAreaGraphics; var totalText; var limitText; var playerHandContainer; var aiHandContainer; var playButton; var statusText; var scoreText; var centerCardContainer; var rulesButton; var rulesContainer; var speechBubble; var playerSpeechBubble; // Initialize deck function initializeDeck() { deck = []; // Add 5 color groups of cards 1-10 (red, blue, yellow, green, purple) var colors = ['red', 'blue', 'yellow', 'green', 'purple']; for (var colorIndex = 0; colorIndex < colors.length; colorIndex++) { for (var value = 1; value <= 10; value++) { deck.push({ value: value, color: colors[colorIndex] }); } } // Add 2 black zero cards deck.push({ value: 0, color: 'black' }); deck.push({ value: 0, color: 'black' }); shuffleDeck(); } function shuffleDeck() { 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; } } function dealCard() { if (deck.length === 0) { initializeDeck(); } return deck.pop(); } function dealInitialHands() { playerHand = []; aiHand = []; for (var i = 0; i < 4; i++) { playerHand.push(dealCard()); aiHand.push(dealCard()); } } function createLimitSelection() { // Add background to setup screen var setupBackground = game.attachAsset('setupBackground', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Add title image in the title area var titleImage = game.attachAsset('titleImage', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 450 }); var limits = [30, 50, 80]; for (var i = 0; i < limits.length; i++) { var button = new LimitButton(limits[i]); button.x = 1024 + (i - 1) * 400; button.y = 1000; limitButtons.push(button); game.addChild(button); } var rounds = [1, 3, 5]; for (var i = 0; i < rounds.length; i++) { var roundButton = new RoundButton(rounds[i]); roundButton.x = 1024 + (i - 1) * 400; roundButton.y = 1300; roundButtons.push(roundButton); game.addChild(roundButton); } var arcadeButton = new Container(); var arcadeGraphics = arcadeButton.attachAsset('button', { anchorX: 0.5, anchorY: 0.5 }); arcadeGraphics.tint = 0x9b59b6; // Purple tint for arcade var arcadeText = new Text2('Arcade', { size: 60, fill: 0xFFFFFF }); arcadeText.anchor.set(0.5, 0.5); arcadeButton.addChild(arcadeText); arcadeButton.x = 1024; arcadeButton.y = 1700; arcadeButton.down = function (x, y, obj) { if (currentGameState === 'setup') { LK.getSound('buttonClick').play(); startArcadeMode(); } }; game.addChild(arcadeButton); // Add rules button rulesButton = new RulesButton(); rulesButton.x = 1850; rulesButton.y = 150; game.addChild(rulesButton); // Add speaker button in bottom right corner var speakerButton = new SpeakerButton(); speakerButton.x = 1900; speakerButton.y = 2600; game.addChild(speakerButton); // Add speaker button in bottom right corner var speakerButton = new SpeakerButton(); speakerButton.x = 1900; speakerButton.y = 2600; game.addChild(speakerButton); // No play button needed - cards auto-play when selected } function selectRounds(rounds) { selectedRounds = rounds; maxRounds = rounds; for (var i = 0; i < roundButtons.length; i++) { roundButtons[i].setSelected(roundButtons[i].rounds === rounds); } checkStartGameReady(); } function selectLimit(limit) { selectedLimit = limit; for (var i = 0; i < limitButtons.length; i++) { limitButtons[i].setSelected(limitButtons[i].limit === limit); } checkStartGameReady(); } function showRules() { if (rulesContainer) { return; // Rules already showing } rulesContainer = new Container(); rulesContainer.x = 1024; rulesContainer.y = 1366; game.addChild(rulesContainer); // Semi-transparent background - larger to better match text boundaries var rulesBackground = rulesContainer.attachAsset('playArea', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 6 }); rulesBackground.alpha = 0.9; rulesBackground.tint = 0x2c3e50; // Rules title var rulesTitle = new Text2('Game Rules', { size: 60, fill: 0xf1c40f }); rulesTitle.anchor.set(0.5, 0.5); rulesTitle.x = 0; rulesTitle.y = -500; rulesContainer.addChild(rulesTitle); // Rules text - reduced size to fit on single screen var rulesText = new Text2('• Players take turns playing cards\n• The total of all played cards is tracked\n• If you exceed the chosen limit, you lose the round\n• Zero cards reset the total to zero\n• Win by reaching the target number of rounds\n• Player with highest card starts (unless they have a zero)', { size: 45, fill: 0xFFFFFF }); rulesText.anchor.set(0.5, 0.5); rulesText.x = 0; rulesText.y = -200; rulesContainer.addChild(rulesText); // Arcade Game Rules heading var arcadeRulesTitle = new Text2('Arcade Game Rules', { size: 50, fill: 0x9b59b6 }); arcadeRulesTitle.anchor.set(0.5, 0.5); arcadeRulesTitle.x = 0; arcadeRulesTitle.y = 150; rulesContainer.addChild(arcadeRulesTitle); // Arcade rules text var arcadeRulesText = new Text2('• Infinite rounds with increasing difficulty\n• Limit starts at 50 and increases by 20 each round\n• Score points based on round number\n• Game ends when you exceed the limit\n• Total never resets between rounds\n• Challenge yourself to reach the highest score!', { size: 40, fill: 0xFFFFFF }); arcadeRulesText.anchor.set(0.5, 0.5); arcadeRulesText.x = 0; arcadeRulesText.y = 300; rulesContainer.addChild(arcadeRulesText); // Close button var closeButton = new Container(); var closeGraphics = closeButton.attachAsset('button', { anchorX: 0.5, anchorY: 0.5 }); closeGraphics.tint = 0xe74c3c; var closeText = new Text2('Close', { size: 40, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeButton.addChild(closeText); closeButton.x = 0; closeButton.y = 580; closeButton.down = function (x, y, obj) { LK.getSound('buttonClick').play(); rulesContainer.destroy(); rulesContainer = null; }; rulesContainer.addChild(closeButton); } function checkStartGameReady() { if (selectedLimit > 0 && selectedRounds > 0) { if (!playButton) { playButton = new Container(); var playGraphics = playButton.attachAsset('button', { anchorX: 0.5, anchorY: 0.5 }); var playText = new Text2('Start Game', { size: 55, fill: 0xFFFFFF }); playText.anchor.set(0.5, 0.5); playButton.addChild(playText); playButton.x = 1024; playButton.y = 1500; playButton.down = function (x, y, obj) { LK.getSound('buttonClick').play(); startGame(); }; game.addChild(playButton); } } } function determineStartingPlayer() { // Find highest card value for each player var playerHighest = 0; var aiHighest = 0; var playerHasZero = false; var aiHasZero = false; // Check player hand for (var i = 0; i < playerHand.length; i++) { if (playerHand[i].value === 0) { playerHasZero = true; } if (playerHand[i].value > playerHighest) { playerHighest = playerHand[i].value; } } // Check AI hand for (var i = 0; i < aiHand.length; i++) { if (aiHand[i].value === 0) { aiHasZero = true; } if (aiHand[i].value > aiHighest) { aiHighest = aiHand[i].value; } } // Determine starting player // If player has highest card but also has a 0, they cannot start if (playerHighest > aiHighest && !playerHasZero) { return 0; // Player starts } // If AI has highest card but also has a 0, they cannot start else if (aiHighest > playerHighest && !aiHasZero) { return 1; // AI starts } // If both have same highest card or both have 0s, prefer the one without 0 else if (playerHighest === aiHighest) { if (!playerHasZero && aiHasZero) { return 0; // Player starts } else if (!aiHasZero && playerHasZero) { return 1; // AI starts } else { return 0; // Default to player if both have/don't have 0s } } // If player has 0 but AI doesn't, AI starts regardless of card values else if (playerHasZero && !aiHasZero) { return 1; } // If AI has 0 but player doesn't, player starts regardless of card values else if (aiHasZero && !playerHasZero) { return 0; } return 0; // Default to player } function startArcadeMode() { isArcadeMode = true; selectedLimit = 50; selectedRounds = 1; maxRounds = 1; arcadeRoundCount = 1; arcadeTotalScore = 0; // Hide arcade button by destroying it for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.down && child.down.toString().indexOf('startArcadeMode') !== -1) { child.destroy(); break; } } // Play arcade music if music is enabled var musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true; if (musicEnabled) { LK.playMusic('arcade'); } startGame(); } function startGame() { currentGameState = 'playing'; currentTotal = 0; maxRounds = selectedRounds; // Initialize deck and deal hands first initializeDeck(); dealInitialHands(); // Determine starting player based on cards currentPlayer = determineStartingPlayer(); // Clear setup UI for (var i = 0; i < limitButtons.length; i++) { limitButtons[i].destroy(); } limitButtons = []; for (var i = 0; i < roundButtons.length; i++) { roundButtons[i].destroy(); } roundButtons = []; if (playButton) { playButton.destroy(); playButton = null; } if (!rulesButton) {} // Add rules button rulesButton = new RulesButton(); rulesButton.x = 1850; rulesButton.y = 150; game.addChild(rulesButton); if (rulesContainer) { rulesContainer.destroy(); rulesContainer = null; } // Hide instruction texts for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child instanceof Text2 && (child.text === 'Choose Target Limit' || child.text === 'Choose Number of Rounds')) { child.destroy(); } } // Initialize game createGameUI(); updateDisplay(); // If AI starts first, trigger AI turn after a short delay if (currentPlayer === 1) { LK.setTimeout(function () { aiTurn(); }, 1500); } } function createGameUI() { // Add background to game screen var backgroundAsset = isArcadeMode ? 'arcadeBackground' : 'gameBackground'; var gameBackground = game.attachAsset(backgroundAsset, { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Play area removed - no gray stripe needed // Center card container for animations centerCardContainer = new Container(); centerCardContainer.x = 1024; centerCardContainer.y = 1366; game.addChild(centerCardContainer); // Total value display (three times larger font, centered) totalText = new Text2('0', { size: 240, fill: 0x2ecc71 }); totalText.anchor.set(0.5, 0.5); totalText.x = 1536; totalText.y = 1366; game.addChild(totalText); // Limit display (positioned below total text) limitText = new Text2('Limit: ' + selectedLimit, { size: 40, fill: 0x000000 }); limitText.anchor.set(0.5, 0.5); limitText.x = 1536; limitText.y = 1500; game.addChild(limitText); // Player hand container playerHandContainer = new Container(); playerHandContainer.x = 1024; playerHandContainer.y = 2200; game.addChild(playerHandContainer); // AI hand container aiHandContainer = new Container(); aiHandContainer.x = 1024; aiHandContainer.y = 600; game.addChild(aiHandContainer); // Score display if (isArcadeMode) { scoreText = new Text2('Round ' + arcadeRoundCount + ' | Score: ' + arcadeTotalScore, { size: 120, fill: 0x000000, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); } else { scoreText = new Text2(playerScore + ' = ' + aiScore, { size: 120, fill: 0x000000, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); } scoreText.anchor.set(0.5, 0.5); scoreText.x = 1024; scoreText.y = 100; game.addChild(scoreText); // Add speaker button in bottom right corner of game screen var gameSpeakerButton = new SpeakerButton(); gameSpeakerButton.x = 1900; gameSpeakerButton.y = 2600; game.addChild(gameSpeakerButton); // Play appropriate music based on game mode var musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true; if (musicEnabled) { if (isArcadeMode) { LK.playMusic('arcade'); } else { LK.playMusic('startup'); } } // No play button needed - cards auto-play when selected } function updateDisplay() { totalText.setText(currentTotal.toString()); // Update total text color based on proximity to limit var ratio = currentTotal / selectedLimit; if (ratio <= 0.6) { totalText.tint = 0x2ecc71; // Green } else if (ratio <= 0.8) { totalText.tint = 0xf1c40f; // Yellow } else { totalText.tint = 0xe74c3c; // Red } if (isArcadeMode) { scoreText.setText('Round ' + arcadeRoundCount + ' | Score: ' + arcadeTotalScore); } else { scoreText.setText(playerScore + ' = ' + aiScore); } // Update player hand only if the number of cards changed if (playerHandContainer.children.length !== playerHand.length) { playerHandContainer.removeChildren(); var cardPositions = [{ x: -675, y: 20, rotation: -0.15 }, { x: -225, y: -10, rotation: -0.05 }, { x: 225, y: 15, rotation: 0.08 }, { x: 675, y: -5, rotation: 0.12 }]; for (var i = 0; i < playerHand.length; i++) { var card = new Card(playerHand[i]); if (i < cardPositions.length) { card.x = cardPositions[i].x; card.y = cardPositions[i].y; card.rotation = cardPositions[i].rotation; } else { card.x = (i - 1.5) * 450; } playerHandContainer.addChild(card); } } // Update AI hand (show card backs) only if the number of cards changed if (aiHandContainer.children.length !== aiHand.length) { aiHandContainer.removeChildren(); for (var i = 0; i < aiHand.length; i++) { var cardBack = aiHandContainer.attachAsset('cardBack' + aiHand[i].value, { anchorX: 0.5, anchorY: 0.5, x: aiCardPositions[i] ? aiCardPositions[i].x : (i - 1.5) * 450, y: aiCardPositions[i] ? aiCardPositions[i].y : 0 }); if (aiCardPositions[i]) { cardBack.rotation = aiCardPositions[i].rotation; } } } if (currentPlayer === 0) { // Remove AI speech bubble if it exists if (speechBubble) { speechBubble.destroy(); speechBubble = null; } // Create speech bubble above player cards if (playerSpeechBubble) { playerSpeechBubble.destroy(); } playerSpeechBubble = new Container(); playerSpeechBubble.x = 1650; playerSpeechBubble.y = 1750; // Add bubble background var bubbleGraphics = playerSpeechBubble.attachAsset('playerSpeechBubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); // Add three thinking dots var dots = []; for (var i = 0; i < 3; i++) { var dot = playerSpeechBubble.attachAsset('thinkingDot', { anchorX: 0.5, anchorY: 0.5, x: (i - 1) * 40, y: 0 }); dots.push(dot); } // Animate dots with staggered timing for (var i = 0; i < dots.length; i++) { (function (dotIndex) { var _animateDot = function animateDot() { tween(dots[dotIndex], { scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(dots[dotIndex], { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { if (playerSpeechBubble && playerSpeechBubble.parent) { LK.setTimeout(_animateDot, 600); } } }); } }); }; LK.setTimeout(_animateDot, dotIndex * 200); })(i); } game.addChild(playerSpeechBubble); } else { // Remove player speech bubble if it exists if (playerSpeechBubble) { playerSpeechBubble.destroy(); playerSpeechBubble = null; } // Create speech bubble under AI cards if (speechBubble) { speechBubble.destroy(); } speechBubble = new Container(); speechBubble.x = 300; speechBubble.y = 150; // Add bubble background var bubbleGraphics = speechBubble.attachAsset('aiSpeechBubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); // Add three thinking dots var dots = []; for (var i = 0; i < 3; i++) { var dot = speechBubble.attachAsset('thinkingDot', { anchorX: 0.5, anchorY: 0.5, x: (i - 1) * 40, y: 0 }); dots.push(dot); } // Animate dots with staggered timing for (var i = 0; i < dots.length; i++) { (function (dotIndex) { var _animateDot = function animateDot() { tween(dots[dotIndex], { scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(dots[dotIndex], { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { if (speechBubble && speechBubble.parent) { LK.setTimeout(_animateDot, 600); } } }); } }); }; LK.setTimeout(_animateDot, dotIndex * 200); })(i); } game.addChild(speechBubble); } } function playSelectedCards() { var selectedCards = []; var selectedIndices = []; for (var i = 0; i < playerHandContainer.children.length; i++) { var card = playerHandContainer.children[i]; if (card.isSelected) { selectedCards.push(card.value); selectedIndices.push(i); } } if (selectedCards.length === 0) { return; } // Calculate total of selected cards var cardTotal = 0; for (var i = 0; i < selectedCards.length; i++) { cardTotal += selectedCards[i]; } currentTotal += cardTotal; LK.getSound('cardPlay').play(); // Animate played cards to center var playedCardData = { value: selectedCards[0], color: playerHand[selectedIndices[0]].color }; var animatedCard = new Card(playedCardData); var sourceCard = playerHandContainer.children[selectedIndices[0]]; var startPos = playerHandContainer.toGlobal(sourceCard.position); var gamePos = game.toLocal(startPos); animatedCard.x = gamePos.x; animatedCard.y = gamePos.y; animatedCard.rotation = sourceCard.rotation; game.addChild(animatedCard); // Random scattered position and rotation for thrown card effect var scatterX = 1024 + (Math.random() - 0.5) * 300; var scatterY = 1366 + (Math.random() - 0.5) * 200; var scatterRotation = (Math.random() - 0.5) * 0.8; tween(animatedCard, { x: scatterX, y: scatterY, rotation: scatterRotation }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Move animated card to center container instead of destroying and recreating game.removeChild(animatedCard); animatedCard.x = scatterX - 1024; animatedCard.y = scatterY - 1366; animatedCard.rotation = scatterRotation; centerCardContainer.addChild(animatedCard); } }); // Remove played cards from hand for (var i = selectedIndices.length - 1; i >= 0; i--) { playerHand.splice(selectedIndices[i], 1); } // Replenish hand while (playerHand.length < 4 && deck.length > 0) { playerHand.push(dealCard()); } // Force recreation of player hand display by clearing it playerHandContainer.removeChildren(); // Remove player speech bubble if (playerSpeechBubble) { playerSpeechBubble.destroy(); playerSpeechBubble = null; } // Check if player exceeded limit if (currentTotal > selectedLimit) { endRound(false); return; } currentPlayer = 1; updateDisplay(); // AI turn after delay LK.setTimeout(function () { aiTurn(); }, 1500); } function aiTimeoutHandler() { // Clear the timeout since we're handling it now if (aiThinkingTimeout) { LK.clearTimeout(aiThinkingTimeout); aiThinkingTimeout = null; } // Remove speech bubble if (speechBubble) { speechBubble.destroy(); speechBubble = null; } // Play a random card var randomIndex = Math.floor(Math.random() * aiHand.length); var playedCard = aiHand[randomIndex]; currentTotal += playedCard.value; // Animate AI played card to center var animatedCard = new Card(playedCard); animatedCard.x = 1024 + aiCardPositions[randomIndex].x; animatedCard.y = 500 + aiCardPositions[randomIndex].y; animatedCard.rotation = aiCardPositions[randomIndex].rotation; game.addChild(animatedCard); // Random scattered position and rotation for thrown card effect var scatterX = 1024 + (Math.random() - 0.5) * 300; var scatterY = 1366 + (Math.random() - 0.5) * 200; var scatterRotation = (Math.random() - 0.5) * 0.8; tween(animatedCard, { x: scatterX, y: scatterY, rotation: scatterRotation }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Move animated card to center container instead of destroying and recreating game.removeChild(animatedCard); animatedCard.x = scatterX - 1024; animatedCard.y = scatterY - 1366; animatedCard.rotation = scatterRotation; centerCardContainer.addChild(animatedCard); // Check if AI exceeded limit if (currentTotal > selectedLimit) { endRound(true); return; } } }); aiHand.splice(randomIndex, 1); // Replenish AI hand while (aiHand.length < 4 && deck.length > 0) { aiHand.push(dealCard()); } LK.getSound('aiCardPlay').play(); currentPlayer = 0; updateDisplay(); } function aiTurn() { // Set up 5-second timeout for AI thinking aiThinkingTimeout = LK.setTimeout(function () { aiTimeoutHandler(); }, 5000); // Simple AI strategy: play lowest safe card(s) var safeCards = []; var safeIndices = []; for (var i = 0; i < aiHand.length; i++) { if (currentTotal + aiHand[i].value <= selectedLimit) { safeCards.push(aiHand[i].value); safeIndices.push(i); } } if (safeCards.length === 0) { // Clear timeout since AI is making a play if (aiThinkingTimeout) { LK.clearTimeout(aiThinkingTimeout); aiThinkingTimeout = null; } // Remove speech bubble if (speechBubble) { speechBubble.destroy(); speechBubble = null; } // AI must play and will lose var minCard = Math.min.apply(Math, aiHand.map(function (card) { return card.value; })); var minIndex = -1; for (var i = 0; i < aiHand.length; i++) { if (aiHand[i].value === minCard) { minIndex = i; break; } } currentTotal += minCard; // Animate AI played card to center for loss case var playedCardData = aiHand[minIndex]; var animatedCard = new Card(playedCardData); animatedCard.x = 1024 + aiCardPositions[minIndex].x; animatedCard.y = 500 + aiCardPositions[minIndex].y; animatedCard.rotation = aiCardPositions[minIndex].rotation; game.addChild(animatedCard); // Random scattered position and rotation for thrown card effect var scatterX = 1024 + (Math.random() - 0.5) * 300; var scatterY = 1366 + (Math.random() - 0.5) * 200; var scatterRotation = (Math.random() - 0.5) * 0.8; tween(animatedCard, { x: scatterX, y: scatterY, rotation: scatterRotation }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Move animated card to center container instead of destroying and recreating game.removeChild(animatedCard); animatedCard.x = scatterX - 1024; animatedCard.y = scatterY - 1366; animatedCard.rotation = scatterRotation; centerCardContainer.addChild(animatedCard); endRound(true); } }); aiHand.splice(minIndex, 1); return; } // Clear timeout since AI is making a play if (aiThinkingTimeout) { LK.clearTimeout(aiThinkingTimeout); aiThinkingTimeout = null; } // Remove speech bubble if (speechBubble) { speechBubble.destroy(); speechBubble = null; } // Play the lowest safe card var minSafe = Math.min.apply(Math, safeCards); var playIndex = -1; for (var i = 0; i < aiHand.length; i++) { if (aiHand[i].value === minSafe) { playIndex = i; break; } } currentTotal += minSafe; // Animate AI played card to center var playedCardData = aiHand[playIndex]; var animatedCard = new Card(playedCardData); animatedCard.x = 1024 + aiCardPositions[playIndex].x; animatedCard.y = 500 + aiCardPositions[playIndex].y; animatedCard.rotation = aiCardPositions[playIndex].rotation; game.addChild(animatedCard); // Random scattered position and rotation for thrown card effect var scatterX = 1024 + (Math.random() - 0.5) * 300; var scatterY = 1366 + (Math.random() - 0.5) * 200; var scatterRotation = (Math.random() - 0.5) * 0.8; tween(animatedCard, { x: scatterX, y: scatterY, rotation: scatterRotation }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Move animated card to center container instead of destroying and recreating game.removeChild(animatedCard); animatedCard.x = scatterX - 1024; animatedCard.y = scatterY - 1366; animatedCard.rotation = scatterRotation; centerCardContainer.addChild(animatedCard); } }); aiHand.splice(playIndex, 1); // Replenish AI hand while (aiHand.length < 4 && deck.length > 0) { aiHand.push(dealCard()); } LK.getSound('aiCardPlay').play(); currentPlayer = 0; updateDisplay(); } function showCountdown() { // Add "Next round" message var nextRoundText = new Text2('Next round', { size: 80, fill: 0xFFFFFF }); nextRoundText.anchor.set(0.5, 0.5); nextRoundText.x = 350; nextRoundText.y = 1100; game.addChild(nextRoundText); var countdownText = new Text2('3', { size: 400, fill: 0x000000 }); countdownText.anchor.set(0.5, 0.5); countdownText.x = 350; countdownText.y = 1366; game.addChild(countdownText); var countdownValue = 3; var countdownTimer = LK.setInterval(function () { countdownValue--; if (countdownValue > 0) { countdownText.setText(countdownValue.toString()); tween(countdownText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(countdownText, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); } else { LK.clearInterval(countdownTimer); countdownText.destroy(); nextRoundText.destroy(); // Only reset currentTotal if not in arcade mode if (!isArcadeMode) { currentTotal = 0; } currentPlayer = 0; centerCardContainer.removeChildren(); dealInitialHands(); updateDisplay(); } }, 1000); } function endRound(playerWon) { if (playerWon) { LK.getSound('gameWin').play(); playerScore++; } else { LK.getSound('gameOver').play(); aiScore++; } updateDisplay(); // Show winner of this round var winnerText = new Text2(playerWon ? 'You Win This Round!' : 'AI Wins This Round!', { size: 100, fill: playerWon ? 0x2ecc71 : 0xe74c3c }); winnerText.anchor.set(0.5, 0.5); winnerText.x = 1024; winnerText.y = 950; game.addChild(winnerText); // Animate winner text tween(winnerText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(winnerText, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeIn }); } }); // Check for game end if (isArcadeMode) { // In arcade mode, player losing ends the game if (!playerWon) { LK.setTimeout(function () { winnerText.destroy(); LK.showGameOver(); }, 2000); return; } // Player won OR AI lost - continue to next round with increased limit and score arcadeRoundCount++; selectedLimit += 20; arcadeTotalScore += arcadeRoundCount * 10; // Award points based on round limitText.setText('Limit: ' + selectedLimit); // Continue to next round (total never resets) LK.setTimeout(function () { winnerText.destroy(); showCountdown(); }, 2000); } else { // Normal mode - check win threshold var winThreshold = Math.ceil(maxRounds / 2); if (playerScore >= winThreshold) { LK.setTimeout(function () { winnerText.destroy(); LK.showYouWin(); }, 2000); return; } else if (aiScore >= winThreshold) { LK.setTimeout(function () { winnerText.destroy(); LK.showGameOver(); }, 2000); return; } // Start new round with countdown after showing winner LK.setTimeout(function () { winnerText.destroy(); showCountdown(); }, 2000); } } // Initialize game createLimitSelection(); // Play startup music only if enabled var musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true; if (musicEnabled) { LK.playMusic('startup'); } game.update = function () { // Game loop updates };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Card = Container.expand(function (cardData) {
var self = Container.call(this);
// Add null check for cardData parameter
if (!cardData) {
cardData = {
value: 0,
color: 'red'
};
}
self.value = cardData.value || 0;
self.color = cardData.color || 'red';
self.isSelected = false;
// Add shadow beneath the card
var cardShadow = self.attachAsset('cardShadow', {
anchorX: 0.5,
anchorY: 0.5,
x: 5,
y: 5,
scaleX: 0.8,
scaleY: 0.3
});
cardShadow.alpha = 0.3;
var cardGraphics = self.attachAsset('card' + self.value, {
anchorX: 0.5,
anchorY: 0.5
});
var cardText = new Text2(self.value.toString(), {
size: 180,
fill: 0xFFFFFF
});
cardText.anchor.set(0.5, 0.5);
cardText.alpha = 0;
self.addChild(cardText);
self.setSelected = function (selected) {
self.isSelected = selected;
if (selected) {
cardGraphics.tint = 0xf1c40f;
self.y -= 20;
} else {
cardGraphics.tint = 0xffffff;
self.y += 20;
}
};
self.down = function (x, y, obj) {
if (currentGameState === 'playing' && currentPlayer === 0) {
self.setSelected(!self.isSelected);
// Auto-play cards when selection changes
LK.setTimeout(function () {
playSelectedCards();
}, 100);
}
};
return self;
});
var LimitButton = Container.expand(function (limit) {
var self = Container.call(this);
self.limit = limit;
self.isSelected = false;
var buttonGraphics = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(limit.toString(), {
size: 100,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.setSelected = function (selected) {
self.isSelected = selected;
buttonGraphics.removeChild(buttonGraphics.children[0]);
if (selected) {
buttonGraphics.addChild(LK.getAsset('selectedButton', {
anchorX: 0.5,
anchorY: 0.5
}));
} else {
buttonGraphics.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
}
};
self.down = function (x, y, obj) {
if (currentGameState === 'setup') {
LK.getSound('buttonClick').play();
selectLimit(self.limit);
}
};
return self;
});
var RoundButton = Container.expand(function (rounds) {
var self = Container.call(this);
self.rounds = rounds;
self.isSelected = false;
var buttonGraphics = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(rounds + ' Round' + (rounds > 1 ? 's' : ''), {
size: 85,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.setSelected = function (selected) {
self.isSelected = selected;
buttonGraphics.removeChild(buttonGraphics.children[0]);
if (selected) {
buttonGraphics.addChild(LK.getAsset('selectedButton', {
anchorX: 0.5,
anchorY: 0.5
}));
} else {
buttonGraphics.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
}
};
self.down = function (x, y, obj) {
if (currentGameState === 'setup') {
LK.getSound('buttonClick').play();
selectRounds(self.rounds);
}
};
return self;
});
var RulesButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('rulesButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2('Rules', {
size: 60,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showRules();
};
return self;
});
var SpeakerButton = Container.expand(function () {
var self = Container.call(this);
// Get music state from storage, default to true (music on)
self.musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true;
// Create speaker icon using a round shape
var speakerIcon = self.attachAsset('thinkingDot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4
});
speakerIcon.tint = 0x3498db; // Blue tint
// Create speaker text
var speakerText = new Text2('♪', {
size: 60,
fill: 0xFFFFFF
});
speakerText.anchor.set(0.5, 0.5);
self.addChild(speakerText);
// Create cross overlay for muted state
var crossOverlay = new Text2('✕', {
size: 80,
fill: 0xe74c3c
});
crossOverlay.anchor.set(0.5, 0.5);
crossOverlay.x = 10;
crossOverlay.y = -10;
self.addChild(crossOverlay);
// Update visual state
self.updateVisual = function () {
if (self.musicEnabled) {
crossOverlay.alpha = 0;
speakerIcon.tint = 0x3498db; // Blue when on
} else {
crossOverlay.alpha = 1;
speakerIcon.tint = 0x7f8c8d; // Gray when off
}
};
// Initial visual state
self.updateVisual();
// Toggle music when clicked
self.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
self.musicEnabled = !self.musicEnabled;
// Save to storage
storage.musicEnabled = self.musicEnabled;
// Update music playback
if (self.musicEnabled) {
// Play appropriate music based on game mode
if (currentGameState === 'playing' && isArcadeMode) {
LK.playMusic('arcade');
} else {
LK.playMusic('startup');
}
} else {
LK.stopMusic();
}
// Update visual
self.updateVisual();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x27ae60
});
/****
* Game Code
****/
// Game state variables
var currentGameState = 'setup'; // 'setup', 'playing', 'gameOver'
var selectedPlayerCount = 2; // Fixed to 2-player mode
var selectedLimit = 0;
var selectedRounds = 0;
var maxRounds = 0;
var currentTotal = 0;
var currentPlayer = 0; // 0 = player, 1 = AI
var playerHand = [];
var aiHand = [];
var deck = [];
var playerScore = 0;
var aiScore = 0;
var aiThinkingTimeout = null;
var isArcadeMode = false;
var arcadeRoundCount = 0;
var arcadeTotalScore = 0;
var aiCardPositions = [{
x: -675,
y: -20,
rotation: 0.12
}, {
x: -225,
y: 10,
rotation: 0.05
}, {
x: 225,
y: -15,
rotation: -0.08
}, {
x: 675,
y: 5,
rotation: -0.12
}];
// UI elements
var limitButtons = [];
var roundButtons = [];
var playAreaGraphics;
var totalText;
var limitText;
var playerHandContainer;
var aiHandContainer;
var playButton;
var statusText;
var scoreText;
var centerCardContainer;
var rulesButton;
var rulesContainer;
var speechBubble;
var playerSpeechBubble;
// Initialize deck
function initializeDeck() {
deck = [];
// Add 5 color groups of cards 1-10 (red, blue, yellow, green, purple)
var colors = ['red', 'blue', 'yellow', 'green', 'purple'];
for (var colorIndex = 0; colorIndex < colors.length; colorIndex++) {
for (var value = 1; value <= 10; value++) {
deck.push({
value: value,
color: colors[colorIndex]
});
}
}
// Add 2 black zero cards
deck.push({
value: 0,
color: 'black'
});
deck.push({
value: 0,
color: 'black'
});
shuffleDeck();
}
function shuffleDeck() {
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;
}
}
function dealCard() {
if (deck.length === 0) {
initializeDeck();
}
return deck.pop();
}
function dealInitialHands() {
playerHand = [];
aiHand = [];
for (var i = 0; i < 4; i++) {
playerHand.push(dealCard());
aiHand.push(dealCard());
}
}
function createLimitSelection() {
// Add background to setup screen
var setupBackground = game.attachAsset('setupBackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Add title image in the title area
var titleImage = game.attachAsset('titleImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 450
});
var limits = [30, 50, 80];
for (var i = 0; i < limits.length; i++) {
var button = new LimitButton(limits[i]);
button.x = 1024 + (i - 1) * 400;
button.y = 1000;
limitButtons.push(button);
game.addChild(button);
}
var rounds = [1, 3, 5];
for (var i = 0; i < rounds.length; i++) {
var roundButton = new RoundButton(rounds[i]);
roundButton.x = 1024 + (i - 1) * 400;
roundButton.y = 1300;
roundButtons.push(roundButton);
game.addChild(roundButton);
}
var arcadeButton = new Container();
var arcadeGraphics = arcadeButton.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
arcadeGraphics.tint = 0x9b59b6; // Purple tint for arcade
var arcadeText = new Text2('Arcade', {
size: 60,
fill: 0xFFFFFF
});
arcadeText.anchor.set(0.5, 0.5);
arcadeButton.addChild(arcadeText);
arcadeButton.x = 1024;
arcadeButton.y = 1700;
arcadeButton.down = function (x, y, obj) {
if (currentGameState === 'setup') {
LK.getSound('buttonClick').play();
startArcadeMode();
}
};
game.addChild(arcadeButton);
// Add rules button
rulesButton = new RulesButton();
rulesButton.x = 1850;
rulesButton.y = 150;
game.addChild(rulesButton);
// Add speaker button in bottom right corner
var speakerButton = new SpeakerButton();
speakerButton.x = 1900;
speakerButton.y = 2600;
game.addChild(speakerButton);
// Add speaker button in bottom right corner
var speakerButton = new SpeakerButton();
speakerButton.x = 1900;
speakerButton.y = 2600;
game.addChild(speakerButton);
// No play button needed - cards auto-play when selected
}
function selectRounds(rounds) {
selectedRounds = rounds;
maxRounds = rounds;
for (var i = 0; i < roundButtons.length; i++) {
roundButtons[i].setSelected(roundButtons[i].rounds === rounds);
}
checkStartGameReady();
}
function selectLimit(limit) {
selectedLimit = limit;
for (var i = 0; i < limitButtons.length; i++) {
limitButtons[i].setSelected(limitButtons[i].limit === limit);
}
checkStartGameReady();
}
function showRules() {
if (rulesContainer) {
return; // Rules already showing
}
rulesContainer = new Container();
rulesContainer.x = 1024;
rulesContainer.y = 1366;
game.addChild(rulesContainer);
// Semi-transparent background - larger to better match text boundaries
var rulesBackground = rulesContainer.attachAsset('playArea', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 6
});
rulesBackground.alpha = 0.9;
rulesBackground.tint = 0x2c3e50;
// Rules title
var rulesTitle = new Text2('Game Rules', {
size: 60,
fill: 0xf1c40f
});
rulesTitle.anchor.set(0.5, 0.5);
rulesTitle.x = 0;
rulesTitle.y = -500;
rulesContainer.addChild(rulesTitle);
// Rules text - reduced size to fit on single screen
var rulesText = new Text2('• Players take turns playing cards\n• The total of all played cards is tracked\n• If you exceed the chosen limit, you lose the round\n• Zero cards reset the total to zero\n• Win by reaching the target number of rounds\n• Player with highest card starts (unless they have a zero)', {
size: 45,
fill: 0xFFFFFF
});
rulesText.anchor.set(0.5, 0.5);
rulesText.x = 0;
rulesText.y = -200;
rulesContainer.addChild(rulesText);
// Arcade Game Rules heading
var arcadeRulesTitle = new Text2('Arcade Game Rules', {
size: 50,
fill: 0x9b59b6
});
arcadeRulesTitle.anchor.set(0.5, 0.5);
arcadeRulesTitle.x = 0;
arcadeRulesTitle.y = 150;
rulesContainer.addChild(arcadeRulesTitle);
// Arcade rules text
var arcadeRulesText = new Text2('• Infinite rounds with increasing difficulty\n• Limit starts at 50 and increases by 20 each round\n• Score points based on round number\n• Game ends when you exceed the limit\n• Total never resets between rounds\n• Challenge yourself to reach the highest score!', {
size: 40,
fill: 0xFFFFFF
});
arcadeRulesText.anchor.set(0.5, 0.5);
arcadeRulesText.x = 0;
arcadeRulesText.y = 300;
rulesContainer.addChild(arcadeRulesText);
// Close button
var closeButton = new Container();
var closeGraphics = closeButton.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
closeGraphics.tint = 0xe74c3c;
var closeText = new Text2('Close', {
size: 40,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.x = 0;
closeButton.y = 580;
closeButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
rulesContainer.destroy();
rulesContainer = null;
};
rulesContainer.addChild(closeButton);
}
function checkStartGameReady() {
if (selectedLimit > 0 && selectedRounds > 0) {
if (!playButton) {
playButton = new Container();
var playGraphics = playButton.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
var playText = new Text2('Start Game', {
size: 55,
fill: 0xFFFFFF
});
playText.anchor.set(0.5, 0.5);
playButton.addChild(playText);
playButton.x = 1024;
playButton.y = 1500;
playButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
startGame();
};
game.addChild(playButton);
}
}
}
function determineStartingPlayer() {
// Find highest card value for each player
var playerHighest = 0;
var aiHighest = 0;
var playerHasZero = false;
var aiHasZero = false;
// Check player hand
for (var i = 0; i < playerHand.length; i++) {
if (playerHand[i].value === 0) {
playerHasZero = true;
}
if (playerHand[i].value > playerHighest) {
playerHighest = playerHand[i].value;
}
}
// Check AI hand
for (var i = 0; i < aiHand.length; i++) {
if (aiHand[i].value === 0) {
aiHasZero = true;
}
if (aiHand[i].value > aiHighest) {
aiHighest = aiHand[i].value;
}
}
// Determine starting player
// If player has highest card but also has a 0, they cannot start
if (playerHighest > aiHighest && !playerHasZero) {
return 0; // Player starts
}
// If AI has highest card but also has a 0, they cannot start
else if (aiHighest > playerHighest && !aiHasZero) {
return 1; // AI starts
}
// If both have same highest card or both have 0s, prefer the one without 0
else if (playerHighest === aiHighest) {
if (!playerHasZero && aiHasZero) {
return 0; // Player starts
} else if (!aiHasZero && playerHasZero) {
return 1; // AI starts
} else {
return 0; // Default to player if both have/don't have 0s
}
}
// If player has 0 but AI doesn't, AI starts regardless of card values
else if (playerHasZero && !aiHasZero) {
return 1;
}
// If AI has 0 but player doesn't, player starts regardless of card values
else if (aiHasZero && !playerHasZero) {
return 0;
}
return 0; // Default to player
}
function startArcadeMode() {
isArcadeMode = true;
selectedLimit = 50;
selectedRounds = 1;
maxRounds = 1;
arcadeRoundCount = 1;
arcadeTotalScore = 0;
// Hide arcade button by destroying it
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.down && child.down.toString().indexOf('startArcadeMode') !== -1) {
child.destroy();
break;
}
}
// Play arcade music if music is enabled
var musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true;
if (musicEnabled) {
LK.playMusic('arcade');
}
startGame();
}
function startGame() {
currentGameState = 'playing';
currentTotal = 0;
maxRounds = selectedRounds;
// Initialize deck and deal hands first
initializeDeck();
dealInitialHands();
// Determine starting player based on cards
currentPlayer = determineStartingPlayer();
// Clear setup UI
for (var i = 0; i < limitButtons.length; i++) {
limitButtons[i].destroy();
}
limitButtons = [];
for (var i = 0; i < roundButtons.length; i++) {
roundButtons[i].destroy();
}
roundButtons = [];
if (playButton) {
playButton.destroy();
playButton = null;
}
if (!rulesButton) {}
// Add rules button
rulesButton = new RulesButton();
rulesButton.x = 1850;
rulesButton.y = 150;
game.addChild(rulesButton);
if (rulesContainer) {
rulesContainer.destroy();
rulesContainer = null;
}
// Hide instruction texts
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child instanceof Text2 && (child.text === 'Choose Target Limit' || child.text === 'Choose Number of Rounds')) {
child.destroy();
}
}
// Initialize game
createGameUI();
updateDisplay();
// If AI starts first, trigger AI turn after a short delay
if (currentPlayer === 1) {
LK.setTimeout(function () {
aiTurn();
}, 1500);
}
}
function createGameUI() {
// Add background to game screen
var backgroundAsset = isArcadeMode ? 'arcadeBackground' : 'gameBackground';
var gameBackground = game.attachAsset(backgroundAsset, {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Play area removed - no gray stripe needed
// Center card container for animations
centerCardContainer = new Container();
centerCardContainer.x = 1024;
centerCardContainer.y = 1366;
game.addChild(centerCardContainer);
// Total value display (three times larger font, centered)
totalText = new Text2('0', {
size: 240,
fill: 0x2ecc71
});
totalText.anchor.set(0.5, 0.5);
totalText.x = 1536;
totalText.y = 1366;
game.addChild(totalText);
// Limit display (positioned below total text)
limitText = new Text2('Limit: ' + selectedLimit, {
size: 40,
fill: 0x000000
});
limitText.anchor.set(0.5, 0.5);
limitText.x = 1536;
limitText.y = 1500;
game.addChild(limitText);
// Player hand container
playerHandContainer = new Container();
playerHandContainer.x = 1024;
playerHandContainer.y = 2200;
game.addChild(playerHandContainer);
// AI hand container
aiHandContainer = new Container();
aiHandContainer.x = 1024;
aiHandContainer.y = 600;
game.addChild(aiHandContainer);
// Score display
if (isArcadeMode) {
scoreText = new Text2('Round ' + arcadeRoundCount + ' | Score: ' + arcadeTotalScore, {
size: 120,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
} else {
scoreText = new Text2(playerScore + ' = ' + aiScore, {
size: 120,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
}
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 100;
game.addChild(scoreText);
// Add speaker button in bottom right corner of game screen
var gameSpeakerButton = new SpeakerButton();
gameSpeakerButton.x = 1900;
gameSpeakerButton.y = 2600;
game.addChild(gameSpeakerButton);
// Play appropriate music based on game mode
var musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true;
if (musicEnabled) {
if (isArcadeMode) {
LK.playMusic('arcade');
} else {
LK.playMusic('startup');
}
}
// No play button needed - cards auto-play when selected
}
function updateDisplay() {
totalText.setText(currentTotal.toString());
// Update total text color based on proximity to limit
var ratio = currentTotal / selectedLimit;
if (ratio <= 0.6) {
totalText.tint = 0x2ecc71; // Green
} else if (ratio <= 0.8) {
totalText.tint = 0xf1c40f; // Yellow
} else {
totalText.tint = 0xe74c3c; // Red
}
if (isArcadeMode) {
scoreText.setText('Round ' + arcadeRoundCount + ' | Score: ' + arcadeTotalScore);
} else {
scoreText.setText(playerScore + ' = ' + aiScore);
}
// Update player hand only if the number of cards changed
if (playerHandContainer.children.length !== playerHand.length) {
playerHandContainer.removeChildren();
var cardPositions = [{
x: -675,
y: 20,
rotation: -0.15
}, {
x: -225,
y: -10,
rotation: -0.05
}, {
x: 225,
y: 15,
rotation: 0.08
}, {
x: 675,
y: -5,
rotation: 0.12
}];
for (var i = 0; i < playerHand.length; i++) {
var card = new Card(playerHand[i]);
if (i < cardPositions.length) {
card.x = cardPositions[i].x;
card.y = cardPositions[i].y;
card.rotation = cardPositions[i].rotation;
} else {
card.x = (i - 1.5) * 450;
}
playerHandContainer.addChild(card);
}
}
// Update AI hand (show card backs) only if the number of cards changed
if (aiHandContainer.children.length !== aiHand.length) {
aiHandContainer.removeChildren();
for (var i = 0; i < aiHand.length; i++) {
var cardBack = aiHandContainer.attachAsset('cardBack' + aiHand[i].value, {
anchorX: 0.5,
anchorY: 0.5,
x: aiCardPositions[i] ? aiCardPositions[i].x : (i - 1.5) * 450,
y: aiCardPositions[i] ? aiCardPositions[i].y : 0
});
if (aiCardPositions[i]) {
cardBack.rotation = aiCardPositions[i].rotation;
}
}
}
if (currentPlayer === 0) {
// Remove AI speech bubble if it exists
if (speechBubble) {
speechBubble.destroy();
speechBubble = null;
}
// Create speech bubble above player cards
if (playerSpeechBubble) {
playerSpeechBubble.destroy();
}
playerSpeechBubble = new Container();
playerSpeechBubble.x = 1650;
playerSpeechBubble.y = 1750;
// Add bubble background
var bubbleGraphics = playerSpeechBubble.attachAsset('playerSpeechBubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
// Add three thinking dots
var dots = [];
for (var i = 0; i < 3; i++) {
var dot = playerSpeechBubble.attachAsset('thinkingDot', {
anchorX: 0.5,
anchorY: 0.5,
x: (i - 1) * 40,
y: 0
});
dots.push(dot);
}
// Animate dots with staggered timing
for (var i = 0; i < dots.length; i++) {
(function (dotIndex) {
var _animateDot = function animateDot() {
tween(dots[dotIndex], {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(dots[dotIndex], {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (playerSpeechBubble && playerSpeechBubble.parent) {
LK.setTimeout(_animateDot, 600);
}
}
});
}
});
};
LK.setTimeout(_animateDot, dotIndex * 200);
})(i);
}
game.addChild(playerSpeechBubble);
} else {
// Remove player speech bubble if it exists
if (playerSpeechBubble) {
playerSpeechBubble.destroy();
playerSpeechBubble = null;
}
// Create speech bubble under AI cards
if (speechBubble) {
speechBubble.destroy();
}
speechBubble = new Container();
speechBubble.x = 300;
speechBubble.y = 150;
// Add bubble background
var bubbleGraphics = speechBubble.attachAsset('aiSpeechBubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
// Add three thinking dots
var dots = [];
for (var i = 0; i < 3; i++) {
var dot = speechBubble.attachAsset('thinkingDot', {
anchorX: 0.5,
anchorY: 0.5,
x: (i - 1) * 40,
y: 0
});
dots.push(dot);
}
// Animate dots with staggered timing
for (var i = 0; i < dots.length; i++) {
(function (dotIndex) {
var _animateDot = function animateDot() {
tween(dots[dotIndex], {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(dots[dotIndex], {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (speechBubble && speechBubble.parent) {
LK.setTimeout(_animateDot, 600);
}
}
});
}
});
};
LK.setTimeout(_animateDot, dotIndex * 200);
})(i);
}
game.addChild(speechBubble);
}
}
function playSelectedCards() {
var selectedCards = [];
var selectedIndices = [];
for (var i = 0; i < playerHandContainer.children.length; i++) {
var card = playerHandContainer.children[i];
if (card.isSelected) {
selectedCards.push(card.value);
selectedIndices.push(i);
}
}
if (selectedCards.length === 0) {
return;
}
// Calculate total of selected cards
var cardTotal = 0;
for (var i = 0; i < selectedCards.length; i++) {
cardTotal += selectedCards[i];
}
currentTotal += cardTotal;
LK.getSound('cardPlay').play();
// Animate played cards to center
var playedCardData = {
value: selectedCards[0],
color: playerHand[selectedIndices[0]].color
};
var animatedCard = new Card(playedCardData);
var sourceCard = playerHandContainer.children[selectedIndices[0]];
var startPos = playerHandContainer.toGlobal(sourceCard.position);
var gamePos = game.toLocal(startPos);
animatedCard.x = gamePos.x;
animatedCard.y = gamePos.y;
animatedCard.rotation = sourceCard.rotation;
game.addChild(animatedCard);
// Random scattered position and rotation for thrown card effect
var scatterX = 1024 + (Math.random() - 0.5) * 300;
var scatterY = 1366 + (Math.random() - 0.5) * 200;
var scatterRotation = (Math.random() - 0.5) * 0.8;
tween(animatedCard, {
x: scatterX,
y: scatterY,
rotation: scatterRotation
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Move animated card to center container instead of destroying and recreating
game.removeChild(animatedCard);
animatedCard.x = scatterX - 1024;
animatedCard.y = scatterY - 1366;
animatedCard.rotation = scatterRotation;
centerCardContainer.addChild(animatedCard);
}
});
// Remove played cards from hand
for (var i = selectedIndices.length - 1; i >= 0; i--) {
playerHand.splice(selectedIndices[i], 1);
}
// Replenish hand
while (playerHand.length < 4 && deck.length > 0) {
playerHand.push(dealCard());
}
// Force recreation of player hand display by clearing it
playerHandContainer.removeChildren();
// Remove player speech bubble
if (playerSpeechBubble) {
playerSpeechBubble.destroy();
playerSpeechBubble = null;
}
// Check if player exceeded limit
if (currentTotal > selectedLimit) {
endRound(false);
return;
}
currentPlayer = 1;
updateDisplay();
// AI turn after delay
LK.setTimeout(function () {
aiTurn();
}, 1500);
}
function aiTimeoutHandler() {
// Clear the timeout since we're handling it now
if (aiThinkingTimeout) {
LK.clearTimeout(aiThinkingTimeout);
aiThinkingTimeout = null;
}
// Remove speech bubble
if (speechBubble) {
speechBubble.destroy();
speechBubble = null;
}
// Play a random card
var randomIndex = Math.floor(Math.random() * aiHand.length);
var playedCard = aiHand[randomIndex];
currentTotal += playedCard.value;
// Animate AI played card to center
var animatedCard = new Card(playedCard);
animatedCard.x = 1024 + aiCardPositions[randomIndex].x;
animatedCard.y = 500 + aiCardPositions[randomIndex].y;
animatedCard.rotation = aiCardPositions[randomIndex].rotation;
game.addChild(animatedCard);
// Random scattered position and rotation for thrown card effect
var scatterX = 1024 + (Math.random() - 0.5) * 300;
var scatterY = 1366 + (Math.random() - 0.5) * 200;
var scatterRotation = (Math.random() - 0.5) * 0.8;
tween(animatedCard, {
x: scatterX,
y: scatterY,
rotation: scatterRotation
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Move animated card to center container instead of destroying and recreating
game.removeChild(animatedCard);
animatedCard.x = scatterX - 1024;
animatedCard.y = scatterY - 1366;
animatedCard.rotation = scatterRotation;
centerCardContainer.addChild(animatedCard);
// Check if AI exceeded limit
if (currentTotal > selectedLimit) {
endRound(true);
return;
}
}
});
aiHand.splice(randomIndex, 1);
// Replenish AI hand
while (aiHand.length < 4 && deck.length > 0) {
aiHand.push(dealCard());
}
LK.getSound('aiCardPlay').play();
currentPlayer = 0;
updateDisplay();
}
function aiTurn() {
// Set up 5-second timeout for AI thinking
aiThinkingTimeout = LK.setTimeout(function () {
aiTimeoutHandler();
}, 5000);
// Simple AI strategy: play lowest safe card(s)
var safeCards = [];
var safeIndices = [];
for (var i = 0; i < aiHand.length; i++) {
if (currentTotal + aiHand[i].value <= selectedLimit) {
safeCards.push(aiHand[i].value);
safeIndices.push(i);
}
}
if (safeCards.length === 0) {
// Clear timeout since AI is making a play
if (aiThinkingTimeout) {
LK.clearTimeout(aiThinkingTimeout);
aiThinkingTimeout = null;
}
// Remove speech bubble
if (speechBubble) {
speechBubble.destroy();
speechBubble = null;
}
// AI must play and will lose
var minCard = Math.min.apply(Math, aiHand.map(function (card) {
return card.value;
}));
var minIndex = -1;
for (var i = 0; i < aiHand.length; i++) {
if (aiHand[i].value === minCard) {
minIndex = i;
break;
}
}
currentTotal += minCard;
// Animate AI played card to center for loss case
var playedCardData = aiHand[minIndex];
var animatedCard = new Card(playedCardData);
animatedCard.x = 1024 + aiCardPositions[minIndex].x;
animatedCard.y = 500 + aiCardPositions[minIndex].y;
animatedCard.rotation = aiCardPositions[minIndex].rotation;
game.addChild(animatedCard);
// Random scattered position and rotation for thrown card effect
var scatterX = 1024 + (Math.random() - 0.5) * 300;
var scatterY = 1366 + (Math.random() - 0.5) * 200;
var scatterRotation = (Math.random() - 0.5) * 0.8;
tween(animatedCard, {
x: scatterX,
y: scatterY,
rotation: scatterRotation
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Move animated card to center container instead of destroying and recreating
game.removeChild(animatedCard);
animatedCard.x = scatterX - 1024;
animatedCard.y = scatterY - 1366;
animatedCard.rotation = scatterRotation;
centerCardContainer.addChild(animatedCard);
endRound(true);
}
});
aiHand.splice(minIndex, 1);
return;
}
// Clear timeout since AI is making a play
if (aiThinkingTimeout) {
LK.clearTimeout(aiThinkingTimeout);
aiThinkingTimeout = null;
}
// Remove speech bubble
if (speechBubble) {
speechBubble.destroy();
speechBubble = null;
}
// Play the lowest safe card
var minSafe = Math.min.apply(Math, safeCards);
var playIndex = -1;
for (var i = 0; i < aiHand.length; i++) {
if (aiHand[i].value === minSafe) {
playIndex = i;
break;
}
}
currentTotal += minSafe;
// Animate AI played card to center
var playedCardData = aiHand[playIndex];
var animatedCard = new Card(playedCardData);
animatedCard.x = 1024 + aiCardPositions[playIndex].x;
animatedCard.y = 500 + aiCardPositions[playIndex].y;
animatedCard.rotation = aiCardPositions[playIndex].rotation;
game.addChild(animatedCard);
// Random scattered position and rotation for thrown card effect
var scatterX = 1024 + (Math.random() - 0.5) * 300;
var scatterY = 1366 + (Math.random() - 0.5) * 200;
var scatterRotation = (Math.random() - 0.5) * 0.8;
tween(animatedCard, {
x: scatterX,
y: scatterY,
rotation: scatterRotation
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Move animated card to center container instead of destroying and recreating
game.removeChild(animatedCard);
animatedCard.x = scatterX - 1024;
animatedCard.y = scatterY - 1366;
animatedCard.rotation = scatterRotation;
centerCardContainer.addChild(animatedCard);
}
});
aiHand.splice(playIndex, 1);
// Replenish AI hand
while (aiHand.length < 4 && deck.length > 0) {
aiHand.push(dealCard());
}
LK.getSound('aiCardPlay').play();
currentPlayer = 0;
updateDisplay();
}
function showCountdown() {
// Add "Next round" message
var nextRoundText = new Text2('Next round', {
size: 80,
fill: 0xFFFFFF
});
nextRoundText.anchor.set(0.5, 0.5);
nextRoundText.x = 350;
nextRoundText.y = 1100;
game.addChild(nextRoundText);
var countdownText = new Text2('3', {
size: 400,
fill: 0x000000
});
countdownText.anchor.set(0.5, 0.5);
countdownText.x = 350;
countdownText.y = 1366;
game.addChild(countdownText);
var countdownValue = 3;
var countdownTimer = LK.setInterval(function () {
countdownValue--;
if (countdownValue > 0) {
countdownText.setText(countdownValue.toString());
tween(countdownText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(countdownText, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
} else {
LK.clearInterval(countdownTimer);
countdownText.destroy();
nextRoundText.destroy();
// Only reset currentTotal if not in arcade mode
if (!isArcadeMode) {
currentTotal = 0;
}
currentPlayer = 0;
centerCardContainer.removeChildren();
dealInitialHands();
updateDisplay();
}
}, 1000);
}
function endRound(playerWon) {
if (playerWon) {
LK.getSound('gameWin').play();
playerScore++;
} else {
LK.getSound('gameOver').play();
aiScore++;
}
updateDisplay();
// Show winner of this round
var winnerText = new Text2(playerWon ? 'You Win This Round!' : 'AI Wins This Round!', {
size: 100,
fill: playerWon ? 0x2ecc71 : 0xe74c3c
});
winnerText.anchor.set(0.5, 0.5);
winnerText.x = 1024;
winnerText.y = 950;
game.addChild(winnerText);
// Animate winner text
tween(winnerText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(winnerText, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// Check for game end
if (isArcadeMode) {
// In arcade mode, player losing ends the game
if (!playerWon) {
LK.setTimeout(function () {
winnerText.destroy();
LK.showGameOver();
}, 2000);
return;
}
// Player won OR AI lost - continue to next round with increased limit and score
arcadeRoundCount++;
selectedLimit += 20;
arcadeTotalScore += arcadeRoundCount * 10; // Award points based on round
limitText.setText('Limit: ' + selectedLimit);
// Continue to next round (total never resets)
LK.setTimeout(function () {
winnerText.destroy();
showCountdown();
}, 2000);
} else {
// Normal mode - check win threshold
var winThreshold = Math.ceil(maxRounds / 2);
if (playerScore >= winThreshold) {
LK.setTimeout(function () {
winnerText.destroy();
LK.showYouWin();
}, 2000);
return;
} else if (aiScore >= winThreshold) {
LK.setTimeout(function () {
winnerText.destroy();
LK.showGameOver();
}, 2000);
return;
}
// Start new round with countdown after showing winner
LK.setTimeout(function () {
winnerText.destroy();
showCountdown();
}, 2000);
}
}
// Initialize game
createLimitSelection();
// Play startup music only if enabled
var musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true;
if (musicEnabled) {
LK.playMusic('startup');
}
game.update = function () {
// Game loop updates
};
Create a 3D button with rounded corners, and make sure it appears in a rectangular shape.. In-Game asset. 2d. High contrast. No shadows
A thought bubble or a speech bubble (as used in animations) with a slightly 3D appearance, designed as an in-game asset, 2D, with shadows. It should not look like a cloud; the outline should be clean and defined, and the shape should be regular—but not perfectly oval or geometric.
The arrow indicator could have a more authentic look—for example, a stone texture covered with moss, with slightly faded colors.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 0, with a white border around it and a gray background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 2, with a white border around it and a blue background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 1, with a white border around it and a yellow background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 3, with a white border around it and a green background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 4, with a white border around it and an orange background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 5, with a white border around it and a purple background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 6, with a white border around it and a turquoise background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 7, with a white border around it and a terracotta background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 8, with a white border around it and a burgundy background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 9, with a white border around it and a red background.. In-Game asset. 2d. High contrast. No shadows
Rectangular card numbered 10, with a white border around it and a dark background.. In-Game asset. 2d. High contrast. No shadows
A rectangular, semi-transparent frame.. In-Game asset. 2d. High contrast. No shadows
The back of the playing card will not have traditional playing card symbols and will include 3D visuals.. In-Game asset. 3d
The picnic blanket in the image should look more like an anime-style (3D) drawing, without changing its colors.
Add small tears in two places and a burn mark in one place.
I want you to write the word “CardiT” in 3D, using vibrant colors. The style should be like animation, but not childish.. In-Game asset. High contrast. No shadows. 3d. Anime