User prompt
Move the location of the ground entity up by 2 units
User prompt
Make the location of all tree entities appear attached to the top area of the ground entity
User prompt
Let the ground entity appear one unit ahead of the tree entity
User prompt
Align and align the position of all trees
User prompt
Please fix the bug: '[object Object]addChildAt: The index 5 supplied is out of bounds 4' in or related to this line: 'game.addChildAt(foregroundTreeLayer, 5);' Line Number: 1573
User prompt
Please fix the bug: '[object Object]addChildAt: The index 5 supplied is out of bounds 4' in or related to this line: 'game.addChildAt(foregroundTreeLayer, 5);' Line Number: 1573
User prompt
Put all tree entities in 2 layers
User prompt
Make the trees random, sometimes very dense, sometimes leaving spaces
User prompt
Delete background breathing animation
User prompt
Arrange the randomness of tree assets in the game so that they come side by side but randomly
User prompt
Change the name of the square button in the game to box 1
User prompt
Delete the animations of the starting gate and the final gate
User prompt
Remove A11 entity from the game
User prompt
Decrease character's swap frame
User prompt
Set the game's distance meter speed to normal
User prompt
Decrease character's swap frame
User prompt
Make the font bold and keep it the same font even if it changes constantly
User prompt
Make the font of the distance meter thicker and its frame larger
User prompt
Change the game's distance meter font to avenir and keep it that way
User prompt
Put a distance meter in the game
User prompt
Put a meter in the game
User prompt
Remove the scoreboard from the game
User prompt
Remove the sun and moon from the game
User prompt
Make me 10 layers and merge the first layer with the background asset and send it to the end
User prompt
Now merge the background asset with layer 1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { totalCoins: 0, powerUps: {}, playerName: "Player", lastScore: 0, highScore: 0, gamesPlayed: 0, totalDistance: 0, bestDistance: 0, achievements: {}, settings: { soundEnabled: true, musicEnabled: true, difficulty: "normal" } }); /**** * Classes ****/ var Box = Container.expand(function () { var self = Container.call(this); var boxGraphics = self.attachAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, tint: 0xFFB347 // Bright orange color }); self.speed = gameSpeed; self.broken = false; self.health = 1; // Can be broken in one hit self.update = function () { self.x -= self.speed; }; // Method to break the box and spawn coins self.breakBox = function () { if (!self.broken) { self.broken = true; // Create breaking effect LK.effects.flashObject(self, 0xFFFFFF, 300); // Spawn 2-3 coins around the box var numCoins = 2 + Math.floor(Math.random() * 2); for (var c = 0; c < numCoins; c++) { var coin = new Coin(); coin.x = self.x + (Math.random() - 0.5) * 100; coin.y = self.y + (Math.random() - 0.5) * 80; coins.push(coin); game.addChild(coin); } // Simple fade out without scaling or rotation tween(self, { alpha: 0 }, { duration: 400, onFinish: function onFinish() { self.destroy(); } }); } }; return self; }); var C2 = Container.expand(function () { var self = Container.call(this); var c2Graphics = self.attachAsset('C2', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.2, scaleY: 1.2 }); // Set custom hitbox dimensions self.hitboxWidth = 200; self.hitboxHeight = 200; // Override width and height properties for collision detection Object.defineProperty(self, 'width', { get: function get() { return self.hitboxWidth; }, set: function set(value) { self.hitboxWidth = value; } }); Object.defineProperty(self, 'height', { get: function get() { return self.hitboxHeight; }, set: function set(value) { self.hitboxHeight = value; } }); self.speed = gameSpeed; self.update = function () { self.x -= self.speed; }; return self; }); var C3 = Container.expand(function () { var self = Container.call(this); var c3Graphics = self.attachAsset('C3', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.2, scaleY: 1.2 }); // Set custom hitbox dimensions self.hitboxWidth = 200; self.hitboxHeight = 200; // Override width and height properties for collision detection Object.defineProperty(self, 'width', { get: function get() { return self.hitboxWidth; }, set: function set(value) { self.hitboxWidth = value; } }); Object.defineProperty(self, 'height', { get: function get() { return self.hitboxHeight; }, set: function set(value) { self.hitboxHeight = value; } }); self.speed = gameSpeed; self.update = function () { self.x -= self.speed; }; return self; }); var C4 = Container.expand(function () { var self = Container.call(this); var c4Graphics = self.attachAsset('C4', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.2, scaleY: 1.2 }); // Set custom hitbox dimensions self.hitboxWidth = 200; self.hitboxHeight = 200; // Override width and height properties for collision detection Object.defineProperty(self, 'width', { get: function get() { return self.hitboxWidth; }, set: function set(value) { self.hitboxWidth = value; } }); Object.defineProperty(self, 'height', { get: function get() { return self.hitboxHeight; }, set: function set(value) { self.hitboxHeight = value; } }); self.speed = gameSpeed; self.update = function () { self.x -= self.speed; }; return self; }); var C5 = Container.expand(function () { var self = Container.call(this); var c5Graphics = self.attachAsset('C5', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.2, scaleY: 1.2 }); // Set custom hitbox dimensions self.hitboxWidth = 200; self.hitboxHeight = 200; // Override width and height properties for collision detection Object.defineProperty(self, 'width', { get: function get() { return self.hitboxWidth; }, set: function set(value) { self.hitboxWidth = value; } }); Object.defineProperty(self, 'height', { get: function get() { return self.hitboxHeight; }, set: function set(value) { self.hitboxHeight = value; } }); self.speed = gameSpeed; self.update = function () { self.x -= self.speed; }; return self; }); var C6 = Container.expand(function () { var self = Container.call(this); var c6Graphics = self.attachAsset('C6', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.2, scaleY: 1.2 }); // Set custom hitbox dimensions self.hitboxWidth = 200; self.hitboxHeight = 200; // Override width and height properties for collision detection Object.defineProperty(self, 'width', { get: function get() { return self.hitboxWidth; }, set: function set(value) { self.hitboxWidth = value; } }); Object.defineProperty(self, 'height', { get: function get() { return self.hitboxHeight; }, set: function set(value) { self.hitboxHeight = value; } }); self.speed = gameSpeed; self.update = function () { self.x -= self.speed; }; return self; }); var Coin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); // Create aura effect var aura = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, alpha: 0.3, tint: 0xFFD700 }); self.speed = gameSpeed; self.collected = false; // Floating animation self.floatOffset = 0; // Start aura pulsing animation function startAuraPulse() { tween(aura, { scaleX: 2.0, scaleY: 2.0, alpha: 0.1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(aura, { scaleX: 1.5, scaleY: 1.5, alpha: 0.3 }, { duration: 800, easing: tween.easeInOut, onFinish: startAuraPulse }); } }); } startAuraPulse(); self.update = function () { self.x -= self.speed; // Floating animation self.floatOffset += 0.15; coinGraphics.y = Math.sin(self.floatOffset) * 10; aura.y = Math.sin(self.floatOffset) * 10; // Rotation animation coinGraphics.rotation += 0.1; aura.rotation -= 0.05; // Counter-rotate aura for visual effect }; return self; }); var DamageBox = Container.expand(function () { var self = Container.call(this); var damageBoxGraphics = self.attachAsset('tnt_box', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, tint: 0x8B4513 // Brown box color with TNT styling }); self.speed = gameSpeed; self.damaged = false; self.update = function () { self.x -= self.speed; }; // Method to cause damage to player self.damagePlayer = function () { if (!self.damaged && !player.isInvulnerable && !godModeActive) { self.damaged = true; playerLives--; // Play heart damage sound when lives decrease LK.getSound('Canazalma').play(); // Add screen shake effect var originalX = game.x; var originalY = game.y; var shakeIntensity = 20; var shakeDuration = 300; // Create shake animation sequence tween(game, { x: originalX + shakeIntensity, y: originalY + shakeIntensity * 0.5 }, { duration: shakeDuration / 6, easing: tween.easeOut, onFinish: function onFinish() { tween(game, { x: originalX - shakeIntensity, y: originalY - shakeIntensity * 0.5 }, { duration: shakeDuration / 6, easing: tween.easeOut, onFinish: function onFinish() { tween(game, { x: originalX + shakeIntensity * 0.5, y: originalY + shakeIntensity * 0.3 }, { duration: shakeDuration / 6, easing: tween.easeOut, onFinish: function onFinish() { tween(game, { x: originalX - shakeIntensity * 0.5, y: originalY - shakeIntensity * 0.3 }, { duration: shakeDuration / 6, easing: tween.easeOut, onFinish: function onFinish() { tween(game, { x: originalX, y: originalY }, { duration: shakeDuration / 3, easing: tween.easeOut }); } }); } }); } }); } }); // Update lives counter text with animation livesText.setText(playerLives); // Font is already set in the Text2 constructor, no need to change it here // Animate lives text when taking damage tween(livesText, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(livesText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeIn }); } }); // Flash heart when taking damage if (playerLives > 0) { LK.effects.flashObject(hearts[0], 0xFF0000, 500); } else { hearts[0].alpha = 0.3; } // Make player invulnerable temporarily player.isInvulnerable = true; LK.effects.flashObject(player, 0xFF0000, 1000); // Remove invulnerability after 2 seconds LK.setTimeout(function () { player.isInvulnerable = false; }, 2000); // Check for game over if (playerLives <= 0) { LK.showGameOver(); } // Create explosion effect when damaged self.createExplosion(); } }; // Method to create explosion effect self.createExplosion = function () { // Create explosion particles for (var p = 0; p < 12; p++) { var particle = LK.getAsset('tnt_box', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, alpha: 1.0, x: self.x, y: self.y, tint: 0x654321 }); game.addChild(particle); var angle = p / 12 * Math.PI * 2; var distance = 100 + Math.random() * 80; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1, rotation: Math.PI * 2 * (Math.random() > 0.5 ? 1 : -1) }, { duration: 600 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } // Flash screen and hide the damage box LK.effects.flashScreen(0xFF0000, 300); tween(self, { alpha: 0, scaleX: 2.0, scaleY: 2.0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var EndGate = Container.expand(function () { var self = Container.call(this); var gateGraphics = self.attachAsset('end_gate', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.2, scaleY: 1.2, tint: 0xFFD700 // Golden color for finish gate }); // Create circular portal effect inside the gate (same as StartingGate) var portalCircle = self.attachAsset('A11', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, x: 0, y: -280, alpha: 0.8, tint: 0x00FFFF }); // Start rotating animation for the portal function startPortalRotation() { tween(portalCircle, { rotation: Math.PI * 2 }, { duration: 800, easing: tween.linear, onFinish: startPortalRotation }); } // Add pulsing animation to make portal more dynamic function startPortalPulse() { tween(portalCircle, { scaleX: 1.2, scaleY: 1.2, alpha: 0.6 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { tween(portalCircle, { scaleX: 1.0, scaleY: 1.0, alpha: 0.8 }, { duration: 1500, easing: tween.easeInOut, onFinish: startPortalPulse }); } }); } startPortalRotation(); startPortalPulse(); // Add victory glow effect var victoryGlow = self.attachAsset('end_gate', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.5, scaleY: 1.5, alpha: 0.4, tint: 0xFFFFFF // White victory glow }); // Start victory glow pulsing animation function startVictoryPulse() { tween(victoryGlow, { scaleX: 1.7, scaleY: 1.7, alpha: 0.1 }, { duration: 900, easing: tween.easeInOut, onFinish: function onFinish() { tween(victoryGlow, { scaleX: 1.5, scaleY: 1.5, alpha: 0.4 }, { duration: 900, easing: tween.easeInOut, onFinish: startVictoryPulse }); } }); } startVictoryPulse(); self.speed = gameSpeed; self.crossed = false; self.update = function () { self.x -= self.speed; }; // Method to trigger win condition self.triggerWin = function () { if (!self.crossed) { self.crossed = true; // Handle chapter completion based on current chapter if (gamePartNumber === 1) { // Complete Chapter 1 and transition to Chapter 2 completePart1(); var completionMessage = 'CHAPTER 1 COMPLETED!'; var completionColor = 0x00FF00; } else { // Complete Chapter 2 - final completion saveCompleteChapter1(); // Save final state } // Create victory particle effect for (var p = 0; p < 20; p++) { var particle = LK.getAsset('end_gate', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, alpha: 1.0, x: self.x, y: self.y - 200, tint: p % 3 === 0 ? 0xFFD700 : p % 3 === 1 ? 0xFFFFFF : 0x00FF00 }); game.addChild(particle); var angle = p / 20 * Math.PI * 2; var distance = 200 + Math.random() * 150; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y - 200 + Math.sin(angle) * distance; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1, rotation: Math.PI * 3 }, { duration: 1500 + Math.random() * 500, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } // Chapter completion message removed for Chapter 2 // Flash screen with victory colors LK.effects.flashScreen(0xFFD700, 800); // Show collectibles summary after a brief delay LK.setTimeout(function () { showCollectiblesSummary(); }, 800); // Handle final win condition based on chapter if (gamePartNumber === 2) { // Final game completion for Chapter 2 LK.setTimeout(function () { LK.showYouWin(); }, 3000); } else { // Chapter 1 - continue to Chapter 2 (transition handled in completePart1) } } }; return self; }); var Gem = Container.expand(function () { var self = Container.call(this); var gemGraphics = self.attachAsset('gem', { anchorX: 0.5, anchorY: 0.5 }); // Create crystalline aura effect var aura1 = self.attachAsset('gem', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3, alpha: 0.4, tint: 0x00FFFF }); var aura2 = self.attachAsset('gem', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.6, scaleY: 1.6, alpha: 0.2, tint: 0x8A2BE2 }); self.speed = gameSpeed; self.collected = false; // Crystal aura remains steady without pulsing animation self.update = function () { self.x -= self.speed; // Crystal remains steady without rotation }; // Method to create particle effect when collected self.createParticles = function () { for (var p = 0; p < 8; p++) { var particle = LK.getAsset('gem', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, alpha: 0.8, x: self.x, y: self.y, tint: p % 2 === 0 ? 0x00FFFF : 0x8A2BE2 }); game.addChild(particle); var angle = p / 8 * Math.PI * 2; var distance = 150 + Math.random() * 100; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1, rotation: Math.PI * 2 }, { duration: 800 + Math.random() * 400, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var foxGraphics = self.attachAsset('C1', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.2, scaleY: 1.2 }); // Set custom hitbox dimensions self.hitboxWidth = 200; self.hitboxHeight = 200; // Override width and height properties for collision detection Object.defineProperty(self, 'width', { get: function get() { return self.hitboxWidth; }, set: function set(value) { self.hitboxWidth = value; } }); Object.defineProperty(self, 'height', { get: function get() { return self.hitboxHeight; }, set: function set(value) { self.hitboxHeight = value; } }); self.isJumping = false; self.velocityY = 0; self.groundY = 2732 - 150 + 20; // Ground level self.jumpPower = -27; self.gravity = 1.2; self.runAnimationTimer = 0; self.isSpinning = false; self.spinDamageRadius = 150; self.jumpsRemaining = 2; // Allow double jump self.jump = function () { if (self.jumpsRemaining > 0) { // First jump or double jump if (!self.isJumping) { self.isJumping = true; } self.velocityY = self.jumpPower; self.jumpsRemaining--; LK.getSound('jump').play(); // Simple jump animation without somersault - just scale effect var jumpDuration = 300; // Shorter duration for simple jump // Different animation for double jump var isDoubleJump = self.jumpsRemaining === 0 && self.isJumping; if (isDoubleJump) { // Double jump animation - enhanced scale effect tween(foxGraphics, { scaleX: 1.4, scaleY: 1.4 }, { duration: jumpDuration, easing: tween.easeOut, onFinish: function onFinish() { tween(foxGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeIn }); } }); } else { // Regular jump animation - simple scale effect tween(foxGraphics, { scaleX: 1.3, scaleY: 1.3 }, { duration: jumpDuration, easing: tween.easeOut, onFinish: function onFinish() { tween(foxGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeIn }); } }); } } }; self.spinAttack = function () { if (!self.isSpinning) { self.isSpinning = true; LK.getSound('jump').play(); // Reuse jump sound for spin // Horizontal rotation animation around character's center tween(foxGraphics, { rotation: Math.PI * 4, // 2 full horizontal rotations scaleX: 1.5, scaleY: 1.5, tint: 0xFF4500 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { self.isSpinning = false; foxGraphics.rotation = 0; foxGraphics.scaleX = 1.0; foxGraphics.scaleY = 1.0; foxGraphics.tint = 0xFFFFFF; } }); } }; self.update = function () { if (self.isJumping) { self.velocityY += self.gravity; self.y += self.velocityY; // Land on ground if (self.y >= self.groundY) { self.y = self.groundY; self.isJumping = false; self.velocityY = 0; self.jumpsRemaining = 2; // Reset double jump when landing // Reset character scale when landing tween(foxGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut }); } } else { // Running animation when on ground self.runAnimationTimer += 0.2; foxGraphics.y = Math.sin(self.runAnimationTimer) * 3; foxGraphics.scaleX = 1.0 + Math.sin(self.runAnimationTimer * 2) * 0.05; } }; return self; }); var StartingGate = Container.expand(function () { var self = Container.call(this); var gateGraphics = self.attachAsset('A1', { anchorX: 0.5, anchorY: 1.0, scaleX: 1.0, scaleY: 1.0 }); // Create circular portal effect inside the gate var portalCircle = self.attachAsset('A11', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, x: 0, y: -200, alpha: 0.8, tint: 0x00FFFF }); // Start rotating animation for the portal function startPortalRotation() { tween(portalCircle, { rotation: Math.PI * 2 }, { duration: 800, easing: tween.linear, onFinish: startPortalRotation }); } // Add pulsing animation to make portal more dynamic function startPortalPulse() { tween(portalCircle, { scaleX: 1.0, scaleY: 1.0, alpha: 0.6 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { tween(portalCircle, { scaleX: 0.8, scaleY: 0.8, alpha: 0.8 }, { duration: 1500, easing: tween.easeInOut, onFinish: startPortalPulse }); } }); } startPortalRotation(); startPortalPulse(); self.speed = gameSpeed; self.update = function () { self.x -= self.speed; }; return self; }); var Store = Container.expand(function () { var self = Container.call(this); self.isOpen = false; self.storeItems = [{ id: 'doubleJump', name: 'Super Jump', price: 50, description: 'Higher jumps!', owned: false }, { id: 'coinMagnet', name: 'Coin Magnet', price: 100, description: 'Attract coins!', owned: false }, { id: 'speedBoost', name: 'Speed Boost', price: 75, description: 'Move faster!', owned: false }, { id: 'shield', name: 'Shield', price: 150, description: 'Extra protection!', owned: false }]; // Load owned items from storage for (var i = 0; i < self.storeItems.length; i++) { var item = self.storeItems[i]; item.owned = storage.powerUps[item.id] || false; } self.openStore = function () { if (self.isOpen) return; self.isOpen = true; // Pause the game when store opens LK.pauseGame(); // Create overlay self.overlay = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 30, scaleY: 40, alpha: 0.9, tint: 0x000033, x: 1024, y: 1366 }); game.addChild(self.overlay); // Create store panel self.panel = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 20, scaleY: 25, tint: 0x1a1a2e, x: 1024, y: 1366 }); game.addChild(self.panel); // Store title self.titleText = new Text2('POWER-UP STORE', { size: 100, fill: 0xFFD700, font: "Avenir", fontWeight: 'bold' }); self.titleText.anchor.set(0.5, 0.5); self.titleText.x = 1024; self.titleText.y = 600; game.addChild(self.titleText); // Coin display var totalCoins = storage.totalCoins || 0; self.coinDisplay = new Text2('Coins: ' + totalCoins, { size: 60, fill: 0xFFD700, font: "Avenir", fontWeight: 'bold' }); self.coinDisplay.anchor.set(0.5, 0.5); self.coinDisplay.x = 1024; self.coinDisplay.y = 700; game.addChild(self.coinDisplay); // Create store items self.itemElements = []; for (var i = 0; i < self.storeItems.length; i++) { self.createStoreItem(i); } // Close button self.closeButton = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, tint: 0xFF4444, x: 1024, y: 2000 }); game.addChild(self.closeButton); self.closeText = new Text2('CLOSE', { size: 50, fill: 0xFFFFFF, font: "Avenir", fontWeight: 'bold' }); self.closeText.anchor.set(0.5, 0.5); self.closeText.x = 1024; self.closeText.y = 2000; game.addChild(self.closeText); // Close button interaction self.closeButton.down = function () { self.closeStore(); }; // Animate store opening tween(self.overlay, { alpha: 0.9 }, { duration: 300 }); tween(self.panel, { scaleX: 20, scaleY: 25 }, { duration: 400, easing: tween.easeOut }); }; self.createStoreItem = function (index) { var item = self.storeItems[index]; var yPos = 850 + index * 180; // Item background var itemBg = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 15, scaleY: 3, tint: item.owned ? 0x004400 : 0x333366, x: 1024, y: yPos }); game.addChild(itemBg); // Item name var nameText = new Text2(item.name, { size: 50, fill: item.owned ? 0x88FF88 : 0xFFFFFF, font: "Avenir", fontWeight: 'bold' }); nameText.anchor.set(0.5, 0.5); nameText.x = 800; nameText.y = yPos - 20; game.addChild(nameText); // Item description var descText = new Text2(item.description, { size: 35, fill: 0xCCCCCC, font: "Avenir" }); descText.anchor.set(0.5, 0.5); descText.x = 800; descText.y = yPos + 20; game.addChild(descText); // Price/Status var priceText = new Text2(item.owned ? 'OWNED' : item.price + ' coins', { size: 45, fill: item.owned ? 0x00FF00 : 0xFFD700, font: "Avenir", fontWeight: 'bold' }); priceText.anchor.set(0.5, 0.5); priceText.x = 1200; priceText.y = yPos; game.addChild(priceText); // Buy button (only if not owned) if (!item.owned) { var buyButton = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 1.5, tint: 0x00AA00, x: 1350, y: yPos }); game.addChild(buyButton); var buyText = new Text2('BUY', { size: 40, fill: 0xFFFFFF, font: "Avenir", fontWeight: 'bold' }); buyText.anchor.set(0.5, 0.5); buyText.x = 1350; buyText.y = yPos; game.addChild(buyText); buyButton.down = function () { self.purchaseItem(index); }; self.itemElements.push({ bg: itemBg, name: nameText, desc: descText, price: priceText, buyBtn: buyButton, buyTxt: buyText }); } else { self.itemElements.push({ bg: itemBg, name: nameText, desc: descText, price: priceText }); } }; self.purchaseItem = function (index) { var item = self.storeItems[index]; var totalCoins = storage.totalCoins || 0; if (totalCoins >= item.price && !item.owned) { // Purchase successful storage.totalCoins = totalCoins - item.price; storage.powerUps[item.id] = true; item.owned = true; // Update coin display self.coinDisplay.setText('Coins: ' + storage.totalCoins); // Flash effect LK.effects.flashScreen(0x00FF00, 300); // Update item appearance var elements = self.itemElements[index]; elements.bg.tint = 0x004400; elements.name.fill = 0x88FF88; elements.price.setText('OWNED'); elements.price.fill = 0x00FF00; if (elements.buyBtn) { elements.buyBtn.destroy(); elements.buyTxt.destroy(); } } else { // Not enough coins LK.effects.flashScreen(0xFF0000, 300); } }; self.closeStore = function () { if (!self.isOpen) return; self.isOpen = false; // Resume the game when store closes LK.resumeGame(); // Clean up all store elements if (self.overlay) self.overlay.destroy(); if (self.panel) self.panel.destroy(); if (self.titleText) self.titleText.destroy(); if (self.coinDisplay) self.coinDisplay.destroy(); if (self.closeButton) self.closeButton.destroy(); if (self.closeText) self.closeText.destroy(); for (var i = 0; i < self.itemElements.length; i++) { var elements = self.itemElements[i]; elements.bg.destroy(); elements.name.destroy(); elements.desc.destroy(); elements.price.destroy(); if (elements.buyBtn) elements.buyBtn.destroy(); if (elements.buyTxt) elements.buyTxt.destroy(); } self.itemElements = []; }; return self; }); var Trail = Container.expand(function () { var self = Container.call(this); var trailGraphics = self.attachAsset('C1', { anchorX: 0.5, anchorY: 1.0, scaleX: 0.6, scaleY: 0.6, alpha: 0.7 }); self.speed = gameSpeed; self.update = function () { self.x -= self.speed; }; return self; }); var Tree = Container.expand(function () { var self = Container.call(this); // Random tree type (1, 2, or 3) var treeType = Math.floor(Math.random() * 3) + 1; var treeAssetName = 'tree' + treeType; // Define distinct colors for each tree type to make them more distinguishable var treeColors = [0x228B22, // Forest Green for tree1 0x8B4513, // Saddle Brown for tree2 0x006400 // Dark Green for tree3 ]; var treeColor = treeColors[treeType - 1]; // Create tree foliage - position directly on ground without trunk var foliage = self.attachAsset(treeAssetName, { anchorX: 0.5, anchorY: 1.0, y: 0, // Position foliage directly on ground scaleX: 2.2, // Make foliage much larger scaleY: 2.2, // Make foliage much larger tint: treeColor // Apply distinctive color }); // Create shadow effect to make tree more distinct var shadow = self.attachAsset(treeAssetName, { anchorX: 0.5, anchorY: 1.0, y: 5, // Slightly offset shadow scaleX: 2.1, scaleY: 2.0, alpha: 0.3, tint: 0x000000 // Black shadow }); self.speed = gameSpeed; self.breathingOffset = Math.random() * Math.PI * 2; // Random phase for each tree self.breathingSpeed = 0.8 + Math.random() * 0.4; // Slight variation in breathing speed // Trees are now fixed - no breathing animation function startBreathing() { // No animation - trees remain static and fixed to ground } // Trees are now fixed - no swaying animation function startSwaying() { // No animation - trees remain static and fixed to ground } startBreathing(); // No swaying animation call - trees stay fixed self.update = function () { self.x -= self.speed; // Trees are now fixed to ground with no floating animations // Keep foliage and shadow at fixed positions foliage.y = 0; // Fixed to ground shadow.y = 5; // Fixed shadow offset // Keep foliage and shadow at fixed scales foliage.scaleX = 2.2; foliage.scaleY = 2.2; shadow.scaleX = 2.1; shadow.scaleY = 2.0; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB, fps: 30 }); /**** * Game Code ****/ var player; var coins = []; var gems = []; var boxes = []; var damageBoxes = []; var trails = []; var trees = []; var gameSpeed = 24; var spawnTimer = 0; var coinCount = 0; var gemCount = 0; var distanceScore = 0; var groundY = 2732 - 150; var playerLives = 3; var trailTimer = 0; var crystalSpawned = false; var endGateSpawned = false; var startingGate; var endGate; var background1, background2; // UI Elements var coinIcon = LK.getAsset('coin_icon', { anchorX: 0.5, anchorY: 0, x: 0, y: 20 }); LK.gui.top.addChild(coinIcon); var coinText = new Text2('0', { size: 120, fill: 0xFFFFFF, font: "Avenir", fontWeight: '900', stroke: 0x000000, strokeThickness: 15 }); coinText.anchor.set(0.5, 0); coinText.x = 0; coinText.y = 90; LK.gui.top.addChild(coinText); var gemIcon = null; var gemText = null; var scoreText = new Text2('Distance: 0', { size: 120, fill: 0xFFFFFF, font: "Avenir", fontStyle: 'italic', fontWeight: 'bold' }); scoreText.anchor.set(1, 0); LK.gui.topRight.addChild(scoreText); // Create heart UI elements - only one heart image with lives counter var hearts = []; // Create single heart image var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, x: 150, y: 193 }); hearts.push(heart); LK.gui.topLeft.addChild(heart); // Create lives counter text var livesText = new Text2('3', { size: 120, fill: 0xFF8C00, font: "Avenir", fontWeight: '900', stroke: 0x000000, strokeThickness: 25 }); livesText.anchor.set(0.5, 0.5); livesText.x = 250; livesText.y = 173; LK.gui.topLeft.addChild(livesText); // Start constant bouncing animation for lives text function startLivesBounce() { tween(livesText, { y: 163 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(livesText, { y: 183 }, { duration: 600, easing: tween.easeInOut, onFinish: startLivesBounce }); } }); } startLivesBounce(); ; // Function to show collectibles summary function showCollectiblesSummary() { // Create dark overlay var overlay = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 30, scaleY: 40, alpha: 0.8, tint: 0x000000, x: 1024, y: 1366 }); game.addChild(overlay); // Create summary panel var summaryPanel = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 18, scaleY: 15, tint: 0x2F4F4F, x: 1024, y: 1366 }); game.addChild(summaryPanel); // Title text var titleText = new Text2('COLLECTIBLES GATHERED', { size: 80, fill: 0xFFD700, font: "Avenir", fontWeight: 'bold' }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 900; game.addChild(titleText); // Create animated collectible items that can be picked up var summaryItems = []; // Create multiple coin items based on coinCount (max 10 for display) var coinsToShow = Math.min(coinCount, 10); for (var c = 0; c < coinsToShow; c++) { var summaryCoins = LK.getAsset('coin', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, x: 600 + c % 5 * 100, y: 1100 + Math.floor(c / 5) * 80, alpha: 0 }); game.addChild(summaryCoins); summaryItems.push(summaryCoins); // Add floating animation var floatOffset = c * 0.5; tween(summaryCoins, { alpha: 1, y: summaryCoins.y - 20 }, { duration: 500 + c * 100, easing: tween.easeOut, onFinish: function onFinish() { // Continuous floating animation function floatAnimation(coin, offset) { tween(coin, { y: coin.y + 15, rotation: coin.rotation + 0.3 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(coin, { y: coin.y - 15, rotation: coin.rotation + 0.3 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { floatAnimation(coin, offset); } }); } }); } floatAnimation(summaryCoins, floatOffset); } }); } // Create multiple gem items based on gemCount (max 5 for display) var gemsToShow = Math.min(gemCount, 5); for (var g = 0; g < gemsToShow; g++) { var summaryGems = LK.getAsset('gem', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6, x: 700 + g * 120, y: 1350, alpha: 0 }); game.addChild(summaryGems); summaryItems.push(summaryGems); // Add sparkling animation tween(summaryGems, { alpha: 1, scaleX: 0.8, scaleY: 0.8 }, { duration: 600 + g * 150, easing: tween.easeOut, onFinish: function onFinish() { // Continuous sparkling animation function sparkleAnimation(gem) { tween(gem, { alpha: 0.7, scaleX: 0.9, scaleY: 0.9, tint: 0x00FFFF }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(gem, { alpha: 1, scaleX: 0.8, scaleY: 0.8, tint: 0xFFFFFF }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { sparkleAnimation(gem); } }); } }); } sparkleAnimation(summaryGems); } }); } // Coins summary text var coinSummaryText = new Text2('Coins Collected: ' + coinCount, { size: 60, fill: 0xFFD700, font: "Avenir", fontWeight: 'bold' }); coinSummaryText.anchor.set(0.5, 0.5); coinSummaryText.x = 1024; coinSummaryText.y = 1050; game.addChild(coinSummaryText); // Gems summary text var gemSummaryText = new Text2('Gems Found: ' + gemCount, { size: 60, fill: 0xFF69B4, font: "Avenir", fontWeight: 'bold' }); gemSummaryText.anchor.set(0.5, 0.5); gemSummaryText.x = 1024; gemSummaryText.y = 1300; game.addChild(gemSummaryText); // Total score var totalText = new Text2('TOTAL SCORE: ' + coinCount, { size: 70, fill: 0x00FF00, font: "Avenir", fontWeight: 'bold' }); totalText.anchor.set(0.5, 0.5); totalText.x = 1024; totalText.y = 1600; game.addChild(totalText); // Save final game statistics var finalDistance = Math.floor(distanceScore / 10); storage.lastScore = coinCount; storage.totalCoins = (storage.totalCoins || 0) + coinCount; if (coinCount > (storage.highScore || 0)) { storage.highScore = coinCount; } if (finalDistance > (storage.bestDistance || 0)) { storage.bestDistance = finalDistance; } storage.gamesPlayed = (storage.gamesPlayed || 0) + 1; // Add instruction text var instructionText = new Text2('Your collected treasures!', { size: 50, fill: 0xFFFFFF, font: "Avenir", fontStyle: 'italic' }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 1500; game.addChild(instructionText); // Animate elements appearing tween(overlay, { alpha: 0.8 }, { duration: 500 }); tween(summaryPanel, { scaleX: 18, scaleY: 15 }, { duration: 600, easing: tween.easeOut }); // Clean up summary after delay LK.setTimeout(function () { overlay.destroy(); summaryPanel.destroy(); titleText.destroy(); coinSummaryText.destroy(); gemSummaryText.destroy(); totalText.destroy(); instructionText.destroy(); // Clean up all summary items for (var i = 0; i < summaryItems.length; i++) { summaryItems[i].destroy(); } }, 2500); } ; ; // Spin button removed // Play background music constantly LK.playMusic('Arkaplanmuzik1'); // Create animated background elements for endless scrolling with third element to fill gaps - merged with layer1 var background1 = layer1.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); var background2 = layer1.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 2250, // Position second background at end of first background y: 0 })); var background3 = layer1.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 4500, // Position third background to eliminate gaps during transitions y: 0 })); // Create animated ground elements for endless scrolling var ground1 = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 0, y: groundY })); var ground2 = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 4008, // Position second ground at end of first ground y: groundY })); var ground3 = game.addChild(LK.getAsset('ground', { anchorX: 0, anchorY: 0, x: 8016, // Position third ground to eliminate gaps during transitions y: groundY })); // Ground animation variables var groundAnimationTimer = 0; // Create sun and moon sun = game.addChild(LK.getAsset('sun', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 400, scaleX: 1.2, scaleY: 1.2 })); moon = game.addChild(LK.getAsset('moon', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 400, scaleX: 1.0, scaleY: 1.0, alpha: 0 })); // Create starting gate startingGate = new StartingGate(); startingGate.x = 400 + 3 * 10; // Position at 3 meters (3m * 10 pixels per meter) startingGate.y = groundY + 75; // Position gate a little higher above the ground game.addChild(startingGate); // Create player (will be added to foreground later) player = new Player(); player.x = 400; player.y = groundY + 20; player.isInvulnerable = false; // Touch controls for swipe up to jump var touchStartY = null; var minSwipeDistance = 100; // Minimum distance for swipe detection game.down = function (x, y, obj) { touchStartY = y; }; game.up = function (x, y, obj) { if (touchStartY !== null) { var swipeDistance = touchStartY - y; // Positive means swipe up if (swipeDistance >= minSwipeDistance) { // Swipe up detected - make player jump player.jump(); } touchStartY = null; } }; // Spin button interaction removed function spawnCoin() { var coin = new Coin(); coin.x = 2048 + 100; // Randomly place coins either high in air or at medium height to avoid ground-level objects var coinHeightOptions = [groundY - 300 - Math.random() * 100, // High in air groundY - 180 - Math.random() * 80 // Medium height ]; coin.y = coinHeightOptions[Math.floor(Math.random() * coinHeightOptions.length)]; coins.push(coin); game.addChild(coin); } function spawnGem() { var gem = new Gem(); gem.x = 2048 + 100; gem.y = groundY - 80 - Math.random() * 200; gems.push(gem); game.addChild(gem); } function spawnBox() { // Randomly decide if this should be a 2-stage box (30% chance) var isTwoStage = Math.random() < 0.3; if (isTwoStage) { // Create bottom box var bottomBox = new Box(); bottomBox.x = 2048 + 100; bottomBox.y = groundY - 45; // Closer to ground level boxes.push(bottomBox); game.addChild(bottomBox); // Create top box var topBox = new Box(); topBox.x = 2048 + 100; topBox.y = groundY - 45 - 120; // Stack on top (120 pixels higher - increased spacing) boxes.push(topBox); game.addChild(topBox); } else { // Create single box as before var box = new Box(); box.x = 2048 + 100; box.y = groundY - 45; // Closer to ground level boxes.push(box); game.addChild(box); } } function spawnDamageBox() { // Create single TNT only var damageBox = new DamageBox(); damageBox.x = 2048 + 100; damageBox.y = groundY - 45; // Position TNT on ground level like boxes damageBoxes.push(damageBox); game.addChild(damageBox); } game.update = function () { // Increase distance score distanceScore += 3; scoreText.setText(Math.floor(distanceScore / 10) + 'm'); // Font is already set in the Text2 constructor, no need to change it here // Character switching logic - cycle through all 12 characters every 5 meters var currentDistance = Math.floor(distanceScore / 10); if (currentDistance >= nextSwapDistance) { // Move to next character in the cycle currentCharacterIndex = (currentCharacterIndex + 1) % characterOrder.length; currentCharacter = characterOrder[currentCharacterIndex]; // Remove old graphics and add new character graphics player.removeChildAt(0); // Remove current graphics var newGraphics = player.attachAsset(currentCharacter, { anchorX: 0.5, anchorY: 1.0, scaleX: 1.2, scaleY: 1.2 }); // Update swap distances lastSwapDistance = currentDistance; nextSwapDistance = currentDistance + 5; // Visual feedback for swap LK.effects.flashObject(player, 0xFFFFFF, 300); } // Gradually increase speed - but respect God mode if (!godModeActive) { gameSpeed = 24 + distanceScore / 1000; normalSpeed = 24 + distanceScore / 1000; // Track normal speed for God mode toggle } else { gameSpeed = normalSpeed * 3; // Maintain high speed in God mode } // Update starting gate if (startingGate && startingGate.parent) { startingGate.speed = gameSpeed; // Remove gate when it goes off screen if (startingGate.x < -200) { startingGate.destroy(); startingGate = null; } } // Update end gate if (endGate && endGate.parent) { endGate.speed = gameSpeed; // Remove gate when it goes off screen (after win condition) if (endGate.x < -400) { endGate.destroy(); endGate = null; } } // Move all background elements with game progression (same speed as ground - no parallax effect) background1.x -= gameSpeed; background2.x -= gameSpeed; background3.x -= gameSpeed; // Reset background positions for endless scrolling with immediate positioning to eliminate gaps if (background1.x <= -2250) { // Find the rightmost background element var rightmostX = Math.max(background2.x, background3.x); // Immediately position to eliminate gaps background1.x = rightmostX + 2250; } if (background2.x <= -2250) { // Find the rightmost background element var rightmostX = Math.max(background1.x, background3.x); // Immediately position to eliminate gaps background2.x = rightmostX + 2250; } if (background3.x <= -2250) { // Find the rightmost background element var rightmostX = Math.max(background1.x, background2.x); // Immediately position to eliminate gaps background3.x = rightmostX + 2250; } // Move all ground elements with game progression ground1.x -= gameSpeed; ground2.x -= gameSpeed; ground3.x -= gameSpeed; // Reset ground positions for endless scrolling with immediate positioning to eliminate gaps if (ground1.x <= -4008) { // Find the rightmost ground element var rightmostX = Math.max(ground2.x, ground3.x); // Immediately position to eliminate gaps ground1.x = rightmostX + 4008; } if (ground2.x <= -4008) { // Find the rightmost ground element var rightmostX = Math.max(ground1.x, ground3.x); // Immediately position to eliminate gaps ground2.x = rightmostX + 4008; } if (ground3.x <= -4008) { // Find the rightmost ground element var rightmostX = Math.max(ground1.x, ground2.x); // Immediately position to eliminate gaps ground3.x = rightmostX + 4008; } // Animate background with subtle floating movement groundAnimationTimer += 0.1; var backgroundOffset = Math.sin(groundAnimationTimer * 0.5) * 1; background1.y = backgroundOffset; background2.y = backgroundOffset; background3.y = backgroundOffset; // Add subtle scale animation to background for depth effect var backgroundScaleAnimation = 1.0 + Math.sin(groundAnimationTimer * 0.3) * 0.01; background1.scaleY = backgroundScaleAnimation; background2.scaleY = backgroundScaleAnimation; background3.scaleY = backgroundScaleAnimation; // Animate ground with subtle movement var groundOffset = Math.sin(groundAnimationTimer) * 2; ground1.y = groundY + groundOffset; ground2.y = groundY + groundOffset; ground3.y = groundY + groundOffset; // Add slight scale animation to ground var scaleAnimation = 1.0 + Math.sin(groundAnimationTimer * 0.5) * 0.02; ground1.scaleY = scaleAnimation; ground2.scaleY = scaleAnimation; ground3.scaleY = scaleAnimation; // Update spawn timer spawnTimer++; trailTimer++; // Create trail when player is jumping if (player.isJumping && trailTimer % 3 == 0) { var trail = new Trail(); trail.x = player.x; trail.y = player.y; trail.speed = gameSpeed; trails.push(trail); game.addChild(trail); // Fade out trail tween(trail, { alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 500, onFinish: function onFinish() { trail.destroy(); } }); } // Spawn additional coins with increased frequency to reach 170 coins by game end if (spawnTimer % 35 == 0 && Math.random() < 0.85) { spawnCoin(); } // Spawn boxes occasionally if (spawnTimer % 90 == 0 && Math.random() < 0.6) { spawnBox(); } // Spawn damage boxes occasionally (less frequent than regular boxes) if (spawnTimer % 120 == 0 && Math.random() < 0.4) { spawnDamageBox(); } // Spawn trees in background much more frequently and multiply them if (spawnTimer % 60 == 0 && Math.random() < 0.9) { // Spawn 2-4 trees per spawn cycle for denser forest var numTrees = 2 + Math.floor(Math.random() * 3); for (var treeIndex = 0; treeIndex < numTrees; treeIndex++) { var tree = new Tree(); tree.x = 2048 + 200 + treeIndex * 150; // Space trees out horizontally tree.y = groundY + 45; // Position trees 45 units lower than ground level // Position trees at different depths for parallax effect - make them even larger and more prominent tree.scaleX = 1.2 + Math.random() * 0.6; // Scale the already larger trees from Tree class tree.scaleY = 1.2 + Math.random() * 0.6; // Scale the already larger trees from Tree class tree.alpha = 0.9 + Math.random() * 0.1; // More opaque to stand out trees.push(tree); // Add trees behind other game elements game.addChildAt(tree, 4); // Insert after background elements but before player } } // Spawn crystal at exactly 500 meters (within 1000 meters) var currentDistance = Math.floor(distanceScore / 10); if (currentDistance >= 500 && !crystalSpawned) { spawnGem(); crystalSpawned = true; } // Spawn end gate at exactly 780 meters for each chapter var endGateDistance = gamePartNumber === 1 ? 780 : 780; // Same distance for both chapters if (currentDistance >= endGateDistance && !endGateSpawned) { endGate = new EndGate(); endGate.x = 2048 + 200; // Spawn off-screen right endGate.y = groundY + 75; // Same position as starting gate game.addChild(endGate); endGateSpawned = true; } // Check if player crosses the end gate if (endGate && endGate.parent && !endGate.crossed) { if (player.x >= endGate.x - 50 && player.x <= endGate.x + 50) { endGate.triggerWin(); } } // Update and check coins for (var j = coins.length - 1; j >= 0; j--) { var coin = coins[j]; coin.speed = gameSpeed; // Check collection or spin attack var distanceX = coin.x - player.x; var distanceY = coin.y - player.y; var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); var isInSpinRange = player.isSpinning && distance <= player.spinDamageRadius; if (!coin.collected && (player.intersects(coin) || isInSpinRange)) { coin.collected = true; coinCount++; // Add coin to total persistent storage storage.totalCoins = (storage.totalCoins || 0) + 1; LK.setScore(coinCount); coinText.setText(coinCount); // Font is already set in the Text2 constructor, no need to change it here // Scoreboard update removed LK.getSound('coin').play(); // Visual feedback - enhanced for spin attacks if (isInSpinRange) { LK.effects.flashObject(coin, 0xFF4500, 300); } else { LK.effects.flashObject(coin, 0xFFFFFF, 300); } tween(coin, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, onFinish: function onFinish() { coin.destroy(); } }); coins.splice(j, 1); continue; } // Remove off-screen coins if (coin.x < -50) { coin.destroy(); coins.splice(j, 1); } } // Update and check gems for (var g = gems.length - 1; g >= 0; g--) { var gem = gems[g]; gem.speed = gameSpeed; // Check collection or spin attack var distanceX = gem.x - player.x; var distanceY = gem.y - player.y; var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); var isInSpinRange = player.isSpinning && distance <= player.spinDamageRadius; if (!gem.collected && (player.intersects(gem) || isInSpinRange)) { gem.collected = true; gemCount++; coinCount += 5; // Gems are worth 5 points // Add gem bonus to total coins storage storage.totalCoins = (storage.totalCoins || 0) + 5; LK.setScore(coinCount); coinText.setText(coinCount); // Scoreboard update removed // Create gem UI elements on first gem collection if (!gemIcon) { gemIcon = LK.getAsset('gem_icon', { anchorX: 0.5, anchorY: 0, x: 150, y: 20 }); LK.gui.top.addChild(gemIcon); gemText = new Text2('0', { size: 100, fill: 0xFF69B4, font: "Avenir", fontWeight: '900', stroke: 0x000000, strokeThickness: 15 }); gemText.anchor.set(0.5, 0); gemText.x = 150; gemText.y = 90; LK.gui.top.addChild(gemText); } gemText.setText(gemCount); // Font is already set in the Text2 constructor, no need to change it here LK.getSound('gem_collect').play(); LK.getSound('happy_giggle').play(); // Fade out gem UI elements after 2 seconds LK.setTimeout(function () { if (gemIcon && gemIcon.parent) { tween(gemIcon, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { gemIcon.destroy(); gemIcon = null; } }); } if (gemText && gemText.parent) { tween(gemText, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { gemText.destroy(); gemText = null; } }); } }, 2000); // Visual feedback with particle explosion - enhanced for spin attacks if (isInSpinRange) { LK.effects.flashScreen(0xFF4500, 200); } else { LK.effects.flashScreen(0x00FFFF, 200); } gem.createParticles(); gem.destroy(); gems.splice(g, 1); continue; } // Remove off-screen gems if (gem.x < -50) { gem.destroy(); gems.splice(g, 1); } } // Update and check boxes for (var b = boxes.length - 1; b >= 0; b--) { var box = boxes[b]; box.speed = gameSpeed; // Check collision with player or spin attack var distanceX = box.x - player.x; var distanceY = box.y - player.y; var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); var isInSpinRange = player.isSpinning && distance <= player.spinDamageRadius; if (!box.broken && (player.intersects(box) || isInSpinRange)) { box.breakBox(); boxes.splice(b, 1); continue; } // Remove off-screen boxes if (box.x < -50) { box.destroy(); boxes.splice(b, 1); } } // Update and check damage boxes for (var d = damageBoxes.length - 1; d >= 0; d--) { var damageBox = damageBoxes[d]; damageBox.speed = gameSpeed; // Check collision with player (damage boxes can't be destroyed by spin attack) if (!damageBox.damaged && player.intersects(damageBox)) { damageBox.damagePlayer(); damageBoxes.splice(d, 1); continue; } // Remove off-screen damage boxes if (damageBox.x < -50) { damageBox.destroy(); damageBoxes.splice(d, 1); } } // Update and clean up trails for (var k = trails.length - 1; k >= 0; k--) { var trail = trails[k]; trail.speed = gameSpeed; // Remove off-screen or completely faded trails if (trail.x < -100 || trail.alpha <= 0.1) { trail.destroy(); trails.splice(k, 1); } } // Update and clean up trees for (var t = trees.length - 1; t >= 0; t--) { var tree = trees[t]; tree.speed = gameSpeed * 0.8; // Trees move slightly slower for depth effect // Remove off-screen trees if (tree.x < -200) { tree.destroy(); trees.splice(t, 1); } } // Zoom in effect - scale the game container by 10 units game.scaleX = 1.0 + 10 / 1000; // Convert 10 units to scale factor game.scaleY = 1.0 + 10 / 1000; // Convert 10 units to scale factor // Update sun and moon cycle cycleTimer++; var cycleProgress = cycleTimer % cycleDuration / cycleDuration; // Sun moves from left to right in an arc var sunProgress = cycleProgress; var sunX = 200 + sunProgress * 1600; // Move from left (200) to right (1800) var sunY = 400 - Math.sin(sunProgress * Math.PI) * 200; // Arc shape - higher in middle // Moon moves from right to left in an arc (opposite direction) var moonProgress = (cycleProgress + 0.5) % 1; // Offset by half cycle var moonX = 1800 - moonProgress * 1600; // Move from right (1800) to left (200) var moonY = 400 - Math.sin(moonProgress * Math.PI) * 200; // Arc shape - higher in middle // Use tweens for smooth movement tween(sun, { x: sunX, y: sunY }, { duration: 50, easing: tween.linear }); tween(moon, { x: moonX, y: moonY }, { duration: 50, easing: tween.linear }); // Day/night transition logic var newIsDay = sunY < 600; // Day when sun is above horizon if (newIsDay !== isDay) { isDay = newIsDay; if (isDay) { // Transition to day tween(sun, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 1000, easing: tween.easeInOut }); tween(moon, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 1000, easing: tween.easeInOut }); // Brighten background for day tween(game, { tint: 0xFFFFFF }, { duration: 1500, easing: tween.easeInOut }); } else { // Transition to night tween(sun, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 1000, easing: tween.easeInOut }); tween(moon, { alpha: 1, scaleX: 1.0, scaleY: 1.0 }, { duration: 1000, easing: tween.easeInOut }); // Darken background for night tween(game, { tint: 0x4A4A6A }, { duration: 1500, easing: tween.easeInOut }); } } // Add gentle pulsing animation to sun if (isDay && sun.alpha > 0.5) { var sunPulse = 1.0 + Math.sin(cycleTimer * 0.1) * 0.05; sun.scaleX = 1.2 * sunPulse; sun.scaleY = 1.2 * sunPulse; } // Add gentle glow animation to moon if (!isDay && moon.alpha > 0.5) { var moonGlow = 1.0 + Math.sin(cycleTimer * 0.08) * 0.03; moon.scaleX = 1.0 * moonGlow; moon.scaleY = 1.0 * moonGlow; } // Music will transition automatically when first track ends // Ensure player stays in foreground by re-adding it if (player.parent !== game) { game.addChild(player); } else { // Move player to top of render order game.removeChild(player); game.addChild(player); } }; // Scoreboard background removed // Game pause/resume functionality var gamePaused = false; var pausedUpdateFunction = null; LK.pauseGame = function () { if (!gamePaused) { gamePaused = true; pausedUpdateFunction = game.update; game.update = function () { // Game is paused - no updates }; } }; LK.resumeGame = function () { if (gamePaused) { gamePaused = false; if (pausedUpdateFunction) { game.update = pausedUpdateFunction; pausedUpdateFunction = null; } } }; // Initialize store var store = new Store(); // God Mode variables var godModeActive = false; var normalSpeed = 24; // God Mode button var godModeButton = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2, tint: 0xFF4444, x: 0, y: 400 }); LK.gui.topLeft.addChild(godModeButton); var godModeButtonText = new Text2('GOD MODE', { size: 35, fill: 0xFFFFFF, font: "Avenir", fontWeight: 'bold' }); godModeButtonText.anchor.set(0.5, 0.5); godModeButtonText.x = 0; godModeButtonText.y = 400; LK.gui.topLeft.addChild(godModeButtonText); // God Mode button interaction godModeButton.down = function () { toggleGodMode(); }; // Teleport button var teleportButton = LK.getAsset('square_button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2, tint: 0x8A2BE2, x: 0, y: 520 }); LK.gui.topLeft.addChild(teleportButton); var teleportButtonText = new Text2('TELEPORT', { size: 30, fill: 0xFFFFFF, font: "Avenir", fontWeight: 'bold' }); teleportButtonText.anchor.set(0.5, 0.5); teleportButtonText.x = 0; teleportButtonText.y = 520; LK.gui.topLeft.addChild(teleportButtonText); // Teleport button interaction teleportButton.down = function () { teleportToLastGate(); }; // Function to teleport 50 meters away from last gate function teleportToLastGate() { var targetDistance = 730; // 50 meters before the gate at 780 meters var currentDistance = Math.floor(distanceScore / 10); if (currentDistance < targetDistance) { // Calculate how much distance to add var distanceToAdd = (targetDistance - currentDistance) * 10; distanceScore += distanceToAdd; // Flash effect when teleporting LK.effects.flashScreen(0x8A2BE2, 500); LK.effects.flashObject(player, 0x8A2BE2, 800); // Update distance display scoreText.setText(Math.floor(distanceScore / 10) + 'm'); // Font is already set in the Text2 constructor, no need to change it here } } // Function to toggle God Mode function toggleGodMode() { godModeActive = !godModeActive; if (godModeActive) { // Activate God Mode player.isInvulnerable = true; gameSpeed = normalSpeed * 3; // Very high speed godModeButton.tint = 0x00FF00; // Green when active godModeButtonText.setText('GOD: ON'); godModeButtonText.fill = 0x00FF00; // Flash effect when activating LK.effects.flashScreen(0xFFD700, 500); LK.effects.flashObject(player, 0xFFD700, 1000); } else { // Deactivate God Mode player.isInvulnerable = false; gameSpeed = normalSpeed + distanceScore / 1000; // Return to normal progression godModeButton.tint = 0xFF4444; // Red when inactive godModeButtonText.setText('GOD MODE'); godModeButtonText.fill = 0xFFFFFF; } } // Store button hidden for now // var storeButton = LK.getAsset('square_button', { // anchorX: 0.5, // anchorY: 0.5, // scaleX: 2, // scaleY: 2, // tint: 0x4444FF, // x: 0, // y: 400 // }); // LK.gui.topLeft.addChild(storeButton); // var storeButtonText = new Text2('STORE', { // size: 40, // fill: 0xFFFFFF, // fontWeight: 'bold' // }); // storeButtonText.anchor.set(0.5, 0.5); // storeButtonText.x = 0; // storeButtonText.y = 400; // LK.gui.topLeft.addChild(storeButtonText); // Store button interaction // storeButton.down = function () { // store.openStore(); // }; // Get player name and last score from storage var playerName = storage.playerName || 'Player'; var lastScore = storage.lastScore || 0; // Sun and moon cycle variables var sun, moon; var cycleTimer = 0; var cycleDuration = 3000; // 50 seconds for full day/night cycle (3000 ticks at 60fps) var isDay = true; // Character switching variables for C1-C6 cycling every 5 meters var characterOrder = ['C1', 'C2', 'C3', 'C4', 'C5', 'C6']; var currentCharacterIndex = 0; var currentCharacter = characterOrder[currentCharacterIndex]; var lastSwapDistance = 0; var nextSwapDistance = 5; // Player name text removed // Combined score text removed // Scoreboard update function removed // CHAPTER 1: THE BEGINNING ADVENTURE // This is the first chapter of the endless runner adventure var gamePartTitle = "CHAPTER 1: THE BEGINNING"; var gamePartNumber = 1; var isFirstChapter = true; // Display Part 1 title at game start var partTitleText = new Text2(gamePartTitle, { size: 80, fill: 0xFFD700, font: "Avenir", fontWeight: 'bold', stroke: 0x000000, strokeThickness: 8 }); partTitleText.anchor.set(0.5, 0.5); partTitleText.x = 1024; partTitleText.y = 300; partTitleText.alpha = 0; game.addChild(partTitleText); // Animate part title appearance tween(partTitleText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Fade out title after showing LK.setTimeout(function () { tween(partTitleText, { alpha: 0 }, { duration: 1500, onFinish: function onFinish() { partTitleText.destroy(); } }); }, 3000); } }); // Function to complete Part 1 and prepare for next part function completePart1() { storage.part1_completed = true; storage.part1_finalScore = coinCount; storage.part1_finalDistance = Math.floor(distanceScore / 10); storage.part1_finalGems = gemCount; storage.part1_completionTime = Date.now(); // Mark that Part 2 is now available storage.part2_unlocked = true; // Prepare transition to Chapter 2 prepareChapter2Transition(); } // Function to prepare and transition to Chapter 2 function prepareChapter2Transition() { // Save Chapter 1 completion data storage.chapter1_completed = true; storage.chapter1_finalScore = coinCount; storage.chapter1_finalDistance = Math.floor(distanceScore / 10); storage.chapter1_finalGems = gemCount; storage.chapter1_completionTime = Date.now(); // Initialize Chapter 2 variables storage.chapter2_unlocked = true; storage.currentChapter = 2; // Create Chapter 2 title display var chapter2Title = new Text2('CHAPTER 2: THE CRYSTAL QUEST', { size: 90, fill: 0x00FFFF, font: "Avenir", fontWeight: 'bold', stroke: 0x000000, strokeThickness: 10 }); chapter2Title.anchor.set(0.5, 0.5); chapter2Title.x = 1024; chapter2Title.y = 1000; chapter2Title.alpha = 0; game.addChild(chapter2Title); // Animate Chapter 2 title appearance tween(chapter2Title, { alpha: 1, y: 900, scaleX: 1.1, scaleY: 1.1 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Show transition message var transitionText = new Text2('Starting Chapter 2...', { size: 60, fill: 0xFFFFFF, font: "Avenir", fontStyle: 'italic' }); transitionText.anchor.set(0.5, 0.5); transitionText.x = 1024; transitionText.y = 1100; transitionText.alpha = 0; game.addChild(transitionText); tween(transitionText, { alpha: 1 }, { duration: 1000, easing: tween.easeIn, onFinish: function onFinish() { // Initialize Chapter 2 after transition LK.setTimeout(function () { initializeChapter2(); }, 1000); } }); // Clean up transition elements after delay LK.setTimeout(function () { chapter2Title.destroy(); transitionText.destroy(); }, 4000); } }); } // Function to save complete Chapter 1 game state function saveCompleteChapter1() { // Save all Chapter 1 game state variables storage.chapter1_complete_coinCount = coinCount; storage.chapter1_complete_gemCount = gemCount; storage.chapter1_complete_distanceScore = distanceScore; storage.chapter1_complete_playerLives = playerLives; storage.chapter1_complete_gameSpeed = gameSpeed; storage.chapter1_complete_currentCharacter = currentCharacter; storage.chapter1_complete_crystalSpawned = crystalSpawned; storage.chapter1_complete_endGateSpawned = endGateSpawned; storage.chapter1_complete_playerX = player.x; storage.chapter1_complete_playerY = player.y; storage.chapter1_complete_isDay = isDay; storage.chapter1_complete_cycleTimer = cycleTimer; storage.chapter1_complete_spawnTimer = spawnTimer; storage.chapter1_complete_trailTimer = trailTimer; // Save God mode state storage.chapter1_complete_godModeActive = godModeActive; storage.chapter1_complete_normalSpeed = normalSpeed; // Save counters for active objects storage.chapter1_complete_coinsCount = coins.length; storage.chapter1_complete_gemsCount = gems.length; storage.chapter1_complete_boxesCount = boxes.length; storage.chapter1_complete_damageBoxesCount = damageBoxes.length; storage.chapter1_complete_treesCount = trees.length; storage.chapter1_complete_trailsCount = trails.length; // Mark Chapter 1 as completed storage.chapter1_fully_completed = true; storage.chapter1_completion_time = Date.now(); // Initialize Chapter 2 preparation storage.chapter2_ready = true; storage.chapter2_unlocked = true; storage.currentChapter = 2; } // Function to save all game variables to Dave 1 profile function saveDave1Profile() { // Save all current game state variables to storage with Dave 1 identifier storage.dave1_coinCount = coinCount; storage.dave1_gemCount = gemCount; storage.dave1_distanceScore = distanceScore; storage.dave1_playerLives = playerLives; storage.dave1_gameSpeed = gameSpeed; storage.dave1_currentCharacter = currentCharacter; storage.dave1_crystalSpawned = crystalSpawned; storage.dave1_endGateSpawned = endGateSpawned; storage.dave1_playerX = player.x; storage.dave1_playerY = player.y; storage.dave1_isDay = isDay; storage.dave1_cycleTimer = cycleTimer; storage.dave1_spawnTimer = spawnTimer; storage.dave1_trailTimer = trailTimer; // Save God mode state storage.dave1_godModeActive = godModeActive; storage.dave1_normalSpeed = normalSpeed; // Save counters for active objects storage.dave1_coinsCount = coins.length; storage.dave1_gemsCount = gems.length; storage.dave1_boxesCount = boxes.length; storage.dave1_damageBoxesCount = damageBoxes.length; storage.dave1_treesCount = trees.length; storage.dave1_trailsCount = trails.length; // Save profile identifier storage.dave1_profileName = "Dave 1"; storage.dave1_saveTime = Date.now(); // Save chapter information storage.dave1_currentChapter = gamePartNumber; storage.dave1_chapterTitle = gamePartTitle; storage.dave1_isFirstChapter = isFirstChapter; // Save chapter progress storage.dave1_chapter1_completed = storage.chapter1_completed || false; storage.dave1_chapter2_unlocked = storage.chapter2_unlocked || false; storage.dave1_currentChapter = storage.currentChapter || 1; } // Function to initialize Chapter 2 with same mechanics as Chapter 1 function initializeChapter2() { // Save Chapter 1 state first saveCompleteChapter1(); // Reset all game variables for Chapter 2 start coinCount = 0; gemCount = 0; distanceScore = 0; playerLives = 3; gameSpeed = 24; normalSpeed = 24; spawnTimer = 0; trailTimer = 0; crystalSpawned = false; endGateSpawned = false; godModeActive = false; cycleTimer = 0; isDay = true; // Clear all object arrays coins = []; gems = []; boxes = []; damageBoxes = []; trails = []; trees = []; // Update chapter info gamePartTitle = "CHAPTER 2: THE CRYSTAL QUEST"; gamePartNumber = 2; isFirstChapter = false; // Reset character cycling for Chapter 2 currentCharacterIndex = 0; currentCharacter = characterOrder[currentCharacterIndex]; lastSwapDistance = 0; nextSwapDistance = 5; // Reset player position player.x = 400; player.y = groundY + 20; player.isInvulnerable = false; // Update UI coinText.setText('0'); // Font is already set in the Text2 constructor, no need to change it here livesText.setText('3'); // Font is already set in the Text2 constructor, no need to change it here // Reset God Mode button godModeButton.tint = 0xFF4444; godModeButtonText.setText('GOD MODE'); godModeButtonText.fill = 0xFFFFFF; // Show Chapter 2 title var chapter2TitleText = new Text2(gamePartTitle, { size: 80, fill: 0x00FFFF, font: "Avenir", fontWeight: 'bold', stroke: 0x000000, strokeThickness: 8 }); chapter2TitleText.anchor.set(0.5, 0.5); chapter2TitleText.x = 1024; chapter2TitleText.y = 300; chapter2TitleText.alpha = 0; game.addChild(chapter2TitleText); // Animate Chapter 2 title appearance tween(chapter2TitleText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Fade out title after showing LK.setTimeout(function () { tween(chapter2TitleText, { alpha: 0 }, { duration: 1500, onFinish: function onFinish() { chapter2TitleText.destroy(); } }); }, 3000); } }); // Create new starting gate for Chapter 2 startingGate = new StartingGate(); startingGate.x = 400 + 3 * 10; startingGate.y = groundY + 75; game.addChild(startingGate); // Player name text update removed } // Create 10 layers and order them from 1 to 10 var layer1 = new Container(); layer1.name = "Layer 1"; layer1.visible = true; // Layer 1 is visible at the back game.addChildAt(layer1, 0); // Insert at index 0 to place at the back var layer2 = new Container(); layer2.name = "Layer 2"; layer2.visible = false; // Layer 2 is in front but not visible game.addChild(layer2); var layer3 = new Container(); layer3.name = "Layer 3"; layer3.visible = false; // Layer 3 is in front but not visible game.addChild(layer3); var layer4 = new Container(); layer4.name = "Layer 4"; layer4.visible = false; // Layer 4 is in front but not visible game.addChild(layer4); var layer5 = new Container(); layer5.name = "Layer 5"; layer5.visible = false; // Layer 5 is in front but not visible game.addChild(layer5); var layer6 = new Container(); layer6.name = "Layer 6"; layer6.visible = false; // Layer 6 is in front but not visible game.addChild(layer6); var layer7 = new Container(); layer7.name = "Layer 7"; layer7.visible = false; // Layer 7 is in front but not visible game.addChild(layer7); var layer8 = new Container(); layer8.name = "Layer 8"; layer8.visible = false; // Layer 8 is in front but not visible game.addChild(layer8); var layer9 = new Container(); layer9.name = "Layer 9"; layer9.visible = false; // Layer 9 is in front but not visible game.addChild(layer9); var layer10 = new Container(); layer10.name = "Layer 10"; layer10.visible = false; // Layer 10 is in front but not visible game.addChild(layer10); ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
totalCoins: 0,
powerUps: {},
playerName: "Player",
lastScore: 0,
highScore: 0,
gamesPlayed: 0,
totalDistance: 0,
bestDistance: 0,
achievements: {},
settings: {
soundEnabled: true,
musicEnabled: true,
difficulty: "normal"
}
});
/****
* Classes
****/
var Box = Container.expand(function () {
var self = Container.call(this);
var boxGraphics = self.attachAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFB347 // Bright orange color
});
self.speed = gameSpeed;
self.broken = false;
self.health = 1; // Can be broken in one hit
self.update = function () {
self.x -= self.speed;
};
// Method to break the box and spawn coins
self.breakBox = function () {
if (!self.broken) {
self.broken = true;
// Create breaking effect
LK.effects.flashObject(self, 0xFFFFFF, 300);
// Spawn 2-3 coins around the box
var numCoins = 2 + Math.floor(Math.random() * 2);
for (var c = 0; c < numCoins; c++) {
var coin = new Coin();
coin.x = self.x + (Math.random() - 0.5) * 100;
coin.y = self.y + (Math.random() - 0.5) * 80;
coins.push(coin);
game.addChild(coin);
}
// Simple fade out without scaling or rotation
tween(self, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
return self;
});
var C2 = Container.expand(function () {
var self = Container.call(this);
var c2Graphics = self.attachAsset('C2', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
// Set custom hitbox dimensions
self.hitboxWidth = 200;
self.hitboxHeight = 200;
// Override width and height properties for collision detection
Object.defineProperty(self, 'width', {
get: function get() {
return self.hitboxWidth;
},
set: function set(value) {
self.hitboxWidth = value;
}
});
Object.defineProperty(self, 'height', {
get: function get() {
return self.hitboxHeight;
},
set: function set(value) {
self.hitboxHeight = value;
}
});
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var C3 = Container.expand(function () {
var self = Container.call(this);
var c3Graphics = self.attachAsset('C3', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
// Set custom hitbox dimensions
self.hitboxWidth = 200;
self.hitboxHeight = 200;
// Override width and height properties for collision detection
Object.defineProperty(self, 'width', {
get: function get() {
return self.hitboxWidth;
},
set: function set(value) {
self.hitboxWidth = value;
}
});
Object.defineProperty(self, 'height', {
get: function get() {
return self.hitboxHeight;
},
set: function set(value) {
self.hitboxHeight = value;
}
});
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var C4 = Container.expand(function () {
var self = Container.call(this);
var c4Graphics = self.attachAsset('C4', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
// Set custom hitbox dimensions
self.hitboxWidth = 200;
self.hitboxHeight = 200;
// Override width and height properties for collision detection
Object.defineProperty(self, 'width', {
get: function get() {
return self.hitboxWidth;
},
set: function set(value) {
self.hitboxWidth = value;
}
});
Object.defineProperty(self, 'height', {
get: function get() {
return self.hitboxHeight;
},
set: function set(value) {
self.hitboxHeight = value;
}
});
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var C5 = Container.expand(function () {
var self = Container.call(this);
var c5Graphics = self.attachAsset('C5', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
// Set custom hitbox dimensions
self.hitboxWidth = 200;
self.hitboxHeight = 200;
// Override width and height properties for collision detection
Object.defineProperty(self, 'width', {
get: function get() {
return self.hitboxWidth;
},
set: function set(value) {
self.hitboxWidth = value;
}
});
Object.defineProperty(self, 'height', {
get: function get() {
return self.hitboxHeight;
},
set: function set(value) {
self.hitboxHeight = value;
}
});
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var C6 = Container.expand(function () {
var self = Container.call(this);
var c6Graphics = self.attachAsset('C6', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
// Set custom hitbox dimensions
self.hitboxWidth = 200;
self.hitboxHeight = 200;
// Override width and height properties for collision detection
Object.defineProperty(self, 'width', {
get: function get() {
return self.hitboxWidth;
},
set: function set(value) {
self.hitboxWidth = value;
}
});
Object.defineProperty(self, 'height', {
get: function get() {
return self.hitboxHeight;
},
set: function set(value) {
self.hitboxHeight = value;
}
});
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Create aura effect
var aura = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.3,
tint: 0xFFD700
});
self.speed = gameSpeed;
self.collected = false;
// Floating animation
self.floatOffset = 0;
// Start aura pulsing animation
function startAuraPulse() {
tween(aura, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0.1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(aura, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: startAuraPulse
});
}
});
}
startAuraPulse();
self.update = function () {
self.x -= self.speed;
// Floating animation
self.floatOffset += 0.15;
coinGraphics.y = Math.sin(self.floatOffset) * 10;
aura.y = Math.sin(self.floatOffset) * 10;
// Rotation animation
coinGraphics.rotation += 0.1;
aura.rotation -= 0.05; // Counter-rotate aura for visual effect
};
return self;
});
var DamageBox = Container.expand(function () {
var self = Container.call(this);
var damageBoxGraphics = self.attachAsset('tnt_box', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0x8B4513 // Brown box color with TNT styling
});
self.speed = gameSpeed;
self.damaged = false;
self.update = function () {
self.x -= self.speed;
};
// Method to cause damage to player
self.damagePlayer = function () {
if (!self.damaged && !player.isInvulnerable && !godModeActive) {
self.damaged = true;
playerLives--;
// Play heart damage sound when lives decrease
LK.getSound('Canazalma').play();
// Add screen shake effect
var originalX = game.x;
var originalY = game.y;
var shakeIntensity = 20;
var shakeDuration = 300;
// Create shake animation sequence
tween(game, {
x: originalX + shakeIntensity,
y: originalY + shakeIntensity * 0.5
}, {
duration: shakeDuration / 6,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: originalX - shakeIntensity,
y: originalY - shakeIntensity * 0.5
}, {
duration: shakeDuration / 6,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: originalX + shakeIntensity * 0.5,
y: originalY + shakeIntensity * 0.3
}, {
duration: shakeDuration / 6,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: originalX - shakeIntensity * 0.5,
y: originalY - shakeIntensity * 0.3
}, {
duration: shakeDuration / 6,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: originalX,
y: originalY
}, {
duration: shakeDuration / 3,
easing: tween.easeOut
});
}
});
}
});
}
});
}
});
// Update lives counter text with animation
livesText.setText(playerLives);
// Font is already set in the Text2 constructor, no need to change it here
// Animate lives text when taking damage
tween(livesText, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(livesText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// Flash heart when taking damage
if (playerLives > 0) {
LK.effects.flashObject(hearts[0], 0xFF0000, 500);
} else {
hearts[0].alpha = 0.3;
}
// Make player invulnerable temporarily
player.isInvulnerable = true;
LK.effects.flashObject(player, 0xFF0000, 1000);
// Remove invulnerability after 2 seconds
LK.setTimeout(function () {
player.isInvulnerable = false;
}, 2000);
// Check for game over
if (playerLives <= 0) {
LK.showGameOver();
}
// Create explosion effect when damaged
self.createExplosion();
}
};
// Method to create explosion effect
self.createExplosion = function () {
// Create explosion particles
for (var p = 0; p < 12; p++) {
var particle = LK.getAsset('tnt_box', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
alpha: 1.0,
x: self.x,
y: self.y,
tint: 0x654321
});
game.addChild(particle);
var angle = p / 12 * Math.PI * 2;
var distance = 100 + Math.random() * 80;
var targetX = self.x + Math.cos(angle) * distance;
var targetY = self.y + Math.sin(angle) * distance;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1,
rotation: Math.PI * 2 * (Math.random() > 0.5 ? 1 : -1)
}, {
duration: 600 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
// Flash screen and hide the damage box
LK.effects.flashScreen(0xFF0000, 300);
tween(self, {
alpha: 0,
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var EndGate = Container.expand(function () {
var self = Container.call(this);
var gateGraphics = self.attachAsset('end_gate', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFFD700 // Golden color for finish gate
});
// Create circular portal effect inside the gate (same as StartingGate)
var portalCircle = self.attachAsset('A11', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
x: 0,
y: -280,
alpha: 0.8,
tint: 0x00FFFF
});
// Start rotating animation for the portal
function startPortalRotation() {
tween(portalCircle, {
rotation: Math.PI * 2
}, {
duration: 800,
easing: tween.linear,
onFinish: startPortalRotation
});
}
// Add pulsing animation to make portal more dynamic
function startPortalPulse() {
tween(portalCircle, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.6
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(portalCircle, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: startPortalPulse
});
}
});
}
startPortalRotation();
startPortalPulse();
// Add victory glow effect
var victoryGlow = self.attachAsset('end_gate', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.4,
tint: 0xFFFFFF // White victory glow
});
// Start victory glow pulsing animation
function startVictoryPulse() {
tween(victoryGlow, {
scaleX: 1.7,
scaleY: 1.7,
alpha: 0.1
}, {
duration: 900,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(victoryGlow, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.4
}, {
duration: 900,
easing: tween.easeInOut,
onFinish: startVictoryPulse
});
}
});
}
startVictoryPulse();
self.speed = gameSpeed;
self.crossed = false;
self.update = function () {
self.x -= self.speed;
};
// Method to trigger win condition
self.triggerWin = function () {
if (!self.crossed) {
self.crossed = true;
// Handle chapter completion based on current chapter
if (gamePartNumber === 1) {
// Complete Chapter 1 and transition to Chapter 2
completePart1();
var completionMessage = 'CHAPTER 1 COMPLETED!';
var completionColor = 0x00FF00;
} else {
// Complete Chapter 2 - final completion
saveCompleteChapter1(); // Save final state
}
// Create victory particle effect
for (var p = 0; p < 20; p++) {
var particle = LK.getAsset('end_gate', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 1.0,
x: self.x,
y: self.y - 200,
tint: p % 3 === 0 ? 0xFFD700 : p % 3 === 1 ? 0xFFFFFF : 0x00FF00
});
game.addChild(particle);
var angle = p / 20 * Math.PI * 2;
var distance = 200 + Math.random() * 150;
var targetX = self.x + Math.cos(angle) * distance;
var targetY = self.y - 200 + Math.sin(angle) * distance;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1,
rotation: Math.PI * 3
}, {
duration: 1500 + Math.random() * 500,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
// Chapter completion message removed for Chapter 2
// Flash screen with victory colors
LK.effects.flashScreen(0xFFD700, 800);
// Show collectibles summary after a brief delay
LK.setTimeout(function () {
showCollectiblesSummary();
}, 800);
// Handle final win condition based on chapter
if (gamePartNumber === 2) {
// Final game completion for Chapter 2
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
} else {
// Chapter 1 - continue to Chapter 2 (transition handled in completePart1)
}
}
};
return self;
});
var Gem = Container.expand(function () {
var self = Container.call(this);
var gemGraphics = self.attachAsset('gem', {
anchorX: 0.5,
anchorY: 0.5
});
// Create crystalline aura effect
var aura1 = self.attachAsset('gem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3,
alpha: 0.4,
tint: 0x00FFFF
});
var aura2 = self.attachAsset('gem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.6,
scaleY: 1.6,
alpha: 0.2,
tint: 0x8A2BE2
});
self.speed = gameSpeed;
self.collected = false;
// Crystal aura remains steady without pulsing animation
self.update = function () {
self.x -= self.speed;
// Crystal remains steady without rotation
};
// Method to create particle effect when collected
self.createParticles = function () {
for (var p = 0; p < 8; p++) {
var particle = LK.getAsset('gem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8,
x: self.x,
y: self.y,
tint: p % 2 === 0 ? 0x00FFFF : 0x8A2BE2
});
game.addChild(particle);
var angle = p / 8 * Math.PI * 2;
var distance = 150 + Math.random() * 100;
var targetX = self.x + Math.cos(angle) * distance;
var targetY = self.y + Math.sin(angle) * distance;
tween(particle, {
x: targetX,
y: targetY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1,
rotation: Math.PI * 2
}, {
duration: 800 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var foxGraphics = self.attachAsset('C1', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
// Set custom hitbox dimensions
self.hitboxWidth = 200;
self.hitboxHeight = 200;
// Override width and height properties for collision detection
Object.defineProperty(self, 'width', {
get: function get() {
return self.hitboxWidth;
},
set: function set(value) {
self.hitboxWidth = value;
}
});
Object.defineProperty(self, 'height', {
get: function get() {
return self.hitboxHeight;
},
set: function set(value) {
self.hitboxHeight = value;
}
});
self.isJumping = false;
self.velocityY = 0;
self.groundY = 2732 - 150 + 20; // Ground level
self.jumpPower = -27;
self.gravity = 1.2;
self.runAnimationTimer = 0;
self.isSpinning = false;
self.spinDamageRadius = 150;
self.jumpsRemaining = 2; // Allow double jump
self.jump = function () {
if (self.jumpsRemaining > 0) {
// First jump or double jump
if (!self.isJumping) {
self.isJumping = true;
}
self.velocityY = self.jumpPower;
self.jumpsRemaining--;
LK.getSound('jump').play();
// Simple jump animation without somersault - just scale effect
var jumpDuration = 300; // Shorter duration for simple jump
// Different animation for double jump
var isDoubleJump = self.jumpsRemaining === 0 && self.isJumping;
if (isDoubleJump) {
// Double jump animation - enhanced scale effect
tween(foxGraphics, {
scaleX: 1.4,
scaleY: 1.4
}, {
duration: jumpDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(foxGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeIn
});
}
});
} else {
// Regular jump animation - simple scale effect
tween(foxGraphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: jumpDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(foxGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
}
};
self.spinAttack = function () {
if (!self.isSpinning) {
self.isSpinning = true;
LK.getSound('jump').play(); // Reuse jump sound for spin
// Horizontal rotation animation around character's center
tween(foxGraphics, {
rotation: Math.PI * 4,
// 2 full horizontal rotations
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFF4500
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isSpinning = false;
foxGraphics.rotation = 0;
foxGraphics.scaleX = 1.0;
foxGraphics.scaleY = 1.0;
foxGraphics.tint = 0xFFFFFF;
}
});
}
};
self.update = function () {
if (self.isJumping) {
self.velocityY += self.gravity;
self.y += self.velocityY;
// Land on ground
if (self.y >= self.groundY) {
self.y = self.groundY;
self.isJumping = false;
self.velocityY = 0;
self.jumpsRemaining = 2; // Reset double jump when landing
// Reset character scale when landing
tween(foxGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
}
} else {
// Running animation when on ground
self.runAnimationTimer += 0.2;
foxGraphics.y = Math.sin(self.runAnimationTimer) * 3;
foxGraphics.scaleX = 1.0 + Math.sin(self.runAnimationTimer * 2) * 0.05;
}
};
return self;
});
var StartingGate = Container.expand(function () {
var self = Container.call(this);
var gateGraphics = self.attachAsset('A1', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.0,
scaleY: 1.0
});
// Create circular portal effect inside the gate
var portalCircle = self.attachAsset('A11', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
x: 0,
y: -200,
alpha: 0.8,
tint: 0x00FFFF
});
// Start rotating animation for the portal
function startPortalRotation() {
tween(portalCircle, {
rotation: Math.PI * 2
}, {
duration: 800,
easing: tween.linear,
onFinish: startPortalRotation
});
}
// Add pulsing animation to make portal more dynamic
function startPortalPulse() {
tween(portalCircle, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.6
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(portalCircle, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: startPortalPulse
});
}
});
}
startPortalRotation();
startPortalPulse();
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Store = Container.expand(function () {
var self = Container.call(this);
self.isOpen = false;
self.storeItems = [{
id: 'doubleJump',
name: 'Super Jump',
price: 50,
description: 'Higher jumps!',
owned: false
}, {
id: 'coinMagnet',
name: 'Coin Magnet',
price: 100,
description: 'Attract coins!',
owned: false
}, {
id: 'speedBoost',
name: 'Speed Boost',
price: 75,
description: 'Move faster!',
owned: false
}, {
id: 'shield',
name: 'Shield',
price: 150,
description: 'Extra protection!',
owned: false
}];
// Load owned items from storage
for (var i = 0; i < self.storeItems.length; i++) {
var item = self.storeItems[i];
item.owned = storage.powerUps[item.id] || false;
}
self.openStore = function () {
if (self.isOpen) return;
self.isOpen = true;
// Pause the game when store opens
LK.pauseGame();
// Create overlay
self.overlay = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 40,
alpha: 0.9,
tint: 0x000033,
x: 1024,
y: 1366
});
game.addChild(self.overlay);
// Create store panel
self.panel = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 25,
tint: 0x1a1a2e,
x: 1024,
y: 1366
});
game.addChild(self.panel);
// Store title
self.titleText = new Text2('POWER-UP STORE', {
size: 100,
fill: 0xFFD700,
font: "Avenir",
fontWeight: 'bold'
});
self.titleText.anchor.set(0.5, 0.5);
self.titleText.x = 1024;
self.titleText.y = 600;
game.addChild(self.titleText);
// Coin display
var totalCoins = storage.totalCoins || 0;
self.coinDisplay = new Text2('Coins: ' + totalCoins, {
size: 60,
fill: 0xFFD700,
font: "Avenir",
fontWeight: 'bold'
});
self.coinDisplay.anchor.set(0.5, 0.5);
self.coinDisplay.x = 1024;
self.coinDisplay.y = 700;
game.addChild(self.coinDisplay);
// Create store items
self.itemElements = [];
for (var i = 0; i < self.storeItems.length; i++) {
self.createStoreItem(i);
}
// Close button
self.closeButton = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
tint: 0xFF4444,
x: 1024,
y: 2000
});
game.addChild(self.closeButton);
self.closeText = new Text2('CLOSE', {
size: 50,
fill: 0xFFFFFF,
font: "Avenir",
fontWeight: 'bold'
});
self.closeText.anchor.set(0.5, 0.5);
self.closeText.x = 1024;
self.closeText.y = 2000;
game.addChild(self.closeText);
// Close button interaction
self.closeButton.down = function () {
self.closeStore();
};
// Animate store opening
tween(self.overlay, {
alpha: 0.9
}, {
duration: 300
});
tween(self.panel, {
scaleX: 20,
scaleY: 25
}, {
duration: 400,
easing: tween.easeOut
});
};
self.createStoreItem = function (index) {
var item = self.storeItems[index];
var yPos = 850 + index * 180;
// Item background
var itemBg = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 15,
scaleY: 3,
tint: item.owned ? 0x004400 : 0x333366,
x: 1024,
y: yPos
});
game.addChild(itemBg);
// Item name
var nameText = new Text2(item.name, {
size: 50,
fill: item.owned ? 0x88FF88 : 0xFFFFFF,
font: "Avenir",
fontWeight: 'bold'
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 800;
nameText.y = yPos - 20;
game.addChild(nameText);
// Item description
var descText = new Text2(item.description, {
size: 35,
fill: 0xCCCCCC,
font: "Avenir"
});
descText.anchor.set(0.5, 0.5);
descText.x = 800;
descText.y = yPos + 20;
game.addChild(descText);
// Price/Status
var priceText = new Text2(item.owned ? 'OWNED' : item.price + ' coins', {
size: 45,
fill: item.owned ? 0x00FF00 : 0xFFD700,
font: "Avenir",
fontWeight: 'bold'
});
priceText.anchor.set(0.5, 0.5);
priceText.x = 1200;
priceText.y = yPos;
game.addChild(priceText);
// Buy button (only if not owned)
if (!item.owned) {
var buyButton = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.5,
tint: 0x00AA00,
x: 1350,
y: yPos
});
game.addChild(buyButton);
var buyText = new Text2('BUY', {
size: 40,
fill: 0xFFFFFF,
font: "Avenir",
fontWeight: 'bold'
});
buyText.anchor.set(0.5, 0.5);
buyText.x = 1350;
buyText.y = yPos;
game.addChild(buyText);
buyButton.down = function () {
self.purchaseItem(index);
};
self.itemElements.push({
bg: itemBg,
name: nameText,
desc: descText,
price: priceText,
buyBtn: buyButton,
buyTxt: buyText
});
} else {
self.itemElements.push({
bg: itemBg,
name: nameText,
desc: descText,
price: priceText
});
}
};
self.purchaseItem = function (index) {
var item = self.storeItems[index];
var totalCoins = storage.totalCoins || 0;
if (totalCoins >= item.price && !item.owned) {
// Purchase successful
storage.totalCoins = totalCoins - item.price;
storage.powerUps[item.id] = true;
item.owned = true;
// Update coin display
self.coinDisplay.setText('Coins: ' + storage.totalCoins);
// Flash effect
LK.effects.flashScreen(0x00FF00, 300);
// Update item appearance
var elements = self.itemElements[index];
elements.bg.tint = 0x004400;
elements.name.fill = 0x88FF88;
elements.price.setText('OWNED');
elements.price.fill = 0x00FF00;
if (elements.buyBtn) {
elements.buyBtn.destroy();
elements.buyTxt.destroy();
}
} else {
// Not enough coins
LK.effects.flashScreen(0xFF0000, 300);
}
};
self.closeStore = function () {
if (!self.isOpen) return;
self.isOpen = false;
// Resume the game when store closes
LK.resumeGame();
// Clean up all store elements
if (self.overlay) self.overlay.destroy();
if (self.panel) self.panel.destroy();
if (self.titleText) self.titleText.destroy();
if (self.coinDisplay) self.coinDisplay.destroy();
if (self.closeButton) self.closeButton.destroy();
if (self.closeText) self.closeText.destroy();
for (var i = 0; i < self.itemElements.length; i++) {
var elements = self.itemElements[i];
elements.bg.destroy();
elements.name.destroy();
elements.desc.destroy();
elements.price.destroy();
if (elements.buyBtn) elements.buyBtn.destroy();
if (elements.buyTxt) elements.buyTxt.destroy();
}
self.itemElements = [];
};
return self;
});
var Trail = Container.expand(function () {
var self = Container.call(this);
var trailGraphics = self.attachAsset('C1', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0.7
});
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Tree = Container.expand(function () {
var self = Container.call(this);
// Random tree type (1, 2, or 3)
var treeType = Math.floor(Math.random() * 3) + 1;
var treeAssetName = 'tree' + treeType;
// Define distinct colors for each tree type to make them more distinguishable
var treeColors = [0x228B22,
// Forest Green for tree1
0x8B4513,
// Saddle Brown for tree2
0x006400 // Dark Green for tree3
];
var treeColor = treeColors[treeType - 1];
// Create tree foliage - position directly on ground without trunk
var foliage = self.attachAsset(treeAssetName, {
anchorX: 0.5,
anchorY: 1.0,
y: 0,
// Position foliage directly on ground
scaleX: 2.2,
// Make foliage much larger
scaleY: 2.2,
// Make foliage much larger
tint: treeColor // Apply distinctive color
});
// Create shadow effect to make tree more distinct
var shadow = self.attachAsset(treeAssetName, {
anchorX: 0.5,
anchorY: 1.0,
y: 5,
// Slightly offset shadow
scaleX: 2.1,
scaleY: 2.0,
alpha: 0.3,
tint: 0x000000 // Black shadow
});
self.speed = gameSpeed;
self.breathingOffset = Math.random() * Math.PI * 2; // Random phase for each tree
self.breathingSpeed = 0.8 + Math.random() * 0.4; // Slight variation in breathing speed
// Trees are now fixed - no breathing animation
function startBreathing() {
// No animation - trees remain static and fixed to ground
}
// Trees are now fixed - no swaying animation
function startSwaying() {
// No animation - trees remain static and fixed to ground
}
startBreathing();
// No swaying animation call - trees stay fixed
self.update = function () {
self.x -= self.speed;
// Trees are now fixed to ground with no floating animations
// Keep foliage and shadow at fixed positions
foliage.y = 0; // Fixed to ground
shadow.y = 5; // Fixed shadow offset
// Keep foliage and shadow at fixed scales
foliage.scaleX = 2.2;
foliage.scaleY = 2.2;
shadow.scaleX = 2.1;
shadow.scaleY = 2.0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB,
fps: 30
});
/****
* Game Code
****/
var player;
var coins = [];
var gems = [];
var boxes = [];
var damageBoxes = [];
var trails = [];
var trees = [];
var gameSpeed = 24;
var spawnTimer = 0;
var coinCount = 0;
var gemCount = 0;
var distanceScore = 0;
var groundY = 2732 - 150;
var playerLives = 3;
var trailTimer = 0;
var crystalSpawned = false;
var endGateSpawned = false;
var startingGate;
var endGate;
var background1, background2;
// UI Elements
var coinIcon = LK.getAsset('coin_icon', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 20
});
LK.gui.top.addChild(coinIcon);
var coinText = new Text2('0', {
size: 120,
fill: 0xFFFFFF,
font: "Avenir",
fontWeight: '900',
stroke: 0x000000,
strokeThickness: 15
});
coinText.anchor.set(0.5, 0);
coinText.x = 0;
coinText.y = 90;
LK.gui.top.addChild(coinText);
var gemIcon = null;
var gemText = null;
var scoreText = new Text2('Distance: 0', {
size: 120,
fill: 0xFFFFFF,
font: "Avenir",
fontStyle: 'italic',
fontWeight: 'bold'
});
scoreText.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreText);
// Create heart UI elements - only one heart image with lives counter
var hearts = [];
// Create single heart image
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
x: 150,
y: 193
});
hearts.push(heart);
LK.gui.topLeft.addChild(heart);
// Create lives counter text
var livesText = new Text2('3', {
size: 120,
fill: 0xFF8C00,
font: "Avenir",
fontWeight: '900',
stroke: 0x000000,
strokeThickness: 25
});
livesText.anchor.set(0.5, 0.5);
livesText.x = 250;
livesText.y = 173;
LK.gui.topLeft.addChild(livesText);
// Start constant bouncing animation for lives text
function startLivesBounce() {
tween(livesText, {
y: 163
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(livesText, {
y: 183
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: startLivesBounce
});
}
});
}
startLivesBounce();
;
// Function to show collectibles summary
function showCollectiblesSummary() {
// Create dark overlay
var overlay = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 40,
alpha: 0.8,
tint: 0x000000,
x: 1024,
y: 1366
});
game.addChild(overlay);
// Create summary panel
var summaryPanel = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 18,
scaleY: 15,
tint: 0x2F4F4F,
x: 1024,
y: 1366
});
game.addChild(summaryPanel);
// Title text
var titleText = new Text2('COLLECTIBLES GATHERED', {
size: 80,
fill: 0xFFD700,
font: "Avenir",
fontWeight: 'bold'
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 900;
game.addChild(titleText);
// Create animated collectible items that can be picked up
var summaryItems = [];
// Create multiple coin items based on coinCount (max 10 for display)
var coinsToShow = Math.min(coinCount, 10);
for (var c = 0; c < coinsToShow; c++) {
var summaryCoins = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
x: 600 + c % 5 * 100,
y: 1100 + Math.floor(c / 5) * 80,
alpha: 0
});
game.addChild(summaryCoins);
summaryItems.push(summaryCoins);
// Add floating animation
var floatOffset = c * 0.5;
tween(summaryCoins, {
alpha: 1,
y: summaryCoins.y - 20
}, {
duration: 500 + c * 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Continuous floating animation
function floatAnimation(coin, offset) {
tween(coin, {
y: coin.y + 15,
rotation: coin.rotation + 0.3
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(coin, {
y: coin.y - 15,
rotation: coin.rotation + 0.3
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
floatAnimation(coin, offset);
}
});
}
});
}
floatAnimation(summaryCoins, floatOffset);
}
});
}
// Create multiple gem items based on gemCount (max 5 for display)
var gemsToShow = Math.min(gemCount, 5);
for (var g = 0; g < gemsToShow; g++) {
var summaryGems = LK.getAsset('gem', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
x: 700 + g * 120,
y: 1350,
alpha: 0
});
game.addChild(summaryGems);
summaryItems.push(summaryGems);
// Add sparkling animation
tween(summaryGems, {
alpha: 1,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 600 + g * 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Continuous sparkling animation
function sparkleAnimation(gem) {
tween(gem, {
alpha: 0.7,
scaleX: 0.9,
scaleY: 0.9,
tint: 0x00FFFF
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(gem, {
alpha: 1,
scaleX: 0.8,
scaleY: 0.8,
tint: 0xFFFFFF
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
sparkleAnimation(gem);
}
});
}
});
}
sparkleAnimation(summaryGems);
}
});
}
// Coins summary text
var coinSummaryText = new Text2('Coins Collected: ' + coinCount, {
size: 60,
fill: 0xFFD700,
font: "Avenir",
fontWeight: 'bold'
});
coinSummaryText.anchor.set(0.5, 0.5);
coinSummaryText.x = 1024;
coinSummaryText.y = 1050;
game.addChild(coinSummaryText);
// Gems summary text
var gemSummaryText = new Text2('Gems Found: ' + gemCount, {
size: 60,
fill: 0xFF69B4,
font: "Avenir",
fontWeight: 'bold'
});
gemSummaryText.anchor.set(0.5, 0.5);
gemSummaryText.x = 1024;
gemSummaryText.y = 1300;
game.addChild(gemSummaryText);
// Total score
var totalText = new Text2('TOTAL SCORE: ' + coinCount, {
size: 70,
fill: 0x00FF00,
font: "Avenir",
fontWeight: 'bold'
});
totalText.anchor.set(0.5, 0.5);
totalText.x = 1024;
totalText.y = 1600;
game.addChild(totalText);
// Save final game statistics
var finalDistance = Math.floor(distanceScore / 10);
storage.lastScore = coinCount;
storage.totalCoins = (storage.totalCoins || 0) + coinCount;
if (coinCount > (storage.highScore || 0)) {
storage.highScore = coinCount;
}
if (finalDistance > (storage.bestDistance || 0)) {
storage.bestDistance = finalDistance;
}
storage.gamesPlayed = (storage.gamesPlayed || 0) + 1;
// Add instruction text
var instructionText = new Text2('Your collected treasures!', {
size: 50,
fill: 0xFFFFFF,
font: "Avenir",
fontStyle: 'italic'
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 1500;
game.addChild(instructionText);
// Animate elements appearing
tween(overlay, {
alpha: 0.8
}, {
duration: 500
});
tween(summaryPanel, {
scaleX: 18,
scaleY: 15
}, {
duration: 600,
easing: tween.easeOut
});
// Clean up summary after delay
LK.setTimeout(function () {
overlay.destroy();
summaryPanel.destroy();
titleText.destroy();
coinSummaryText.destroy();
gemSummaryText.destroy();
totalText.destroy();
instructionText.destroy();
// Clean up all summary items
for (var i = 0; i < summaryItems.length; i++) {
summaryItems[i].destroy();
}
}, 2500);
}
;
;
// Spin button removed
// Play background music constantly
LK.playMusic('Arkaplanmuzik1');
// Create animated background elements for endless scrolling with third element to fill gaps - merged with layer1
var background1 = layer1.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
var background2 = layer1.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 2250,
// Position second background at end of first background
y: 0
}));
var background3 = layer1.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 4500,
// Position third background to eliminate gaps during transitions
y: 0
}));
// Create animated ground elements for endless scrolling
var ground1 = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: groundY
}));
var ground2 = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 4008,
// Position second ground at end of first ground
y: groundY
}));
var ground3 = game.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 8016,
// Position third ground to eliminate gaps during transitions
y: groundY
}));
// Ground animation variables
var groundAnimationTimer = 0;
// Create sun and moon
sun = game.addChild(LK.getAsset('sun', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 400,
scaleX: 1.2,
scaleY: 1.2
}));
moon = game.addChild(LK.getAsset('moon', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 400,
scaleX: 1.0,
scaleY: 1.0,
alpha: 0
}));
// Create starting gate
startingGate = new StartingGate();
startingGate.x = 400 + 3 * 10; // Position at 3 meters (3m * 10 pixels per meter)
startingGate.y = groundY + 75; // Position gate a little higher above the ground
game.addChild(startingGate);
// Create player (will be added to foreground later)
player = new Player();
player.x = 400;
player.y = groundY + 20;
player.isInvulnerable = false;
// Touch controls for swipe up to jump
var touchStartY = null;
var minSwipeDistance = 100; // Minimum distance for swipe detection
game.down = function (x, y, obj) {
touchStartY = y;
};
game.up = function (x, y, obj) {
if (touchStartY !== null) {
var swipeDistance = touchStartY - y; // Positive means swipe up
if (swipeDistance >= minSwipeDistance) {
// Swipe up detected - make player jump
player.jump();
}
touchStartY = null;
}
};
// Spin button interaction removed
function spawnCoin() {
var coin = new Coin();
coin.x = 2048 + 100;
// Randomly place coins either high in air or at medium height to avoid ground-level objects
var coinHeightOptions = [groundY - 300 - Math.random() * 100,
// High in air
groundY - 180 - Math.random() * 80 // Medium height
];
coin.y = coinHeightOptions[Math.floor(Math.random() * coinHeightOptions.length)];
coins.push(coin);
game.addChild(coin);
}
function spawnGem() {
var gem = new Gem();
gem.x = 2048 + 100;
gem.y = groundY - 80 - Math.random() * 200;
gems.push(gem);
game.addChild(gem);
}
function spawnBox() {
// Randomly decide if this should be a 2-stage box (30% chance)
var isTwoStage = Math.random() < 0.3;
if (isTwoStage) {
// Create bottom box
var bottomBox = new Box();
bottomBox.x = 2048 + 100;
bottomBox.y = groundY - 45; // Closer to ground level
boxes.push(bottomBox);
game.addChild(bottomBox);
// Create top box
var topBox = new Box();
topBox.x = 2048 + 100;
topBox.y = groundY - 45 - 120; // Stack on top (120 pixels higher - increased spacing)
boxes.push(topBox);
game.addChild(topBox);
} else {
// Create single box as before
var box = new Box();
box.x = 2048 + 100;
box.y = groundY - 45; // Closer to ground level
boxes.push(box);
game.addChild(box);
}
}
function spawnDamageBox() {
// Create single TNT only
var damageBox = new DamageBox();
damageBox.x = 2048 + 100;
damageBox.y = groundY - 45; // Position TNT on ground level like boxes
damageBoxes.push(damageBox);
game.addChild(damageBox);
}
game.update = function () {
// Increase distance score
distanceScore += 3;
scoreText.setText(Math.floor(distanceScore / 10) + 'm');
// Font is already set in the Text2 constructor, no need to change it here
// Character switching logic - cycle through all 12 characters every 5 meters
var currentDistance = Math.floor(distanceScore / 10);
if (currentDistance >= nextSwapDistance) {
// Move to next character in the cycle
currentCharacterIndex = (currentCharacterIndex + 1) % characterOrder.length;
currentCharacter = characterOrder[currentCharacterIndex];
// Remove old graphics and add new character graphics
player.removeChildAt(0); // Remove current graphics
var newGraphics = player.attachAsset(currentCharacter, {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
// Update swap distances
lastSwapDistance = currentDistance;
nextSwapDistance = currentDistance + 5;
// Visual feedback for swap
LK.effects.flashObject(player, 0xFFFFFF, 300);
}
// Gradually increase speed - but respect God mode
if (!godModeActive) {
gameSpeed = 24 + distanceScore / 1000;
normalSpeed = 24 + distanceScore / 1000; // Track normal speed for God mode toggle
} else {
gameSpeed = normalSpeed * 3; // Maintain high speed in God mode
}
// Update starting gate
if (startingGate && startingGate.parent) {
startingGate.speed = gameSpeed;
// Remove gate when it goes off screen
if (startingGate.x < -200) {
startingGate.destroy();
startingGate = null;
}
}
// Update end gate
if (endGate && endGate.parent) {
endGate.speed = gameSpeed;
// Remove gate when it goes off screen (after win condition)
if (endGate.x < -400) {
endGate.destroy();
endGate = null;
}
}
// Move all background elements with game progression (same speed as ground - no parallax effect)
background1.x -= gameSpeed;
background2.x -= gameSpeed;
background3.x -= gameSpeed;
// Reset background positions for endless scrolling with immediate positioning to eliminate gaps
if (background1.x <= -2250) {
// Find the rightmost background element
var rightmostX = Math.max(background2.x, background3.x);
// Immediately position to eliminate gaps
background1.x = rightmostX + 2250;
}
if (background2.x <= -2250) {
// Find the rightmost background element
var rightmostX = Math.max(background1.x, background3.x);
// Immediately position to eliminate gaps
background2.x = rightmostX + 2250;
}
if (background3.x <= -2250) {
// Find the rightmost background element
var rightmostX = Math.max(background1.x, background2.x);
// Immediately position to eliminate gaps
background3.x = rightmostX + 2250;
}
// Move all ground elements with game progression
ground1.x -= gameSpeed;
ground2.x -= gameSpeed;
ground3.x -= gameSpeed;
// Reset ground positions for endless scrolling with immediate positioning to eliminate gaps
if (ground1.x <= -4008) {
// Find the rightmost ground element
var rightmostX = Math.max(ground2.x, ground3.x);
// Immediately position to eliminate gaps
ground1.x = rightmostX + 4008;
}
if (ground2.x <= -4008) {
// Find the rightmost ground element
var rightmostX = Math.max(ground1.x, ground3.x);
// Immediately position to eliminate gaps
ground2.x = rightmostX + 4008;
}
if (ground3.x <= -4008) {
// Find the rightmost ground element
var rightmostX = Math.max(ground1.x, ground2.x);
// Immediately position to eliminate gaps
ground3.x = rightmostX + 4008;
}
// Animate background with subtle floating movement
groundAnimationTimer += 0.1;
var backgroundOffset = Math.sin(groundAnimationTimer * 0.5) * 1;
background1.y = backgroundOffset;
background2.y = backgroundOffset;
background3.y = backgroundOffset;
// Add subtle scale animation to background for depth effect
var backgroundScaleAnimation = 1.0 + Math.sin(groundAnimationTimer * 0.3) * 0.01;
background1.scaleY = backgroundScaleAnimation;
background2.scaleY = backgroundScaleAnimation;
background3.scaleY = backgroundScaleAnimation;
// Animate ground with subtle movement
var groundOffset = Math.sin(groundAnimationTimer) * 2;
ground1.y = groundY + groundOffset;
ground2.y = groundY + groundOffset;
ground3.y = groundY + groundOffset;
// Add slight scale animation to ground
var scaleAnimation = 1.0 + Math.sin(groundAnimationTimer * 0.5) * 0.02;
ground1.scaleY = scaleAnimation;
ground2.scaleY = scaleAnimation;
ground3.scaleY = scaleAnimation;
// Update spawn timer
spawnTimer++;
trailTimer++;
// Create trail when player is jumping
if (player.isJumping && trailTimer % 3 == 0) {
var trail = new Trail();
trail.x = player.x;
trail.y = player.y;
trail.speed = gameSpeed;
trails.push(trail);
game.addChild(trail);
// Fade out trail
tween(trail, {
alpha: 0,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 500,
onFinish: function onFinish() {
trail.destroy();
}
});
}
// Spawn additional coins with increased frequency to reach 170 coins by game end
if (spawnTimer % 35 == 0 && Math.random() < 0.85) {
spawnCoin();
}
// Spawn boxes occasionally
if (spawnTimer % 90 == 0 && Math.random() < 0.6) {
spawnBox();
}
// Spawn damage boxes occasionally (less frequent than regular boxes)
if (spawnTimer % 120 == 0 && Math.random() < 0.4) {
spawnDamageBox();
}
// Spawn trees in background much more frequently and multiply them
if (spawnTimer % 60 == 0 && Math.random() < 0.9) {
// Spawn 2-4 trees per spawn cycle for denser forest
var numTrees = 2 + Math.floor(Math.random() * 3);
for (var treeIndex = 0; treeIndex < numTrees; treeIndex++) {
var tree = new Tree();
tree.x = 2048 + 200 + treeIndex * 150; // Space trees out horizontally
tree.y = groundY + 45; // Position trees 45 units lower than ground level
// Position trees at different depths for parallax effect - make them even larger and more prominent
tree.scaleX = 1.2 + Math.random() * 0.6; // Scale the already larger trees from Tree class
tree.scaleY = 1.2 + Math.random() * 0.6; // Scale the already larger trees from Tree class
tree.alpha = 0.9 + Math.random() * 0.1; // More opaque to stand out
trees.push(tree);
// Add trees behind other game elements
game.addChildAt(tree, 4); // Insert after background elements but before player
}
}
// Spawn crystal at exactly 500 meters (within 1000 meters)
var currentDistance = Math.floor(distanceScore / 10);
if (currentDistance >= 500 && !crystalSpawned) {
spawnGem();
crystalSpawned = true;
}
// Spawn end gate at exactly 780 meters for each chapter
var endGateDistance = gamePartNumber === 1 ? 780 : 780; // Same distance for both chapters
if (currentDistance >= endGateDistance && !endGateSpawned) {
endGate = new EndGate();
endGate.x = 2048 + 200; // Spawn off-screen right
endGate.y = groundY + 75; // Same position as starting gate
game.addChild(endGate);
endGateSpawned = true;
}
// Check if player crosses the end gate
if (endGate && endGate.parent && !endGate.crossed) {
if (player.x >= endGate.x - 50 && player.x <= endGate.x + 50) {
endGate.triggerWin();
}
}
// Update and check coins
for (var j = coins.length - 1; j >= 0; j--) {
var coin = coins[j];
coin.speed = gameSpeed;
// Check collection or spin attack
var distanceX = coin.x - player.x;
var distanceY = coin.y - player.y;
var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
var isInSpinRange = player.isSpinning && distance <= player.spinDamageRadius;
if (!coin.collected && (player.intersects(coin) || isInSpinRange)) {
coin.collected = true;
coinCount++;
// Add coin to total persistent storage
storage.totalCoins = (storage.totalCoins || 0) + 1;
LK.setScore(coinCount);
coinText.setText(coinCount);
// Font is already set in the Text2 constructor, no need to change it here
// Scoreboard update removed
LK.getSound('coin').play();
// Visual feedback - enhanced for spin attacks
if (isInSpinRange) {
LK.effects.flashObject(coin, 0xFF4500, 300);
} else {
LK.effects.flashObject(coin, 0xFFFFFF, 300);
}
tween(coin, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
coin.destroy();
}
});
coins.splice(j, 1);
continue;
}
// Remove off-screen coins
if (coin.x < -50) {
coin.destroy();
coins.splice(j, 1);
}
}
// Update and check gems
for (var g = gems.length - 1; g >= 0; g--) {
var gem = gems[g];
gem.speed = gameSpeed;
// Check collection or spin attack
var distanceX = gem.x - player.x;
var distanceY = gem.y - player.y;
var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
var isInSpinRange = player.isSpinning && distance <= player.spinDamageRadius;
if (!gem.collected && (player.intersects(gem) || isInSpinRange)) {
gem.collected = true;
gemCount++;
coinCount += 5; // Gems are worth 5 points
// Add gem bonus to total coins storage
storage.totalCoins = (storage.totalCoins || 0) + 5;
LK.setScore(coinCount);
coinText.setText(coinCount);
// Scoreboard update removed
// Create gem UI elements on first gem collection
if (!gemIcon) {
gemIcon = LK.getAsset('gem_icon', {
anchorX: 0.5,
anchorY: 0,
x: 150,
y: 20
});
LK.gui.top.addChild(gemIcon);
gemText = new Text2('0', {
size: 100,
fill: 0xFF69B4,
font: "Avenir",
fontWeight: '900',
stroke: 0x000000,
strokeThickness: 15
});
gemText.anchor.set(0.5, 0);
gemText.x = 150;
gemText.y = 90;
LK.gui.top.addChild(gemText);
}
gemText.setText(gemCount);
// Font is already set in the Text2 constructor, no need to change it here
LK.getSound('gem_collect').play();
LK.getSound('happy_giggle').play();
// Fade out gem UI elements after 2 seconds
LK.setTimeout(function () {
if (gemIcon && gemIcon.parent) {
tween(gemIcon, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
gemIcon.destroy();
gemIcon = null;
}
});
}
if (gemText && gemText.parent) {
tween(gemText, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
gemText.destroy();
gemText = null;
}
});
}
}, 2000);
// Visual feedback with particle explosion - enhanced for spin attacks
if (isInSpinRange) {
LK.effects.flashScreen(0xFF4500, 200);
} else {
LK.effects.flashScreen(0x00FFFF, 200);
}
gem.createParticles();
gem.destroy();
gems.splice(g, 1);
continue;
}
// Remove off-screen gems
if (gem.x < -50) {
gem.destroy();
gems.splice(g, 1);
}
}
// Update and check boxes
for (var b = boxes.length - 1; b >= 0; b--) {
var box = boxes[b];
box.speed = gameSpeed;
// Check collision with player or spin attack
var distanceX = box.x - player.x;
var distanceY = box.y - player.y;
var distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
var isInSpinRange = player.isSpinning && distance <= player.spinDamageRadius;
if (!box.broken && (player.intersects(box) || isInSpinRange)) {
box.breakBox();
boxes.splice(b, 1);
continue;
}
// Remove off-screen boxes
if (box.x < -50) {
box.destroy();
boxes.splice(b, 1);
}
}
// Update and check damage boxes
for (var d = damageBoxes.length - 1; d >= 0; d--) {
var damageBox = damageBoxes[d];
damageBox.speed = gameSpeed;
// Check collision with player (damage boxes can't be destroyed by spin attack)
if (!damageBox.damaged && player.intersects(damageBox)) {
damageBox.damagePlayer();
damageBoxes.splice(d, 1);
continue;
}
// Remove off-screen damage boxes
if (damageBox.x < -50) {
damageBox.destroy();
damageBoxes.splice(d, 1);
}
}
// Update and clean up trails
for (var k = trails.length - 1; k >= 0; k--) {
var trail = trails[k];
trail.speed = gameSpeed;
// Remove off-screen or completely faded trails
if (trail.x < -100 || trail.alpha <= 0.1) {
trail.destroy();
trails.splice(k, 1);
}
}
// Update and clean up trees
for (var t = trees.length - 1; t >= 0; t--) {
var tree = trees[t];
tree.speed = gameSpeed * 0.8; // Trees move slightly slower for depth effect
// Remove off-screen trees
if (tree.x < -200) {
tree.destroy();
trees.splice(t, 1);
}
}
// Zoom in effect - scale the game container by 10 units
game.scaleX = 1.0 + 10 / 1000; // Convert 10 units to scale factor
game.scaleY = 1.0 + 10 / 1000; // Convert 10 units to scale factor
// Update sun and moon cycle
cycleTimer++;
var cycleProgress = cycleTimer % cycleDuration / cycleDuration;
// Sun moves from left to right in an arc
var sunProgress = cycleProgress;
var sunX = 200 + sunProgress * 1600; // Move from left (200) to right (1800)
var sunY = 400 - Math.sin(sunProgress * Math.PI) * 200; // Arc shape - higher in middle
// Moon moves from right to left in an arc (opposite direction)
var moonProgress = (cycleProgress + 0.5) % 1; // Offset by half cycle
var moonX = 1800 - moonProgress * 1600; // Move from right (1800) to left (200)
var moonY = 400 - Math.sin(moonProgress * Math.PI) * 200; // Arc shape - higher in middle
// Use tweens for smooth movement
tween(sun, {
x: sunX,
y: sunY
}, {
duration: 50,
easing: tween.linear
});
tween(moon, {
x: moonX,
y: moonY
}, {
duration: 50,
easing: tween.linear
});
// Day/night transition logic
var newIsDay = sunY < 600; // Day when sun is above horizon
if (newIsDay !== isDay) {
isDay = newIsDay;
if (isDay) {
// Transition to day
tween(sun, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 1000,
easing: tween.easeInOut
});
tween(moon, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1000,
easing: tween.easeInOut
});
// Brighten background for day
tween(game, {
tint: 0xFFFFFF
}, {
duration: 1500,
easing: tween.easeInOut
});
} else {
// Transition to night
tween(sun, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1000,
easing: tween.easeInOut
});
tween(moon, {
alpha: 1,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1000,
easing: tween.easeInOut
});
// Darken background for night
tween(game, {
tint: 0x4A4A6A
}, {
duration: 1500,
easing: tween.easeInOut
});
}
}
// Add gentle pulsing animation to sun
if (isDay && sun.alpha > 0.5) {
var sunPulse = 1.0 + Math.sin(cycleTimer * 0.1) * 0.05;
sun.scaleX = 1.2 * sunPulse;
sun.scaleY = 1.2 * sunPulse;
}
// Add gentle glow animation to moon
if (!isDay && moon.alpha > 0.5) {
var moonGlow = 1.0 + Math.sin(cycleTimer * 0.08) * 0.03;
moon.scaleX = 1.0 * moonGlow;
moon.scaleY = 1.0 * moonGlow;
}
// Music will transition automatically when first track ends
// Ensure player stays in foreground by re-adding it
if (player.parent !== game) {
game.addChild(player);
} else {
// Move player to top of render order
game.removeChild(player);
game.addChild(player);
}
};
// Scoreboard background removed
// Game pause/resume functionality
var gamePaused = false;
var pausedUpdateFunction = null;
LK.pauseGame = function () {
if (!gamePaused) {
gamePaused = true;
pausedUpdateFunction = game.update;
game.update = function () {
// Game is paused - no updates
};
}
};
LK.resumeGame = function () {
if (gamePaused) {
gamePaused = false;
if (pausedUpdateFunction) {
game.update = pausedUpdateFunction;
pausedUpdateFunction = null;
}
}
};
// Initialize store
var store = new Store();
// God Mode variables
var godModeActive = false;
var normalSpeed = 24;
// God Mode button
var godModeButton = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2,
tint: 0xFF4444,
x: 0,
y: 400
});
LK.gui.topLeft.addChild(godModeButton);
var godModeButtonText = new Text2('GOD MODE', {
size: 35,
fill: 0xFFFFFF,
font: "Avenir",
fontWeight: 'bold'
});
godModeButtonText.anchor.set(0.5, 0.5);
godModeButtonText.x = 0;
godModeButtonText.y = 400;
LK.gui.topLeft.addChild(godModeButtonText);
// God Mode button interaction
godModeButton.down = function () {
toggleGodMode();
};
// Teleport button
var teleportButton = LK.getAsset('square_button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2,
tint: 0x8A2BE2,
x: 0,
y: 520
});
LK.gui.topLeft.addChild(teleportButton);
var teleportButtonText = new Text2('TELEPORT', {
size: 30,
fill: 0xFFFFFF,
font: "Avenir",
fontWeight: 'bold'
});
teleportButtonText.anchor.set(0.5, 0.5);
teleportButtonText.x = 0;
teleportButtonText.y = 520;
LK.gui.topLeft.addChild(teleportButtonText);
// Teleport button interaction
teleportButton.down = function () {
teleportToLastGate();
};
// Function to teleport 50 meters away from last gate
function teleportToLastGate() {
var targetDistance = 730; // 50 meters before the gate at 780 meters
var currentDistance = Math.floor(distanceScore / 10);
if (currentDistance < targetDistance) {
// Calculate how much distance to add
var distanceToAdd = (targetDistance - currentDistance) * 10;
distanceScore += distanceToAdd;
// Flash effect when teleporting
LK.effects.flashScreen(0x8A2BE2, 500);
LK.effects.flashObject(player, 0x8A2BE2, 800);
// Update distance display
scoreText.setText(Math.floor(distanceScore / 10) + 'm');
// Font is already set in the Text2 constructor, no need to change it here
}
}
// Function to toggle God Mode
function toggleGodMode() {
godModeActive = !godModeActive;
if (godModeActive) {
// Activate God Mode
player.isInvulnerable = true;
gameSpeed = normalSpeed * 3; // Very high speed
godModeButton.tint = 0x00FF00; // Green when active
godModeButtonText.setText('GOD: ON');
godModeButtonText.fill = 0x00FF00;
// Flash effect when activating
LK.effects.flashScreen(0xFFD700, 500);
LK.effects.flashObject(player, 0xFFD700, 1000);
} else {
// Deactivate God Mode
player.isInvulnerable = false;
gameSpeed = normalSpeed + distanceScore / 1000; // Return to normal progression
godModeButton.tint = 0xFF4444; // Red when inactive
godModeButtonText.setText('GOD MODE');
godModeButtonText.fill = 0xFFFFFF;
}
}
// Store button hidden for now
// var storeButton = LK.getAsset('square_button', {
// anchorX: 0.5,
// anchorY: 0.5,
// scaleX: 2,
// scaleY: 2,
// tint: 0x4444FF,
// x: 0,
// y: 400
// });
// LK.gui.topLeft.addChild(storeButton);
// var storeButtonText = new Text2('STORE', {
// size: 40,
// fill: 0xFFFFFF,
// fontWeight: 'bold'
// });
// storeButtonText.anchor.set(0.5, 0.5);
// storeButtonText.x = 0;
// storeButtonText.y = 400;
// LK.gui.topLeft.addChild(storeButtonText);
// Store button interaction
// storeButton.down = function () {
// store.openStore();
// };
// Get player name and last score from storage
var playerName = storage.playerName || 'Player';
var lastScore = storage.lastScore || 0;
// Sun and moon cycle variables
var sun, moon;
var cycleTimer = 0;
var cycleDuration = 3000; // 50 seconds for full day/night cycle (3000 ticks at 60fps)
var isDay = true;
// Character switching variables for C1-C6 cycling every 5 meters
var characterOrder = ['C1', 'C2', 'C3', 'C4', 'C5', 'C6'];
var currentCharacterIndex = 0;
var currentCharacter = characterOrder[currentCharacterIndex];
var lastSwapDistance = 0;
var nextSwapDistance = 5;
// Player name text removed
// Combined score text removed
// Scoreboard update function removed
// CHAPTER 1: THE BEGINNING ADVENTURE
// This is the first chapter of the endless runner adventure
var gamePartTitle = "CHAPTER 1: THE BEGINNING";
var gamePartNumber = 1;
var isFirstChapter = true;
// Display Part 1 title at game start
var partTitleText = new Text2(gamePartTitle, {
size: 80,
fill: 0xFFD700,
font: "Avenir",
fontWeight: 'bold',
stroke: 0x000000,
strokeThickness: 8
});
partTitleText.anchor.set(0.5, 0.5);
partTitleText.x = 1024;
partTitleText.y = 300;
partTitleText.alpha = 0;
game.addChild(partTitleText);
// Animate part title appearance
tween(partTitleText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fade out title after showing
LK.setTimeout(function () {
tween(partTitleText, {
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
partTitleText.destroy();
}
});
}, 3000);
}
});
// Function to complete Part 1 and prepare for next part
function completePart1() {
storage.part1_completed = true;
storage.part1_finalScore = coinCount;
storage.part1_finalDistance = Math.floor(distanceScore / 10);
storage.part1_finalGems = gemCount;
storage.part1_completionTime = Date.now();
// Mark that Part 2 is now available
storage.part2_unlocked = true;
// Prepare transition to Chapter 2
prepareChapter2Transition();
}
// Function to prepare and transition to Chapter 2
function prepareChapter2Transition() {
// Save Chapter 1 completion data
storage.chapter1_completed = true;
storage.chapter1_finalScore = coinCount;
storage.chapter1_finalDistance = Math.floor(distanceScore / 10);
storage.chapter1_finalGems = gemCount;
storage.chapter1_completionTime = Date.now();
// Initialize Chapter 2 variables
storage.chapter2_unlocked = true;
storage.currentChapter = 2;
// Create Chapter 2 title display
var chapter2Title = new Text2('CHAPTER 2: THE CRYSTAL QUEST', {
size: 90,
fill: 0x00FFFF,
font: "Avenir",
fontWeight: 'bold',
stroke: 0x000000,
strokeThickness: 10
});
chapter2Title.anchor.set(0.5, 0.5);
chapter2Title.x = 1024;
chapter2Title.y = 1000;
chapter2Title.alpha = 0;
game.addChild(chapter2Title);
// Animate Chapter 2 title appearance
tween(chapter2Title, {
alpha: 1,
y: 900,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show transition message
var transitionText = new Text2('Starting Chapter 2...', {
size: 60,
fill: 0xFFFFFF,
font: "Avenir",
fontStyle: 'italic'
});
transitionText.anchor.set(0.5, 0.5);
transitionText.x = 1024;
transitionText.y = 1100;
transitionText.alpha = 0;
game.addChild(transitionText);
tween(transitionText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
// Initialize Chapter 2 after transition
LK.setTimeout(function () {
initializeChapter2();
}, 1000);
}
});
// Clean up transition elements after delay
LK.setTimeout(function () {
chapter2Title.destroy();
transitionText.destroy();
}, 4000);
}
});
}
// Function to save complete Chapter 1 game state
function saveCompleteChapter1() {
// Save all Chapter 1 game state variables
storage.chapter1_complete_coinCount = coinCount;
storage.chapter1_complete_gemCount = gemCount;
storage.chapter1_complete_distanceScore = distanceScore;
storage.chapter1_complete_playerLives = playerLives;
storage.chapter1_complete_gameSpeed = gameSpeed;
storage.chapter1_complete_currentCharacter = currentCharacter;
storage.chapter1_complete_crystalSpawned = crystalSpawned;
storage.chapter1_complete_endGateSpawned = endGateSpawned;
storage.chapter1_complete_playerX = player.x;
storage.chapter1_complete_playerY = player.y;
storage.chapter1_complete_isDay = isDay;
storage.chapter1_complete_cycleTimer = cycleTimer;
storage.chapter1_complete_spawnTimer = spawnTimer;
storage.chapter1_complete_trailTimer = trailTimer;
// Save God mode state
storage.chapter1_complete_godModeActive = godModeActive;
storage.chapter1_complete_normalSpeed = normalSpeed;
// Save counters for active objects
storage.chapter1_complete_coinsCount = coins.length;
storage.chapter1_complete_gemsCount = gems.length;
storage.chapter1_complete_boxesCount = boxes.length;
storage.chapter1_complete_damageBoxesCount = damageBoxes.length;
storage.chapter1_complete_treesCount = trees.length;
storage.chapter1_complete_trailsCount = trails.length;
// Mark Chapter 1 as completed
storage.chapter1_fully_completed = true;
storage.chapter1_completion_time = Date.now();
// Initialize Chapter 2 preparation
storage.chapter2_ready = true;
storage.chapter2_unlocked = true;
storage.currentChapter = 2;
}
// Function to save all game variables to Dave 1 profile
function saveDave1Profile() {
// Save all current game state variables to storage with Dave 1 identifier
storage.dave1_coinCount = coinCount;
storage.dave1_gemCount = gemCount;
storage.dave1_distanceScore = distanceScore;
storage.dave1_playerLives = playerLives;
storage.dave1_gameSpeed = gameSpeed;
storage.dave1_currentCharacter = currentCharacter;
storage.dave1_crystalSpawned = crystalSpawned;
storage.dave1_endGateSpawned = endGateSpawned;
storage.dave1_playerX = player.x;
storage.dave1_playerY = player.y;
storage.dave1_isDay = isDay;
storage.dave1_cycleTimer = cycleTimer;
storage.dave1_spawnTimer = spawnTimer;
storage.dave1_trailTimer = trailTimer;
// Save God mode state
storage.dave1_godModeActive = godModeActive;
storage.dave1_normalSpeed = normalSpeed;
// Save counters for active objects
storage.dave1_coinsCount = coins.length;
storage.dave1_gemsCount = gems.length;
storage.dave1_boxesCount = boxes.length;
storage.dave1_damageBoxesCount = damageBoxes.length;
storage.dave1_treesCount = trees.length;
storage.dave1_trailsCount = trails.length;
// Save profile identifier
storage.dave1_profileName = "Dave 1";
storage.dave1_saveTime = Date.now();
// Save chapter information
storage.dave1_currentChapter = gamePartNumber;
storage.dave1_chapterTitle = gamePartTitle;
storage.dave1_isFirstChapter = isFirstChapter;
// Save chapter progress
storage.dave1_chapter1_completed = storage.chapter1_completed || false;
storage.dave1_chapter2_unlocked = storage.chapter2_unlocked || false;
storage.dave1_currentChapter = storage.currentChapter || 1;
}
// Function to initialize Chapter 2 with same mechanics as Chapter 1
function initializeChapter2() {
// Save Chapter 1 state first
saveCompleteChapter1();
// Reset all game variables for Chapter 2 start
coinCount = 0;
gemCount = 0;
distanceScore = 0;
playerLives = 3;
gameSpeed = 24;
normalSpeed = 24;
spawnTimer = 0;
trailTimer = 0;
crystalSpawned = false;
endGateSpawned = false;
godModeActive = false;
cycleTimer = 0;
isDay = true;
// Clear all object arrays
coins = [];
gems = [];
boxes = [];
damageBoxes = [];
trails = [];
trees = [];
// Update chapter info
gamePartTitle = "CHAPTER 2: THE CRYSTAL QUEST";
gamePartNumber = 2;
isFirstChapter = false;
// Reset character cycling for Chapter 2
currentCharacterIndex = 0;
currentCharacter = characterOrder[currentCharacterIndex];
lastSwapDistance = 0;
nextSwapDistance = 5;
// Reset player position
player.x = 400;
player.y = groundY + 20;
player.isInvulnerable = false;
// Update UI
coinText.setText('0');
// Font is already set in the Text2 constructor, no need to change it here
livesText.setText('3');
// Font is already set in the Text2 constructor, no need to change it here
// Reset God Mode button
godModeButton.tint = 0xFF4444;
godModeButtonText.setText('GOD MODE');
godModeButtonText.fill = 0xFFFFFF;
// Show Chapter 2 title
var chapter2TitleText = new Text2(gamePartTitle, {
size: 80,
fill: 0x00FFFF,
font: "Avenir",
fontWeight: 'bold',
stroke: 0x000000,
strokeThickness: 8
});
chapter2TitleText.anchor.set(0.5, 0.5);
chapter2TitleText.x = 1024;
chapter2TitleText.y = 300;
chapter2TitleText.alpha = 0;
game.addChild(chapter2TitleText);
// Animate Chapter 2 title appearance
tween(chapter2TitleText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fade out title after showing
LK.setTimeout(function () {
tween(chapter2TitleText, {
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
chapter2TitleText.destroy();
}
});
}, 3000);
}
});
// Create new starting gate for Chapter 2
startingGate = new StartingGate();
startingGate.x = 400 + 3 * 10;
startingGate.y = groundY + 75;
game.addChild(startingGate);
// Player name text update removed
}
// Create 10 layers and order them from 1 to 10
var layer1 = new Container();
layer1.name = "Layer 1";
layer1.visible = true; // Layer 1 is visible at the back
game.addChildAt(layer1, 0); // Insert at index 0 to place at the back
var layer2 = new Container();
layer2.name = "Layer 2";
layer2.visible = false; // Layer 2 is in front but not visible
game.addChild(layer2);
var layer3 = new Container();
layer3.name = "Layer 3";
layer3.visible = false; // Layer 3 is in front but not visible
game.addChild(layer3);
var layer4 = new Container();
layer4.name = "Layer 4";
layer4.visible = false; // Layer 4 is in front but not visible
game.addChild(layer4);
var layer5 = new Container();
layer5.name = "Layer 5";
layer5.visible = false; // Layer 5 is in front but not visible
game.addChild(layer5);
var layer6 = new Container();
layer6.name = "Layer 6";
layer6.visible = false; // Layer 6 is in front but not visible
game.addChild(layer6);
var layer7 = new Container();
layer7.name = "Layer 7";
layer7.visible = false; // Layer 7 is in front but not visible
game.addChild(layer7);
var layer8 = new Container();
layer8.name = "Layer 8";
layer8.visible = false; // Layer 8 is in front but not visible
game.addChild(layer8);
var layer9 = new Container();
layer9.name = "Layer 9";
layer9.visible = false; // Layer 9 is in front but not visible
game.addChild(layer9);
var layer10 = new Container();
layer10.name = "Layer 10";
layer10.visible = false; // Layer 10 is in front but not visible
game.addChild(layer10);
;
Just crystal
Just his head
Background, endless, forest, winter, cartoon. In-Game asset. 2d. High contrast. No shadows
Only the line of the ears and the color of the paws should be gray
Only the line of the ears and the color of the paws should be gray
Let C2 have the character's color
Only the line of the ears and the color of the paws should be gray
Delete the character on it
A version without snow
Koyu mavi elips start butonu. In-Game asset. 2d. High contrast. No shadows. In-Game asset. 2d. High contrast. No shadows
Uçan bir dinazor. In-Game asset. 2d. High contrast. No shadows
Alev topu. In-Game asset. 2d. High contrast. No shadows
Mavi top rasengan top. In-Game asset. 2d. High contrast. No shadows
jump
Sound effect
coin
Sound effect
Arkaplanmuzik
Music
gem_collect
Sound effect
happy_giggle
Sound effect
Canazalma
Sound effect
Arkaplanmuzik1
Music
wumpa1
Sound effect
cancan
Sound effect
box_break
Sound effect
tnt_break
Sound effect
enemy_sound
Sound effect
bullet_sound
Sound effect