User prompt
add background themes of levels as assets I want to customize them too
User prompt
while trying to hit a wall and make a double jump if I don't release my finger pressing the wall side of the screen on time, my second finger pressing the other side of the screen does not allow me to change horizontal movement direction, please fix this bug and make much more smoother somehow
User prompt
player still need to release one finger to start the direction shift, I want to add a feature where even a second finger touches the screen the movement should change according to the last finger touching the screen ignoring the previous commands
User prompt
after double jump the player shouldn't have a need to release the screen to change direction of the movement. I'm having some performance problems in smoothness of the movement of the player while playing on mobile, please fix
User prompt
make clouds a bit more transparent
User prompt
now use cloud assets to properly show clouds in the background. clouds should act like platforms that cannot be landed on
User prompt
fix bugs in clouds
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'if (clouds.length === 0) {' Line Number: 246
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'lastCloudY')' in or related to this line: 'if (typeof cameraY.lastCloudY === "undefined") {' Line Number: 242
User prompt
Update clouds in game.update, move with camera and remove/add as needed
User prompt
they still don't move down with the camera, let's remove everything related to clouds from the code but keep their assets for later use
User prompt
they shouldn't remain in their position during the whole level, as player goes up during the level clouds should exit the screen
User prompt
some clouds should move to the right and some should move to the left horizontally but they should all get out of the screen vertically down
User prompt
they still don't follow the same logic. they spawn somewhere in the theme during the level and remain there during the whole level. They should be going down with the platforms as the player goes up during the level
User prompt
cloud logic does not work as intended they should flow down with the platforms as the player goes up
User prompt
clouds should be positioned similar with the platforms and as platforms disappear when player goes up clouds should follow the same logic
User prompt
create an asset/assets for the clouds so that I can change their looks
User prompt
they don't scroll with the camera right now please fix it
User prompt
they shouldn't remain on the spawn Y they should move down in Y as player goes up in Y
User prompt
clouds shouldn't move up as the player goes up, they should remain in the position they were spawned and then get out of the screen from the bottom as player advances up in the platforms, similar logic to the coins
User prompt
they still stay at their starting position and move only horizontally
User prompt
fix the cloud animations they should disappear with the platforms as the character advances up
User prompt
every level should last for 40 platforms and change the background to a more dynamic cloud themed background in each level
User prompt
now change from chipplatform1 to 2 every 40 platforms and make it the same for each increment in the game
User prompt
still shows final score as 0
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // --- Coin class: collectible, always same visual size, shows value popup --- var Coin = Container.expand(function () { var self = Container.call(this); // Coin type: 1, 2, or 3 (for +1, +3, +5) self.coinType = 1; self.value = 1; self.assetId = 'chipCoin1'; // Set asset and value based on type if (typeof arguments[0] === "number") { if (arguments[0] === 1) { self.coinType = 1; self.value = 1; self.assetId = 'chipCoin1'; } else if (arguments[0] === 2) { self.coinType = 2; self.value = 3; self.assetId = 'chipCoin2'; } else if (arguments[0] === 3) { self.coinType = 3; self.value = 5; self.assetId = 'chipCoin3'; } } // Always render coins at the same visual size (height 80px) var targetHeight = 80; var assetInfo = LK.getAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5 }); var scale = targetHeight / assetInfo.height; var coinGfx = self.attachAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: scale, scaleY: scale }); // For collision self.radius = assetInfo.width * scale / 2; // Show value popup when collected self.showValuePopup = function () { var txt = new Text2('+' + self.value, { size: 60, fill: self.value === 1 ? "#ffe066" : self.value === 3 ? "#a78bfa" : "#38bdf8", font: "Impact" }); txt.anchor.set(0.5, 0.5); txt.x = self.x; txt.y = self.y - 40; game.addChild(txt); // Ensure popup is above player if (txt.parent && player && txt.parent.children.indexOf(txt) < txt.parent.children.indexOf(player)) { txt.parent.setChildIndex(txt, txt.parent.children.length - 1); } tween(txt, { y: txt.y - 80, alpha: 0 }, { duration: 700, onFinish: function onFinish() { txt.destroy(); } }); }; return self; }); // PokerChip class: draggable, rotatable, circular var PokerChip = Container.expand(function () { var self = Container.call(this); // Asset id and color are passed in self.assetId = self.assetId || 'chipRed'; var chip = self.attachAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5 }); // For rotation handle var handle = self.attachAsset('chipCoin1', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.18, scaleY: 0.18, y: -chip.height / 2 - 40 }); handle.alpha = 0.7; // For drag/rotate state self.isDragging = false; self.isRotating = false; self.dragOffsetX = 0; self.dragOffsetY = 0; self.startAngle = 0; self.startRotation = 0; // For hit testing self.radius = chip.width / 2; // Used to distinguish between drag and rotate self.down = function (x, y, obj) { var local = self.toLocal({ x: x, y: y }); // If touch is on handle, start rotating var dx = local.x - handle.x; var dy = local.y - handle.y; if (dx * dx + dy * dy < handle.width / 2 * (handle.width / 2)) { self.isRotating = true; // Angle from center to pointer var cx = self.x, cy = self.y; self.startAngle = Math.atan2(y - cy, x - cx); self.startRotation = self.rotation; } else { // Otherwise, start dragging self.isDragging = true; self.dragOffsetX = x - self.x; self.dragOffsetY = y - self.y; } // Bring to front if (self.parent) { self.parent.setChildIndex(self, self.parent.children.length - 1); } }; self.up = function (x, y, obj) { self.isDragging = false; self.isRotating = false; }; // No per-chip move handler; handled globally return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x18181b }); /**** * Game Code ****/ // blue coin, value 5 // purple coin, value 3 // gold coin, value 1 // Gold // Neon // Metal // Ice // Stone // Night // Candy // Magic // Mud // Forest // Grass // was chipWhite // was chipOrange // was chipGreen // was chipYellow // was chipPurple // was chipPink // was chipCyan // was chipBrown // was chipBlack // Additional chip assets for more levels // --- Icy Tower Constants --- var highScore = storage.highScore || 0; var GAME_W = 2048; var GAME_H = 2732; var PLATFORM_W = 400; var PLATFORM_H = 110; var PLATFORM_SPACING_MIN = 320; var PLATFORM_SPACING_MAX = 440; var PLAYER_W = 120; var PLAYER_H = 120; var GRAVITY = 2.2; var JUMP_VELOCITY = -48; var MOVE_SPEED = 22; var PLATFORM_X_MARGIN = 120; var CAMERA_OFFSET = 900; // How far from bottom the player is kept // --- State --- var platforms = []; var coins = []; // All active coins var player = null; var vy = 0; var vx = 0; var isJumping = false; var isTouching = false; var touchStartX = 0; var cameraY = 0; var maxHeight = 0; var gameOver = false; game.hasDoubleJumped = false; // --- Assets --- var playerAsset = LK.getAsset('chipCharacter', { anchorX: 0.5, anchorY: 1, scaleX: PLAYER_W / 320, scaleY: PLAYER_H / 320 }); // Level themes: background color and platform asset per level // Platform color is always high-contrast with background for visibility var LEVEL_THEMES = [{ // 1 Grass bg: 0x18181b, platformAsset: 'chipPlatform1', platformColor: 0xfacc15 }, { // 2 Forest bg: 0x1e293b, platformAsset: 'chipPlatform2', platformColor: 0xf1f5f9 }, { // 3 Mud bg: 0x3b1e1e, platformAsset: 'chipPlatform3', platformColor: 0xffffff }, { // 4 Magic bg: 0x2d1e3b, platformAsset: 'chipPlatform4', platformColor: 0xffe066 }, { // 5 Candy bg: 0x3b2d1e, platformAsset: 'chipPlatform5', platformColor: 0x22223b }, { // 6 Night bg: 0x1e3b2d, platformAsset: 'chipPlatform6', platformColor: 0xf8fafc }, { // 7 Stone bg: 0x3b1e2d, platformAsset: 'chipPlatform7', platformColor: 0x22223b }, { // 8 Ice bg: 0x1e2d3b, platformAsset: 'chipPlatform8', platformColor: 0x22223b }, { // 9 Metal bg: 0x2d3b1e, platformAsset: 'chipPlatform9', platformColor: 0xf8fafc }, { // 10 Neon bg: 0x000000, platformAsset: 'chipPlatform10', platformColor: 0xfacc15 }, { // 11 Special (chipPlatform11) bg: 0x22223b, platformAsset: 'chipPlatform11', platformColor: 0xffffff }]; // Helper to get current theme index based on platformsPassed/level function getThemeIndex(score) { // Every level lasts for 20 platforms var idx = Math.floor(platformsPassed / 20); if (idx < 0) idx = 0; if (idx >= LEVEL_THEMES.length) idx = LEVEL_THEMES.length - 1; return idx; } // Helper to get current theme object function getCurrentTheme(score) { return LEVEL_THEMES[getThemeIndex(score)]; } // Used for initial platform asset (will be replaced in createPlatform) var platformAsset = LK.getAsset('chipPlatform8', { anchorX: 0.5, anchorY: 0.5, scaleX: PLATFORM_W / 1100, scaleY: PLATFORM_H / 700 }); // --- UI --- // Platform pass counter var platformsPassed = 0; // Track total coins collected var coinsCollected = 0; // --- Score & Coins UI --- // Create a container for the UI background and labels var uiContainer = new Container(); // Add a black rectangle background behind the UI (covers full top width, rectangle shape) var uiBgRect = LK.getAsset('uiTopBgRect', { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: GAME_W / 100, scaleY: 1 }); uiContainer.addChild(uiBgRect); var uiBgWidth = GAME_W; var uiBgHeight = 110; // "Coins: <number>" label (first, left) var coinsLabelTxt = new Text2('Coins: 0', { size: 70, fill: 0xFFE066, font: "Impact" }); coinsLabelTxt.anchor.set(0, 0.5); // left aligned, vertically centered coinsLabelTxt.x = 60; coinsLabelTxt.y = uiBgHeight / 2; // "Score:" label (right of coins, with extra spacing) var scoreLabelTxt = new Text2('Score:', { size: 70, fill: "#fff", font: "Impact" }); scoreLabelTxt.anchor.set(0, 0.5); // left aligned, vertically centered scoreLabelTxt.x = coinsLabelTxt.x + coinsLabelTxt.width + 80; scoreLabelTxt.y = uiBgHeight / 2; // Score value (right of label, with spacing) var scoreTxt = new Text2('0', { size: 80, fill: "#fff", font: "Impact" }); scoreTxt.anchor.set(0, 0.5); // left aligned, vertically centered scoreTxt.x = scoreLabelTxt.x + scoreLabelTxt.width + 30; scoreTxt.y = uiBgHeight / 2; // Add all to container in new order: coins, score label, score value uiContainer.addChild(coinsLabelTxt); uiContainer.addChild(scoreLabelTxt); uiContainer.addChild(scoreTxt); // Center the UI container at the top of the screen uiContainer.x = 0; uiContainer.y = 0; // Add to GUI overlay (top center) LK.gui.top.addChild(uiContainer); // Proper high score text object for updating best score var highScoreTxt = new Text2('', { size: 60, fill: "#fff", font: "Impact" }); highScoreTxt.anchor.set(1, 0.5); // right aligned, vertically centered highScoreTxt.x = GAME_W - 60; highScoreTxt.y = uiBgHeight / 2; uiContainer.addChild(highScoreTxt); // Listen for game over and update final score in the popup LK.on('gameover', function () { // Show the correct final score in the game over popup (works for mobile and desktop) if (typeof LK.setFinalScore === "function") { LK.setFinalScore(platformsPassed); } // For mobile: also try to set the score in the popup if LK.gui exists and has a final score label // (This is defensive: if the popup is not yet shown, this will do nothing) LK.setTimeout(function () { if (typeof LK.gui !== "undefined" && LK.gui !== null) { var popup = LK.gui.center && LK.gui.center.children ? LK.gui.center : null; if (popup && popup.children) { for (var i = 0; i < popup.children.length; ++i) { var child = popup.children[i]; if (child && typeof child.setText === "function" && typeof child.text === "string" && child.text.indexOf("Final Score") !== -1) { child.setText("Final Score: " + platformsPassed); } } } } }, 50); // Delay to ensure popup is rendered before updating }); // --- Helper: create a platform at (x, y) --- function createPlatform(x, y, width) { var plat = new Container(); // Make each new platform a bit harder as level increases // Find the last platform's width if any, otherwise use PLATFORM_W var lastPlat = platforms.length > 0 ? platforms[platforms.length - 1] : null; var prevW = lastPlat && lastPlat.width ? lastPlat.width : PLATFORM_W; var level = getThemeIndex(platformsPassed); // Platform width shrinks with level, min 220, max 1100 // Make width decrease more gradually per level (every 20 platforms) var baseW = 820 - level * 40; if (baseW < 220) baseW = 220; var w; if (typeof width === "number") { w = width; } else { // Each new platform is 4% wider than the previous, but capped by baseW for the level w = Math.min(prevW * 1.04, baseW); } // Determine theme based on platformsPassed for new platforms var theme = getCurrentTheme(platformsPassed); var assetId = theme.platformAsset; var platformColor = theme.platformColor; // Get original asset size for aspect ratio var assetInfo = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); var origW = assetInfo.width; var origH = assetInfo.height; var scaleX = w / origW; var scaleY = PLATFORM_H / origH; // To preserve aspect ratio, use the smaller scale var scale = Math.min(scaleX, scaleY); var platGfx = plat.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: scale, scaleY: scale, color: platformColor }); plat.x = x; plat.y = y; plat.width = w; plat.height = PLATFORM_H; plat.PLATFORM_H = PLATFORM_H; game.addChild(plat); platforms.push(plat); // --- Coin placement logic --- // Don't place coins on the first platform (player start) if (platforms.length > 1) { // 50% chance to spawn a coin on this platform if (Math.random() < 0.5) { // Randomly pick coin type: 60% +1, 30% +3, 10% +5 var r = Math.random(); var coinType = 1; if (r > 0.9) coinType = 3;else if (r > 0.6) coinType = 2; // Place coin at center of platform, slightly above var coin = new Coin(coinType); coin.x = plat.x; coin.y = plat.y - PLATFORM_H / 2 - 40; game.addChild(coin); coins.push(coin); } } return plat; } // --- Helper: find a safe X for a new platform, given previous platform --- function getSafePlatformX(prevPlat, width) { // Always keep new platform horizontally reachable from previous var minX = Math.max(PLATFORM_X_MARGIN + width / 2, prevPlat ? prevPlat.x - 400 : PLATFORM_X_MARGIN + width / 2); var maxX = Math.min(GAME_W - PLATFORM_X_MARGIN - width / 2, prevPlat ? prevPlat.x + 400 : GAME_W - PLATFORM_X_MARGIN - width / 2); if (minX > maxX) minX = maxX = prevPlat ? prevPlat.x : GAME_W / 2; return minX + Math.random() * (maxX - minX); } // --- Helper: reset game state --- function resetGame() { // Remove old coins for (var i = coins.length - 1; i >= 0; --i) { if (coins[i] && typeof coins[i].destroy === "function") { coins[i].destroy(); } } coins = []; // Remove old platforms for (var i = 0; i < platforms.length; ++i) { platforms[i].destroy(); } platforms = []; // Remove player if (player) player.destroy(); // Create player player = new Container(); var pGfx = player.attachAsset('chipCharacter', { anchorX: 0.5, anchorY: 1, scaleX: PLAYER_W / 320, scaleY: PLAYER_H / 320 }); player.x = GAME_W / 2; player.y = GAME_H - 400; player.width = PLAYER_W; player.height = PLAYER_H; game.addChild(player); vy = 0; vx = 0; isJumping = false; isTouching = false; cameraY = 0; maxHeight = 0; gameOver = false; // Create initial platforms var y = GAME_H - 200; // First platform: centered and wide, guaranteed under player var firstPlat = createPlatform(GAME_W / 2, y, 820); // Place player directly above first platform player.x = firstPlat.x; player.y = firstPlat.y - PLATFORM_H / 2 - PLAYER_H / 2 + 10; // Center player above platform, slightly above // Make platforms much wider and closer for easy jumps, but scale with level var level = getThemeIndex(platformsPassed); var EASY_PLATFORM_W = 820 - level * 40; if (EASY_PLATFORM_W < 220) EASY_PLATFORM_W = 220; // Keep vertical spacing constant for all levels var EASY_PLATFORM_SPACING = 240; var prevPlat = firstPlat; y -= EASY_PLATFORM_SPACING; for (var i = 1; i < 12; ++i) { var px = getSafePlatformX(prevPlat, EASY_PLATFORM_W); var plat = createPlatform(px, y, EASY_PLATFORM_W); prevPlat = plat; y -= EASY_PLATFORM_SPACING; } // Sort platforms by y platforms.sort(function (a, b) { return a.y - b.y; }); // Score platformsPassed = 0; coinsCollected = 0; highScoreTxt.setText('Best: ' + highScore); // Reset theme and background game.lastThemeIndex = -1; game.setBackgroundColor(getCurrentTheme(platformsPassed).bg); } // --- Helper: check collision between player and platform --- function playerOnPlatform() { for (var i = 0; i < platforms.length; ++i) { var plat = platforms[i]; // Only check if player is falling if (vy >= 0) { var px = player.x; var py = player.y; var platTop = plat.y - (plat.PLATFORM_H ? plat.PLATFORM_H : PLATFORM_H) / 2; var platLeft = plat.x - plat.width / 2; var platRight = plat.x + plat.width / 2; // Use previous player y for more reliable collision if (typeof player.lastY === "undefined") player.lastY = py - vy; // Check if player's feet crossed the platform top this frame if (player.lastY <= platTop && py >= platTop) { // Require player to be horizontally within platform bounds (with a small margin) if (px > platLeft + 10 && px < platRight - 10) { player.lastY = py; // update for next frame return plat; } } } } if (typeof player !== "undefined") player.lastY = player.y; return null; } // --- Touch controls: left/right jump --- game.down = function (x, y, obj) { if (gameOver) return; isTouching = true; touchStartX = x; // Set direction to move while finger is held if (x < GAME_W / 2) { vx = -MOVE_SPEED; } else { vx = MOVE_SPEED; } // If player is on ground/platform, jump if (!isJumping && !game.hasDoubleJumped) { vy = JUMP_VELOCITY; isJumping = true; } }; game.up = function (x, y, obj) { isTouching = false; vx = 0; }; game.move = function (x, y, obj) { // Only update direction if finger is held if (isTouching && !gameOver) { if (x < GAME_W / 2) { vx = -MOVE_SPEED; } else { vx = MOVE_SPEED; } } }; // --- Main update loop --- game.update = function () { if (gameOver) return; // Physics if (typeof player.lastVy === "undefined") player.lastVy = vy; vy += GRAVITY; player.x += vx; player.y += vy; // Clamp player to screen and tumble on wall hit if (player.x < PLAYER_W / 2) { player.x = PLAYER_W / 2; // Tumble: rotate player quickly if (player && player.children && player.children.length > 0) { var pGfx = player.children[0]; tween(pGfx, { rotation: pGfx.rotation - Math.PI * 2 }, { duration: 400, onFinish: function onFinish() { pGfx.rotation = 0; } }); } // Play shout sound when hitting left wall, with cooldown if (typeof game.lastShoutTime === "undefined") game.lastShoutTime = 0; var nowWall = Date.now(); if (nowWall - game.lastShoutTime > 500) { // (shout sound now played only on successful double jump) // game.lastShoutTime = nowWall; // keep cooldown for future use if needed game.lastShoutTime = nowWall; } // Double jump when hitting the wall, but only if not already double jumped without release if (!game.hasDoubleJumped) { vy = JUMP_VELOCITY * 2; isJumping = true; game.hasDoubleJumped = true; // Play shout sound only if double jump is successful LK.getSound('shout').play(); } } if (player.x > GAME_W - PLAYER_W / 2) { player.x = GAME_W - PLAYER_W / 2; // Tumble: rotate player quickly if (player && player.children && player.children.length > 0) { var pGfx = player.children[0]; tween(pGfx, { rotation: pGfx.rotation + Math.PI * 2 }, { duration: 400, onFinish: function onFinish() { pGfx.rotation = 0; } }); } // Play shout sound when hitting right wall, with cooldown if (typeof game.lastShoutTime === "undefined") game.lastShoutTime = 0; var nowWall = Date.now(); if (nowWall - game.lastShoutTime > 500) { // (shout sound now played only on successful double jump) // game.lastShoutTime = nowWall; // keep cooldown for future use if needed game.lastShoutTime = nowWall; } // Double jump when hitting the wall, but only if not already double jumped without release if (!game.hasDoubleJumped) { vy = JUMP_VELOCITY * 2; isJumping = true; game.hasDoubleJumped = true; // Play shout sound only if double jump is successful LK.getSound('shout').play(); } } // Platform collision var plat = playerOnPlatform(); if (plat && vy > 0) { player.y = plat.y - PLATFORM_H / 2; vy = JUMP_VELOCITY; isJumping = false; game.hasDoubleJumped = false; // Reset double jump lock only when landing // Always play jump animation here for consistency if (player && player.children && player.children.length > 0) { var pGfx = player.children[0]; // Squash down, stretch wide, add a little rotation (no color change) tween(pGfx, { scaleY: 0.7, scaleX: 1.25, rotation: 0.18 }, { duration: 90, onFinish: function onFinish() { // Stretch up, squash in, overshoot a bit for bounce tween(pGfx, { scaleY: 1.18 * PLAYER_H / 320, scaleX: 0.88 * PLAYER_W / 320, rotation: -0.08 }, { duration: 90, onFinish: function onFinish() { // Return to normal tween(pGfx, { scaleY: PLAYER_H / 320, scaleX: PLAYER_W / 320, rotation: 0 }, { duration: 90 }); } }); } }); } } else { isJumping = true; // No shout or fall sound here } // --- Coin collection --- for (var j = coins.length - 1; j >= 0; --j) { var coin = coins[j]; // Simple circle collision var dx = player.x - coin.x; var dy = player.y - PLAYER_H / 2 - coin.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < (PLAYER_W / 2 + coin.radius) * 0.7) { // Collect coin coin.showValuePopup(); // Add to score (platformsPassed is main score, but we can show a popup) platformsPassed += coin.value; // Add to coins collected coinsCollected += coin.value; coins.splice(j, 1); coin.destroy(); } } // Camera follows player upward if (player.y < GAME_H - CAMERA_OFFSET) { var diff = GAME_H - CAMERA_OFFSET - player.y; cameraY += diff; // Move all platforms and player down by diff for (var i = 0; i < platforms.length; ++i) { platforms[i].y += diff; } // Move all coins down by diff so they stay visually attached to their platform for (var i = 0; i < coins.length; ++i) { coins[i].y += diff; } player.y += diff; maxHeight += diff; } // Always keep player in front of platforms if (player.parent && player.parent.children.indexOf(player) !== player.parent.children.length - 1) { player.parent.setChildIndex(player, player.parent.children.length - 1); } // Remove platforms that are off screen, add new ones at top for (var i = platforms.length - 1; i >= 0; --i) { if (platforms[i].y > GAME_H + 100) { // Remove any coins that are visually on this platform (within platform width and just above it) for (var j = coins.length - 1; j >= 0; --j) { var coin = coins[j]; // Coin is considered on this platform if its x is within platform width and y is just above platform if (coin.x >= platforms[i].x - platforms[i].width / 2 && coin.x <= platforms[i].x + platforms[i].width / 2 && Math.abs(coin.y - (platforms[i].y - PLATFORM_H / 2 - 40)) < 60) { coin.destroy(); coins.splice(j, 1); } } platforms[i].destroy(); platforms.splice(i, 1); // Only increment platformsPassed if not incremented by coin platformsPassed++; } } // Add new platforms if needed while (platforms.length < 12) { var topY = platforms[0].y; // Make platform width and spacing harder as level increases var level = getThemeIndex(platformsPassed); var EASY_PLATFORM_W = 820 - level * 40; if (EASY_PLATFORM_W < 220) EASY_PLATFORM_W = 220; // Keep vertical spacing constant for all levels var EASY_PLATFORM_SPACING = 240; var newY = topY - EASY_PLATFORM_SPACING; var prevPlat = platforms[0]; var px = getSafePlatformX(prevPlat, EASY_PLATFORM_W); var plat = createPlatform(px, newY, EASY_PLATFORM_W); platforms.sort(function (a, b) { return a.y - b.y; }); } // Score: based on platforms passed scoreTxt.setText(platformsPassed.toString()); // Update coins label to show current coins collected coinsLabelTxt.setText('Coins: ' + coinsCollected); // Change background color if level changes var themeIndex = getThemeIndex(platformsPassed); if (game.lastThemeIndex !== themeIndex) { game.setBackgroundColor(getCurrentTheme(platformsPassed).bg); game.lastThemeIndex = themeIndex; } // High score logic (still based on maxHeight climbed) var score = Math.floor(maxHeight / 10); if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('Best: ' + highScore); } // Game over: fall below screen if (player.y > GAME_H + 200) { gameOver = true; // Play fall sound when it's certain the player is going to die LK.getSound('fall').play(); // Show game over after short delay LK.setTimeout(function () { LK.showGameOver(); }, 1200); } // Update lastVy for next frame player.lastVy = vy; }; // --- Start game --- resetGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// --- Coin class: collectible, always same visual size, shows value popup ---
var Coin = Container.expand(function () {
var self = Container.call(this);
// Coin type: 1, 2, or 3 (for +1, +3, +5)
self.coinType = 1;
self.value = 1;
self.assetId = 'chipCoin1';
// Set asset and value based on type
if (typeof arguments[0] === "number") {
if (arguments[0] === 1) {
self.coinType = 1;
self.value = 1;
self.assetId = 'chipCoin1';
} else if (arguments[0] === 2) {
self.coinType = 2;
self.value = 3;
self.assetId = 'chipCoin2';
} else if (arguments[0] === 3) {
self.coinType = 3;
self.value = 5;
self.assetId = 'chipCoin3';
}
}
// Always render coins at the same visual size (height 80px)
var targetHeight = 80;
var assetInfo = LK.getAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
var scale = targetHeight / assetInfo.height;
var coinGfx = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scale,
scaleY: scale
});
// For collision
self.radius = assetInfo.width * scale / 2;
// Show value popup when collected
self.showValuePopup = function () {
var txt = new Text2('+' + self.value, {
size: 60,
fill: self.value === 1 ? "#ffe066" : self.value === 3 ? "#a78bfa" : "#38bdf8",
font: "Impact"
});
txt.anchor.set(0.5, 0.5);
txt.x = self.x;
txt.y = self.y - 40;
game.addChild(txt);
// Ensure popup is above player
if (txt.parent && player && txt.parent.children.indexOf(txt) < txt.parent.children.indexOf(player)) {
txt.parent.setChildIndex(txt, txt.parent.children.length - 1);
}
tween(txt, {
y: txt.y - 80,
alpha: 0
}, {
duration: 700,
onFinish: function onFinish() {
txt.destroy();
}
});
};
return self;
});
// PokerChip class: draggable, rotatable, circular
var PokerChip = Container.expand(function () {
var self = Container.call(this);
// Asset id and color are passed in
self.assetId = self.assetId || 'chipRed';
var chip = self.attachAsset(self.assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// For rotation handle
var handle = self.attachAsset('chipCoin1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.18,
scaleY: 0.18,
y: -chip.height / 2 - 40
});
handle.alpha = 0.7;
// For drag/rotate state
self.isDragging = false;
self.isRotating = false;
self.dragOffsetX = 0;
self.dragOffsetY = 0;
self.startAngle = 0;
self.startRotation = 0;
// For hit testing
self.radius = chip.width / 2;
// Used to distinguish between drag and rotate
self.down = function (x, y, obj) {
var local = self.toLocal({
x: x,
y: y
});
// If touch is on handle, start rotating
var dx = local.x - handle.x;
var dy = local.y - handle.y;
if (dx * dx + dy * dy < handle.width / 2 * (handle.width / 2)) {
self.isRotating = true;
// Angle from center to pointer
var cx = self.x,
cy = self.y;
self.startAngle = Math.atan2(y - cy, x - cx);
self.startRotation = self.rotation;
} else {
// Otherwise, start dragging
self.isDragging = true;
self.dragOffsetX = x - self.x;
self.dragOffsetY = y - self.y;
}
// Bring to front
if (self.parent) {
self.parent.setChildIndex(self, self.parent.children.length - 1);
}
};
self.up = function (x, y, obj) {
self.isDragging = false;
self.isRotating = false;
};
// No per-chip move handler; handled globally
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x18181b
});
/****
* Game Code
****/
// blue coin, value 5
// purple coin, value 3
// gold coin, value 1
// Gold
// Neon
// Metal
// Ice
// Stone
// Night
// Candy
// Magic
// Mud
// Forest
// Grass
// was chipWhite
// was chipOrange
// was chipGreen
// was chipYellow
// was chipPurple
// was chipPink
// was chipCyan
// was chipBrown
// was chipBlack
// Additional chip assets for more levels
// --- Icy Tower Constants ---
var highScore = storage.highScore || 0;
var GAME_W = 2048;
var GAME_H = 2732;
var PLATFORM_W = 400;
var PLATFORM_H = 110;
var PLATFORM_SPACING_MIN = 320;
var PLATFORM_SPACING_MAX = 440;
var PLAYER_W = 120;
var PLAYER_H = 120;
var GRAVITY = 2.2;
var JUMP_VELOCITY = -48;
var MOVE_SPEED = 22;
var PLATFORM_X_MARGIN = 120;
var CAMERA_OFFSET = 900; // How far from bottom the player is kept
// --- State ---
var platforms = [];
var coins = []; // All active coins
var player = null;
var vy = 0;
var vx = 0;
var isJumping = false;
var isTouching = false;
var touchStartX = 0;
var cameraY = 0;
var maxHeight = 0;
var gameOver = false;
game.hasDoubleJumped = false;
// --- Assets ---
var playerAsset = LK.getAsset('chipCharacter', {
anchorX: 0.5,
anchorY: 1,
scaleX: PLAYER_W / 320,
scaleY: PLAYER_H / 320
});
// Level themes: background color and platform asset per level
// Platform color is always high-contrast with background for visibility
var LEVEL_THEMES = [{
// 1 Grass
bg: 0x18181b,
platformAsset: 'chipPlatform1',
platformColor: 0xfacc15
}, {
// 2 Forest
bg: 0x1e293b,
platformAsset: 'chipPlatform2',
platformColor: 0xf1f5f9
}, {
// 3 Mud
bg: 0x3b1e1e,
platformAsset: 'chipPlatform3',
platformColor: 0xffffff
}, {
// 4 Magic
bg: 0x2d1e3b,
platformAsset: 'chipPlatform4',
platformColor: 0xffe066
}, {
// 5 Candy
bg: 0x3b2d1e,
platformAsset: 'chipPlatform5',
platformColor: 0x22223b
}, {
// 6 Night
bg: 0x1e3b2d,
platformAsset: 'chipPlatform6',
platformColor: 0xf8fafc
}, {
// 7 Stone
bg: 0x3b1e2d,
platformAsset: 'chipPlatform7',
platformColor: 0x22223b
}, {
// 8 Ice
bg: 0x1e2d3b,
platformAsset: 'chipPlatform8',
platformColor: 0x22223b
}, {
// 9 Metal
bg: 0x2d3b1e,
platformAsset: 'chipPlatform9',
platformColor: 0xf8fafc
}, {
// 10 Neon
bg: 0x000000,
platformAsset: 'chipPlatform10',
platformColor: 0xfacc15
}, {
// 11 Special (chipPlatform11)
bg: 0x22223b,
platformAsset: 'chipPlatform11',
platformColor: 0xffffff
}];
// Helper to get current theme index based on platformsPassed/level
function getThemeIndex(score) {
// Every level lasts for 20 platforms
var idx = Math.floor(platformsPassed / 20);
if (idx < 0) idx = 0;
if (idx >= LEVEL_THEMES.length) idx = LEVEL_THEMES.length - 1;
return idx;
}
// Helper to get current theme object
function getCurrentTheme(score) {
return LEVEL_THEMES[getThemeIndex(score)];
}
// Used for initial platform asset (will be replaced in createPlatform)
var platformAsset = LK.getAsset('chipPlatform8', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: PLATFORM_W / 1100,
scaleY: PLATFORM_H / 700
});
// --- UI ---
// Platform pass counter
var platformsPassed = 0;
// Track total coins collected
var coinsCollected = 0;
// --- Score & Coins UI ---
// Create a container for the UI background and labels
var uiContainer = new Container();
// Add a black rectangle background behind the UI (covers full top width, rectangle shape)
var uiBgRect = LK.getAsset('uiTopBgRect', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: GAME_W / 100,
scaleY: 1
});
uiContainer.addChild(uiBgRect);
var uiBgWidth = GAME_W;
var uiBgHeight = 110;
// "Coins: <number>" label (first, left)
var coinsLabelTxt = new Text2('Coins: 0', {
size: 70,
fill: 0xFFE066,
font: "Impact"
});
coinsLabelTxt.anchor.set(0, 0.5); // left aligned, vertically centered
coinsLabelTxt.x = 60;
coinsLabelTxt.y = uiBgHeight / 2;
// "Score:" label (right of coins, with extra spacing)
var scoreLabelTxt = new Text2('Score:', {
size: 70,
fill: "#fff",
font: "Impact"
});
scoreLabelTxt.anchor.set(0, 0.5); // left aligned, vertically centered
scoreLabelTxt.x = coinsLabelTxt.x + coinsLabelTxt.width + 80;
scoreLabelTxt.y = uiBgHeight / 2;
// Score value (right of label, with spacing)
var scoreTxt = new Text2('0', {
size: 80,
fill: "#fff",
font: "Impact"
});
scoreTxt.anchor.set(0, 0.5); // left aligned, vertically centered
scoreTxt.x = scoreLabelTxt.x + scoreLabelTxt.width + 30;
scoreTxt.y = uiBgHeight / 2;
// Add all to container in new order: coins, score label, score value
uiContainer.addChild(coinsLabelTxt);
uiContainer.addChild(scoreLabelTxt);
uiContainer.addChild(scoreTxt);
// Center the UI container at the top of the screen
uiContainer.x = 0;
uiContainer.y = 0;
// Add to GUI overlay (top center)
LK.gui.top.addChild(uiContainer);
// Proper high score text object for updating best score
var highScoreTxt = new Text2('', {
size: 60,
fill: "#fff",
font: "Impact"
});
highScoreTxt.anchor.set(1, 0.5); // right aligned, vertically centered
highScoreTxt.x = GAME_W - 60;
highScoreTxt.y = uiBgHeight / 2;
uiContainer.addChild(highScoreTxt);
// Listen for game over and update final score in the popup
LK.on('gameover', function () {
// Show the correct final score in the game over popup (works for mobile and desktop)
if (typeof LK.setFinalScore === "function") {
LK.setFinalScore(platformsPassed);
}
// For mobile: also try to set the score in the popup if LK.gui exists and has a final score label
// (This is defensive: if the popup is not yet shown, this will do nothing)
LK.setTimeout(function () {
if (typeof LK.gui !== "undefined" && LK.gui !== null) {
var popup = LK.gui.center && LK.gui.center.children ? LK.gui.center : null;
if (popup && popup.children) {
for (var i = 0; i < popup.children.length; ++i) {
var child = popup.children[i];
if (child && typeof child.setText === "function" && typeof child.text === "string" && child.text.indexOf("Final Score") !== -1) {
child.setText("Final Score: " + platformsPassed);
}
}
}
}
}, 50); // Delay to ensure popup is rendered before updating
});
// --- Helper: create a platform at (x, y) ---
function createPlatform(x, y, width) {
var plat = new Container();
// Make each new platform a bit harder as level increases
// Find the last platform's width if any, otherwise use PLATFORM_W
var lastPlat = platforms.length > 0 ? platforms[platforms.length - 1] : null;
var prevW = lastPlat && lastPlat.width ? lastPlat.width : PLATFORM_W;
var level = getThemeIndex(platformsPassed);
// Platform width shrinks with level, min 220, max 1100
// Make width decrease more gradually per level (every 20 platforms)
var baseW = 820 - level * 40;
if (baseW < 220) baseW = 220;
var w;
if (typeof width === "number") {
w = width;
} else {
// Each new platform is 4% wider than the previous, but capped by baseW for the level
w = Math.min(prevW * 1.04, baseW);
}
// Determine theme based on platformsPassed for new platforms
var theme = getCurrentTheme(platformsPassed);
var assetId = theme.platformAsset;
var platformColor = theme.platformColor;
// Get original asset size for aspect ratio
var assetInfo = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
var origW = assetInfo.width;
var origH = assetInfo.height;
var scaleX = w / origW;
var scaleY = PLATFORM_H / origH;
// To preserve aspect ratio, use the smaller scale
var scale = Math.min(scaleX, scaleY);
var platGfx = plat.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scale,
scaleY: scale,
color: platformColor
});
plat.x = x;
plat.y = y;
plat.width = w;
plat.height = PLATFORM_H;
plat.PLATFORM_H = PLATFORM_H;
game.addChild(plat);
platforms.push(plat);
// --- Coin placement logic ---
// Don't place coins on the first platform (player start)
if (platforms.length > 1) {
// 50% chance to spawn a coin on this platform
if (Math.random() < 0.5) {
// Randomly pick coin type: 60% +1, 30% +3, 10% +5
var r = Math.random();
var coinType = 1;
if (r > 0.9) coinType = 3;else if (r > 0.6) coinType = 2;
// Place coin at center of platform, slightly above
var coin = new Coin(coinType);
coin.x = plat.x;
coin.y = plat.y - PLATFORM_H / 2 - 40;
game.addChild(coin);
coins.push(coin);
}
}
return plat;
}
// --- Helper: find a safe X for a new platform, given previous platform ---
function getSafePlatformX(prevPlat, width) {
// Always keep new platform horizontally reachable from previous
var minX = Math.max(PLATFORM_X_MARGIN + width / 2, prevPlat ? prevPlat.x - 400 : PLATFORM_X_MARGIN + width / 2);
var maxX = Math.min(GAME_W - PLATFORM_X_MARGIN - width / 2, prevPlat ? prevPlat.x + 400 : GAME_W - PLATFORM_X_MARGIN - width / 2);
if (minX > maxX) minX = maxX = prevPlat ? prevPlat.x : GAME_W / 2;
return minX + Math.random() * (maxX - minX);
}
// --- Helper: reset game state ---
function resetGame() {
// Remove old coins
for (var i = coins.length - 1; i >= 0; --i) {
if (coins[i] && typeof coins[i].destroy === "function") {
coins[i].destroy();
}
}
coins = [];
// Remove old platforms
for (var i = 0; i < platforms.length; ++i) {
platforms[i].destroy();
}
platforms = [];
// Remove player
if (player) player.destroy();
// Create player
player = new Container();
var pGfx = player.attachAsset('chipCharacter', {
anchorX: 0.5,
anchorY: 1,
scaleX: PLAYER_W / 320,
scaleY: PLAYER_H / 320
});
player.x = GAME_W / 2;
player.y = GAME_H - 400;
player.width = PLAYER_W;
player.height = PLAYER_H;
game.addChild(player);
vy = 0;
vx = 0;
isJumping = false;
isTouching = false;
cameraY = 0;
maxHeight = 0;
gameOver = false;
// Create initial platforms
var y = GAME_H - 200;
// First platform: centered and wide, guaranteed under player
var firstPlat = createPlatform(GAME_W / 2, y, 820);
// Place player directly above first platform
player.x = firstPlat.x;
player.y = firstPlat.y - PLATFORM_H / 2 - PLAYER_H / 2 + 10; // Center player above platform, slightly above
// Make platforms much wider and closer for easy jumps, but scale with level
var level = getThemeIndex(platformsPassed);
var EASY_PLATFORM_W = 820 - level * 40;
if (EASY_PLATFORM_W < 220) EASY_PLATFORM_W = 220;
// Keep vertical spacing constant for all levels
var EASY_PLATFORM_SPACING = 240;
var prevPlat = firstPlat;
y -= EASY_PLATFORM_SPACING;
for (var i = 1; i < 12; ++i) {
var px = getSafePlatformX(prevPlat, EASY_PLATFORM_W);
var plat = createPlatform(px, y, EASY_PLATFORM_W);
prevPlat = plat;
y -= EASY_PLATFORM_SPACING;
}
// Sort platforms by y
platforms.sort(function (a, b) {
return a.y - b.y;
});
// Score
platformsPassed = 0;
coinsCollected = 0;
highScoreTxt.setText('Best: ' + highScore);
// Reset theme and background
game.lastThemeIndex = -1;
game.setBackgroundColor(getCurrentTheme(platformsPassed).bg);
}
// --- Helper: check collision between player and platform ---
function playerOnPlatform() {
for (var i = 0; i < platforms.length; ++i) {
var plat = platforms[i];
// Only check if player is falling
if (vy >= 0) {
var px = player.x;
var py = player.y;
var platTop = plat.y - (plat.PLATFORM_H ? plat.PLATFORM_H : PLATFORM_H) / 2;
var platLeft = plat.x - plat.width / 2;
var platRight = plat.x + plat.width / 2;
// Use previous player y for more reliable collision
if (typeof player.lastY === "undefined") player.lastY = py - vy;
// Check if player's feet crossed the platform top this frame
if (player.lastY <= platTop && py >= platTop) {
// Require player to be horizontally within platform bounds (with a small margin)
if (px > platLeft + 10 && px < platRight - 10) {
player.lastY = py; // update for next frame
return plat;
}
}
}
}
if (typeof player !== "undefined") player.lastY = player.y;
return null;
}
// --- Touch controls: left/right jump ---
game.down = function (x, y, obj) {
if (gameOver) return;
isTouching = true;
touchStartX = x;
// Set direction to move while finger is held
if (x < GAME_W / 2) {
vx = -MOVE_SPEED;
} else {
vx = MOVE_SPEED;
}
// If player is on ground/platform, jump
if (!isJumping && !game.hasDoubleJumped) {
vy = JUMP_VELOCITY;
isJumping = true;
}
};
game.up = function (x, y, obj) {
isTouching = false;
vx = 0;
};
game.move = function (x, y, obj) {
// Only update direction if finger is held
if (isTouching && !gameOver) {
if (x < GAME_W / 2) {
vx = -MOVE_SPEED;
} else {
vx = MOVE_SPEED;
}
}
};
// --- Main update loop ---
game.update = function () {
if (gameOver) return;
// Physics
if (typeof player.lastVy === "undefined") player.lastVy = vy;
vy += GRAVITY;
player.x += vx;
player.y += vy;
// Clamp player to screen and tumble on wall hit
if (player.x < PLAYER_W / 2) {
player.x = PLAYER_W / 2;
// Tumble: rotate player quickly
if (player && player.children && player.children.length > 0) {
var pGfx = player.children[0];
tween(pGfx, {
rotation: pGfx.rotation - Math.PI * 2
}, {
duration: 400,
onFinish: function onFinish() {
pGfx.rotation = 0;
}
});
}
// Play shout sound when hitting left wall, with cooldown
if (typeof game.lastShoutTime === "undefined") game.lastShoutTime = 0;
var nowWall = Date.now();
if (nowWall - game.lastShoutTime > 500) {
// (shout sound now played only on successful double jump)
// game.lastShoutTime = nowWall; // keep cooldown for future use if needed
game.lastShoutTime = nowWall;
}
// Double jump when hitting the wall, but only if not already double jumped without release
if (!game.hasDoubleJumped) {
vy = JUMP_VELOCITY * 2;
isJumping = true;
game.hasDoubleJumped = true;
// Play shout sound only if double jump is successful
LK.getSound('shout').play();
}
}
if (player.x > GAME_W - PLAYER_W / 2) {
player.x = GAME_W - PLAYER_W / 2;
// Tumble: rotate player quickly
if (player && player.children && player.children.length > 0) {
var pGfx = player.children[0];
tween(pGfx, {
rotation: pGfx.rotation + Math.PI * 2
}, {
duration: 400,
onFinish: function onFinish() {
pGfx.rotation = 0;
}
});
}
// Play shout sound when hitting right wall, with cooldown
if (typeof game.lastShoutTime === "undefined") game.lastShoutTime = 0;
var nowWall = Date.now();
if (nowWall - game.lastShoutTime > 500) {
// (shout sound now played only on successful double jump)
// game.lastShoutTime = nowWall; // keep cooldown for future use if needed
game.lastShoutTime = nowWall;
}
// Double jump when hitting the wall, but only if not already double jumped without release
if (!game.hasDoubleJumped) {
vy = JUMP_VELOCITY * 2;
isJumping = true;
game.hasDoubleJumped = true;
// Play shout sound only if double jump is successful
LK.getSound('shout').play();
}
}
// Platform collision
var plat = playerOnPlatform();
if (plat && vy > 0) {
player.y = plat.y - PLATFORM_H / 2;
vy = JUMP_VELOCITY;
isJumping = false;
game.hasDoubleJumped = false; // Reset double jump lock only when landing
// Always play jump animation here for consistency
if (player && player.children && player.children.length > 0) {
var pGfx = player.children[0];
// Squash down, stretch wide, add a little rotation (no color change)
tween(pGfx, {
scaleY: 0.7,
scaleX: 1.25,
rotation: 0.18
}, {
duration: 90,
onFinish: function onFinish() {
// Stretch up, squash in, overshoot a bit for bounce
tween(pGfx, {
scaleY: 1.18 * PLAYER_H / 320,
scaleX: 0.88 * PLAYER_W / 320,
rotation: -0.08
}, {
duration: 90,
onFinish: function onFinish() {
// Return to normal
tween(pGfx, {
scaleY: PLAYER_H / 320,
scaleX: PLAYER_W / 320,
rotation: 0
}, {
duration: 90
});
}
});
}
});
}
} else {
isJumping = true;
// No shout or fall sound here
}
// --- Coin collection ---
for (var j = coins.length - 1; j >= 0; --j) {
var coin = coins[j];
// Simple circle collision
var dx = player.x - coin.x;
var dy = player.y - PLAYER_H / 2 - coin.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < (PLAYER_W / 2 + coin.radius) * 0.7) {
// Collect coin
coin.showValuePopup();
// Add to score (platformsPassed is main score, but we can show a popup)
platformsPassed += coin.value;
// Add to coins collected
coinsCollected += coin.value;
coins.splice(j, 1);
coin.destroy();
}
}
// Camera follows player upward
if (player.y < GAME_H - CAMERA_OFFSET) {
var diff = GAME_H - CAMERA_OFFSET - player.y;
cameraY += diff;
// Move all platforms and player down by diff
for (var i = 0; i < platforms.length; ++i) {
platforms[i].y += diff;
}
// Move all coins down by diff so they stay visually attached to their platform
for (var i = 0; i < coins.length; ++i) {
coins[i].y += diff;
}
player.y += diff;
maxHeight += diff;
}
// Always keep player in front of platforms
if (player.parent && player.parent.children.indexOf(player) !== player.parent.children.length - 1) {
player.parent.setChildIndex(player, player.parent.children.length - 1);
}
// Remove platforms that are off screen, add new ones at top
for (var i = platforms.length - 1; i >= 0; --i) {
if (platforms[i].y > GAME_H + 100) {
// Remove any coins that are visually on this platform (within platform width and just above it)
for (var j = coins.length - 1; j >= 0; --j) {
var coin = coins[j];
// Coin is considered on this platform if its x is within platform width and y is just above platform
if (coin.x >= platforms[i].x - platforms[i].width / 2 && coin.x <= platforms[i].x + platforms[i].width / 2 && Math.abs(coin.y - (platforms[i].y - PLATFORM_H / 2 - 40)) < 60) {
coin.destroy();
coins.splice(j, 1);
}
}
platforms[i].destroy();
platforms.splice(i, 1);
// Only increment platformsPassed if not incremented by coin
platformsPassed++;
}
}
// Add new platforms if needed
while (platforms.length < 12) {
var topY = platforms[0].y;
// Make platform width and spacing harder as level increases
var level = getThemeIndex(platformsPassed);
var EASY_PLATFORM_W = 820 - level * 40;
if (EASY_PLATFORM_W < 220) EASY_PLATFORM_W = 220;
// Keep vertical spacing constant for all levels
var EASY_PLATFORM_SPACING = 240;
var newY = topY - EASY_PLATFORM_SPACING;
var prevPlat = platforms[0];
var px = getSafePlatformX(prevPlat, EASY_PLATFORM_W);
var plat = createPlatform(px, newY, EASY_PLATFORM_W);
platforms.sort(function (a, b) {
return a.y - b.y;
});
}
// Score: based on platforms passed
scoreTxt.setText(platformsPassed.toString());
// Update coins label to show current coins collected
coinsLabelTxt.setText('Coins: ' + coinsCollected);
// Change background color if level changes
var themeIndex = getThemeIndex(platformsPassed);
if (game.lastThemeIndex !== themeIndex) {
game.setBackgroundColor(getCurrentTheme(platformsPassed).bg);
game.lastThemeIndex = themeIndex;
}
// High score logic (still based on maxHeight climbed)
var score = Math.floor(maxHeight / 10);
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('Best: ' + highScore);
}
// Game over: fall below screen
if (player.y > GAME_H + 200) {
gameOver = true;
// Play fall sound when it's certain the player is going to die
LK.getSound('fall').play();
// Show game over after short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 1200);
}
// Update lastVy for next frame
player.lastVy = vy;
};
// --- Start game ---
resetGame();
icy tower guy. In-Game asset. 2d. High contrast. No shadows
mario or icy tower like platforms. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
icy tower advanced level platform. In-Game asset. 2d. High contrast. No shadows
diamond. In-Game asset. 2d. High contrast. No shadows
dollar. In-Game asset. 2d. High contrast. No shadows
Design a single floating 2D game platform made of levitating crystal shards, connected by glowing magical runes or light energy. No ice or snow. The platform should feel arcane and unique. No background.. In-Game asset. 2d. High contrast. No shadows
rectangle shape jumping platform for a simple 2D game. In-Game asset. 2d. High contrast. No shadows
super mario facing camera. In-Game asset. 2d. High contrast. No shadows
blue transparent cloud. In-Game asset. 2d. High contrast. No shadows
bright transparent cloud. In-Game asset. 2d. High contrast. No shadows
fluffy transparent cloud. In-Game asset. 2d. High contrast. No shadows
orange transparent cloud. In-Game asset. 2d. High contrast. No shadows
grey transparent cloud. In-Game asset. 2d. High contrast. No shadows
star. In-Game asset. 2d. High contrast. No shadows
icy tower background without platforms, just walls. In-Game asset. 2d. High contrast. No shadows
just a start line without any text. In-Game asset. 2d. High contrast. No shadows
stuart little jumping and raised its arms. In-Game asset. 2d. High contrast. No shadows. facing camera
shout
Sound effect
fall
Sound effect
darara
Music
garavel-1
Sound effect
garavel-2
Sound effect
garavel-3
Sound effect
garavel-4
Sound effect
garavel-5
Sound effect
death-1
Sound effect
death-2
Sound effect
opening-sound
Sound effect
opening-music
Music
game-theme-song-1
Music
game-theme-song-2
Music
game-theme-song-3
Music
game-theme-song-4
Music