User prompt
Please fix the bug: 'Uncaught ReferenceError: auth is not defined' in or related to this line: 'if (auth.isOwner()) {' Line Number: 632
User prompt
Please fix the bug: 'Uncaught TypeError: LK.isOwner is not a function' in or related to this line: 'if (LK.isOwner()) {' Line Number: 632
User prompt
Only the owner can click on it
User prompt
Now add a button on the top of the screen next to the level text and it'll say owner only
User prompt
The limit for now should be 10 levels
User prompt
When you finish level 1 you should get teleported to level 2
User prompt
When you finish the level U SHOULD BE TELEPORTED TO THE NEXT LEVEL
User prompt
I DONT Want the game to save progress
User prompt
Remove the settings
User prompt
Please fix the bug: 'Uncaught ReferenceError: showSettings is not defined' in or related to this line: 'showSettings();' Line Number: 754
User prompt
Move the settings button to the right corner in the top
User prompt
If you leave the game and come back it won't save your progress ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
While a person is playing the game there should always be a settings button
User prompt
When you die your game gets paused and it can't stop fix this problem
User prompt
Make little green blocks on moving platforms that will help you by making you stick to the platforms that are moving ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
While on a moving platform your character should stick to it if you don't move it
User prompt
Please fix the bug: 'TypeError: easing is not a function' in or related to this line: 'sprite.scale.set(1, scale);' Line Number: 605
User prompt
Please fix the bug: 'TypeError: easing is not a function' in or related to this line: 'sprite.alpha = 0.8;' Line Number: 85
User prompt
When you're on a moving platform you should move along with it
User prompt
Please fix the bug: 'TypeError: easing is not a function' in or related to this line: 'sprite.scale.y = scale;' Line Number: 597
User prompt
Please fix the bug: 'TypeError: easing is not a function' in or related to this line: 'sprite.scale.y = scale;' Line Number: 597 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
After clicking play again you'll be teleported to level 2
User prompt
Whenever someone plays the game they should start at level 1 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Please fix the bug: 'TypeError: easing is not a function' in or related to this line: 'sprite.scale.set(1, scale);' Line Number: 593
User prompt
Then after you click play again you'll be teleported to level 2
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { currentLevel: 1, coins: 0, highScore: 0 }); /**** * Classes ****/ var Coin = Container.expand(function () { var self = Container.call(this); // Coin sprite var sprite = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); // Animate coin self.update = function () { sprite.rotation += 0.05; }; self.collect = function () { LK.getSound('coin').play(); self.destroy(); }; return self; }); var ControlButton = Container.expand(function (type) { var self = Container.call(this); // Control button properties self.type = type; // 'jump', 'left', or 'right' self.isPressed = false; // Button sprite var sprite = self.attachAsset(type + 'Button', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); // Button icon (text inside the button) var icon; if (type === 'jump') { icon = new Text2('↑', { size: 100, fill: 0xFFFFFF }); } else if (type === 'left') { icon = new Text2('←', { size: 100, fill: 0xFFFFFF }); } else if (type === 'right') { icon = new Text2('→', { size: 100, fill: 0xFFFFFF }); } if (icon) { icon.anchor.set(0.5, 0.5); self.addChild(icon); } self.down = function (x, y, obj) { self.isPressed = true; sprite.alpha = 0.8; }; self.up = function (x, y, obj) { self.isPressed = false; sprite.alpha = 0.5; }; return self; }); var Enemy = Container.expand(function (patrolDistance) { var self = Container.call(this); // Default parameter patrolDistance = patrolDistance || 200; // Enemy properties self.speed = 3; self.direction = 1; self.patrolDistance = patrolDistance; self.startX = 0; // Enemy sprite var sprite = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.x += self.speed * self.direction; // Check if we need to change direction if (Math.abs(self.x - self.startX) > self.patrolDistance) { self.direction *= -1; sprite.scaleX = -sprite.scaleX; // Flip the enemy when changing direction } }; self.setStartPosition = function (x, y) { self.x = x; self.y = y; self.startX = x; }; return self; }); var Goal = Container.expand(function () { var self = Container.call(this); // Goal sprite var sprite = self.attachAsset('goal', { anchorX: 0.5, anchorY: 0.5 }); self.reach = function () { LK.getSound('win').play(); LK.effects.flashObject(self, 0x00ff00, 1000); // Set storage to level 2 storage.currentLevel = 2; // Create level complete message var levelUpText = new Text2('Level ' + level + ' Complete!', { size: 80, fill: 0xFFFFFF }); levelUpText.anchor.set(0.5, 0.5); levelUpText.x = 2048 / 2; levelUpText.y = 2732 / 2 - 100; game.addChild(levelUpText); // Create "Next Level" message var nextLevelText = new Text2('Next Level Starting Soon...', { size: 50, fill: 0xFFFFFF }); nextLevelText.anchor.set(0.5, 0.5); nextLevelText.x = 2048 / 2; nextLevelText.y = 2732 / 2; game.addChild(nextLevelText); // Animate the text tween(levelUpText, { y: levelUpText.y - 50, alpha: 0 }, { duration: 1500, delay: 1000 }); tween(nextLevelText, { y: nextLevelText.y + 50, alpha: 0 }, { duration: 1500, delay: 1000 }); // Show win screen with delay LK.setTimeout(function () { game.removeChild(levelUpText); game.removeChild(nextLevelText); LK.showYouWin(); }, 2500); }; return self; }); var Platform = Container.expand(function (width, height, isMoving) { var self = Container.call(this); // Default parameters width = width || 200; height = height || 30; isMoving = isMoving || false; // Platform properties self.isMoving = isMoving; self.moveSpeed = 2; self.direction = 1; self.moveDistance = 300; self.startX = 0; // Platform sprite var sprite = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5, width: width, height: height }); self.update = function () { if (self.isMoving) { self.x += self.moveSpeed * self.direction; // Check if we need to change direction if (Math.abs(self.x - self.startX) > self.moveDistance) { self.direction *= -1; } } }; self.setStartPosition = function (x, y) { self.x = x; self.y = y; self.startX = x; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); // Player properties self.vx = 0; self.vy = 0; self.speed = 8; self.jumpForce = -25; // Increased jump force from -20 to -25 self.gravity = 0.8; // Reduced gravity from 1 to 0.8 self.isJumping = false; self.isDead = false; self.facingRight = true; self.canJump = false; // Create a simple block character with eyes and smiley face // Main block (body) var block = self.addChild(LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80, tint: 0x4287f5 // Blue block color })); // Eyes (small white rectangles) var leftEye = self.addChild(LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, width: 10, height: 10, x: -20, y: -15, tint: 0xFFFFFF // White eyes })); var rightEye = self.addChild(LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, width: 10, height: 10, x: 20, y: -15, tint: 0xFFFFFF // White eyes })); // Smile (simple arc using three rectangles) var leftSmile = self.addChild(LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, width: 15, height: 5, x: -15, y: 15, tint: 0x000000 // Black smile })); var rightSmile = self.addChild(LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, width: 15, height: 5, x: 15, y: 15, tint: 0x000000 // Black smile })); var bottomSmile = self.addChild(LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, width: 30, height: 5, x: 0, y: 20, tint: 0x000000 // Black smile })); // Store reference to all parts for animation self.parts = { block: block, leftEye: leftEye, rightEye: rightEye, leftSmile: leftSmile, rightSmile: rightSmile, bottomSmile: bottomSmile }; // Create a hitbox for collision detection (invisible) var sprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, alpha: 0 // Make invisible, just for collision }); self.update = function () { if (self.isDead) { return; } // Store previous position to track changes self.lastX = self.x; self.lastY = self.y; // Apply gravity with terminal velocity to prevent falling too fast self.vy = Math.min(self.vy + self.gravity, 25); // Apply air resistance when jumping if (self.isJumping) { self.vx *= 0.98; // Less friction in air } else { self.vx *= 0.85; // More friction on ground } // Add coyote time for more forgiving jumps if (self.canJump) { self.lastOnGroundTime = LK.ticks; } // Allow jump within 7 frames of leaving a platform (coyote time) if (self.lastOnGroundTime && LK.ticks - self.lastOnGroundTime < 7) { self.canJump = true; } // Update position based on velocity with subpixel movement for smoother motion self.x += self.vx; self.y += self.vy; // Reset jump state if on ground if (self.canJump) { self.isJumping = false; } else { self.isJumping = true; } // Only update animations every other frame to reduce CPU load if (LK.ticks % 2 === 0) { // Keep player oriented based on direction if (self.vx > 0.5 && !self.facingRight) { self.facingRight = true; sprite.scaleX = 1; // Flip all parts to face right Object.values(self.parts).forEach(function (part) { part.scaleX = Math.abs(part.scaleX); }); self.parts.leftEye.x = -20; self.parts.rightEye.x = 20; self.parts.leftSmile.x = -15; self.parts.rightSmile.x = 15; self.parts.bottomSmile.x = 0; } else if (self.vx < -0.5 && self.facingRight) { self.facingRight = false; sprite.scaleX = -1; // Flip all parts to face left Object.values(self.parts).forEach(function (part) { part.scaleX = -Math.abs(part.scaleX); }); self.parts.leftEye.x = 20; self.parts.rightEye.x = -20; self.parts.leftSmile.x = 15; self.parts.rightSmile.x = -15; self.parts.bottomSmile.x = 0; } // Dynamic block rotation based on velocity if (Math.abs(self.vx) > 0.5) { // Proportional tilt based on speed var tiltAmount = Math.min(Math.abs(self.vx) * 0.01, 0.15); self.parts.block.rotation = self.vx > 0 ? tiltAmount : -tiltAmount; } else { // Reset when standing still if (self.parts.block.rotation !== 0) { tween(self.parts.block, { rotation: 0 }, { duration: 150, easing: tween.easeOutQuad }); } } // Enhanced jumping animation if (self.isJumping) { // Dynamic squish based on vertical velocity var jumpSquish = Math.min(Math.abs(self.vy) * 0.02, 0.3); // Rising vs falling animation if (self.vy < 0) { // Rising - stretch vertically self.parts.block.scaleY = 1 + jumpSquish; self.parts.block.scaleX = self.facingRight ? 1 - jumpSquish * 0.5 : -(1 - jumpSquish * 0.5); } else { // Falling - compress horizontally self.parts.block.scaleY = 1 - jumpSquish * 0.7; self.parts.block.scaleX = self.facingRight ? 1 + jumpSquish : -(1 + jumpSquish); } } else { // Subtle bounce when on ground and moving if (Math.abs(self.vx) > 1) { var bounceAmount = Math.sin(LK.ticks * 0.2) * 0.05; self.parts.block.scaleY = 1 + bounceAmount; self.parts.block.scaleX = self.facingRight ? 1 - bounceAmount * 0.5 : -(1 - bounceAmount * 0.5); } else { // Reset scale when not jumping or moving self.parts.block.scaleY = 1; self.parts.block.scaleX = self.facingRight ? 1 : -1; } } // Enhanced blinking system if (!self.nextBlinkTime) { self.nextBlinkTime = LK.ticks + Math.floor(Math.random() * 180) + 180; // 3-6 seconds at 60fps } if (LK.ticks >= self.nextBlinkTime) { // Hide eyes for blinking with smooth animation tween(self.parts.leftEye, { scaleY: 0.1 }, { duration: 100 }); tween(self.parts.rightEye, { scaleY: 0.1 }, { duration: 100 }); // Show eyes after blink with smooth animation if (LK.ticks >= self.nextBlinkTime + 5) { tween(self.parts.leftEye, { scaleY: 1 }, { duration: 100 }); tween(self.parts.rightEye, { scaleY: 1 }, { duration: 100 }); // Set next blink time self.nextBlinkTime = LK.ticks + Math.floor(Math.random() * 180) + 180; } } } }; self.jump = function () { if (self.canJump && !self.isDead) { self.vy = self.jumpForce; self.isJumping = true; LK.getSound('jump').play(); // Add jump animation with tween tween(self.parts.block, { scaleY: 0.7, scaleX: self.facingRight ? 1.3 : -1.3 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { // Return to normal after the jump animation tween(self.parts.block, { scaleY: 1, scaleX: self.facingRight ? 1 : -1 }, { duration: 300, easing: tween.easeOutQuad }); } }); // Make the face look excited during jump tween(self.parts.leftEye, { y: -20 }, { duration: 300, easing: tween.easeOutQuad }); tween(self.parts.rightEye, { y: -20 }, { duration: 300, easing: tween.easeOutQuad }); tween(self.parts.bottomSmile, { y: 15 }, { duration: 300, easing: tween.easeOutQuad }); // Reset face after jump animation LK.setTimeout(function () { if (!self.isDead) { tween(self.parts.leftEye, { y: -15 }, { duration: 300, easing: tween.easeInOutQuad }); tween(self.parts.rightEye, { y: -15 }, { duration: 300, easing: tween.easeInOutQuad }); tween(self.parts.bottomSmile, { y: 20 }, { duration: 300, easing: tween.easeInOutQuad }); } }, 350); self.canJump = false; } }; self.moveLeft = function () { if (!self.isDead) { // Smooth acceleration to the left self.vx = Math.max(self.vx - 1.5, -self.speed); // Add slight lean effect when moving left if (self.facingRight) { self.facingRight = false; tween(self.parts.block, { rotation: -0.1 }, { duration: 200, easing: tween.easeOutQuad }); } } }; self.moveRight = function () { if (!self.isDead) { // Smooth acceleration to the right self.vx = Math.min(self.vx + 1.5, self.speed); // Add slight lean effect when moving right if (!self.facingRight) { self.facingRight = true; tween(self.parts.block, { rotation: 0.1 }, { duration: 200, easing: tween.easeOutQuad }); } } }; self.stop = function () { if (!self.isDead) { // Smooth deceleration to stop if (Math.abs(self.vx) < 0.5) { self.vx = 0; } else { self.vx *= 0.8; // Gradually slow down } // Return to upright position when stopping tween(self.parts.block, { rotation: 0 }, { duration: 200, easing: tween.easeOutQuad }); } }; self.die = function () { if (!self.isDead) { self.isDead = true; LK.getSound('hit').play(); LK.effects.flashObject(self, 0xff0000, 500); // Block death animation - scatter the block parts Object.values(self.parts).forEach(function (part) { // Use tween to create a scatter effect tween(part, { y: part.y + (Math.random() * 300 - 150), x: part.x + (Math.random() * 300 - 150), rotation: Math.floor(Math.random() * 4) * (Math.PI / 2), alpha: 0 }, { duration: 800, easing: 'easeInQuad' }); }); LK.setTimeout(function () { LK.showGameOver(); }, 1000); } }; return self; }); var Spike = Container.expand(function () { var self = Container.call(this); // Spike sprite var sprite = self.attachAsset('spike', { anchorX: 0.5, anchorY: 0.5 }); // Make spike pointy with rotation sprite.rotation = Math.PI; // Animation for spike self.update = function () { // Subtle pulsing animation if (!self.pulseDirection) { self.pulseDirection = 1; self.pulseValue = 0; } self.pulseValue += 0.02 * self.pulseDirection; if (self.pulseValue > 1 || self.pulseValue < 0) { self.pulseDirection *= -1; } var scale = 0.95 + self.pulseValue * 0.1; sprite.scale.set(1, scale); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB // Sky blue }); /**** * Game Code ****/ // Game variables var player; var platforms = []; var coins = []; var enemies = []; var goal; var jumpButton; var leftButton; var rightButton; var cameraPosX = 0; var cameraPosY = 0; var worldContainer; var uiContainer; var levelWidth = 5000; // The total width of the level var groundY = 2732 - 100; // Y position of the ground var score = 0; var level = 1; // Initialize UI var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); LK.gui.topRight.addChild(scoreTxt); scoreTxt.x = -250; // Offset from topRight anchor var levelTxt = new Text2('Level: ' + level, { size: 60, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); LK.gui.top.addChild(levelTxt); // Initialize world container to allow camera movement worldContainer = new Container(); game.addChild(worldContainer); // Create player player = new Player(); player.x = 200; player.y = groundY - 200; worldContainer.addChild(player); // Create control buttons jumpButton = new ControlButton('jump'); jumpButton.x = 2048 - 250; jumpButton.y = 2732 - 250; jumpButton.scale.set(1.5, 1.5); // Make jump button 50% larger game.addChild(jumpButton); leftButton = new ControlButton('left'); leftButton.x = 200; leftButton.y = 2732 - 250; leftButton.scale.set(1.5, 1.5); // Make left button 50% larger game.addChild(leftButton); rightButton = new ControlButton('right'); rightButton.x = 500; rightButton.y = 2732 - 250; rightButton.scale.set(1.5, 1.5); // Make right button 50% larger game.addChild(rightButton); // Create level function function createLevel(level) { // Clear previous level objects for (var i = platforms.length - 1; i >= 0; i--) { worldContainer.removeChild(platforms[i]); platforms.splice(i, 1); } for (var i = coins.length - 1; i >= 0; i--) { worldContainer.removeChild(coins[i]); coins.splice(i, 1); } for (var i = enemies.length - 1; i >= 0; i--) { worldContainer.removeChild(enemies[i]); enemies.splice(i, 1); } if (goal) { worldContainer.removeChild(goal); goal = null; } // Create ground - ensure complete coverage for (var i = 0; i < levelWidth; i += 400) { var ground = new Platform(400, 50, false); ground.setStartPosition(i + 200, groundY); // Apply brown color to ground platforms var groundSprite = ground.children[0]; tween(groundSprite, { tint: 0x8b4513 }, { duration: 0 }); // Make ground brown worldContainer.addChild(ground); platforms.push(ground); } // Add additional terrain features // Add dirt layer below ground for (var i = 0; i < levelWidth; i += 400) { var dirt = new Platform(400, 100, false); dirt.setStartPosition(i + 200, groundY + 75); var dirtSprite = dirt.children[0]; tween(dirtSprite, { tint: 0x5d3a1a // Darker brown for dirt }, { duration: 0 }); worldContainer.addChild(dirt); platforms.push(dirt); } // Add rocky foundation at the bottom for (var i = 0; i < levelWidth; i += 400) { var rock = new Platform(400, 200, false); rock.setStartPosition(i + 200, groundY + 225); var rockSprite = rock.children[0]; tween(rockSprite, { tint: 0x444444 // Dark gray for rocks }, { duration: 0 }); worldContainer.addChild(rock); platforms.push(rock); } // Update level width based on level if (level === 2) { levelWidth = 6000; // Extend level width for level 2 } else { levelWidth = 5000; // Default level width } // Level-specific configurations switch (level) { case 1: // Level 1: Basic platforms and coins createPlatform(600, groundY - 200, 200, 30, false); createPlatform(900, groundY - 350, 200, 30, false); createPlatform(1300, groundY - 500, 200, 30, false); createPlatform(1700, groundY - 350, 200, 30, false); createPlatform(2100, groundY - 200, 200, 30, false); createCoin(600, groundY - 300); createCoin(900, groundY - 450); createCoin(1300, groundY - 600); createCoin(1700, groundY - 450); createCoin(2100, groundY - 300); // Place goal at the end of level 1 createGoal(2400, groundY - 100); break; case 2: // Level 2: Moving platforms, enemies and spikes // Starting platforms createPlatform(500, groundY - 200, 200, 30, false); createPlatform(800, groundY - 300, 200, 30, true); // First section - moving platform challenge createPlatform(1200, groundY - 400, 150, 30, true); createPlatform(1600, groundY - 350, 150, 30, true); createPlatform(2000, groundY - 400, 150, 30, true); // Middle section - spike hazards createPlatform(2400, groundY - 300, 300, 30, false); createSpike(2350, groundY - 350); createSpike(2450, groundY - 350); createSpike(2550, groundY - 350); // Enemy section with platforms createPlatform(2800, groundY - 200, 200, 30, false); createPlatform(3200, groundY - 350, 200, 30, false); createPlatform(3600, groundY - 500, 200, 30, true); createPlatform(4000, groundY - 350, 200, 30, false); // Final challenge section createPlatform(4400, groundY - 300, 150, 30, true); createPlatform(4700, groundY - 400, 150, 30, true); createPlatform(5000, groundY - 300, 200, 30, false); // Coins strategically placed createCoin(500, groundY - 300); createCoin(800, groundY - 400); createCoin(1200, groundY - 500); createCoin(1600, groundY - 450); createCoin(2000, groundY - 500); createCoin(2400, groundY - 400); createCoin(3200, groundY - 450); createCoin(3600, groundY - 600); createCoin(4000, groundY - 450); createCoin(4700, groundY - 500); createCoin(5000, groundY - 400); // Enemies patrolling createEnemy(1000, groundY - 50, 300); createEnemy(2000, groundY - 50, 400); createEnemy(3000, groundY - 50, 500); createEnemy(4000, groundY - 50, 600); // Additional spikes on the ground createSpike(1500, groundY - 50); createSpike(2500, groundY - 50); createSpike(3500, groundY - 50); createSpike(4500, groundY - 50); // Place goal at the end of level 2 createGoal(5200, groundY - 100); break; case 3: // Level 3: Complex platforming with more enemies createPlatform(500, groundY - 200, 200, 30, false); createPlatform(800, groundY - 350, 200, 30, true); createPlatform(1200, groundY - 500, 200, 30, false); createPlatform(1600, groundY - 650, 200, 30, true); createPlatform(2000, groundY - 500, 200, 30, false); createPlatform(2400, groundY - 350, 200, 30, true); createPlatform(2800, groundY - 200, 200, 30, false); createPlatform(3200, groundY - 350, 200, 30, true); createCoin(500, groundY - 300); createCoin(800, groundY - 450); createCoin(1200, groundY - 600); createCoin(1600, groundY - 750); createCoin(2000, groundY - 600); createCoin(2400, groundY - 450); createCoin(2800, groundY - 300); createCoin(3200, groundY - 450); createEnemy(700, groundY - 50, 200); createEnemy(1400, groundY - 50, 300); createEnemy(2100, groundY - 50, 400); createEnemy(2800, groundY - 50, 500); // Place goal at the end of level 3 createGoal(3600, groundY - 100); break; default: // If level > 3, generate a procedural level with increasing difficulty var platformCount = level * 5; var enemyCount = level * 2; var coinCount = level * 5; // Create platforms with varying heights and spacing for (var i = 0; i < platformCount; i++) { var x = 500 + i * 500; var y = groundY - (Math.random() * 500 + 100); var isMoving = Math.random() > 0.5; createPlatform(x, y, 200, 30, isMoving); // Add a coin above each platform createCoin(x, y - 100); } // Add enemies for (var i = 0; i < enemyCount; i++) { var x = 800 + i * 800; var patrolDistance = 200 + Math.random() * 300; createEnemy(x, groundY - 50, patrolDistance); } // Place goal at the end createGoal(500 + platformCount * 500 + 200, groundY - 100); break; } // Reset player position player.x = 200; player.y = groundY - 200; player.vx = 0; player.vy = 0; player.isDead = false; // Reset camera cameraPosX = 0; worldContainer.x = 0; } // Helper functions for creating level elements function createPlatform(x, y, width, height, isMoving) { var platform = new Platform(width, height, isMoving); platform.setStartPosition(x, y); worldContainer.addChild(platform); platforms.push(platform); return platform; } // Helper function to create spikes function createSpike(x, y) { var spike = new Spike(); spike.x = x; spike.y = y; worldContainer.addChild(spike); enemies.push(spike); // Add to enemies array for collision detection return spike; } function createCoin(x, y) { var coin = new Coin(); coin.x = x; coin.y = y; worldContainer.addChild(coin); coins.push(coin); return coin; } function createEnemy(x, y, patrolDistance) { var enemy = new Enemy(patrolDistance); enemy.setStartPosition(x, y); worldContainer.addChild(enemy); enemies.push(enemy); return enemy; } function createGoal(x, y) { goal = new Goal(); goal.x = x; goal.y = y; worldContainer.addChild(goal); return goal; } // Check collisions between player and platforms function checkPlatformCollisions() { player.canJump = false; // Pre-calculate player dimensions once var playerWidth = player.children[0].width; var playerHeight = player.children[0].height; var playerLeft = player.x - playerWidth / 2; var playerRight = player.x + playerWidth / 2; var playerTop = player.y - playerHeight / 2; var playerBottom = player.y + playerHeight / 2; // Only check platforms that are visible in the camera view (+ some margin) var cameraLeftX = cameraPosX - 200; var cameraRightX = cameraPosX + 2048 + 200; for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; // Quick check if platform is in camera view if (platform.x + 200 < cameraLeftX || platform.x - 200 > cameraRightX) { continue; // Skip platforms outside camera view } // Get platform dimensions (adjust based on asset size) var platformWidth = platform.children[0].width; var platformHeight = platform.children[0].height; var platformLeft = platform.x - platformWidth / 2; var platformRight = platform.x + platformWidth / 2; var platformTop = platform.y - platformHeight / 2; var platformBottom = platform.y + platformHeight / 2; // Check for collision if (playerRight > platformLeft && playerLeft < platformRight && playerBottom > platformTop && playerTop < platformBottom) { // Calculate overlap on each side var overlapLeft = playerRight - platformLeft; var overlapRight = platformRight - playerLeft; var overlapTop = playerBottom - platformTop; var overlapBottom = platformBottom - playerTop; // Find the smallest overlap var minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom); // Resolve collision based on the smallest overlap if (minOverlap === overlapTop && player.vy >= 0) { // Landing on top of platform // Use a much larger fixed offset for legs to prevent glitching through platform var legOffset = 50; // Significantly increased offset value to ensure proper positioning player.y = platformTop - playerHeight / 2 - legOffset; player.vy = 0; player.canJump = true; // Apply slight downward force to keep player grounded player.lastGroundedY = player.y; // If platform is moving, adjust player x position if (platform.isMoving) { player.x += platform.moveSpeed * platform.direction; } } else if (minOverlap === overlapBottom && player.vy <= 0) { // Hitting bottom of platform player.y = platformBottom + playerHeight / 2; player.vy = 0; } else if (minOverlap === overlapLeft && player.vx > 0) { // Hitting left side of platform player.x = platformLeft - playerWidth / 2; player.vx = 0; } else if (minOverlap === overlapRight && player.vx < 0) { // Hitting right side of platform player.x = platformRight + playerWidth / 2; player.vx = 0; } } } } // Check collisions with coins function checkCoinCollisions() { // Only check coins that are visible in the camera view (+ some margin) var cameraLeftX = cameraPosX - 100; var cameraRightX = cameraPosX + 2048 + 100; for (var i = coins.length - 1; i >= 0; i--) { var coin = coins[i]; // Skip coins outside camera view if (coin.x < cameraLeftX || coin.x > cameraRightX) { continue; } if (player.intersects(coin)) { // Collect coin score += 10; scoreTxt.setText('Score: ' + score); storage.coins = (storage.coins || 0) + 1; coin.collect(); coins.splice(i, 1); } } } // Check collisions with enemies function checkEnemyCollisions() { // Only check enemies that are visible in the camera view (+ some margin) var cameraLeftX = cameraPosX - 100; var cameraRightX = cameraPosX + 2048 + 100; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Skip enemies outside camera view if (enemy.x < cameraLeftX || enemy.x > cameraRightX) { continue; } if (player.intersects(enemy) && !player.isDead) { // Check if player is landing on enemy (from above) var playerBottom = player.y + player.children[0].height / 2; var enemyTop = enemy.y - enemy.children[0].height / 2; if (player.vy > 0 && playerBottom - enemyTop < 20) { // Bounce off enemy player.vy = player.jumpForce * 0.7; // Remove enemy enemy.destroy(); enemies.splice(i, 1); // Add points score += 50; scoreTxt.setText('Score: ' + score); } else { // Player dies player.die(); } break; } } } // Check if player reached the goal function checkGoalCollision() { if (goal && player.intersects(goal)) { goal.reach(); } } // Update camera to follow player function updateCamera() { // Only follow horizontally var targetX = Math.max(0, Math.min(levelWidth - 2048, player.x - 2048 / 2)); cameraPosX += (targetX - cameraPosX) * 0.1; // Smooth camera movement worldContainer.x = -cameraPosX; } // Check if player fell off the level function checkFallOffLevel() { if (player.y > 2732 + 200 && !player.isDead) { player.die(); } } // Get the current level from storage level = storage.currentLevel || 1; levelTxt.setText('Level: ' + level); // Initialize the level createLevel(level); // Play background music LK.playMusic('gameMusic'); // Main game loop game.update = function () { // Process player controls - only move when buttons are explicitly pressed if (jumpButton.isPressed) { player.jump(); } if (leftButton.isPressed) { player.moveLeft(); } else if (rightButton.isPressed) { player.moveRight(); } else { player.stop(); } // Update player player.update(); // Update camera first to determine what's visible updateCamera(); // Only update objects that are visible in the camera view (+ some margin) var cameraLeftX = cameraPosX - 200; var cameraRightX = cameraPosX + 2048 + 200; // Update platforms for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; // Only update platforms near the camera view if (platform.x + 200 >= cameraLeftX && platform.x - 200 <= cameraRightX) { platform.update(); } } // Update coins for (var i = 0; i < coins.length; i++) { var coin = coins[i]; // Only update coins near the camera view if (coin.x >= cameraLeftX && coin.x <= cameraRightX) { coin.update(); } } // Update enemies for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Only update enemies near the camera view if (enemy.x + 200 >= cameraLeftX && enemy.x - 200 <= cameraRightX) { enemy.update(); } } // Check collisions checkPlatformCollisions(); checkCoinCollisions(); checkEnemyCollisions(); checkGoalCollision(); checkFallOffLevel(); }; // Game-wide touch events game.down = function (x, y, obj) { // Only process direct button presses // No automatic button activation when touching the game area // Store the touch ID only if touching a control button if (obj.target && (obj.target === jumpButton || obj.target === leftButton || obj.target === rightButton)) { game.activeTouchId = obj.event ? obj.event.pointerId : null; } }; game.move = function (x, y, obj) { // No additional move handling needed at game level }; game.up = function (x, y, obj) { // Only process the button that was actually released // Don't reset all buttons on any touch release if (obj.target === leftButton) { leftButton.up(x, y, obj); } else if (obj.target === rightButton) { rightButton.up(x, y, obj); } else if (obj.target === jumpButton) { jumpButton.up(x, y, obj); } // Reset the touch ID only if this is the touch we're tracking if (game.activeTouchId === (obj.event ? obj.event.pointerId : null)) { game.activeTouchId = null; } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
coins: 0,
highScore: 0
});
/****
* Classes
****/
var Coin = Container.expand(function () {
var self = Container.call(this);
// Coin sprite
var sprite = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Animate coin
self.update = function () {
sprite.rotation += 0.05;
};
self.collect = function () {
LK.getSound('coin').play();
self.destroy();
};
return self;
});
var ControlButton = Container.expand(function (type) {
var self = Container.call(this);
// Control button properties
self.type = type; // 'jump', 'left', or 'right'
self.isPressed = false;
// Button sprite
var sprite = self.attachAsset(type + 'Button', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
// Button icon (text inside the button)
var icon;
if (type === 'jump') {
icon = new Text2('↑', {
size: 100,
fill: 0xFFFFFF
});
} else if (type === 'left') {
icon = new Text2('←', {
size: 100,
fill: 0xFFFFFF
});
} else if (type === 'right') {
icon = new Text2('→', {
size: 100,
fill: 0xFFFFFF
});
}
if (icon) {
icon.anchor.set(0.5, 0.5);
self.addChild(icon);
}
self.down = function (x, y, obj) {
self.isPressed = true;
sprite.alpha = 0.8;
};
self.up = function (x, y, obj) {
self.isPressed = false;
sprite.alpha = 0.5;
};
return self;
});
var Enemy = Container.expand(function (patrolDistance) {
var self = Container.call(this);
// Default parameter
patrolDistance = patrolDistance || 200;
// Enemy properties
self.speed = 3;
self.direction = 1;
self.patrolDistance = patrolDistance;
self.startX = 0;
// Enemy sprite
var sprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x += self.speed * self.direction;
// Check if we need to change direction
if (Math.abs(self.x - self.startX) > self.patrolDistance) {
self.direction *= -1;
sprite.scaleX = -sprite.scaleX; // Flip the enemy when changing direction
}
};
self.setStartPosition = function (x, y) {
self.x = x;
self.y = y;
self.startX = x;
};
return self;
});
var Goal = Container.expand(function () {
var self = Container.call(this);
// Goal sprite
var sprite = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5
});
self.reach = function () {
LK.getSound('win').play();
LK.effects.flashObject(self, 0x00ff00, 1000);
// Set storage to level 2
storage.currentLevel = 2;
// Create level complete message
var levelUpText = new Text2('Level ' + level + ' Complete!', {
size: 80,
fill: 0xFFFFFF
});
levelUpText.anchor.set(0.5, 0.5);
levelUpText.x = 2048 / 2;
levelUpText.y = 2732 / 2 - 100;
game.addChild(levelUpText);
// Create "Next Level" message
var nextLevelText = new Text2('Next Level Starting Soon...', {
size: 50,
fill: 0xFFFFFF
});
nextLevelText.anchor.set(0.5, 0.5);
nextLevelText.x = 2048 / 2;
nextLevelText.y = 2732 / 2;
game.addChild(nextLevelText);
// Animate the text
tween(levelUpText, {
y: levelUpText.y - 50,
alpha: 0
}, {
duration: 1500,
delay: 1000
});
tween(nextLevelText, {
y: nextLevelText.y + 50,
alpha: 0
}, {
duration: 1500,
delay: 1000
});
// Show win screen with delay
LK.setTimeout(function () {
game.removeChild(levelUpText);
game.removeChild(nextLevelText);
LK.showYouWin();
}, 2500);
};
return self;
});
var Platform = Container.expand(function (width, height, isMoving) {
var self = Container.call(this);
// Default parameters
width = width || 200;
height = height || 30;
isMoving = isMoving || false;
// Platform properties
self.isMoving = isMoving;
self.moveSpeed = 2;
self.direction = 1;
self.moveDistance = 300;
self.startX = 0;
// Platform sprite
var sprite = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5,
width: width,
height: height
});
self.update = function () {
if (self.isMoving) {
self.x += self.moveSpeed * self.direction;
// Check if we need to change direction
if (Math.abs(self.x - self.startX) > self.moveDistance) {
self.direction *= -1;
}
}
};
self.setStartPosition = function (x, y) {
self.x = x;
self.y = y;
self.startX = x;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Player properties
self.vx = 0;
self.vy = 0;
self.speed = 8;
self.jumpForce = -25; // Increased jump force from -20 to -25
self.gravity = 0.8; // Reduced gravity from 1 to 0.8
self.isJumping = false;
self.isDead = false;
self.facingRight = true;
self.canJump = false;
// Create a simple block character with eyes and smiley face
// Main block (body)
var block = self.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 80,
tint: 0x4287f5 // Blue block color
}));
// Eyes (small white rectangles)
var leftEye = self.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
width: 10,
height: 10,
x: -20,
y: -15,
tint: 0xFFFFFF // White eyes
}));
var rightEye = self.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
width: 10,
height: 10,
x: 20,
y: -15,
tint: 0xFFFFFF // White eyes
}));
// Smile (simple arc using three rectangles)
var leftSmile = self.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
width: 15,
height: 5,
x: -15,
y: 15,
tint: 0x000000 // Black smile
}));
var rightSmile = self.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
width: 15,
height: 5,
x: 15,
y: 15,
tint: 0x000000 // Black smile
}));
var bottomSmile = self.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
width: 30,
height: 5,
x: 0,
y: 20,
tint: 0x000000 // Black smile
}));
// Store reference to all parts for animation
self.parts = {
block: block,
leftEye: leftEye,
rightEye: rightEye,
leftSmile: leftSmile,
rightSmile: rightSmile,
bottomSmile: bottomSmile
};
// Create a hitbox for collision detection (invisible)
var sprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0 // Make invisible, just for collision
});
self.update = function () {
if (self.isDead) {
return;
}
// Store previous position to track changes
self.lastX = self.x;
self.lastY = self.y;
// Apply gravity with terminal velocity to prevent falling too fast
self.vy = Math.min(self.vy + self.gravity, 25);
// Apply air resistance when jumping
if (self.isJumping) {
self.vx *= 0.98; // Less friction in air
} else {
self.vx *= 0.85; // More friction on ground
}
// Add coyote time for more forgiving jumps
if (self.canJump) {
self.lastOnGroundTime = LK.ticks;
}
// Allow jump within 7 frames of leaving a platform (coyote time)
if (self.lastOnGroundTime && LK.ticks - self.lastOnGroundTime < 7) {
self.canJump = true;
}
// Update position based on velocity with subpixel movement for smoother motion
self.x += self.vx;
self.y += self.vy;
// Reset jump state if on ground
if (self.canJump) {
self.isJumping = false;
} else {
self.isJumping = true;
}
// Only update animations every other frame to reduce CPU load
if (LK.ticks % 2 === 0) {
// Keep player oriented based on direction
if (self.vx > 0.5 && !self.facingRight) {
self.facingRight = true;
sprite.scaleX = 1;
// Flip all parts to face right
Object.values(self.parts).forEach(function (part) {
part.scaleX = Math.abs(part.scaleX);
});
self.parts.leftEye.x = -20;
self.parts.rightEye.x = 20;
self.parts.leftSmile.x = -15;
self.parts.rightSmile.x = 15;
self.parts.bottomSmile.x = 0;
} else if (self.vx < -0.5 && self.facingRight) {
self.facingRight = false;
sprite.scaleX = -1;
// Flip all parts to face left
Object.values(self.parts).forEach(function (part) {
part.scaleX = -Math.abs(part.scaleX);
});
self.parts.leftEye.x = 20;
self.parts.rightEye.x = -20;
self.parts.leftSmile.x = 15;
self.parts.rightSmile.x = -15;
self.parts.bottomSmile.x = 0;
}
// Dynamic block rotation based on velocity
if (Math.abs(self.vx) > 0.5) {
// Proportional tilt based on speed
var tiltAmount = Math.min(Math.abs(self.vx) * 0.01, 0.15);
self.parts.block.rotation = self.vx > 0 ? tiltAmount : -tiltAmount;
} else {
// Reset when standing still
if (self.parts.block.rotation !== 0) {
tween(self.parts.block, {
rotation: 0
}, {
duration: 150,
easing: tween.easeOutQuad
});
}
}
// Enhanced jumping animation
if (self.isJumping) {
// Dynamic squish based on vertical velocity
var jumpSquish = Math.min(Math.abs(self.vy) * 0.02, 0.3);
// Rising vs falling animation
if (self.vy < 0) {
// Rising - stretch vertically
self.parts.block.scaleY = 1 + jumpSquish;
self.parts.block.scaleX = self.facingRight ? 1 - jumpSquish * 0.5 : -(1 - jumpSquish * 0.5);
} else {
// Falling - compress horizontally
self.parts.block.scaleY = 1 - jumpSquish * 0.7;
self.parts.block.scaleX = self.facingRight ? 1 + jumpSquish : -(1 + jumpSquish);
}
} else {
// Subtle bounce when on ground and moving
if (Math.abs(self.vx) > 1) {
var bounceAmount = Math.sin(LK.ticks * 0.2) * 0.05;
self.parts.block.scaleY = 1 + bounceAmount;
self.parts.block.scaleX = self.facingRight ? 1 - bounceAmount * 0.5 : -(1 - bounceAmount * 0.5);
} else {
// Reset scale when not jumping or moving
self.parts.block.scaleY = 1;
self.parts.block.scaleX = self.facingRight ? 1 : -1;
}
}
// Enhanced blinking system
if (!self.nextBlinkTime) {
self.nextBlinkTime = LK.ticks + Math.floor(Math.random() * 180) + 180; // 3-6 seconds at 60fps
}
if (LK.ticks >= self.nextBlinkTime) {
// Hide eyes for blinking with smooth animation
tween(self.parts.leftEye, {
scaleY: 0.1
}, {
duration: 100
});
tween(self.parts.rightEye, {
scaleY: 0.1
}, {
duration: 100
});
// Show eyes after blink with smooth animation
if (LK.ticks >= self.nextBlinkTime + 5) {
tween(self.parts.leftEye, {
scaleY: 1
}, {
duration: 100
});
tween(self.parts.rightEye, {
scaleY: 1
}, {
duration: 100
});
// Set next blink time
self.nextBlinkTime = LK.ticks + Math.floor(Math.random() * 180) + 180;
}
}
}
};
self.jump = function () {
if (self.canJump && !self.isDead) {
self.vy = self.jumpForce;
self.isJumping = true;
LK.getSound('jump').play();
// Add jump animation with tween
tween(self.parts.block, {
scaleY: 0.7,
scaleX: self.facingRight ? 1.3 : -1.3
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Return to normal after the jump animation
tween(self.parts.block, {
scaleY: 1,
scaleX: self.facingRight ? 1 : -1
}, {
duration: 300,
easing: tween.easeOutQuad
});
}
});
// Make the face look excited during jump
tween(self.parts.leftEye, {
y: -20
}, {
duration: 300,
easing: tween.easeOutQuad
});
tween(self.parts.rightEye, {
y: -20
}, {
duration: 300,
easing: tween.easeOutQuad
});
tween(self.parts.bottomSmile, {
y: 15
}, {
duration: 300,
easing: tween.easeOutQuad
});
// Reset face after jump animation
LK.setTimeout(function () {
if (!self.isDead) {
tween(self.parts.leftEye, {
y: -15
}, {
duration: 300,
easing: tween.easeInOutQuad
});
tween(self.parts.rightEye, {
y: -15
}, {
duration: 300,
easing: tween.easeInOutQuad
});
tween(self.parts.bottomSmile, {
y: 20
}, {
duration: 300,
easing: tween.easeInOutQuad
});
}
}, 350);
self.canJump = false;
}
};
self.moveLeft = function () {
if (!self.isDead) {
// Smooth acceleration to the left
self.vx = Math.max(self.vx - 1.5, -self.speed);
// Add slight lean effect when moving left
if (self.facingRight) {
self.facingRight = false;
tween(self.parts.block, {
rotation: -0.1
}, {
duration: 200,
easing: tween.easeOutQuad
});
}
}
};
self.moveRight = function () {
if (!self.isDead) {
// Smooth acceleration to the right
self.vx = Math.min(self.vx + 1.5, self.speed);
// Add slight lean effect when moving right
if (!self.facingRight) {
self.facingRight = true;
tween(self.parts.block, {
rotation: 0.1
}, {
duration: 200,
easing: tween.easeOutQuad
});
}
}
};
self.stop = function () {
if (!self.isDead) {
// Smooth deceleration to stop
if (Math.abs(self.vx) < 0.5) {
self.vx = 0;
} else {
self.vx *= 0.8; // Gradually slow down
}
// Return to upright position when stopping
tween(self.parts.block, {
rotation: 0
}, {
duration: 200,
easing: tween.easeOutQuad
});
}
};
self.die = function () {
if (!self.isDead) {
self.isDead = true;
LK.getSound('hit').play();
LK.effects.flashObject(self, 0xff0000, 500);
// Block death animation - scatter the block parts
Object.values(self.parts).forEach(function (part) {
// Use tween to create a scatter effect
tween(part, {
y: part.y + (Math.random() * 300 - 150),
x: part.x + (Math.random() * 300 - 150),
rotation: Math.floor(Math.random() * 4) * (Math.PI / 2),
alpha: 0
}, {
duration: 800,
easing: 'easeInQuad'
});
});
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
// Spike sprite
var sprite = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
// Make spike pointy with rotation
sprite.rotation = Math.PI;
// Animation for spike
self.update = function () {
// Subtle pulsing animation
if (!self.pulseDirection) {
self.pulseDirection = 1;
self.pulseValue = 0;
}
self.pulseValue += 0.02 * self.pulseDirection;
if (self.pulseValue > 1 || self.pulseValue < 0) {
self.pulseDirection *= -1;
}
var scale = 0.95 + self.pulseValue * 0.1;
sprite.scale.set(1, scale);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue
});
/****
* Game Code
****/
// Game variables
var player;
var platforms = [];
var coins = [];
var enemies = [];
var goal;
var jumpButton;
var leftButton;
var rightButton;
var cameraPosX = 0;
var cameraPosY = 0;
var worldContainer;
var uiContainer;
var levelWidth = 5000; // The total width of the level
var groundY = 2732 - 100; // Y position of the ground
var score = 0;
var level = 1;
// Initialize UI
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -250; // Offset from topRight anchor
var levelTxt = new Text2('Level: ' + level, {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.top.addChild(levelTxt);
// Initialize world container to allow camera movement
worldContainer = new Container();
game.addChild(worldContainer);
// Create player
player = new Player();
player.x = 200;
player.y = groundY - 200;
worldContainer.addChild(player);
// Create control buttons
jumpButton = new ControlButton('jump');
jumpButton.x = 2048 - 250;
jumpButton.y = 2732 - 250;
jumpButton.scale.set(1.5, 1.5); // Make jump button 50% larger
game.addChild(jumpButton);
leftButton = new ControlButton('left');
leftButton.x = 200;
leftButton.y = 2732 - 250;
leftButton.scale.set(1.5, 1.5); // Make left button 50% larger
game.addChild(leftButton);
rightButton = new ControlButton('right');
rightButton.x = 500;
rightButton.y = 2732 - 250;
rightButton.scale.set(1.5, 1.5); // Make right button 50% larger
game.addChild(rightButton);
// Create level function
function createLevel(level) {
// Clear previous level objects
for (var i = platforms.length - 1; i >= 0; i--) {
worldContainer.removeChild(platforms[i]);
platforms.splice(i, 1);
}
for (var i = coins.length - 1; i >= 0; i--) {
worldContainer.removeChild(coins[i]);
coins.splice(i, 1);
}
for (var i = enemies.length - 1; i >= 0; i--) {
worldContainer.removeChild(enemies[i]);
enemies.splice(i, 1);
}
if (goal) {
worldContainer.removeChild(goal);
goal = null;
}
// Create ground - ensure complete coverage
for (var i = 0; i < levelWidth; i += 400) {
var ground = new Platform(400, 50, false);
ground.setStartPosition(i + 200, groundY);
// Apply brown color to ground platforms
var groundSprite = ground.children[0];
tween(groundSprite, {
tint: 0x8b4513
}, {
duration: 0
}); // Make ground brown
worldContainer.addChild(ground);
platforms.push(ground);
}
// Add additional terrain features
// Add dirt layer below ground
for (var i = 0; i < levelWidth; i += 400) {
var dirt = new Platform(400, 100, false);
dirt.setStartPosition(i + 200, groundY + 75);
var dirtSprite = dirt.children[0];
tween(dirtSprite, {
tint: 0x5d3a1a // Darker brown for dirt
}, {
duration: 0
});
worldContainer.addChild(dirt);
platforms.push(dirt);
}
// Add rocky foundation at the bottom
for (var i = 0; i < levelWidth; i += 400) {
var rock = new Platform(400, 200, false);
rock.setStartPosition(i + 200, groundY + 225);
var rockSprite = rock.children[0];
tween(rockSprite, {
tint: 0x444444 // Dark gray for rocks
}, {
duration: 0
});
worldContainer.addChild(rock);
platforms.push(rock);
}
// Update level width based on level
if (level === 2) {
levelWidth = 6000; // Extend level width for level 2
} else {
levelWidth = 5000; // Default level width
}
// Level-specific configurations
switch (level) {
case 1:
// Level 1: Basic platforms and coins
createPlatform(600, groundY - 200, 200, 30, false);
createPlatform(900, groundY - 350, 200, 30, false);
createPlatform(1300, groundY - 500, 200, 30, false);
createPlatform(1700, groundY - 350, 200, 30, false);
createPlatform(2100, groundY - 200, 200, 30, false);
createCoin(600, groundY - 300);
createCoin(900, groundY - 450);
createCoin(1300, groundY - 600);
createCoin(1700, groundY - 450);
createCoin(2100, groundY - 300);
// Place goal at the end of level 1
createGoal(2400, groundY - 100);
break;
case 2:
// Level 2: Moving platforms, enemies and spikes
// Starting platforms
createPlatform(500, groundY - 200, 200, 30, false);
createPlatform(800, groundY - 300, 200, 30, true);
// First section - moving platform challenge
createPlatform(1200, groundY - 400, 150, 30, true);
createPlatform(1600, groundY - 350, 150, 30, true);
createPlatform(2000, groundY - 400, 150, 30, true);
// Middle section - spike hazards
createPlatform(2400, groundY - 300, 300, 30, false);
createSpike(2350, groundY - 350);
createSpike(2450, groundY - 350);
createSpike(2550, groundY - 350);
// Enemy section with platforms
createPlatform(2800, groundY - 200, 200, 30, false);
createPlatform(3200, groundY - 350, 200, 30, false);
createPlatform(3600, groundY - 500, 200, 30, true);
createPlatform(4000, groundY - 350, 200, 30, false);
// Final challenge section
createPlatform(4400, groundY - 300, 150, 30, true);
createPlatform(4700, groundY - 400, 150, 30, true);
createPlatform(5000, groundY - 300, 200, 30, false);
// Coins strategically placed
createCoin(500, groundY - 300);
createCoin(800, groundY - 400);
createCoin(1200, groundY - 500);
createCoin(1600, groundY - 450);
createCoin(2000, groundY - 500);
createCoin(2400, groundY - 400);
createCoin(3200, groundY - 450);
createCoin(3600, groundY - 600);
createCoin(4000, groundY - 450);
createCoin(4700, groundY - 500);
createCoin(5000, groundY - 400);
// Enemies patrolling
createEnemy(1000, groundY - 50, 300);
createEnemy(2000, groundY - 50, 400);
createEnemy(3000, groundY - 50, 500);
createEnemy(4000, groundY - 50, 600);
// Additional spikes on the ground
createSpike(1500, groundY - 50);
createSpike(2500, groundY - 50);
createSpike(3500, groundY - 50);
createSpike(4500, groundY - 50);
// Place goal at the end of level 2
createGoal(5200, groundY - 100);
break;
case 3:
// Level 3: Complex platforming with more enemies
createPlatform(500, groundY - 200, 200, 30, false);
createPlatform(800, groundY - 350, 200, 30, true);
createPlatform(1200, groundY - 500, 200, 30, false);
createPlatform(1600, groundY - 650, 200, 30, true);
createPlatform(2000, groundY - 500, 200, 30, false);
createPlatform(2400, groundY - 350, 200, 30, true);
createPlatform(2800, groundY - 200, 200, 30, false);
createPlatform(3200, groundY - 350, 200, 30, true);
createCoin(500, groundY - 300);
createCoin(800, groundY - 450);
createCoin(1200, groundY - 600);
createCoin(1600, groundY - 750);
createCoin(2000, groundY - 600);
createCoin(2400, groundY - 450);
createCoin(2800, groundY - 300);
createCoin(3200, groundY - 450);
createEnemy(700, groundY - 50, 200);
createEnemy(1400, groundY - 50, 300);
createEnemy(2100, groundY - 50, 400);
createEnemy(2800, groundY - 50, 500);
// Place goal at the end of level 3
createGoal(3600, groundY - 100);
break;
default:
// If level > 3, generate a procedural level with increasing difficulty
var platformCount = level * 5;
var enemyCount = level * 2;
var coinCount = level * 5;
// Create platforms with varying heights and spacing
for (var i = 0; i < platformCount; i++) {
var x = 500 + i * 500;
var y = groundY - (Math.random() * 500 + 100);
var isMoving = Math.random() > 0.5;
createPlatform(x, y, 200, 30, isMoving);
// Add a coin above each platform
createCoin(x, y - 100);
}
// Add enemies
for (var i = 0; i < enemyCount; i++) {
var x = 800 + i * 800;
var patrolDistance = 200 + Math.random() * 300;
createEnemy(x, groundY - 50, patrolDistance);
}
// Place goal at the end
createGoal(500 + platformCount * 500 + 200, groundY - 100);
break;
}
// Reset player position
player.x = 200;
player.y = groundY - 200;
player.vx = 0;
player.vy = 0;
player.isDead = false;
// Reset camera
cameraPosX = 0;
worldContainer.x = 0;
}
// Helper functions for creating level elements
function createPlatform(x, y, width, height, isMoving) {
var platform = new Platform(width, height, isMoving);
platform.setStartPosition(x, y);
worldContainer.addChild(platform);
platforms.push(platform);
return platform;
}
// Helper function to create spikes
function createSpike(x, y) {
var spike = new Spike();
spike.x = x;
spike.y = y;
worldContainer.addChild(spike);
enemies.push(spike); // Add to enemies array for collision detection
return spike;
}
function createCoin(x, y) {
var coin = new Coin();
coin.x = x;
coin.y = y;
worldContainer.addChild(coin);
coins.push(coin);
return coin;
}
function createEnemy(x, y, patrolDistance) {
var enemy = new Enemy(patrolDistance);
enemy.setStartPosition(x, y);
worldContainer.addChild(enemy);
enemies.push(enemy);
return enemy;
}
function createGoal(x, y) {
goal = new Goal();
goal.x = x;
goal.y = y;
worldContainer.addChild(goal);
return goal;
}
// Check collisions between player and platforms
function checkPlatformCollisions() {
player.canJump = false;
// Pre-calculate player dimensions once
var playerWidth = player.children[0].width;
var playerHeight = player.children[0].height;
var playerLeft = player.x - playerWidth / 2;
var playerRight = player.x + playerWidth / 2;
var playerTop = player.y - playerHeight / 2;
var playerBottom = player.y + playerHeight / 2;
// Only check platforms that are visible in the camera view (+ some margin)
var cameraLeftX = cameraPosX - 200;
var cameraRightX = cameraPosX + 2048 + 200;
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
// Quick check if platform is in camera view
if (platform.x + 200 < cameraLeftX || platform.x - 200 > cameraRightX) {
continue; // Skip platforms outside camera view
}
// Get platform dimensions (adjust based on asset size)
var platformWidth = platform.children[0].width;
var platformHeight = platform.children[0].height;
var platformLeft = platform.x - platformWidth / 2;
var platformRight = platform.x + platformWidth / 2;
var platformTop = platform.y - platformHeight / 2;
var platformBottom = platform.y + platformHeight / 2;
// Check for collision
if (playerRight > platformLeft && playerLeft < platformRight && playerBottom > platformTop && playerTop < platformBottom) {
// Calculate overlap on each side
var overlapLeft = playerRight - platformLeft;
var overlapRight = platformRight - playerLeft;
var overlapTop = playerBottom - platformTop;
var overlapBottom = platformBottom - playerTop;
// Find the smallest overlap
var minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom);
// Resolve collision based on the smallest overlap
if (minOverlap === overlapTop && player.vy >= 0) {
// Landing on top of platform
// Use a much larger fixed offset for legs to prevent glitching through platform
var legOffset = 50; // Significantly increased offset value to ensure proper positioning
player.y = platformTop - playerHeight / 2 - legOffset;
player.vy = 0;
player.canJump = true;
// Apply slight downward force to keep player grounded
player.lastGroundedY = player.y;
// If platform is moving, adjust player x position
if (platform.isMoving) {
player.x += platform.moveSpeed * platform.direction;
}
} else if (minOverlap === overlapBottom && player.vy <= 0) {
// Hitting bottom of platform
player.y = platformBottom + playerHeight / 2;
player.vy = 0;
} else if (minOverlap === overlapLeft && player.vx > 0) {
// Hitting left side of platform
player.x = platformLeft - playerWidth / 2;
player.vx = 0;
} else if (minOverlap === overlapRight && player.vx < 0) {
// Hitting right side of platform
player.x = platformRight + playerWidth / 2;
player.vx = 0;
}
}
}
}
// Check collisions with coins
function checkCoinCollisions() {
// Only check coins that are visible in the camera view (+ some margin)
var cameraLeftX = cameraPosX - 100;
var cameraRightX = cameraPosX + 2048 + 100;
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
// Skip coins outside camera view
if (coin.x < cameraLeftX || coin.x > cameraRightX) {
continue;
}
if (player.intersects(coin)) {
// Collect coin
score += 10;
scoreTxt.setText('Score: ' + score);
storage.coins = (storage.coins || 0) + 1;
coin.collect();
coins.splice(i, 1);
}
}
}
// Check collisions with enemies
function checkEnemyCollisions() {
// Only check enemies that are visible in the camera view (+ some margin)
var cameraLeftX = cameraPosX - 100;
var cameraRightX = cameraPosX + 2048 + 100;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Skip enemies outside camera view
if (enemy.x < cameraLeftX || enemy.x > cameraRightX) {
continue;
}
if (player.intersects(enemy) && !player.isDead) {
// Check if player is landing on enemy (from above)
var playerBottom = player.y + player.children[0].height / 2;
var enemyTop = enemy.y - enemy.children[0].height / 2;
if (player.vy > 0 && playerBottom - enemyTop < 20) {
// Bounce off enemy
player.vy = player.jumpForce * 0.7;
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
// Add points
score += 50;
scoreTxt.setText('Score: ' + score);
} else {
// Player dies
player.die();
}
break;
}
}
}
// Check if player reached the goal
function checkGoalCollision() {
if (goal && player.intersects(goal)) {
goal.reach();
}
}
// Update camera to follow player
function updateCamera() {
// Only follow horizontally
var targetX = Math.max(0, Math.min(levelWidth - 2048, player.x - 2048 / 2));
cameraPosX += (targetX - cameraPosX) * 0.1; // Smooth camera movement
worldContainer.x = -cameraPosX;
}
// Check if player fell off the level
function checkFallOffLevel() {
if (player.y > 2732 + 200 && !player.isDead) {
player.die();
}
}
// Get the current level from storage
level = storage.currentLevel || 1;
levelTxt.setText('Level: ' + level);
// Initialize the level
createLevel(level);
// Play background music
LK.playMusic('gameMusic');
// Main game loop
game.update = function () {
// Process player controls - only move when buttons are explicitly pressed
if (jumpButton.isPressed) {
player.jump();
}
if (leftButton.isPressed) {
player.moveLeft();
} else if (rightButton.isPressed) {
player.moveRight();
} else {
player.stop();
}
// Update player
player.update();
// Update camera first to determine what's visible
updateCamera();
// Only update objects that are visible in the camera view (+ some margin)
var cameraLeftX = cameraPosX - 200;
var cameraRightX = cameraPosX + 2048 + 200;
// Update platforms
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
// Only update platforms near the camera view
if (platform.x + 200 >= cameraLeftX && platform.x - 200 <= cameraRightX) {
platform.update();
}
}
// Update coins
for (var i = 0; i < coins.length; i++) {
var coin = coins[i];
// Only update coins near the camera view
if (coin.x >= cameraLeftX && coin.x <= cameraRightX) {
coin.update();
}
}
// Update enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Only update enemies near the camera view
if (enemy.x + 200 >= cameraLeftX && enemy.x - 200 <= cameraRightX) {
enemy.update();
}
}
// Check collisions
checkPlatformCollisions();
checkCoinCollisions();
checkEnemyCollisions();
checkGoalCollision();
checkFallOffLevel();
};
// Game-wide touch events
game.down = function (x, y, obj) {
// Only process direct button presses
// No automatic button activation when touching the game area
// Store the touch ID only if touching a control button
if (obj.target && (obj.target === jumpButton || obj.target === leftButton || obj.target === rightButton)) {
game.activeTouchId = obj.event ? obj.event.pointerId : null;
}
};
game.move = function (x, y, obj) {
// No additional move handling needed at game level
};
game.up = function (x, y, obj) {
// Only process the button that was actually released
// Don't reset all buttons on any touch release
if (obj.target === leftButton) {
leftButton.up(x, y, obj);
} else if (obj.target === rightButton) {
rightButton.up(x, y, obj);
} else if (obj.target === jumpButton) {
jumpButton.up(x, y, obj);
}
// Reset the touch ID only if this is the touch we're tracking
if (game.activeTouchId === (obj.event ? obj.event.pointerId : null)) {
game.activeTouchId = null;
}
};