User prompt
fix some data messages overflow
User prompt
make ardabuggy text change color every second βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
change ardabuggy text a bit down outside 5x5 line background
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'bgSquare.x')' in or related to this line: 'ardabuggyText.x = bgSquare.x + bgSquareWidth / 2 + 24;' Line Number: 120
User prompt
put a text "x: @ardabuggy" outside right down 5x5 line
User prompt
remove algorithm
User prompt
fix game stopped while playing and waste score
User prompt
Please fix the bug: 'storage.get is not a function. (In 'storage.get('playedSpins')', 'storage.get' is undefined)' in or related to this line: 'var playedSpins = storage.get('playedSpins') || 0;' Line Number: 984 βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1
User prompt
make a algorithm if player play many times it will be leave with 0 score
User prompt
put up to the screen big "DONER SPINNER" with doner texture
User prompt
make multis more rare
User prompt
fix multis luck
User prompt
now make it x10 can spawn luck %0.5 x25 luck %0.1 x50 luck %0.05 x100 %0.01 and like other multis
User prompt
add drop animation to all blocks when clicking spin βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
remove enter nane things
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'var block = grid[y][x];' Line Number: 898
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'var block = grid[y][x];' Line Number: 892
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'var block = grid[y][x];' Line Number: 889
User prompt
fix total score 0 in start
User prompt
make enter name screen in first play add alphabet buttons and we can save our names βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1
User prompt
if 2 or more multi spawned addition the multis like 5 and 2 spawned same round them multi 5+2x multi
User prompt
now make 5x can spawn make 5x luck is %1 and make like 2x but its multi the current win 5x
User prompt
fix 2x message remove (2x assets) message
User prompt
remove normal win text
User prompt
not like that like if current win show 300 normal win show /2 amount
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Block class: represents a single block in the grid var Block = Container.expand(function () { var self = Container.call(this); // Properties self.genus = 0; // 0-7 self.gridX = 0; // 0-4 self.gridY = 0; // 0-4 // Asset for the block var blockAsset = null; // Set genus and update asset self.setGenus = function (genus) { self.genus = genus; // Remove old asset if present if (blockAsset) { self.removeChild(blockAsset); } // Each genus gets a unique color and shape var colors = [0xffffff, // white 0x222222, // black 0xffeb3b, // yellow 0xff9800, // orange 0x2196f3, // blue 0x4caf50, // green 0x9c27b0, // purple 0xf44336 // red ]; var shapes = ['box', 'ellipse', 'box', 'ellipse', 'box', 'ellipse', 'box', 'ellipse']; blockAsset = self.attachAsset('block_genus_' + genus, { width: blockSize, height: blockSize, color: colors[genus], shape: shapes[genus], anchorX: 0.5, anchorY: 0.5 }); }; // Animate breaking self.breakAnim = function (_onFinish) { tween(self, { scaleX: 1.3, scaleY: 1.3, alpha: 0 }, { duration: 200, easing: tween.cubicOut, onFinish: function onFinish() { self.scaleX = 1; self.scaleY = 1; self.alpha = 1; if (_onFinish) _onFinish(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // red, ellipse // purple, box // green, ellipse // blue, box // orange, ellipse // yellow, box // black, ellipse // white, box // 8 unique block assets for 8 genus types // --- Constants --- function _typeof2(o) { "@babel/helpers - typeof"; return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof2(o); } function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } var gridSize = 5; var genusCount = 8; var blockSize = 210; // Increased block size for bigger blocks var gridPadding = 22; // Slightly increased padding for visual separation var gridTotalSize = blockSize * gridSize + gridPadding * (gridSize - 1); var gridStartX = Math.floor((2048 - gridTotalSize) / 2) + blockSize / 2; var gridStartY = 400 + blockSize / 2; // leave space for Spin button // --- Fire Effect Background removed --- // --- Background Square (slightly smaller than the 5x5 grid area) --- var bgSquareWidth = gridTotalSize + 12; // background is a bit bigger than grid, but not overflowing var bgSquareHeight = gridTotalSize + 12; var bgSquareX = 2048 / 2; var bgSquareY = gridStartY + gridTotalSize / 2 - blockSize / 2; var bgSquare = LK.getAsset('background_square', { width: bgSquareWidth, height: bgSquareHeight, color: 0x181818, shape: 'box', anchorX: 0.5, anchorY: 0.5, x: bgSquareX, y: bgSquareY }); bgSquare.alpha = 0.92; game.addChild(bgSquare); // --- Current Win Bar UI (above the 5x5 grid) --- var winBarWidth = 320; var winBarHeight = 90; var winBarMax = 5000; // Arbitrary max for full bar, can be tuned var winBarY = gridStartY - blockSize / 2 - 60; // 60px above the grid var winBarBg = LK.getAsset('score_bar_bg', { width: winBarWidth, height: winBarHeight, color: 0x333333, shape: 'box', anchorX: 0.0, anchorY: 0.5, x: gridStartX, // left aligned with grid y: winBarY }); game.addChild(winBarBg); var winBarFill = LK.getAsset('score_bar_fill', { width: 1, height: winBarHeight - 18, color: 0x30a262, shape: 'box', anchorX: 0.0, anchorY: 0.5, x: gridStartX, y: winBarY }); game.addChild(winBarFill); var winBarText = new Text2('0', { size: 60, fill: 0xffffff }); winBarText.anchor.set(0.5, 0.5); winBarText.x = gridStartX + winBarWidth / 2; winBarText.y = winBarY; game.addChild(winBarText); // Normal win amount text (shown above current win bar if 2x is present) var normalWinText = new Text2('', { size: 48, fill: 0xffeb3b }); normalWinText.anchor.set(0.5, 1); normalWinText.x = winBarText.x; normalWinText.y = winBarText.y - winBarHeight / 2 - 18; normalWinText.visible = false; game.addChild(normalWinText); // "Current Win:" label (left of win bar) var winLabel = new Text2('Current Win:', { size: 54, fill: 0xffffff }); winLabel.anchor.set(1, 0.5); winLabel.x = gridStartX - 24; winLabel.y = winBarY; game.addChild(winLabel); // --- Total Score Bar UI (below the 5x5 line, left aligned) --- var scoreBarWidth = 320; var scoreBarHeight = 110; var scoreBarMax = 5000; // Arbitrary max for full bar, can be tuned var scoreBarY = bgSquareY + bgSquareHeight / 2 + 12 + 40; // 12px gap below bg, then 40px (half of old below_5x5_rect height) var scoreBarBg = LK.getAsset('score_bar_bg', { width: scoreBarWidth, height: scoreBarHeight, color: 0x333333, shape: 'box', anchorX: 0.0, anchorY: 0.5, x: gridStartX, y: scoreBarY }); game.addChild(scoreBarBg); var scoreBarFill = LK.getAsset('score_bar_fill', { width: 1, height: scoreBarHeight - 20, color: 0x4caf50, shape: 'box', anchorX: 0.0, anchorY: 0.5, x: gridStartX, y: scoreBarY }); game.addChild(scoreBarFill); var scoreBarText = new Text2('0', { size: 90, fill: 0xffffff }); scoreBarText.anchor.set(0.5, 0.5); scoreBarText.x = gridStartX + scoreBarWidth / 2; scoreBarText.y = scoreBarY; game.addChild(scoreBarText); // "Total Score" label (left of score bar) var scoreLabel = new Text2('Total Score', { size: 54, fill: 0xffffff }); scoreLabel.anchor.set(1, 0.5); scoreLabel.x = gridStartX - 24; scoreLabel.y = scoreBarY; game.addChild(scoreLabel); // (scoreTxt is now handled by scoreBarText above) scoreTxt = scoreBarText; // --- Current Win value tracking --- var currentWin = 0; // --- Rainbow Square Background removed --- // --- State --- var grid = []; // 2D array [y][x] of Block var score = 0; var isSpinning = false; // --- UI Elements --- var spinBtn = null; // --- Score Bar UI --- // (All score bar variables are now initialized above, right after bgSquare) // Helper to update score bar fill and text function updateScoreBar() { // Total Score bar var fillRatio = Math.min(score / scoreBarMax, 1); var targetWidth = Math.floor((scoreBarWidth - 24) * fillRatio); if (scoreBarFill && _typeof(scoreBarFill) === "object") { tween(scoreBarFill, { width: targetWidth }, { duration: 180, easing: tween.cubicOut }); } if (scoreBarText) scoreBarText.setText(score); if (scoreBarBg && scoreBarFill) { scoreBarFill.x = scoreBarBg.x; scoreBarFill.y = scoreBarBg.y; } if (scoreBarBg && scoreBarText) { scoreBarText.x = scoreBarBg.x + scoreBarWidth / 2; scoreBarText.y = scoreBarBg.y; } // Current Win bar var winFillRatio = Math.min(currentWin / winBarMax, 1); var winTargetWidth = Math.floor((winBarWidth - 20) * winFillRatio); if (winBarFill && _typeof(winBarFill) === "object") { tween(winBarFill, { width: winTargetWidth }, { duration: 180, easing: tween.cubicOut }); } if (winBarText) winBarText.setText(currentWin); if (winBarBg && winBarFill) { winBarFill.x = winBarBg.x; winBarFill.y = winBarBg.y; } if (winBarBg && winBarText) { winBarText.x = winBarBg.x + winBarWidth / 2; winBarText.y = winBarBg.y; } } // --- Helper Functions --- // Generate a random genus (0-7) function randomGenus() { return Math.floor(Math.random() * genusCount); } // Place a block at grid[x][y] function placeBlock(x, y, genus) { var block = new Block(); block.setGenus(genus); block.gridX = x; block.gridY = y; block.x = gridStartX + x * (blockSize + gridPadding); block.y = gridStartY + y * (blockSize + gridPadding); block.scaleX = 1; block.scaleY = 1; block.alpha = 1; game.addChild(block); // Add click handler to show genus breakdown window block.down = function (x, y, obj) { // Remove any existing breakdown window if (game._blockBreakdownWindow && game._blockBreakdownWindow.length) { for (var i = 0; i < game._blockBreakdownWindow.length; i++) { if (game._blockBreakdownWindow[i] && game._blockBreakdownWindow[i].destroy) game._blockBreakdownWindow[i].destroy(); } game._blockBreakdownWindow = []; } // Calculate genus count in grid var genusCountInGrid = 0; for (var yy = 0; yy < gridSize; yy++) { for (var xx = 0; xx < gridSize; xx++) { if (grid[yy][xx] && grid[yy][xx].genus === genus) genusCountInGrid++; } } // Calculate per block score var perBlockScore = Math.floor(10 * (genus + 1) * (spinAmount / 500)); // Window background var winW = 1000, winH = 600; // Center the window in the main screen var winX = (2048 - winW) / 2; var winY = (2732 - winH) / 2; var winBg = LK.getAsset('score_bar_bg', { width: winW, height: winH, color: 0x181818, shape: 'box', anchorX: 0, anchorY: 0, x: winX, y: winY }); winBg.alpha = 0.97; // Block icon var blockIcon = LK.getAsset('block_genus_' + genus, { width: 120, height: 120, anchorX: 0, anchorY: 0.5, x: winX + 48, y: winY + winH / 2 }); // Texts: "6x(score) = total", "7x(score) = total", etc, stacked under each other var breakdownTexts = []; var breakdownFontSize = 80; var breakdownSpacing = 18; // Always show breakdown lines for 6, 7, 8, 9, ... up to genusCountInGrid, but only if genusCountInGrid >= 6 if (genusCountInGrid >= 6) { var breakdownLineCount = genusCountInGrid - 6 + 1; var stackStartY = winY + winH / 2 - (breakdownLineCount - 1) * (breakdownFontSize + breakdownSpacing) / 2; for (var i = 6; i <= genusCountInGrid; i++) { var lineScore = i * perBlockScore; // Show: "6x <score per block> = <total>", e.g. "6x 120 = 720" var breakdownLine = new Text2(i + "x " + perBlockScore + " = " + lineScore, { size: breakdownFontSize, fill: 0xffffff }); breakdownLine.anchor.set(0, 0.5); breakdownLine.x = winX + 200; // Stack each line under the previous with spacing breakdownLine.y = stackStartY + (i - 6) * (breakdownFontSize + breakdownSpacing); game.addChild(breakdownLine); breakdownTexts.push(breakdownLine); } } // Title var titleText = new Text2("Block Genus " + genus, { size: 54, fill: 0xffeb3b }); titleText.anchor.set(0, 0); titleText.x = winX + 48; titleText.y = winY + 32; // Close button var closeBtn = LK.getAsset('score_bar_bg', { width: 80, height: 80, color: 0xd83318, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, x: winX + winW - 60, y: winY + 60 }); var closeTxt = new Text2('Γ', { size: 60, fill: 0xffffff }); closeTxt.anchor.set(0.5, 0.5); closeTxt.x = closeBtn.x; closeTxt.y = closeBtn.y; closeBtn.down = function () { if (game._blockBreakdownWindow && game._blockBreakdownWindow.length) { for (var i = 0; i < game._blockBreakdownWindow.length; i++) { if (game._blockBreakdownWindow[i] && game._blockBreakdownWindow[i].destroy) game._blockBreakdownWindow[i].destroy(); } game._blockBreakdownWindow = []; } }; // Add to game // Add all breakdown window elements to LK.gui.center to ensure they are above all game elements LK.gui.center.addChild(winBg); LK.gui.center.addChild(blockIcon); LK.gui.center.addChild(titleText); LK.gui.center.addChild(closeBtn); LK.gui.center.addChild(closeTxt); // Track for removal (include all breakdownTexts) var breakdownWindowNodes = [winBg, blockIcon, titleText, closeBtn, closeTxt]; for (var i = 0; i < breakdownTexts.length; i++) { LK.gui.center.addChild(breakdownTexts[i]); breakdownWindowNodes.push(breakdownTexts[i]); } game._blockBreakdownWindow = breakdownWindowNodes; }; return block; } // Remove a block from the game and grid function removeBlock(block) { if (block) { block.destroy(); } } // Fill the grid with random blocks, destroying old ones function fillGridRandom() { // Remove old blocks and clear grid for (var y = 0; y < gridSize; y++) { for (var x = 0; x < gridSize; x++) { if (grid[y][x]) { removeBlock(grid[y][x]); } grid[y][x] = null; } } // Fill with new random blocks for every cell in the 5x5 grid for (var y = 0; y < gridSize; y++) { for (var x = 0; x < gridSize; x++) { if (!grid[y][x]) { // Only fill empty cells // 5% chance to spawn a 2x block (use special asset for 2x) var genus; var block; if (Math.random() < 0.05) { genus = 8; // Use genus 8 for 2x block logic block = placeBlock(x, y, genus); // Remove the default block asset and attach the 'x2' asset if (block && block.children && block.children.length > 0) { block.removeChild(block.children[0]); } var x2Asset = block.attachAsset('x2', { width: blockSize, height: blockSize, anchorX: 0.5, anchorY: 0.5 }); } else { genus = randomGenus(); block = placeBlock(x, y, genus); } tween(block, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 220, easing: tween.cubicOut }); grid[y][x] = block; } } } } // Find all genus with 7 or more blocks, return array of {genus, blocks: [Block]} function findMatches() { // Count blocks per genus var genusBlocks = []; for (var g = 0; g < genusCount; g++) { genusBlocks[g] = []; } for (var y = 0; y < gridSize; y++) { for (var x = 0; x < gridSize; x++) { var block = grid[y][x]; if (block && typeof block.genus === "number" && block.genus >= 0 && block.genus < genusCount) { genusBlocks[block.genus].push(block); } } } var matches = []; for (var g = 0; g < genusCount; g++) { if (genusBlocks[g].length >= 6) { matches.push({ genus: g, blocks: genusBlocks[g] }); } } return matches; } // Break all matched blocks, animate, update score, and refill function breakAndRefill(matches, _onFinish2) { if (matches.length === 0) { if (_onFinish2) _onFinish2(); return; } var blocksToBreak = []; for (var i = 0; i < matches.length; i++) { blocksToBreak = blocksToBreak.concat(matches[i].blocks); } // Remove duplicates (shouldn't be any, but just in case) var uniqueBlocks = []; var seen = {}; for (var i = 0; i < blocksToBreak.length; i++) { var b = blocksToBreak[i]; var key = b.gridX + ',' + b.gridY; if (!seen[key]) { uniqueBlocks.push(b); seen[key] = true; } } // Animate breaking // --- Stacked breakdown text logic for fast breaks --- if (!breakAndRefill.activeBreakdowns) breakAndRefill.activeBreakdowns = []; var breakdownTextNodes = []; var breakdownFontSize = 44; var breakdownDuration = 1200; var breakdownFade = 400; // Arrange breakdowns vertically outside and to the right of the 5x5 grid var breakdownCellW = 140; var breakdownCellH = 84; // Increased for more vertical space between messages // Place to the right of the 5x5 grid, not under the score bar var breakdownStartX = bgSquare.x + bgSquareWidth / 2 + 60; var breakdownStartY = bgSquare.y - bgSquareHeight / 2; // Find the next available vertical slot for each new breakdown var usedRows = []; for (var b = 0; b < breakAndRefill.activeBreakdowns.length; b++) { var batch = breakAndRefill.activeBreakdowns[b]; for (var j = 0; j < batch.length; j += 2) { // asset is batch[j], text is batch[j+1] if (batch[j] && typeof batch[j].__breakdownRow === "number") { usedRows.push(batch[j].__breakdownRow); } } } for (var i = 0; i < matches.length; i++) { var genus = matches[i].genus; var count = matches[i].blocks.length; if (count >= 6) { // Find the next available row (vertical stacking) var row = 0; while (usedRows.indexOf(row) !== -1) { row++; } usedRows.push(row); var asset = LK.getAsset('block_genus_' + genus, { width: 54, height: 54, anchorX: 0, anchorY: 0.5, x: breakdownStartX, y: breakdownStartY + row * breakdownCellH }); asset.__breakdownRow = row; var perBlockScore = Math.floor(10 * (genus + 1) * (spinAmount / 500)); var extraBlockScore = Math.floor(10 * (genus + 1) * (spinAmount / 500)) * Math.max(0, count - 6); var totalScore = perBlockScore * count + extraBlockScore; var text = new Text2(count + "x" + perBlockScore + (count > 6 ? " + " + (count - 6) + "x" + perBlockScore : "") + " = " + totalScore, { size: breakdownFontSize, fill: 0xffffff }); text.anchor.set(0, 0.5); text.x = breakdownStartX + 60; text.y = breakdownStartY + row * breakdownCellH + 36; // Adjusted for new cell height game.addChild(asset); game.addChild(text); breakdownTextNodes.push(asset, text); } } // Track this batch for stacking breakAndRefill.activeBreakdowns.push(breakdownTextNodes); // Remove this batch after duration, and update stacking for remaining var breakdownClearTimeout = LK.setTimeout(function () { // Destroy this batch for (var i = 0; i < breakdownTextNodes.length; i++) { if (breakdownTextNodes[i] && breakdownTextNodes[i].destroy) breakdownTextNodes[i].destroy(); } // Remove this batch from activeBreakdowns var idx = breakAndRefill.activeBreakdowns.indexOf(breakdownTextNodes); if (idx !== -1) breakAndRefill.activeBreakdowns.splice(idx, 1); // Move up any remaining breakdowns (re-stack vertically) for (var b = 0; b < breakAndRefill.activeBreakdowns.length; b++) { var batch = breakAndRefill.activeBreakdowns[b]; // Find the new row for this batch (b) for (var j = 0; j < batch.length; j += 2) { // asset is batch[j], text is batch[j+1] if (batch[j]) { batch[j].y = breakdownStartY + b * breakdownCellH; batch[j].__breakdownRow = b; } if (batch[j + 1]) { batch[j + 1].y = breakdownStartY + b * breakdownCellH + 36; // Adjusted for new cell height } } } }, breakdownDuration + breakdownFade); var brokenCount = 0; for (var i = 0; i < uniqueBlocks.length; i++) { (function (block) { block.breakAnim(function () { // Remove from grid grid[block.gridY][block.gridX] = null; removeBlock(block); brokenCount++; // When all blocks are broken, refill if (brokenCount === uniqueBlocks.length) { // Update score: 10 points per block, +10 per extra block above 6, * genus+1 var winScore = 0; var maxMultiplier = 1; var foundMultiplier = null; var has2xBlock = false; // Check if any 2x block is present in the grid for (var y2 = 0; y2 < gridSize; y2++) { for (var x2 = 0; x2 < gridSize; x2++) { if (grid[y2][x2] && grid[y2][x2].genus === 8) { has2xBlock = true; break; } } if (has2xBlock) break; } for (var m = 0; m < matches.length; m++) { var count = matches[m].blocks.length; var genus = matches[m].genus; // --- Sometimes give low scores: 30% chance to halve the score for this break --- var baseScore = (10 * count + 10 * Math.max(0, count - 6)) * (genus + 1); if (Math.random() < 0.3) { baseScore = Math.floor(baseScore * 0.5); } score += baseScore; winScore += baseScore; } // If 2x block is present, double the winScore and show a quick animation if (has2xBlock && winScore > 0) { var normalWinAmount = winScore; winScore = winScore * 2; // Animate winBarText to indicate 2x tween(winBarText, { scaleX: 1.4, scaleY: 1.4, tint: 0xffeb3b }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { tween(winBarText, { scaleX: 1, scaleY: 1, tint: 0xffffff }, { duration: 180, easing: tween.cubicIn }); } }); // Show data message for 2x var dataMsgAsset = LK.getAsset('x2', { width: 60, height: 60, anchorX: 0, anchorY: 0.5, x: winBarText.x + 120, y: winBarText.y }); var dataMsgText = new Text2("(2x assets) 2x the current win", { size: 38, fill: 0xffeb3b }); dataMsgText.anchor.set(0, 0.5); dataMsgText.x = dataMsgAsset.x + 70; dataMsgText.y = winBarText.y; game.addChild(dataMsgAsset); game.addChild(dataMsgText); // Show normal win amount above current win bar, keep visible while 2x is present if (typeof normalWinText !== "undefined" && normalWinText) { // Show normal win as half of current win (since 2x is present) var normalWinAmount = Math.floor(winScore / 2); normalWinText.setText("normal win: " + normalWinAmount); normalWinText.visible = true; // Hide after 1.2s, but only if no 2x block remains LK.setTimeout(function () { // Check if any 2x block is still present var stillHas2x = false; for (var y2 = 0; y2 < gridSize; y2++) { for (var x2 = 0; x2 < gridSize; x2++) { if (grid[y2][x2] && grid[y2][x2].genus === 8) { stillHas2x = true; break; } } if (stillHas2x) break; } if (!stillHas2x) { normalWinText.visible = false; } }, 1200); } // Remove after 1.2s LK.setTimeout(function () { if (dataMsgAsset && dataMsgAsset.destroy) dataMsgAsset.destroy(); if (dataMsgText && dataMsgText.destroy) dataMsgText.destroy(); }, 1200); } currentWin += winScore; updateScore(); updateScoreBar(); // Refill broken blocks with new random blocks var dropAnimCount = 0; var dropAnimTotal = 0; for (var y = 0; y < gridSize; y++) { for (var x = 0; x < gridSize; x++) { if (!grid[y][x]) { var genus = randomGenus(); var newBlock = placeBlock(x, y, genus); grid[y][x] = newBlock; // Animate drop from above var targetY = newBlock.y; newBlock.y = targetY - 180; dropAnimTotal++; tween(newBlock, { y: targetY }, { duration: 220, easing: tween.cubicOut, onFinish: function onFinish() { dropAnimCount++; // Only proceed after all drop animations are done if (dropAnimCount === dropAnimTotal) { // After refill, check for new matches recursively // --- BREAK COOLDOWN: Wait before checking for new matches --- LK.setTimeout(function () { var newMatches = findMatches(); if (newMatches.length > 0) { breakAndRefill(newMatches, _onFinish2); } else { // Add currentWin to total score after all breaks score += currentWin; currentWin = 0; updateScore(); updateScoreBar(); if (_onFinish2) _onFinish2(); } }, 320); // 320ms cooldown after drop } } }); } } } // If no blocks needed to drop, continue immediately if (dropAnimTotal === 0) { // --- BREAK COOLDOWN: Wait before checking for new matches --- LK.setTimeout(function () { var newMatches = findMatches(); if (newMatches.length > 0) { breakAndRefill(newMatches, _onFinish2); } else { if (_onFinish2) _onFinish2(); } }, 320); // 320ms cooldown if no drop } // After refill, check for new matches recursively // (removed duplicate immediate check) } }); })(uniqueBlocks[i]); } } // Update score text and LK score function updateScore() { LK.setScore(score); if (scoreBarText) scoreBarText.setText(score); // Update score bar if (typeof updateScoreBar === "function") updateScoreBar(); // Optionally, animate score text for feedback if (scoreBarText) { tween(scoreBarText, { scaleX: 1.18, scaleY: 1.18 }, { duration: 80, easing: tween.cubicOut, onFinish: function onFinish() { tween(scoreBarText, { scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.cubicIn }); } }); } } // Check if any matches are possible (i.e., if any genus has 6+ blocks) function hasMatches() { var matches = findMatches(); return matches.length > 0; } // Handle Spin button press function onSpin() { if (isSpinning) return; if (score < spinAmount) { // Not enough score to spin, show game over LK.effects.flashScreen(0x000000, 600); LK.showGameOver(); return; } score -= spinAmount; currentWin = 0; updateScore(); updateScoreBar(); isSpinning = true; // Animate all blocks out (optional, for feedback) for (var y = 0; y < gridSize; y++) { for (var x = 0; x < gridSize; x++) { var block = grid[y][x]; if (block) { tween(block, { scaleX: 0.7, scaleY: 0.7, alpha: 0.3 }, { duration: 120, easing: tween.cubicIn }); } } } // Remove cooldown for first spin, add cooldown only after first break if (typeof onSpin.firstSpin === "undefined") { onSpin.firstSpin = true; } var doSpin = function doSpin() { fillGridRandom(); // After refill, check for matches and break recursively var matches = findMatches(); if (matches.length > 0) { // Wrap breakAndRefill to add 3s cooldown after first break breakAndRefill(matches, function () { isSpinning = false; // After all matches, check if any more matches are possible if (!hasMatches()) { // Only show game over if score is less than 500, otherwise allow player to spin again if (score < 500) { LK.effects.flashScreen(0x000000, 600); LK.showGameOver(); } } }); } else { isSpinning = false; // If no matches at all, only show game over if score is less than 500 if (score < 500) { LK.effects.flashScreen(0x000000, 600); LK.showGameOver(); } } }; if (onSpin.firstSpin) { // No cooldown for first spin onSpin.firstSpin = false; doSpin(); // immediate, no timeout } else { doSpin(); // immediate, no timeout } } // --- UI Setup --- // --- Spin Amount UI --- // State for spin amount var spinAmount = 500; var spinAmountMin = 200; var spinAmountMax = 4000; var spinAmountStep = 50; // - Button (red) var minusBtn = LK.getAsset('score_bar_bg', { width: 100, height: 100, color: 0xd83318, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 260, y: gridStartY + gridTotalSize + 140 + 60 }); game.addChild(minusBtn); // - Text var minusText = new Text2('-', { size: 90, fill: 0xffffff }); minusText.anchor.set(0.5, 0.5); minusText.x = minusBtn.x; minusText.y = minusBtn.y; game.addChild(minusText); // + Button (green) var plusBtn = LK.getAsset('score_bar_bg', { width: 100, height: 100, color: 0x30a262, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 260, y: gridStartY + gridTotalSize + 140 + 60 }); game.addChild(plusBtn); // + Text var plusText = new Text2('+', { size: 90, fill: 0xffffff }); plusText.anchor.set(0.5, 0.5); plusText.x = plusBtn.x; plusText.y = plusBtn.y; game.addChild(plusText); // Spin Amount Button (triangle) var spinAmountBtn = LK.getAsset('score_bar_bg', { width: 120, height: 120, color: 0xf9dd57, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: gridStartY + gridTotalSize + 140 + 60 }); game.addChild(spinAmountBtn); // Triangle icon (drawn with text) var triangleText = new Text2('β²', { size: 60, fill: 0x222222 }); triangleText.anchor.set(0.5, 0.5); triangleText.x = spinAmountBtn.x; triangleText.y = spinAmountBtn.y - 30; game.addChild(triangleText); // Spin Amount Value Text var spinAmountText = new Text2(spinAmount + '', { size: 60, fill: 0x222222 }); spinAmountText.anchor.set(0.5, 0.5); spinAmountText.x = spinAmountBtn.x; spinAmountText.y = spinAmountBtn.y + 30; game.addChild(spinAmountText); // Update spin amount text helper function updateSpinAmountText() { spinAmountText.setText(spinAmount + ''); } // - Button logic minusBtn.down = function (x, y, obj) { if (spinAmount > spinAmountMin) { spinAmount -= spinAmountStep; if (spinAmount < spinAmountMin) spinAmount = spinAmountMin; updateSpinAmountText(); // Animate feedback tween(minusBtn, { scaleX: 0.92, scaleY: 0.92 }, { duration: 60, easing: tween.cubicIn, onFinish: function onFinish() { tween(minusBtn, { scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.cubicOut }); } }); } }; // + Button logic plusBtn.down = function (x, y, obj) { if (spinAmount < spinAmountMax) { spinAmount += spinAmountStep; if (spinAmount > spinAmountMax) spinAmount = spinAmountMax; updateSpinAmountText(); // Animate feedback tween(plusBtn, { scaleX: 0.92, scaleY: 0.92 }, { duration: 60, easing: tween.cubicIn, onFinish: function onFinish() { tween(plusBtn, { scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.cubicOut }); } }); } }; // Spin Amount Button logic (optional: could cycle through preset values, but here does nothing) spinAmountBtn.down = function (x, y, obj) { // Animate feedback tween(spinAmountBtn, { scaleX: 0.92, scaleY: 0.92 }, { duration: 60, easing: tween.cubicIn, onFinish: function onFinish() { tween(spinAmountBtn, { scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.cubicOut }); } }); }; // --- Spin Button (circular, below the grid and spin amount UI) --- var spinBtnRadius = 140; spinBtn = LK.getAsset('spinBtn', { width: spinBtnRadius * 2, height: spinBtnRadius * 2, color: 0xf9dd57, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: gridStartY + gridTotalSize + spinBtnRadius + 260 // moved below the grid and spin amount UI }); game.addChild(spinBtn); // --- Input Handling --- // Only allow pressing Spin button (not grid) spinBtn.down = function (x, y, obj) { if (isSpinning) return; onSpin(); // Animate button press tween(spinBtn, { scaleX: 0.92, scaleY: 0.92 }, { duration: 60, easing: tween.cubicIn, onFinish: function onFinish() { tween(spinBtn, { scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.cubicOut }); } }); // Removed spinBtnTxt animation as spinBtnTxt does not exist }; // --- Game Initialization --- // Initialize grid array for (var y = 0; y < gridSize; y++) { grid[y] = []; for (var x = 0; x < gridSize; x++) { grid[y][x] = null; } } // Start with 2000 score, do not fill grid or break until spin is pressed score = 2000; updateScore(); // Do not fillGridRandom or breakAndRefill here; wait for spin button // --- Game Update Loop (not used, but required for LK) --- game.update = function () { // No per-frame logic needed };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Block class: represents a single block in the grid
var Block = Container.expand(function () {
var self = Container.call(this);
// Properties
self.genus = 0; // 0-7
self.gridX = 0; // 0-4
self.gridY = 0; // 0-4
// Asset for the block
var blockAsset = null;
// Set genus and update asset
self.setGenus = function (genus) {
self.genus = genus;
// Remove old asset if present
if (blockAsset) {
self.removeChild(blockAsset);
}
// Each genus gets a unique color and shape
var colors = [0xffffff,
// white
0x222222,
// black
0xffeb3b,
// yellow
0xff9800,
// orange
0x2196f3,
// blue
0x4caf50,
// green
0x9c27b0,
// purple
0xf44336 // red
];
var shapes = ['box', 'ellipse', 'box', 'ellipse', 'box', 'ellipse', 'box', 'ellipse'];
blockAsset = self.attachAsset('block_genus_' + genus, {
width: blockSize,
height: blockSize,
color: colors[genus],
shape: shapes[genus],
anchorX: 0.5,
anchorY: 0.5
});
};
// Animate breaking
self.breakAnim = function (_onFinish) {
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 0
}, {
duration: 200,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.scaleX = 1;
self.scaleY = 1;
self.alpha = 1;
if (_onFinish) _onFinish();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// red, ellipse
// purple, box
// green, ellipse
// blue, box
// orange, ellipse
// yellow, box
// black, ellipse
// white, box
// 8 unique block assets for 8 genus types
// --- Constants ---
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof2(o);
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var gridSize = 5;
var genusCount = 8;
var blockSize = 210; // Increased block size for bigger blocks
var gridPadding = 22; // Slightly increased padding for visual separation
var gridTotalSize = blockSize * gridSize + gridPadding * (gridSize - 1);
var gridStartX = Math.floor((2048 - gridTotalSize) / 2) + blockSize / 2;
var gridStartY = 400 + blockSize / 2; // leave space for Spin button
// --- Fire Effect Background removed ---
// --- Background Square (slightly smaller than the 5x5 grid area) ---
var bgSquareWidth = gridTotalSize + 12; // background is a bit bigger than grid, but not overflowing
var bgSquareHeight = gridTotalSize + 12;
var bgSquareX = 2048 / 2;
var bgSquareY = gridStartY + gridTotalSize / 2 - blockSize / 2;
var bgSquare = LK.getAsset('background_square', {
width: bgSquareWidth,
height: bgSquareHeight,
color: 0x181818,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
x: bgSquareX,
y: bgSquareY
});
bgSquare.alpha = 0.92;
game.addChild(bgSquare);
// --- Current Win Bar UI (above the 5x5 grid) ---
var winBarWidth = 320;
var winBarHeight = 90;
var winBarMax = 5000; // Arbitrary max for full bar, can be tuned
var winBarY = gridStartY - blockSize / 2 - 60; // 60px above the grid
var winBarBg = LK.getAsset('score_bar_bg', {
width: winBarWidth,
height: winBarHeight,
color: 0x333333,
shape: 'box',
anchorX: 0.0,
anchorY: 0.5,
x: gridStartX,
// left aligned with grid
y: winBarY
});
game.addChild(winBarBg);
var winBarFill = LK.getAsset('score_bar_fill', {
width: 1,
height: winBarHeight - 18,
color: 0x30a262,
shape: 'box',
anchorX: 0.0,
anchorY: 0.5,
x: gridStartX,
y: winBarY
});
game.addChild(winBarFill);
var winBarText = new Text2('0', {
size: 60,
fill: 0xffffff
});
winBarText.anchor.set(0.5, 0.5);
winBarText.x = gridStartX + winBarWidth / 2;
winBarText.y = winBarY;
game.addChild(winBarText);
// Normal win amount text (shown above current win bar if 2x is present)
var normalWinText = new Text2('', {
size: 48,
fill: 0xffeb3b
});
normalWinText.anchor.set(0.5, 1);
normalWinText.x = winBarText.x;
normalWinText.y = winBarText.y - winBarHeight / 2 - 18;
normalWinText.visible = false;
game.addChild(normalWinText);
// "Current Win:" label (left of win bar)
var winLabel = new Text2('Current Win:', {
size: 54,
fill: 0xffffff
});
winLabel.anchor.set(1, 0.5);
winLabel.x = gridStartX - 24;
winLabel.y = winBarY;
game.addChild(winLabel);
// --- Total Score Bar UI (below the 5x5 line, left aligned) ---
var scoreBarWidth = 320;
var scoreBarHeight = 110;
var scoreBarMax = 5000; // Arbitrary max for full bar, can be tuned
var scoreBarY = bgSquareY + bgSquareHeight / 2 + 12 + 40; // 12px gap below bg, then 40px (half of old below_5x5_rect height)
var scoreBarBg = LK.getAsset('score_bar_bg', {
width: scoreBarWidth,
height: scoreBarHeight,
color: 0x333333,
shape: 'box',
anchorX: 0.0,
anchorY: 0.5,
x: gridStartX,
y: scoreBarY
});
game.addChild(scoreBarBg);
var scoreBarFill = LK.getAsset('score_bar_fill', {
width: 1,
height: scoreBarHeight - 20,
color: 0x4caf50,
shape: 'box',
anchorX: 0.0,
anchorY: 0.5,
x: gridStartX,
y: scoreBarY
});
game.addChild(scoreBarFill);
var scoreBarText = new Text2('0', {
size: 90,
fill: 0xffffff
});
scoreBarText.anchor.set(0.5, 0.5);
scoreBarText.x = gridStartX + scoreBarWidth / 2;
scoreBarText.y = scoreBarY;
game.addChild(scoreBarText);
// "Total Score" label (left of score bar)
var scoreLabel = new Text2('Total Score', {
size: 54,
fill: 0xffffff
});
scoreLabel.anchor.set(1, 0.5);
scoreLabel.x = gridStartX - 24;
scoreLabel.y = scoreBarY;
game.addChild(scoreLabel);
// (scoreTxt is now handled by scoreBarText above)
scoreTxt = scoreBarText;
// --- Current Win value tracking ---
var currentWin = 0;
// --- Rainbow Square Background removed ---
// --- State ---
var grid = []; // 2D array [y][x] of Block
var score = 0;
var isSpinning = false;
// --- UI Elements ---
var spinBtn = null;
// --- Score Bar UI ---
// (All score bar variables are now initialized above, right after bgSquare)
// Helper to update score bar fill and text
function updateScoreBar() {
// Total Score bar
var fillRatio = Math.min(score / scoreBarMax, 1);
var targetWidth = Math.floor((scoreBarWidth - 24) * fillRatio);
if (scoreBarFill && _typeof(scoreBarFill) === "object") {
tween(scoreBarFill, {
width: targetWidth
}, {
duration: 180,
easing: tween.cubicOut
});
}
if (scoreBarText) scoreBarText.setText(score);
if (scoreBarBg && scoreBarFill) {
scoreBarFill.x = scoreBarBg.x;
scoreBarFill.y = scoreBarBg.y;
}
if (scoreBarBg && scoreBarText) {
scoreBarText.x = scoreBarBg.x + scoreBarWidth / 2;
scoreBarText.y = scoreBarBg.y;
}
// Current Win bar
var winFillRatio = Math.min(currentWin / winBarMax, 1);
var winTargetWidth = Math.floor((winBarWidth - 20) * winFillRatio);
if (winBarFill && _typeof(winBarFill) === "object") {
tween(winBarFill, {
width: winTargetWidth
}, {
duration: 180,
easing: tween.cubicOut
});
}
if (winBarText) winBarText.setText(currentWin);
if (winBarBg && winBarFill) {
winBarFill.x = winBarBg.x;
winBarFill.y = winBarBg.y;
}
if (winBarBg && winBarText) {
winBarText.x = winBarBg.x + winBarWidth / 2;
winBarText.y = winBarBg.y;
}
}
// --- Helper Functions ---
// Generate a random genus (0-7)
function randomGenus() {
return Math.floor(Math.random() * genusCount);
}
// Place a block at grid[x][y]
function placeBlock(x, y, genus) {
var block = new Block();
block.setGenus(genus);
block.gridX = x;
block.gridY = y;
block.x = gridStartX + x * (blockSize + gridPadding);
block.y = gridStartY + y * (blockSize + gridPadding);
block.scaleX = 1;
block.scaleY = 1;
block.alpha = 1;
game.addChild(block);
// Add click handler to show genus breakdown window
block.down = function (x, y, obj) {
// Remove any existing breakdown window
if (game._blockBreakdownWindow && game._blockBreakdownWindow.length) {
for (var i = 0; i < game._blockBreakdownWindow.length; i++) {
if (game._blockBreakdownWindow[i] && game._blockBreakdownWindow[i].destroy) game._blockBreakdownWindow[i].destroy();
}
game._blockBreakdownWindow = [];
}
// Calculate genus count in grid
var genusCountInGrid = 0;
for (var yy = 0; yy < gridSize; yy++) {
for (var xx = 0; xx < gridSize; xx++) {
if (grid[yy][xx] && grid[yy][xx].genus === genus) genusCountInGrid++;
}
}
// Calculate per block score
var perBlockScore = Math.floor(10 * (genus + 1) * (spinAmount / 500));
// Window background
var winW = 1000,
winH = 600;
// Center the window in the main screen
var winX = (2048 - winW) / 2;
var winY = (2732 - winH) / 2;
var winBg = LK.getAsset('score_bar_bg', {
width: winW,
height: winH,
color: 0x181818,
shape: 'box',
anchorX: 0,
anchorY: 0,
x: winX,
y: winY
});
winBg.alpha = 0.97;
// Block icon
var blockIcon = LK.getAsset('block_genus_' + genus, {
width: 120,
height: 120,
anchorX: 0,
anchorY: 0.5,
x: winX + 48,
y: winY + winH / 2
});
// Texts: "6x(score) = total", "7x(score) = total", etc, stacked under each other
var breakdownTexts = [];
var breakdownFontSize = 80;
var breakdownSpacing = 18;
// Always show breakdown lines for 6, 7, 8, 9, ... up to genusCountInGrid, but only if genusCountInGrid >= 6
if (genusCountInGrid >= 6) {
var breakdownLineCount = genusCountInGrid - 6 + 1;
var stackStartY = winY + winH / 2 - (breakdownLineCount - 1) * (breakdownFontSize + breakdownSpacing) / 2;
for (var i = 6; i <= genusCountInGrid; i++) {
var lineScore = i * perBlockScore;
// Show: "6x <score per block> = <total>", e.g. "6x 120 = 720"
var breakdownLine = new Text2(i + "x " + perBlockScore + " = " + lineScore, {
size: breakdownFontSize,
fill: 0xffffff
});
breakdownLine.anchor.set(0, 0.5);
breakdownLine.x = winX + 200;
// Stack each line under the previous with spacing
breakdownLine.y = stackStartY + (i - 6) * (breakdownFontSize + breakdownSpacing);
game.addChild(breakdownLine);
breakdownTexts.push(breakdownLine);
}
}
// Title
var titleText = new Text2("Block Genus " + genus, {
size: 54,
fill: 0xffeb3b
});
titleText.anchor.set(0, 0);
titleText.x = winX + 48;
titleText.y = winY + 32;
// Close button
var closeBtn = LK.getAsset('score_bar_bg', {
width: 80,
height: 80,
color: 0xd83318,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
x: winX + winW - 60,
y: winY + 60
});
var closeTxt = new Text2('Γ', {
size: 60,
fill: 0xffffff
});
closeTxt.anchor.set(0.5, 0.5);
closeTxt.x = closeBtn.x;
closeTxt.y = closeBtn.y;
closeBtn.down = function () {
if (game._blockBreakdownWindow && game._blockBreakdownWindow.length) {
for (var i = 0; i < game._blockBreakdownWindow.length; i++) {
if (game._blockBreakdownWindow[i] && game._blockBreakdownWindow[i].destroy) game._blockBreakdownWindow[i].destroy();
}
game._blockBreakdownWindow = [];
}
};
// Add to game
// Add all breakdown window elements to LK.gui.center to ensure they are above all game elements
LK.gui.center.addChild(winBg);
LK.gui.center.addChild(blockIcon);
LK.gui.center.addChild(titleText);
LK.gui.center.addChild(closeBtn);
LK.gui.center.addChild(closeTxt);
// Track for removal (include all breakdownTexts)
var breakdownWindowNodes = [winBg, blockIcon, titleText, closeBtn, closeTxt];
for (var i = 0; i < breakdownTexts.length; i++) {
LK.gui.center.addChild(breakdownTexts[i]);
breakdownWindowNodes.push(breakdownTexts[i]);
}
game._blockBreakdownWindow = breakdownWindowNodes;
};
return block;
}
// Remove a block from the game and grid
function removeBlock(block) {
if (block) {
block.destroy();
}
}
// Fill the grid with random blocks, destroying old ones
function fillGridRandom() {
// Remove old blocks and clear grid
for (var y = 0; y < gridSize; y++) {
for (var x = 0; x < gridSize; x++) {
if (grid[y][x]) {
removeBlock(grid[y][x]);
}
grid[y][x] = null;
}
}
// Fill with new random blocks for every cell in the 5x5 grid
for (var y = 0; y < gridSize; y++) {
for (var x = 0; x < gridSize; x++) {
if (!grid[y][x]) {
// Only fill empty cells
// 5% chance to spawn a 2x block (use special asset for 2x)
var genus;
var block;
if (Math.random() < 0.05) {
genus = 8; // Use genus 8 for 2x block logic
block = placeBlock(x, y, genus);
// Remove the default block asset and attach the 'x2' asset
if (block && block.children && block.children.length > 0) {
block.removeChild(block.children[0]);
}
var x2Asset = block.attachAsset('x2', {
width: blockSize,
height: blockSize,
anchorX: 0.5,
anchorY: 0.5
});
} else {
genus = randomGenus();
block = placeBlock(x, y, genus);
}
tween(block, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 220,
easing: tween.cubicOut
});
grid[y][x] = block;
}
}
}
}
// Find all genus with 7 or more blocks, return array of {genus, blocks: [Block]}
function findMatches() {
// Count blocks per genus
var genusBlocks = [];
for (var g = 0; g < genusCount; g++) {
genusBlocks[g] = [];
}
for (var y = 0; y < gridSize; y++) {
for (var x = 0; x < gridSize; x++) {
var block = grid[y][x];
if (block && typeof block.genus === "number" && block.genus >= 0 && block.genus < genusCount) {
genusBlocks[block.genus].push(block);
}
}
}
var matches = [];
for (var g = 0; g < genusCount; g++) {
if (genusBlocks[g].length >= 6) {
matches.push({
genus: g,
blocks: genusBlocks[g]
});
}
}
return matches;
}
// Break all matched blocks, animate, update score, and refill
function breakAndRefill(matches, _onFinish2) {
if (matches.length === 0) {
if (_onFinish2) _onFinish2();
return;
}
var blocksToBreak = [];
for (var i = 0; i < matches.length; i++) {
blocksToBreak = blocksToBreak.concat(matches[i].blocks);
}
// Remove duplicates (shouldn't be any, but just in case)
var uniqueBlocks = [];
var seen = {};
for (var i = 0; i < blocksToBreak.length; i++) {
var b = blocksToBreak[i];
var key = b.gridX + ',' + b.gridY;
if (!seen[key]) {
uniqueBlocks.push(b);
seen[key] = true;
}
}
// Animate breaking
// --- Stacked breakdown text logic for fast breaks ---
if (!breakAndRefill.activeBreakdowns) breakAndRefill.activeBreakdowns = [];
var breakdownTextNodes = [];
var breakdownFontSize = 44;
var breakdownDuration = 1200;
var breakdownFade = 400;
// Arrange breakdowns vertically outside and to the right of the 5x5 grid
var breakdownCellW = 140;
var breakdownCellH = 84; // Increased for more vertical space between messages
// Place to the right of the 5x5 grid, not under the score bar
var breakdownStartX = bgSquare.x + bgSquareWidth / 2 + 60;
var breakdownStartY = bgSquare.y - bgSquareHeight / 2;
// Find the next available vertical slot for each new breakdown
var usedRows = [];
for (var b = 0; b < breakAndRefill.activeBreakdowns.length; b++) {
var batch = breakAndRefill.activeBreakdowns[b];
for (var j = 0; j < batch.length; j += 2) {
// asset is batch[j], text is batch[j+1]
if (batch[j] && typeof batch[j].__breakdownRow === "number") {
usedRows.push(batch[j].__breakdownRow);
}
}
}
for (var i = 0; i < matches.length; i++) {
var genus = matches[i].genus;
var count = matches[i].blocks.length;
if (count >= 6) {
// Find the next available row (vertical stacking)
var row = 0;
while (usedRows.indexOf(row) !== -1) {
row++;
}
usedRows.push(row);
var asset = LK.getAsset('block_genus_' + genus, {
width: 54,
height: 54,
anchorX: 0,
anchorY: 0.5,
x: breakdownStartX,
y: breakdownStartY + row * breakdownCellH
});
asset.__breakdownRow = row;
var perBlockScore = Math.floor(10 * (genus + 1) * (spinAmount / 500));
var extraBlockScore = Math.floor(10 * (genus + 1) * (spinAmount / 500)) * Math.max(0, count - 6);
var totalScore = perBlockScore * count + extraBlockScore;
var text = new Text2(count + "x" + perBlockScore + (count > 6 ? " + " + (count - 6) + "x" + perBlockScore : "") + " = " + totalScore, {
size: breakdownFontSize,
fill: 0xffffff
});
text.anchor.set(0, 0.5);
text.x = breakdownStartX + 60;
text.y = breakdownStartY + row * breakdownCellH + 36; // Adjusted for new cell height
game.addChild(asset);
game.addChild(text);
breakdownTextNodes.push(asset, text);
}
}
// Track this batch for stacking
breakAndRefill.activeBreakdowns.push(breakdownTextNodes);
// Remove this batch after duration, and update stacking for remaining
var breakdownClearTimeout = LK.setTimeout(function () {
// Destroy this batch
for (var i = 0; i < breakdownTextNodes.length; i++) {
if (breakdownTextNodes[i] && breakdownTextNodes[i].destroy) breakdownTextNodes[i].destroy();
}
// Remove this batch from activeBreakdowns
var idx = breakAndRefill.activeBreakdowns.indexOf(breakdownTextNodes);
if (idx !== -1) breakAndRefill.activeBreakdowns.splice(idx, 1);
// Move up any remaining breakdowns (re-stack vertically)
for (var b = 0; b < breakAndRefill.activeBreakdowns.length; b++) {
var batch = breakAndRefill.activeBreakdowns[b];
// Find the new row for this batch (b)
for (var j = 0; j < batch.length; j += 2) {
// asset is batch[j], text is batch[j+1]
if (batch[j]) {
batch[j].y = breakdownStartY + b * breakdownCellH;
batch[j].__breakdownRow = b;
}
if (batch[j + 1]) {
batch[j + 1].y = breakdownStartY + b * breakdownCellH + 36; // Adjusted for new cell height
}
}
}
}, breakdownDuration + breakdownFade);
var brokenCount = 0;
for (var i = 0; i < uniqueBlocks.length; i++) {
(function (block) {
block.breakAnim(function () {
// Remove from grid
grid[block.gridY][block.gridX] = null;
removeBlock(block);
brokenCount++;
// When all blocks are broken, refill
if (brokenCount === uniqueBlocks.length) {
// Update score: 10 points per block, +10 per extra block above 6, * genus+1
var winScore = 0;
var maxMultiplier = 1;
var foundMultiplier = null;
var has2xBlock = false;
// Check if any 2x block is present in the grid
for (var y2 = 0; y2 < gridSize; y2++) {
for (var x2 = 0; x2 < gridSize; x2++) {
if (grid[y2][x2] && grid[y2][x2].genus === 8) {
has2xBlock = true;
break;
}
}
if (has2xBlock) break;
}
for (var m = 0; m < matches.length; m++) {
var count = matches[m].blocks.length;
var genus = matches[m].genus;
// --- Sometimes give low scores: 30% chance to halve the score for this break ---
var baseScore = (10 * count + 10 * Math.max(0, count - 6)) * (genus + 1);
if (Math.random() < 0.3) {
baseScore = Math.floor(baseScore * 0.5);
}
score += baseScore;
winScore += baseScore;
}
// If 2x block is present, double the winScore and show a quick animation
if (has2xBlock && winScore > 0) {
var normalWinAmount = winScore;
winScore = winScore * 2;
// Animate winBarText to indicate 2x
tween(winBarText, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xffeb3b
}, {
duration: 180,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(winBarText, {
scaleX: 1,
scaleY: 1,
tint: 0xffffff
}, {
duration: 180,
easing: tween.cubicIn
});
}
});
// Show data message for 2x
var dataMsgAsset = LK.getAsset('x2', {
width: 60,
height: 60,
anchorX: 0,
anchorY: 0.5,
x: winBarText.x + 120,
y: winBarText.y
});
var dataMsgText = new Text2("(2x assets) 2x the current win", {
size: 38,
fill: 0xffeb3b
});
dataMsgText.anchor.set(0, 0.5);
dataMsgText.x = dataMsgAsset.x + 70;
dataMsgText.y = winBarText.y;
game.addChild(dataMsgAsset);
game.addChild(dataMsgText);
// Show normal win amount above current win bar, keep visible while 2x is present
if (typeof normalWinText !== "undefined" && normalWinText) {
// Show normal win as half of current win (since 2x is present)
var normalWinAmount = Math.floor(winScore / 2);
normalWinText.setText("normal win: " + normalWinAmount);
normalWinText.visible = true;
// Hide after 1.2s, but only if no 2x block remains
LK.setTimeout(function () {
// Check if any 2x block is still present
var stillHas2x = false;
for (var y2 = 0; y2 < gridSize; y2++) {
for (var x2 = 0; x2 < gridSize; x2++) {
if (grid[y2][x2] && grid[y2][x2].genus === 8) {
stillHas2x = true;
break;
}
}
if (stillHas2x) break;
}
if (!stillHas2x) {
normalWinText.visible = false;
}
}, 1200);
}
// Remove after 1.2s
LK.setTimeout(function () {
if (dataMsgAsset && dataMsgAsset.destroy) dataMsgAsset.destroy();
if (dataMsgText && dataMsgText.destroy) dataMsgText.destroy();
}, 1200);
}
currentWin += winScore;
updateScore();
updateScoreBar();
// Refill broken blocks with new random blocks
var dropAnimCount = 0;
var dropAnimTotal = 0;
for (var y = 0; y < gridSize; y++) {
for (var x = 0; x < gridSize; x++) {
if (!grid[y][x]) {
var genus = randomGenus();
var newBlock = placeBlock(x, y, genus);
grid[y][x] = newBlock;
// Animate drop from above
var targetY = newBlock.y;
newBlock.y = targetY - 180;
dropAnimTotal++;
tween(newBlock, {
y: targetY
}, {
duration: 220,
easing: tween.cubicOut,
onFinish: function onFinish() {
dropAnimCount++;
// Only proceed after all drop animations are done
if (dropAnimCount === dropAnimTotal) {
// After refill, check for new matches recursively
// --- BREAK COOLDOWN: Wait before checking for new matches ---
LK.setTimeout(function () {
var newMatches = findMatches();
if (newMatches.length > 0) {
breakAndRefill(newMatches, _onFinish2);
} else {
// Add currentWin to total score after all breaks
score += currentWin;
currentWin = 0;
updateScore();
updateScoreBar();
if (_onFinish2) _onFinish2();
}
}, 320); // 320ms cooldown after drop
}
}
});
}
}
}
// If no blocks needed to drop, continue immediately
if (dropAnimTotal === 0) {
// --- BREAK COOLDOWN: Wait before checking for new matches ---
LK.setTimeout(function () {
var newMatches = findMatches();
if (newMatches.length > 0) {
breakAndRefill(newMatches, _onFinish2);
} else {
if (_onFinish2) _onFinish2();
}
}, 320); // 320ms cooldown if no drop
}
// After refill, check for new matches recursively
// (removed duplicate immediate check)
}
});
})(uniqueBlocks[i]);
}
}
// Update score text and LK score
function updateScore() {
LK.setScore(score);
if (scoreBarText) scoreBarText.setText(score);
// Update score bar
if (typeof updateScoreBar === "function") updateScoreBar();
// Optionally, animate score text for feedback
if (scoreBarText) {
tween(scoreBarText, {
scaleX: 1.18,
scaleY: 1.18
}, {
duration: 80,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(scoreBarText, {
scaleX: 1,
scaleY: 1
}, {
duration: 80,
easing: tween.cubicIn
});
}
});
}
}
// Check if any matches are possible (i.e., if any genus has 6+ blocks)
function hasMatches() {
var matches = findMatches();
return matches.length > 0;
}
// Handle Spin button press
function onSpin() {
if (isSpinning) return;
if (score < spinAmount) {
// Not enough score to spin, show game over
LK.effects.flashScreen(0x000000, 600);
LK.showGameOver();
return;
}
score -= spinAmount;
currentWin = 0;
updateScore();
updateScoreBar();
isSpinning = true;
// Animate all blocks out (optional, for feedback)
for (var y = 0; y < gridSize; y++) {
for (var x = 0; x < gridSize; x++) {
var block = grid[y][x];
if (block) {
tween(block, {
scaleX: 0.7,
scaleY: 0.7,
alpha: 0.3
}, {
duration: 120,
easing: tween.cubicIn
});
}
}
}
// Remove cooldown for first spin, add cooldown only after first break
if (typeof onSpin.firstSpin === "undefined") {
onSpin.firstSpin = true;
}
var doSpin = function doSpin() {
fillGridRandom();
// After refill, check for matches and break recursively
var matches = findMatches();
if (matches.length > 0) {
// Wrap breakAndRefill to add 3s cooldown after first break
breakAndRefill(matches, function () {
isSpinning = false;
// After all matches, check if any more matches are possible
if (!hasMatches()) {
// Only show game over if score is less than 500, otherwise allow player to spin again
if (score < 500) {
LK.effects.flashScreen(0x000000, 600);
LK.showGameOver();
}
}
});
} else {
isSpinning = false;
// If no matches at all, only show game over if score is less than 500
if (score < 500) {
LK.effects.flashScreen(0x000000, 600);
LK.showGameOver();
}
}
};
if (onSpin.firstSpin) {
// No cooldown for first spin
onSpin.firstSpin = false;
doSpin(); // immediate, no timeout
} else {
doSpin(); // immediate, no timeout
}
}
// --- UI Setup ---
// --- Spin Amount UI ---
// State for spin amount
var spinAmount = 500;
var spinAmountMin = 200;
var spinAmountMax = 4000;
var spinAmountStep = 50;
// - Button (red)
var minusBtn = LK.getAsset('score_bar_bg', {
width: 100,
height: 100,
color: 0xd83318,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 260,
y: gridStartY + gridTotalSize + 140 + 60
});
game.addChild(minusBtn);
// - Text
var minusText = new Text2('-', {
size: 90,
fill: 0xffffff
});
minusText.anchor.set(0.5, 0.5);
minusText.x = minusBtn.x;
minusText.y = minusBtn.y;
game.addChild(minusText);
// + Button (green)
var plusBtn = LK.getAsset('score_bar_bg', {
width: 100,
height: 100,
color: 0x30a262,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 260,
y: gridStartY + gridTotalSize + 140 + 60
});
game.addChild(plusBtn);
// + Text
var plusText = new Text2('+', {
size: 90,
fill: 0xffffff
});
plusText.anchor.set(0.5, 0.5);
plusText.x = plusBtn.x;
plusText.y = plusBtn.y;
game.addChild(plusText);
// Spin Amount Button (triangle)
var spinAmountBtn = LK.getAsset('score_bar_bg', {
width: 120,
height: 120,
color: 0xf9dd57,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: gridStartY + gridTotalSize + 140 + 60
});
game.addChild(spinAmountBtn);
// Triangle icon (drawn with text)
var triangleText = new Text2('β²', {
size: 60,
fill: 0x222222
});
triangleText.anchor.set(0.5, 0.5);
triangleText.x = spinAmountBtn.x;
triangleText.y = spinAmountBtn.y - 30;
game.addChild(triangleText);
// Spin Amount Value Text
var spinAmountText = new Text2(spinAmount + '', {
size: 60,
fill: 0x222222
});
spinAmountText.anchor.set(0.5, 0.5);
spinAmountText.x = spinAmountBtn.x;
spinAmountText.y = spinAmountBtn.y + 30;
game.addChild(spinAmountText);
// Update spin amount text helper
function updateSpinAmountText() {
spinAmountText.setText(spinAmount + '');
}
// - Button logic
minusBtn.down = function (x, y, obj) {
if (spinAmount > spinAmountMin) {
spinAmount -= spinAmountStep;
if (spinAmount < spinAmountMin) spinAmount = spinAmountMin;
updateSpinAmountText();
// Animate feedback
tween(minusBtn, {
scaleX: 0.92,
scaleY: 0.92
}, {
duration: 60,
easing: tween.cubicIn,
onFinish: function onFinish() {
tween(minusBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 80,
easing: tween.cubicOut
});
}
});
}
};
// + Button logic
plusBtn.down = function (x, y, obj) {
if (spinAmount < spinAmountMax) {
spinAmount += spinAmountStep;
if (spinAmount > spinAmountMax) spinAmount = spinAmountMax;
updateSpinAmountText();
// Animate feedback
tween(plusBtn, {
scaleX: 0.92,
scaleY: 0.92
}, {
duration: 60,
easing: tween.cubicIn,
onFinish: function onFinish() {
tween(plusBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 80,
easing: tween.cubicOut
});
}
});
}
};
// Spin Amount Button logic (optional: could cycle through preset values, but here does nothing)
spinAmountBtn.down = function (x, y, obj) {
// Animate feedback
tween(spinAmountBtn, {
scaleX: 0.92,
scaleY: 0.92
}, {
duration: 60,
easing: tween.cubicIn,
onFinish: function onFinish() {
tween(spinAmountBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 80,
easing: tween.cubicOut
});
}
});
};
// --- Spin Button (circular, below the grid and spin amount UI) ---
var spinBtnRadius = 140;
spinBtn = LK.getAsset('spinBtn', {
width: spinBtnRadius * 2,
height: spinBtnRadius * 2,
color: 0xf9dd57,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: gridStartY + gridTotalSize + spinBtnRadius + 260 // moved below the grid and spin amount UI
});
game.addChild(spinBtn);
// --- Input Handling ---
// Only allow pressing Spin button (not grid)
spinBtn.down = function (x, y, obj) {
if (isSpinning) return;
onSpin();
// Animate button press
tween(spinBtn, {
scaleX: 0.92,
scaleY: 0.92
}, {
duration: 60,
easing: tween.cubicIn,
onFinish: function onFinish() {
tween(spinBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 80,
easing: tween.cubicOut
});
}
});
// Removed spinBtnTxt animation as spinBtnTxt does not exist
};
// --- Game Initialization ---
// Initialize grid array
for (var y = 0; y < gridSize; y++) {
grid[y] = [];
for (var x = 0; x < gridSize; x++) {
grid[y][x] = null;
}
}
// Start with 2000 score, do not fill grid or break until spin is pressed
score = 2000;
updateScore();
// Do not fillGridRandom or breakAndRefill here; wait for spin button
// --- Game Update Loop (not used, but required for LK) ---
game.update = function () {
// No per-frame logic needed
};
doner. 2d. High contrast. No shadows
ayran. 2d. High contrast. No shadows
lettuce. 2d. High contrast. No shadows
tomato. 2d. High contrast. No shadows
beef. 2d. High contrast. No shadows
ketchup. 2d. High contrast. No shadows
purple cabbage. 2d. High contrast. No shadows
mayonnaise. 2d. High contrast. No shadows
return symbol
doner kebab master. 2d. Like doner store background
Mustard Bottle, label is x2. In-Game asset. 2d. High contrast. No shadows
Mustard Bottle, label is x5. In-Game asset. 2d. High contrast. No shadows
Mustard Bottle, label is x10. In-Game asset. 2d. High contrast. No shadows
Mustard Bottle, label is x25. In-Game asset. 2d. High contrast. No shadows
Mustard Bottle, label is x50. In-Game asset. 2d. High contrast. No shadows
Mustard Bottle, label is x100. In-Game asset. 2d. High contrast. No shadows
crushed mustard bottle. In-Game asset. 2d. High contrast. No shadows