User prompt
remove the checkpoint platform logic and ensure that each level consists only 50 platforms not more or less
User prompt
remove the checkpoint platform logic and ensure that each level consists only 50 platforms not more or less
User prompt
Enhance UI to show Floor, Coins, and Score at the same time in a clear, aligned row
User prompt
enhance the UI to show Floor, Coins and Score at the same time
User prompt
even if the theme continues after 50th platform starting from the 51th change the chipPlatform
User prompt
UI should show floor as total platformsPassed+1 but platformsPassed should start from 0 and when it reaches 50 chipPlatform should change
User prompt
If each theme has 50 floors than Floor: in the UI shows a wrong number because while playing I see more than 60 every time I finish the grass theme (when I reach the last platform in the theme)
User prompt
In the grass theme there should be 50 platforms not 70
User prompt
didn't work make it passive not dynamic just 50 platforms in each theme easy as that
User prompt
still the problem persists, there should be 50 platforms in each theme and that's it
User prompt
again there were 60 platforms in the first level, green theme whatever you call it. there should be 50 in each
User prompt
In grass theme there were more than 60 floors, each them should have exactly 50 floors. also Score: in the UI now disappeared make it appear back
User prompt
enhance the UI some text is out of screen and some overlap fix these
User prompt
show floor count in the top UI left to the "Coins:"
User prompt
ensure every level has 50 floors
User prompt
ensure each theme lasts for 50 floors and show the count of floor in the UI
User prompt
add floor and score they completely diasppeared
User prompt
reposition and recolor UI score table to be more mid-screen and prevent overlap, with distinct colors for each text
User prompt
after coins in UI in the top where we show the scores add Floor: and show the number of platforms climbed so far (it should be score-coins)
User prompt
remove the checkpoint platform logic
User prompt
ensure every theme lasts 50 platforms
User prompt
platforms after the checkpoint platforms are still not visible before passing the checkpoint platform, they become only visible after passing it, which shouldn't be the case
User prompt
problem about not being able to see platforms after the checkpoint platform persists
User prompt
as I advance in the game checkpoint platform logic fucks up, fix it and also fix the bug where platforms above the checkpoint platform can only be seen after landing on the checkpoint platform (this shouldn't be the way, they should be flowing like normal platforms do)
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'y')' in or related to this line: 'var topY = platforms[0].y;' Line Number: 1444
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Cloud class: background only, moves with camera, not collidable var Cloud = Container.expand(function () { var self = Container.call(this); // Pick a random cloud asset var cloudAssets = ['cloudBlue', 'cloudBright', 'cloudFluffy', 'cloudGray', 'cloudSoft']; var assetId = cloudAssets[Math.floor(Math.random() * cloudAssets.length)]; var assetInfo = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Scale clouds to a reasonable size for background var targetHeight = 180 + Math.random() * 80; // 180-260px var scale = targetHeight / assetInfo.height; var cloudGfx = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: scale, scaleY: scale, alpha: 0.45 + Math.random() * 0.15 // more transparent clouds }); // Set initial position (x, y) and speed self.x = Math.random() * 2048; self.y = Math.random() * 2048; self.speedY = 0; // Will be set by camera movement // Give some clouds a gentle horizontal drift self.driftX = (Math.random() < 0.5 ? -1 : 1) * (0.2 + Math.random() * 0.5); // Clouds are always behind everything self.setToBack = function () { if (self.parent && self.parent.children.indexOf(self) > 0) { self.parent.setChildIndex(self, 0); } }; // No collision, no input self.update = function () { // Track lastX for border collision detection if (typeof self.lastX === "undefined") self.lastX = self.x; // Horizontal drift self.x += self.driftX; // Prevent clouds from reaching within 1/30 of the screen X from any edge var minCloudX = BORDER_LEFT_X; var maxCloudX = BORDER_RIGHT_X; // Bounce off left transparent border (minCloudX) if (self.lastX > minCloudX && self.x <= minCloudX) { self.x = minCloudX; self.driftX = Math.abs(self.driftX); // move right } // If cloud is ever inside the left transparent border, force it back and reverse if (self.x < minCloudX) { self.x = minCloudX; self.driftX = Math.abs(self.driftX); // move right } // Bounce off right transparent border (maxCloudX) if (self.lastX < maxCloudX && self.x >= maxCloudX) { self.x = maxCloudX; self.driftX = -Math.abs(self.driftX); // move left } // If cloud is ever inside the right transparent border, force it back and reverse if (self.x > maxCloudX) { self.x = maxCloudX; self.driftX = -Math.abs(self.driftX); // move left } // Remove wrap horizontally (clouds never go past borders) // Update lastX for next frame self.lastX = self.x; // Vertical movement is handled by camera diff in game.update }; return self; }); // --- 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: simple collectible poker chip (not draggable, not rotatable) var PokerChip = Container.expand(function () { var self = Container.call(this); // Asset id and color are passed in, default to 'chipCoin1' self.assetId = self.assetId || 'chipCoin1'; var chipAssetInfo = LK.getAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5 }); // Always render poker chips at a consistent visual size (height 80px) var targetHeight = 80; var scale = targetHeight / chipAssetInfo.height; var chip = self.attachAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: scale, scaleY: scale }); // For hit testing self.radius = chipAssetInfo.width * scale / 2; // Show value popup when collected (optional, can be customized) self.value = 1; self.showValuePopup = function () { var txt = new Text2('+1', { size: 60, fill: 0xFFE066, font: "Impact" }); txt.anchor.set(0.5, 0.5); txt.x = self.x; txt.y = self.y - 40; game.addChild(txt); tween(txt, { y: txt.y - 80, alpha: 0 }, { duration: 700, onFinish: function onFinish() { txt.destroy(); } }); }; return self; }); // --- Star class: collectible, animated, optimized for performance --- var Star = Container.expand(function () { var self = Container.call(this); // Use the new collectibleStar asset self.assetId = 'collectibleStar'; self.value = 10; // Stars are worth +10 var assetInfo = LK.getAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5 }); // Always render stars at the same visual size (height 70px) var targetHeight = 70; var scale = targetHeight / assetInfo.height; var starGfx = self.attachAsset(self.assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: scale, scaleY: scale, alpha: 0.92 }); // For collision self.radius = assetInfo.width * scale / 2; // Animate star: slow rotation and pulsing self.update = function () { if (starGfx) { starGfx.rotation += 0.09; var pulse = 0.95 + 0.08 * Math.sin(Date.now() / 180 + self.x); starGfx.scale.x = scale * pulse; starGfx.scale.y = scale * pulse; } }; // Show value popup when collected self.showValuePopup = function () { var txt = new Text2('+10', { size: 60, fill: 0xFFF7B2, font: "Impact" }); txt.anchor.set(0.5, 0.5); txt.x = self.x; txt.y = self.y - 40; game.addChild(txt); 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; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x18181b }); /**** * Game Code ****/ // (Cloud update and spawn logic removed) // 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 --- // Cloud shape assets for customizable cloud looks // Collectible star asset (yellow star shape) var highScore = storage.highScore || 0; var GAME_W = 2048; var GAME_H = 2732; var PLATFORM_W = 400; // Define new left/right border X values at the intersection of opaque and transparent background var BORDER_LEFT_X = Math.floor(GAME_W / 30); var BORDER_RIGHT_X = GAME_W - Math.floor(GAME_W / 30); 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 // --- Cloud background state --- var clouds = []; var NUM_CLOUDS = 8; // Number of clouds to show in background // --- State --- var platforms = []; var coins = []; // All active coins var stars = []; // All active stars var player = null; var vy = 0; var vx = 0; var isJumping = false; var isTouching = false; var touchStartX = 0; // Track all active touches (for multi-touch) var activeTouches = []; 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 50 platforms var idx = Math.floor(platformsPassed / 50); 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 if (typeof LK.setFinalScore === "function") { LK.setFinalScore(platformsPassed); } }); // --- 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; // Only allow up to 50 platforms per level var platNum = platformsPassed + platforms.length; var themeLength = 50; if (platNum >= themeLength) { return null; } 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 and Star placement logic --- // Don't place coins or stars 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); } // 15% chance to spawn a star (but not if a coin is already placed) if (Math.random() < 0.15 && coins.length > 0 && coins[coins.length - 1].x !== plat.x) { var star = new Star(); star.x = plat.x + (Math.random() - 0.5) * (plat.width * 0.4); // randomize a bit star.y = plat.y - PLATFORM_H / 2 - 100; game.addChild(star); stars.push(star); } } 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 stars for (var i = stars.length - 1; i >= 0; --i) { if (stars[i] && typeof stars[i].destroy === "function") { stars[i].destroy(); } } stars = []; // 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; // Use bgTheme asset classes for background var themeIdx = getThemeIndex(platformsPassed); // Map for each theme: [opaqueAsset, transparentAsset] var bgThemeAssets = [['bgThemeGrassOpaque', 'bgThemeGrassTrans'], // 0 Grass ['bgThemeForestOpaque', 'bgThemeForestTrans'], // 1 Forest ['bgThemeMudOpaque', 'bgThemeMudTrans'], // 2 Mud ['bgThemeMagicOpaque', 'bgThemeMagicTrans'], // 3 Magic ['bgThemeCandyOpaque', 'bgThemeCandyTrans'], // 4 Candy ['bgThemeNightOpaque', 'bgThemeNightTrans'], // 5 Night ['bgThemeStoneOpaque', 'bgThemeStoneTrans'], // 6 Stone ['bgThemeIceOpaque', 'bgThemeIceTrans'], // 7 Ice ['bgThemeMetalOpaque', 'bgThemeMetalTrans'], // 8 Metal ['bgThemeNeonOpaque', 'bgThemeNeonTrans'], // 9 Neon ['bgThemeSpecialOpaque', 'bgThemeSpecialTrans'] // 10 Special ]; if (themeIdx < 0) themeIdx = 0; if (themeIdx >= bgThemeAssets.length) themeIdx = bgThemeAssets.length - 1; // Remove previous background asset if any if (game._bgThemeAsset) { if (typeof game._bgThemeAsset.destroy === "function") { game._bgThemeAsset.destroy(); } game._bgThemeAsset = null; } // Add new background asset as the first child (behind everything) var bgOpaqueAssetId = bgThemeAssets[themeIdx][0]; var bgTransAssetId = bgThemeAssets[themeIdx][1]; // Create a container for the background with three segments: left, center, right var bgThemeContainer = new Container(); var bgOpaqueAssetInfo = LK.getAsset(bgOpaqueAssetId, { anchorX: 0, anchorY: 0 }); var bgTransAssetInfo = LK.getAsset(bgTransAssetId, { anchorX: 0, anchorY: 0 }); var bgWidth = GAME_W; var leftW = Math.floor(bgWidth / 30); var rightW = Math.floor(bgWidth / 30); var centerW = bgWidth - leftW - rightW; // Left 1/30 (opaque) var bgLeft = LK.getAsset(bgOpaqueAssetId, { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: leftW / bgOpaqueAssetInfo.width, scaleY: GAME_H / bgOpaqueAssetInfo.height, alpha: 1 }); bgThemeContainer.addChild(bgLeft); // Center (transparent) var bgCenter = LK.getAsset(bgTransAssetId, { anchorX: 0, anchorY: 0, x: leftW, y: 0, scaleX: centerW / bgTransAssetInfo.width, scaleY: GAME_H / bgTransAssetInfo.height, alpha: 0.3 }); bgThemeContainer.addChild(bgCenter); // Right 1/30 (opaque) var bgRight = LK.getAsset(bgOpaqueAssetId, { anchorX: 0, anchorY: 0, x: leftW + centerW, y: 0, scaleX: rightW / bgOpaqueAssetInfo.width, scaleY: GAME_H / bgOpaqueAssetInfo.height, alpha: 1 }); bgThemeContainer.addChild(bgRight); game.addChild(bgThemeContainer); if (game.children && game.children.length > 1) { game.setChildIndex(bgThemeContainer, 0); } game._bgThemeAsset = bgThemeContainer; // Remove old clouds for (var i = clouds.length - 1; i >= 0; --i) { if (clouds[i] && typeof clouds[i].destroy === "function") { clouds[i].destroy(); } } clouds = []; // Add new clouds to background for (var i = 0; i < NUM_CLOUDS; ++i) { var cloud = new Cloud(); // Distribute vertically in the upper 2/3 of the screen cloud.y = Math.random() * (GAME_H * 0.66); cloud.x = Math.random() * GAME_W; game.addChild(cloud); cloud.setToBack && cloud.setToBack(); clouds.push(cloud); } // (Cloud background reset removed) } // --- 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; // Add this touch to activeTouches if (obj && obj.event && typeof obj.event.identifier !== "undefined") { // Remove if already present (shouldn't happen, but for safety) for (var i = 0; i < activeTouches.length; ++i) { if (activeTouches[i].id === obj.event.identifier) { activeTouches.splice(i, 1); break; } } activeTouches.push({ id: obj.event.identifier, x: x, y: y }); } else { // Fallback for mouse or single touch activeTouches = [{ id: 0, x: x, y: y }]; } isTouching = true; touchStartX = x; // Always use the last finger down for direction, even after double jump var lastTouch = activeTouches[activeTouches.length - 1]; if (lastTouch && lastTouch.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; } // No-op: direction change after double jump is handled in move handler }; game.up = function (x, y, obj) { // Remove this touch from activeTouches if (obj && obj.event && typeof obj.event.identifier !== "undefined") { for (var i = 0; i < activeTouches.length; ++i) { if (activeTouches[i].id === obj.event.identifier) { activeTouches.splice(i, 1); break; } } } else { // Fallback for mouse or single touch activeTouches = []; } if (activeTouches.length === 0) { isTouching = false; vx = 0; } else { // Use the new last finger for direction, even after double jump var lastTouch = activeTouches[activeTouches.length - 1]; if (lastTouch && lastTouch.x < GAME_W / 2) { vx = -MOVE_SPEED; } else { vx = MOVE_SPEED; } } }; game.move = function (x, y, obj) { // Update the position of the moving finger in activeTouches if (obj && obj.event && typeof obj.event.identifier !== "undefined") { for (var i = 0; i < activeTouches.length; ++i) { if (activeTouches[i].id === obj.event.identifier) { activeTouches[i].x = x; activeTouches[i].y = y; break; } } } else if (activeTouches.length > 0) { // Fallback for mouse or single touch activeTouches[activeTouches.length - 1].x = x; activeTouches[activeTouches.length - 1].y = y; } // Always use the last finger for direction, even after double jump or wall hit if (activeTouches.length > 0 && !gameOver) { var lastTouch = activeTouches[activeTouches.length - 1]; if (lastTouch && lastTouch.x < GAME_W / 2) { vx = -MOVE_SPEED; } else { vx = MOVE_SPEED; } } else if (!isTouching) { vx = 0; } // No else: vx is always set while touching, even after double jump }; // --- Main update loop --- game.update = function () { if (gameOver) return; // Physics if (typeof player.lastVy === "undefined") player.lastVy = vy; if (typeof player.lastVx === "undefined") player.lastVx = vx; // Smooth velocity interpolation for mobile performance // Use a simple inertia/lerp for vx to avoid abrupt changes var targetVx = vx; if (typeof player.smoothVx === "undefined") player.smoothVx = vx; // Increase lerp factor for even smoother and more responsive movement player.smoothVx += (targetVx - player.smoothVx) * 0.32; // 0.32 is more responsive and smooth vy += GRAVITY; player.x += player.smoothVx; player.y += vy; player.lastVx = player.smoothVx; // Clamp player to new left/right border and tumble on border hit if (player.x < BORDER_LEFT_X + PLAYER_W / 2) { player.x = BORDER_LEFT_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 border, with cooldown if (typeof game.lastShoutTime === "undefined") game.lastShoutTime = 0; var nowWall = Date.now(); if (nowWall - game.lastShoutTime > 500) { game.lastShoutTime = nowWall; } // Double jump when hitting the border, but only if not already double jumped without release if (!game.hasDoubleJumped) { vy = JUMP_VELOCITY * 2; isJumping = true; game.hasDoubleJumped = true; // Emit 10-14 animated stars from player after double jump, with color cycling and rainbow effect var numEmitStars = 10 + Math.floor(Math.random() * 5); // 10-14 // Rainbow arc effect: create a rainbow container behind the player var rainbowArc = new Container(); rainbowArc.x = player.x; rainbowArc.y = player.y - PLAYER_H / 2; rainbowArc._life = 0; rainbowArc._maxLife = 36; rainbowArc._rainbowSegments = []; var rainbowColors = [0xff3b30, 0xff9500, 0xffcc00, 0x4cd964, 0x5ac8fa, 0x007aff, 0x5856d6]; var rainbowRadius = 110; var rainbowWidth = 22; for (var seg = 0; seg < rainbowColors.length; ++seg) { var segAsset = LK.getAsset('collectibleStar', { anchorX: 0.5, anchorY: 0.5, scaleX: rainbowWidth / 90 * (1.1 + seg * 0.08), scaleY: rainbowWidth / 90 * (1.1 + seg * 0.08), tint: rainbowColors[seg], alpha: 0.38 }); // Position in an arc behind the player var arcAngle = Math.PI * (0.5 + 0.7 * (seg / (rainbowColors.length - 1))); segAsset.x = Math.cos(arcAngle) * rainbowRadius; segAsset.y = Math.sin(arcAngle) * rainbowRadius * 0.7; rainbowArc.addChild(segAsset); rainbowArc._rainbowSegments.push(segAsset); } game.addChild(rainbowArc); // Animate rainbow arc fade and scale rainbowArc.update = function () { rainbowArc._life++; var t = rainbowArc._life / rainbowArc._maxLife; rainbowArc.alpha = 0.7 * (1 - t); rainbowArc.scale.x = 1 + 0.25 * t; rainbowArc.scale.y = 1 + 0.25 * t; rainbowArc.x = player.x; rainbowArc.y = player.y - PLAYER_H / 2; if (rainbowArc._life > rainbowArc._maxLife) { if (typeof rainbowArc.destroy === "function") rainbowArc.destroy(); } }; stars.push(rainbowArc); for (var emitIdx = 0; emitIdx < numEmitStars; ++emitIdx) { var emitStar = new Star(); emitStar._emittedAnimation = true; // Mark as animation-only, not collectible emitStar.x = player.x; emitStar.y = player.y - PLAYER_H / 2; // Give each star a random direction and speed var angle = Math.PI * 2 * (emitIdx / numEmitStars) + (Math.random() - 0.5) * 0.5; var speed = 18 + Math.random() * 8; emitStar._vx = Math.cos(angle) * speed; emitStar._vy = Math.sin(angle) * speed - 6; emitStar._life = 0; emitStar._maxLife = 32 + Math.random() * 10; // Color cycling: pick a random start color and cycle through a palette var colorCycle = [0xfff7b2, 0xffe066, 0xffb347, 0xff3b30, 0x4cd964, 0x5ac8fa, 0x007aff, 0x5856d6, 0xffcc00]; var colorIdx = Math.floor(Math.random() * colorCycle.length); // Override update for animation emitStar.update = function (star, colorIdx) { return function () { star.x += star._vx; star.y += star._vy; star._vy += 1.1; // gravity star._vx *= 0.96; // friction star._life++; // Color cycling: change color every 6 frames if (star.children && star.children[0]) { var cycleStep = Math.floor(star._life / 6); var nextColor = colorCycle[(colorIdx + cycleStep) % colorCycle.length]; star.children[0].tint = nextColor; // Fade out star.children[0].alpha = Math.max(0, 0.92 * (1 - star._life / star._maxLife)); } if (star._life > star._maxLife) { if (typeof star.destroy === "function") star.destroy(); var idx = stars.indexOf(star); if (idx !== -1) stars.splice(idx, 1); } }; }(emitStar, colorIdx); game.addChild(emitStar); stars.push(emitStar); } // Play shout sound only if double jump is successful LK.getSound('shout').play(); } } if (player.x > BORDER_RIGHT_X - PLAYER_W / 2) { player.x = BORDER_RIGHT_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 right border, with cooldown if (typeof game.lastShoutTime === "undefined") game.lastShoutTime = 0; var nowWall = Date.now(); if (nowWall - game.lastShoutTime > 500) { game.lastShoutTime = nowWall; } // Double jump when hitting the border, but only if not already double jumped without release if (!game.hasDoubleJumped) { vy = JUMP_VELOCITY * 2; isJumping = true; game.hasDoubleJumped = true; // Emit 10-14 animated stars from player after double jump, with color cycling and rainbow effect var numEmitStars = 10 + Math.floor(Math.random() * 5); // 10-14 // Rainbow arc effect: create a rainbow container behind the player var rainbowArc = new Container(); rainbowArc.x = player.x; rainbowArc.y = player.y - PLAYER_H / 2; rainbowArc._life = 0; rainbowArc._maxLife = 36; rainbowArc._rainbowSegments = []; var rainbowColors = [0xff3b30, 0xff9500, 0xffcc00, 0x4cd964, 0x5ac8fa, 0x007aff, 0x5856d6]; var rainbowRadius = 110; var rainbowWidth = 22; for (var seg = 0; seg < rainbowColors.length; ++seg) { var segAsset = LK.getAsset('collectibleStar', { anchorX: 0.5, anchorY: 0.5, scaleX: rainbowWidth / 90 * (1.1 + seg * 0.08), scaleY: rainbowWidth / 90 * (1.1 + seg * 0.08), tint: rainbowColors[seg], alpha: 0.38 }); // Position in an arc behind the player var arcAngle = Math.PI * (0.5 + 0.7 * (seg / (rainbowColors.length - 1))); segAsset.x = Math.cos(arcAngle) * rainbowRadius; segAsset.y = Math.sin(arcAngle) * rainbowRadius * 0.7; rainbowArc.addChild(segAsset); rainbowArc._rainbowSegments.push(segAsset); } game.addChild(rainbowArc); // Animate rainbow arc fade and scale rainbowArc.update = function () { rainbowArc._life++; var t = rainbowArc._life / rainbowArc._maxLife; rainbowArc.alpha = 0.7 * (1 - t); rainbowArc.scale.x = 1 + 0.25 * t; rainbowArc.scale.y = 1 + 0.25 * t; rainbowArc.x = player.x; rainbowArc.y = player.y - PLAYER_H / 2; if (rainbowArc._life > rainbowArc._maxLife) { if (typeof rainbowArc.destroy === "function") rainbowArc.destroy(); } }; stars.push(rainbowArc); for (var emitIdx = 0; emitIdx < numEmitStars; ++emitIdx) { var emitStar = new Star(); emitStar._emittedAnimation = true; // Mark as animation-only, not collectible emitStar.x = player.x; emitStar.y = player.y - PLAYER_H / 2; // Give each star a random direction and speed var angle = Math.PI * 2 * (emitIdx / numEmitStars) + (Math.random() - 0.5) * 0.5; var speed = 18 + Math.random() * 8; emitStar._vx = Math.cos(angle) * speed; emitStar._vy = Math.sin(angle) * speed - 6; emitStar._life = 0; emitStar._maxLife = 32 + Math.random() * 10; // Color cycling: pick a random start color and cycle through a palette var colorCycle = [0xfff7b2, 0xffe066, 0xffb347, 0xff3b30, 0x4cd964, 0x5ac8fa, 0x007aff, 0x5856d6, 0xffcc00]; var colorIdx = Math.floor(Math.random() * colorCycle.length); // Override update for animation emitStar.update = function (star, colorIdx) { return function () { star.x += star._vx; star.y += star._vy; star._vy += 1.1; // gravity star._vx *= 0.96; // friction star._life++; // Color cycling: change color every 6 frames if (star.children && star.children[0]) { var cycleStep = Math.floor(star._life / 6); var nextColor = colorCycle[(colorIdx + cycleStep) % colorCycle.length]; star.children[0].tint = nextColor; // Fade out star.children[0].alpha = Math.max(0, 0.92 * (1 - star._life / star._maxLife)); } if (star._life > star._maxLife) { if (typeof star.destroy === "function") star.destroy(); var idx = stars.indexOf(star); if (idx !== -1) stars.splice(idx, 1); } }; }(emitStar, colorIdx); game.addChild(emitStar); stars.push(emitStar); } // Play shout sound only if double jump is successful LK.getSound('shout').play(); } } // Platform collision var plat = playerOnPlatform(); // (Removed logic that made checkpoint platforms appear after passing. Now, checkpoint platforms always come down from the top like other platforms.) 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 --- // Use last position to detect passing through coins, not just landing on top if (typeof player.lastX === "undefined") player.lastX = player.x; if (typeof player.lastY === "undefined") player.lastY = player.y; for (var j = coins.length - 1; j >= 0; --j) { var coin = coins[j]; // Simple circle collision, check both current and previous position for pass-through var dx = player.x - coin.x; var dy = player.y - PLAYER_H / 2 - coin.y; var dist = Math.sqrt(dx * dx + dy * dy); var lastDx = player.lastX - coin.x; var lastDy = player.lastY - PLAYER_H / 2 - coin.y; var lastDist = Math.sqrt(lastDx * lastDx + lastDy * lastDy); var collectRadius = (PLAYER_W / 2 + coin.radius) * 0.7; // If player is within collect radius now or passed through it this frame if (dist < collectRadius || lastDist > collectRadius && dist < collectRadius || lastDist < collectRadius && dist < collectRadius) { // 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(); } } // --- Star collection --- // Use last position to detect passing through stars, not just landing on top for (var j = stars.length - 1; j >= 0; --j) { var star = stars[j]; var dx = player.x - star.x; var dy = player.y - PLAYER_H / 2 - star.y; var dist = Math.sqrt(dx * dx + dy * dy); var lastDx = player.lastX - star.x; var lastDy = player.lastY - PLAYER_H / 2 - star.y; var lastDist = Math.sqrt(lastDx * lastDx + lastDy * lastDy); var collectRadius = (PLAYER_W / 2 + star.radius) * 0.7; // Only collect stars that are not "emitted" animation stars (i.e., only collectible platform stars) // We'll mark emitted stars with a flag: star._emittedAnimation === true if ((dist < collectRadius || lastDist > collectRadius && dist < collectRadius || lastDist < collectRadius && dist < collectRadius) && !star._emittedAnimation) { // Collect star star.showValuePopup(); // Add to score (stars give +10) platformsPassed += star.value; coinsCollected += star.value; stars.splice(j, 1); star.destroy(); } } // Update lastX/lastY for next frame player.lastX = player.x; player.lastY = player.y; // 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; } // Move all stars down by diff so they stay visually attached to their platform for (var i = 0; i < stars.length; ++i) { stars[i].y += diff; } // Move all clouds down by diff, and update their position for (var i = clouds.length - 1; i >= 0; --i) { var cloud = clouds[i]; cloud.y += diff * 0.6; // Parallax: move slower than platforms for depth cloud.update && cloud.update(); // Remove cloud if it goes off bottom, add new one at top if (cloud.y > GAME_H + 200) { cloud.destroy(); clouds.splice(i, 1); // Add new cloud at top var newCloud = new Cloud(); newCloud.x = Math.random() * GAME_W; newCloud.y = -100 - Math.random() * 200; game.addChild(newCloud); newCloud.setToBack && newCloud.setToBack(); clouds.push(newCloud); } } 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); } } // Remove any stars that are visually on this platform (within platform width and just above it) for (var j = stars.length - 1; j >= 0; --j) { var star = stars[j]; if (star.x >= platforms[i].x - platforms[i].width / 2 && star.x <= platforms[i].x + platforms[i].width / 2 && Math.abs(star.y - (platforms[i].y - PLATFORM_H / 2 - 100)) < 80) { star.destroy(); stars.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) { // Use bgTheme asset classes for background on level change var bgThemeAssets = [['bgThemeGrassOpaque', 'bgThemeGrassTrans'], // 0 Grass ['bgThemeForestOpaque', 'bgThemeForestTrans'], // 1 Forest ['bgThemeMudOpaque', 'bgThemeMudTrans'], // 2 Mud ['bgThemeMagicOpaque', 'bgThemeMagicTrans'], // 3 Magic ['bgThemeCandyOpaque', 'bgThemeCandyTrans'], // 4 Candy ['bgThemeNightOpaque', 'bgThemeNightTrans'], // 5 Night ['bgThemeStoneOpaque', 'bgThemeStoneTrans'], // 6 Stone ['bgThemeIceOpaque', 'bgThemeIceTrans'], // 7 Ice ['bgThemeMetalOpaque', 'bgThemeMetalTrans'], // 8 Metal ['bgThemeNeonOpaque', 'bgThemeNeonTrans'], // 9 Neon ['bgThemeSpecialOpaque', 'bgThemeSpecialTrans'] // 10 Special ]; var idx = themeIndex; if (idx < 0) idx = 0; if (idx >= bgThemeAssets.length) idx = bgThemeAssets.length - 1; // Remove previous background asset if any if (game._bgThemeAsset) { if (typeof game._bgThemeAsset.destroy === "function") { game._bgThemeAsset.destroy(); } game._bgThemeAsset = null; } // Add new background asset as the first child (behind everything) var bgOpaqueAssetId = bgThemeAssets[idx][0]; var bgTransAssetId = bgThemeAssets[idx][1]; // Create a container for the background with three segments: left, center, right var bgThemeContainer = new Container(); var bgOpaqueAssetInfo = LK.getAsset(bgOpaqueAssetId, { anchorX: 0, anchorY: 0 }); var bgTransAssetInfo = LK.getAsset(bgTransAssetId, { anchorX: 0, anchorY: 0 }); var bgWidth = GAME_W; var leftW = Math.floor(bgWidth / 30); var rightW = Math.floor(bgWidth / 30); var centerW = bgWidth - leftW - rightW; // Left 1/30 (opaque) var bgLeft = LK.getAsset(bgOpaqueAssetId, { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: leftW / bgOpaqueAssetInfo.width, scaleY: GAME_H / bgOpaqueAssetInfo.height, alpha: 1 }); bgThemeContainer.addChild(bgLeft); // Center (transparent) var bgCenter = LK.getAsset(bgTransAssetId, { anchorX: 0, anchorY: 0, x: leftW, y: 0, scaleX: centerW / bgTransAssetInfo.width, scaleY: GAME_H / bgTransAssetInfo.height, alpha: 0.3 }); bgThemeContainer.addChild(bgCenter); // Right 1/30 (opaque) var bgRight = LK.getAsset(bgOpaqueAssetId, { anchorX: 0, anchorY: 0, x: leftW + centerW, y: 0, scaleX: rightW / bgOpaqueAssetInfo.width, scaleY: GAME_H / bgOpaqueAssetInfo.height, alpha: 1 }); bgThemeContainer.addChild(bgRight); game.addChild(bgThemeContainer); if (game.children && game.children.length > 1) { game.setChildIndex(bgThemeContainer, 0); } game._bgThemeAsset = bgThemeContainer; 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; // Update clouds for horizontal drift (even if camera doesn't move) for (var i = 0; i < clouds.length; ++i) { if (clouds[i] && typeof clouds[i].update === "function") { clouds[i].update(); } } // Update stars for animation for (var i = 0; i < stars.length; ++i) { if (stars[i] && typeof stars[i].update === "function") { stars[i].update(); } } // (Cloud update and spawn logic removed) }; // --- Start game --- resetGame();
===================================================================
--- original.js
+++ change.js
@@ -490,8 +490,9 @@
var themeLength = 50;
if (platNum >= themeLength) {
return null;
}
+ 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
@@ -518,15 +519,13 @@
scaleX: scale,
scaleY: scale,
color: platformColor
});
- // Standard platform placement
plat.x = x;
plat.y = y;
plat.width = w;
plat.height = PLATFORM_H;
plat.PLATFORM_H = PLATFORM_H;
- plat.isCheckpoint = false;
game.addChild(plat);
platforms.push(plat);
// --- Coin and Star placement logic ---
// Don't place coins or stars on the first platform (player start)
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