/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bomb = Container.expand(function () { var self = Container.call(this); var bombGraphics = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); self.isBomb = true; self.speed = 172.5; if (isInfiniteMode) { self.speed = 172.5 * 3.6; // 3.6x faster than normal speed (20% increase) } else if (isHellMode) { self.speed *= 4; // 4x speed in hell mode } self.caught = false; self.thrown = false; self.throwVelocityX = 0; self.throwVelocityY = 0; self.update = function () { if (self.thrown) { self.x += self.throwVelocityX; self.y += self.throwVelocityY; } else if (!self.caught) { self.y += self.speed * (1 / 60); } }; return self; }); var Character = Container.expand(function () { var self = Container.call(this); var characterGraphics = self.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }); self.heldPackage = null; self.targetRotation = 0; self.update = function () { var rotationDiff = self.targetRotation - self.rotation; while (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI; while (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI; self.rotation += rotationDiff * 0.1; if (self.heldPackage) { // Position package in front of character based on rotation var packageDistance = 90; // Distance in front of character var packageX = self.x + Math.cos(self.rotation) * packageDistance; var packageY = self.y + Math.sin(self.rotation) * packageDistance; // Smoothly tween package to new position for better visual flow tween(self.heldPackage, { x: packageX, y: packageY, rotation: self.rotation }, { duration: 100, easing: tween.easeOut }); } }; return self; }); var Confetti = Container.expand(function (colorValue) { var self = Container.call(this); var confettiGraphics = self.attachAsset('confetti', { anchorX: 0.5, anchorY: 0.5 }); confettiGraphics.tint = colorValue; self.velocityX = (Math.random() - 0.5) * 400; self.velocityY = (Math.random() - 0.5) * 400; self.life = 60; self.update = function () { self.x += self.velocityX * (1 / 60); self.y += self.velocityY * (1 / 60); self.velocityY += 300 * (1 / 60); self.life--; if (self.life <= 0) { self.alpha = 0; } }; return self; }); var Package = Container.expand(function (colorValue) { var self = Container.call(this); var assetName = 'yellowpacket'; if (colorValue === 0x0066ff) assetName = 'bluepacket';else if (colorValue === 0xff0000) assetName = 'redpacket';else if (colorValue === 0xff8800) assetName = 'orangepacket'; var packageGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.colorValue = colorValue || 0xffffff; self.speed = 172.5; // 150 * 1.15 = 15% increase if (isInfiniteMode) { self.speed = 172.5 * 3.6; // 3.6x faster than normal speed (20% increase) } else if (isHellMode) { self.speed *= 4; // 4x speed in hell mode } self.caught = false; self.thrown = false; self.throwVelocityX = 0; self.throwVelocityY = 0; self.update = function () { if (self.thrown) { self.x += self.throwVelocityX; self.y += self.throwVelocityY; } else if (!self.caught) { self.y += self.speed * (1 / 60); } }; return self; }); var TargetBox = Container.expand(function (colorValue) { var self = Container.call(this); var assetName = 'targetYellow'; if (colorValue === 0x0066ff) assetName = 'targetBlue';else if (colorValue === 0xff0000) assetName = 'targetRed';else if (colorValue === 0xff8800) assetName = 'targetOrange'; var targetGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.colorValue = colorValue; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ var gameWidth = 2048; var gameHeight = 2732; var arenaSize = 1600; var arenaX = gameWidth / 2; var arenaY = gameHeight / 2; // Game arena var arena = game.addChild(LK.getAsset('arena', { anchorX: 0.5, anchorY: 0.5, x: arenaX, y: arenaY })); // Character var character = game.addChild(new Character()); character.x = arenaX; character.y = arenaY; // Target boxes at corners var targets = []; var targetColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; // yellow, blue, red, orange var targetPositions = [{ x: arenaX - arenaSize / 2 + 180, y: arenaY - arenaSize / 2 + 180 }, // top-left { x: arenaX + arenaSize / 2 - 180, y: arenaY - arenaSize / 2 + 180 }, // top-right { x: arenaX - arenaSize / 2 + 180, y: arenaY + arenaSize / 2 - 180 }, // bottom-left { x: arenaX + arenaSize / 2 - 180, y: arenaY + arenaSize / 2 - 180 } // bottom-right ]; for (var i = 0; i < 4; i++) { var target = game.addChild(new TargetBox(targetColors[i])); target.x = targetPositions[i].x; target.y = targetPositions[i].y; targets.push(target); } // Game state var packages = []; var confettiParticles = []; var gameTime = 0; var gameDuration = 30000; // 30 seconds for levels 1-3, 240000 for final level var packageSpawnInterval = 2000; // 2 seconds var lastPackageSpawn = 0; var gameStarted = false; var gameEnded = false; var gameReady = false; var currentLevel = 1; var levelCompleted = false; var finalLevelStarted = false; var isHellMode = false; var hellModeSelected = false; var isInfiniteMode = false; var infiniteModeSelected = false; var infiniteMusicTimer = 0; var infiniteMusicInterval = 108000; // 108 seconds in milliseconds // Arena animation variables var arenaAnimationTimes = [11, 23, 31, 41, 53, 63, 75, 85, 96, 107, 116, 128, 137, 149, 157, 169, 180, 190, 202, 214, 222, 232, 241, 253, 263, 271, 280, 290, 301, 310, 321, 329, 339, 349, 361, 370, 382, 394, 405, 414, 423, 435, 444, 452, 461, 471, 479, 490, 500, 510, 519, 527, 538, 546, 558, 567, 575, 584, 596, 608]; var currentArenaAnimationIndex = 0; var isArenaAnimating = false; var arenaAnimationStep = 0; // Bomb mechanic variables var lastBombSpawn = 0; var bombSpawnInterval = 3000; // 3 seconds var bombHoldTimer = 0; var bombWarningText = null; // Predetermined color sequence for level 1 var colorSequenceLevel1 = [0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xff0000, 0x0066ff, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff8800, 0xff0000, 0xffff00, 0x0066ff]; // Level 2 color sequence and timing intervals (in milliseconds) var colorSequenceLevel2 = [0xff0000, 0xff8800, 0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xff0000, 0x0066ff, 0xffff00, 0xff8800, 0x0066ff, 0xffff00]; var timingIntervalsLevel2 = [1720, 2150, 1290, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1720, 1720, 1290, 1720, 1720, 1720, 1720, 1720, 2150, 1720, 1290]; // Level 3 color sequence and timing intervals (in milliseconds) var colorSequenceLevel3 = [0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0x0066ff, 0xffff00]; var timingIntervalsLevel3 = [1720, 1720, 2150, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1290, 1720]; // Final level color sequence (longer sequence for 4 minutes) var colorSequenceFinal = [0xff0000, 0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000]; var currentSequenceIndex = 0; var currentTimingIndex = 0; // UI var scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var timeTxt = new Text2('Time: 30', { size: 60, fill: 0xFFFFFF }); timeTxt.anchor.set(1, 0); LK.gui.topRight.addChild(timeTxt); // Level display var levelText = new Text2('LEVEL 1', { size: 120, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = gameWidth / 2; levelText.y = gameHeight / 2 - 250; game.addChild(levelText); // Ready button var readyButton = LK.getAsset('ready', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2, y: gameHeight / 2 }); readyButton.tint = 0x666666; // Dark grey passive effect readyButton.alpha = 0.5; // Additional passive effect game.addChild(readyButton); // Hell mode selection UI var normalModeButton = new Text2('NORMAL', { size: 120, fill: 0x00FF00 }); normalModeButton.anchor.set(0.5, 0.5); normalModeButton.x = gameWidth / 2 - 220; normalModeButton.y = gameHeight / 2 + 130; game.addChild(normalModeButton); var hellModeButton = LK.getAsset('hell', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2 + 220, y: gameHeight / 2 + 130, scaleX: 2, scaleY: 2 }); game.addChild(hellModeButton); // Infinite button (only for level 1) var infiniteButton = LK.getAsset('infinite1', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2, y: gameHeight / 2 + 430, scaleX: 4.76, scaleY: 4.76 }); game.addChild(infiniteButton); // Infinite button animation variables var infiniteTextures = ['infinite1', 'infinite2', 'infinite3', 'infinite4', 'infinite5', 'infinite6', 'infinite7', 'infinite8']; var currentInfiniteTextureIndex = 0; var infiniteAnimationTimer = 0; var infiniteAnimationSpeed = 4; // Change texture every 4 frames (about 15 fps at 60fps) // Helper functions function getColorName(colorValue) { switch (colorValue) { case 0xffff00: return 'Yellow'; case 0x0066ff: return 'Blue'; case 0xff0000: return 'Red'; case 0xff8800: return 'Orange'; default: return 'Unknown'; } } function spawnPackage() { var currentColorSequence; var packageColor; if (isInfiniteMode) { // Infinite mode: completely random colors var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; packageColor = randomColors[Math.floor(Math.random() * randomColors.length)]; } else { if (currentLevel === 1) { currentColorSequence = colorSequenceLevel1; } else if (currentLevel === 2) { currentColorSequence = colorSequenceLevel2; } else if (currentLevel === 3) { currentColorSequence = colorSequenceLevel3; } else { currentColorSequence = colorSequenceFinal; } if (currentSequenceIndex >= currentColorSequence.length) return; packageColor = currentColorSequence[currentSequenceIndex]; currentSequenceIndex++; } var newPackage = new Package(packageColor); newPackage.x = character.x; // Spawn directly above character newPackage.y = arenaY - arenaSize / 2 - 200; // Spawn higher above arena packages.push(newPackage); game.addChild(newPackage); // Hell mode: 20% chance to spawn a second package 0.05 seconds later if (isHellMode && Math.random() < 0.2) { LK.setTimeout(function () { // Get next color from sequence for the second package var secondPackageColor; if (currentSequenceIndex < currentColorSequence.length) { secondPackageColor = currentColorSequence[currentSequenceIndex]; currentSequenceIndex++; } else if (currentLevel === 4) { // For final level, loop back to beginning currentSequenceIndex = 0; secondPackageColor = currentColorSequence[currentSequenceIndex]; currentSequenceIndex++; } else { // Use random color if sequence exhausted var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; secondPackageColor = randomColors[Math.floor(Math.random() * randomColors.length)]; } var secondPackage = new Package(secondPackageColor); secondPackage.x = character.x; secondPackage.y = arenaY - arenaSize / 2 - 200; packages.push(secondPackage); game.addChild(secondPackage); }, 150); // 0.15 seconds = 150ms } } function spawnBomb() { var newBomb = new Bomb(); newBomb.x = character.x; // Spawn directly above character newBomb.y = arenaY - arenaSize / 2 - 200; // Spawn higher above arena packages.push(newBomb); game.addChild(newBomb); } function createConfettiExplosion(x, y, color) { var allColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; // yellow, blue, red, orange // Create more particles for bigger explosion (25 instead of 15) for (var i = 0; i < 25; i++) { var confettiColor; if (i < 18) { // Most particles (72%) use the main color confettiColor = color; } else { // Some particles (28%) use random other colors confettiColor = allColors[Math.floor(Math.random() * allColors.length)]; } var confetti = new Confetti(confettiColor); confetti.x = x; confetti.y = y; // Scale up confetti particles by 1.5x for bigger effect tween(confetti, { scaleX: 1.5, scaleY: 1.5 }, { duration: 50, easing: tween.easeOut }); confettiParticles.push(confetti); game.addChild(confetti); } } function startArenaAnimation() { if (isArenaAnimating) return; isArenaAnimating = true; arenaAnimationStep = 0; // Start animation sequence animateArenaStep(); } function animateArenaStep() { var arenaAssets = ['arena2', 'arena3', 'arena4']; if (arenaAnimationStep < arenaAssets.length) { // Change to next animation frame var newArena = LK.getAsset(arenaAssets[arenaAnimationStep], { anchorX: 0.5, anchorY: 0.5, x: arenaX, y: arenaY, scaleX: 12, // Scale to match original arena size scaleY: 12 }); // Add new arena behind all other objects (at index 1, after original arena) game.addChildAt(newArena, 1); // Auto-remove after 1.6 seconds LK.setTimeout(function () { newArena.destroy(); }, 1600); arenaAnimationStep++; // Schedule next step after 400ms LK.setTimeout(function () { animateArenaStep(); }, 400); } else { // Animation complete isArenaAnimating = false; } } function createFireExplosion(x, y) { var flameColors = [0xFF4500, 0xFF6347, 0xFF0000, 0xFFA500, 0xFFD700]; // Orange, tomato, red, orange, gold // Create 30 flame particles for explosion effect for (var i = 0; i < 30; i++) { var flameColor = flameColors[Math.floor(Math.random() * flameColors.length)]; var flame = new Confetti(flameColor); flame.x = x; flame.y = y; // Enhanced velocities for explosive effect flame.velocityX = (Math.random() - 0.5) * 600; flame.velocityY = (Math.random() - 0.5) * 600; flame.life = 90; // Medium life for flames // Scale and fade effect using tween tween(flame, { scaleX: 2, scaleY: 2 }, { duration: 80, easing: tween.easeOut }); tween(flame, { alpha: 0 }, { duration: 1500, easing: tween.easeOut }); confettiParticles.push(flame); game.addChild(flame); } } function createFireworks() { var fireworksColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0x00ff00, 0xff00ff, 0x00ffff]; // Create multiple firework explosions across the screen for (var f = 0; f < 8; f++) { var fireworkX = Math.random() * gameWidth; var fireworkY = Math.random() * (gameHeight * 0.6) + gameHeight * 0.2; var fireworkColor = fireworksColors[Math.floor(Math.random() * fireworksColors.length)]; // Create 40 particles per firework for spectacular effect for (var i = 0; i < 40; i++) { var firework = new Confetti(fireworkColor); firework.x = fireworkX; firework.y = fireworkY; // Enhanced velocities for bigger explosion firework.velocityX = (Math.random() - 0.5) * 800; firework.velocityY = (Math.random() - 0.5) * 600 - 200; firework.life = 120; // Longer life for fireworks // Scale and fade effect using tween tween(firework, { scaleX: 2.5, scaleY: 2.5 }, { duration: 100, easing: tween.easeOut }); tween(firework, { alpha: 0 }, { duration: 2000, easing: tween.easeOut }); confettiParticles.push(firework); game.addChild(firework); } } } function checkPackageTargetCollision(packageObj, target) { var dx = packageObj.x - target.x; var dy = packageObj.y - target.y; var distance = Math.sqrt(dx * dx + dy * dy); // Orange target gets larger hitbox (50% larger than base 120 = 180) if (target.colorValue === 0xff8800) { return distance < 180; // Orange target gets bigger hitbox } return distance < 156; // Increased from 104 to 156 (1.5x scale) for other targets } function throwPackage(targetX, targetY) { if (!character.heldPackage) { // Empty click penalty LK.setScore(LK.getScore() - 15); LK.getSound('miss').play(); return; } var packageToThrow = character.heldPackage; character.heldPackage = null; // Clear bomb warning if it exists if (bombWarningText) { bombWarningText.destroy(); bombWarningText = null; } var dx = targetX - packageToThrow.x; var dy = targetY - packageToThrow.y; var distance = Math.sqrt(dx * dx + dy * dy); var speed = 800; packageToThrow.throwVelocityX = dx / distance * speed * (1 / 60); packageToThrow.throwVelocityY = dy / distance * speed * (1 / 60); packageToThrow.thrown = true; LK.getSound('throw').play(); } // Event handlers game.move = function (x, y, obj) { var dx = x - character.x; var dy = y - character.y; character.targetRotation = Math.atan2(dy, dx); }; game.down = function (x, y, obj) { if (!gameReady) { // Check infinite button first (only for level 1) if (currentLevel === 1) { var infiniteButtonX = gameWidth / 2; var infiniteButtonY = gameHeight / 2 + 430; var infiniteButtonWidth = 476; // 4.76x scale of 200px width = 952, so half is 476 var infiniteButtonHeight = 110; // 4.76x scale of 46.1px height = 219, so half is 110 if (x >= infiniteButtonX - infiniteButtonWidth && x <= infiniteButtonX + infiniteButtonWidth && y >= infiniteButtonY - infiniteButtonHeight && y <= infiniteButtonY + infiniteButtonHeight) { // Start infinite mode isInfiniteMode = true; infiniteModeSelected = true; hellModeSelected = true; // Skip mode selection since infinite mode is already chosen gameReady = true; gameStarted = true; gameTime = 0; lastPackageSpawn = 0; // Set score to 10000 for infinite mode LK.setScore(10000); // Clean up UI elements and stop animation if (infiniteButton && infiniteButton.parent) { infiniteButton.destroy(); infiniteButton = null; } // Clean up any queued infinite buttons from animation for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) { if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) { infiniteButtonQueue[q].destroy(); } } infiniteButtonQueue = []; // Clear the queue if (levelText && levelText.parent) { levelText.destroy(); } if (readyButton && readyButton.parent) { readyButton.destroy(); } if (normalModeButton && normalModeButton.parent) { normalModeButton.destroy(); } if (hellModeButton && hellModeButton.parent) { hellModeButton.destroy(); } // Start infinite mode music LK.playMusic('bgmusicinfinite'); return; } } // Check mode selection if not selected if (!hellModeSelected) { // Check normal mode button var normalButtonX = gameWidth / 2 - 220; var normalButtonY = gameHeight / 2 + 130; var normalButtonWidth = 150; var normalButtonHeight = 60; if (x >= normalButtonX - normalButtonWidth && x <= normalButtonX + normalButtonWidth && y >= normalButtonY - normalButtonHeight && y <= normalButtonY + normalButtonHeight) { isHellMode = false; hellModeSelected = true; normalModeButton.destroy(); hellModeButton.destroy(); // Hide infinite button when normal mode is selected if (infiniteButton && infiniteButton.parent) { infiniteButton.destroy(); infiniteButton = null; } // Clean up any queued infinite buttons from animation for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) { if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) { infiniteButtonQueue[q].destroy(); } } infiniteButtonQueue = []; // Clear the queue // Remove passive effect from ready button readyButton.tint = 0xFFFFFF; readyButton.alpha = 1; return; } // Check hell mode button var hellButtonX = gameWidth / 2 + 220; var hellButtonY = gameHeight / 2 + 130; var hellButtonWidth = 100; var hellButtonHeight = 100; if (x >= hellButtonX - hellButtonWidth && x <= hellButtonX + hellButtonWidth && y >= hellButtonY - hellButtonHeight && y <= hellButtonY + hellButtonHeight) { isHellMode = true; hellModeSelected = true; normalModeButton.destroy(); hellModeButton.destroy(); // Hide infinite button when hell mode is selected if (infiniteButton && infiniteButton.parent) { infiniteButton.destroy(); infiniteButton = null; } // Clean up any queued infinite buttons from animation for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) { if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) { infiniteButtonQueue[q].destroy(); } } infiniteButtonQueue = []; // Clear the queue // Remove passive effect from ready button readyButton.tint = 0xFFFFFF; readyButton.alpha = 1; return; } return; } // Check if clicked on ready button area (only after mode selected) if (hellModeSelected) { var buttonX = gameWidth / 2; var buttonY = gameHeight / 2; var buttonWidth = 200; var buttonHeight = 100; if (x >= buttonX - buttonWidth && x <= buttonX + buttonWidth && y >= buttonY - buttonHeight && y <= buttonY + buttonHeight) { gameReady = true; readyButton.destroy(); levelText.destroy(); } } return; } if (gameEnded) return; throwPackage(x, y); }; // Main game update game.update = function () { if (!gameReady) return; if (!gameStarted) { gameStarted = true; gameTime = 0; // Reset game time when starting lastPackageSpawn = 0; // Reset package spawn timer // Remove infinite button when game starts (level 1 only) if (currentLevel === 1 && infiniteButton && infiniteButton.parent) { infiniteButton.destroy(); infiniteButton = null; } if (currentLevel === 1) { LK.getSound('level1').play(); LK.playMusic('bgmusic'); } else if (currentLevel === 2) { LK.getSound('level2').play(); LK.playMusic('bgmusic'); } else if (currentLevel === 3) { LK.getSound('level3').play(); LK.playMusic('bgmusic3'); } else if (currentLevel === 4) { LK.getSound('final').play(); LK.playMusic('bgmusicfinal'); gameDuration = 240000; // 4 minutes for final level finalLevelStarted = true; } } if (gameEnded) return; // Arena animation trigger at predetermined times - works for all levels var currentGameTimeInSeconds = Math.floor(gameTime / 1000); if (currentArenaAnimationIndex < arenaAnimationTimes.length && currentGameTimeInSeconds >= arenaAnimationTimes[currentArenaAnimationIndex]) { startArenaAnimation(); currentArenaAnimationIndex++; } // Check for insufficient score in final level if (currentLevel === 4) { if (LK.getScore() < -200) { gameEnded = true; LK.showGameOver(); return; } } gameTime += 1000 / 60; // Increment game time only after ready if (isInfiniteMode) { var elapsedTime = Math.floor(gameTime / 1000); timeTxt.setText('Time: ' + elapsedTime); } else { var remainingTime = Math.max(0, Math.ceil((gameDuration - gameTime) / 1000)); timeTxt.setText('Time: ' + remainingTime); } // Infinite mode music timer if (isInfiniteMode) { infiniteMusicTimer += 1000 / 60; if (infiniteMusicTimer >= infiniteMusicInterval) { LK.playMusic('bgmusicinfinite'); infiniteMusicTimer = 0; } } // Check for level completion (skip for infinite mode) if (!isInfiniteMode && gameTime >= gameDuration) { if (currentLevel === 1) { // Level 1 completed, prepare for level 2 currentLevel = 2; levelCompleted = true; gameStarted = false; gameReady = false; gameEnded = false; gameTime = 0; lastPackageSpawn = 0; currentSequenceIndex = 0; currentTimingIndex = 0; currentArenaAnimationIndex = 0; // Reset arena animation timing hellModeSelected = false; // Reset mode selection for next level // Reset bomb mechanics lastBombSpawn = 0; bombHoldTimer = 0; if (bombWarningText) { bombWarningText.destroy(); bombWarningText = null; } // Reset character state character.heldPackage = null; character.x = arenaX; character.y = arenaY; character.rotation = 0; character.targetRotation = 0; // Clear all packages for (var p = packages.length - 1; p >= 0; p--) { packages[p].destroy(); packages.splice(p, 1); } // Clear all confetti for (var c = confettiParticles.length - 1; c >= 0; c--) { confettiParticles[c].destroy(); confettiParticles.splice(c, 1); } // Show level 2 ready screen levelText = new Text2('LEVEL 2', { size: 120, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = gameWidth / 2; levelText.y = gameHeight / 2 - 250; game.addChild(levelText); readyButton = LK.getAsset('ready', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2, y: gameHeight / 2 }); readyButton.tint = 0x666666; // Dark grey passive effect readyButton.alpha = 0.5; // Additional passive effect game.addChild(readyButton); // Hell mode selection UI for level 2 normalModeButton = new Text2('NORMAL', { size: 120, fill: 0x00FF00 }); normalModeButton.anchor.set(0.5, 0.5); normalModeButton.x = gameWidth / 2 - 220; normalModeButton.y = gameHeight / 2 + 130; game.addChild(normalModeButton); hellModeButton = LK.getAsset('hell', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2 + 220, y: gameHeight / 2 + 130, scaleX: 2, scaleY: 2 }); game.addChild(hellModeButton); return; } else if (currentLevel === 2) { // Level 2 completed, prepare for level 3 currentLevel = 3; levelCompleted = true; gameStarted = false; gameReady = false; gameEnded = false; gameTime = 0; lastPackageSpawn = 0; currentSequenceIndex = 0; currentTimingIndex = 0; currentArenaAnimationIndex = 0; // Reset arena animation timing hellModeSelected = false; // Reset mode selection for level 3 // Reset bomb mechanics lastBombSpawn = 0; bombHoldTimer = 0; if (bombWarningText) { bombWarningText.destroy(); bombWarningText = null; } // Reset character state character.heldPackage = null; character.x = arenaX; character.y = arenaY; character.rotation = 0; character.targetRotation = 0; // Clear all packages for (var p = packages.length - 1; p >= 0; p--) { packages[p].destroy(); packages.splice(p, 1); } // Clear all confetti for (var c = confettiParticles.length - 1; c >= 0; c--) { confettiParticles[c].destroy(); confettiParticles.splice(c, 1); } // Show level 3 ready screen levelText = new Text2('LEVEL 3', { size: 120, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = gameWidth / 2; levelText.y = gameHeight / 2 - 250; game.addChild(levelText); readyButton = LK.getAsset('ready', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2, y: gameHeight / 2 }); readyButton.tint = 0x666666; // Dark grey passive effect readyButton.alpha = 0.5; // Additional passive effect game.addChild(readyButton); // Hell mode selection UI for level 3 normalModeButton = new Text2('NORMAL', { size: 120, fill: 0x00FF00 }); normalModeButton.anchor.set(0.5, 0.5); normalModeButton.x = gameWidth / 2 - 220; normalModeButton.y = gameHeight / 2 + 130; game.addChild(normalModeButton); hellModeButton = LK.getAsset('hell', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2 + 220, y: gameHeight / 2 + 130, scaleX: 2, scaleY: 2 }); game.addChild(hellModeButton); return; } else if (currentLevel === 3) { // Level 3 completed, prepare for final level currentLevel = 4; levelCompleted = true; gameStarted = false; gameReady = false; gameEnded = false; gameTime = 0; lastPackageSpawn = 0; currentSequenceIndex = 0; currentTimingIndex = 0; currentArenaAnimationIndex = 0; // Reset arena animation timing hellModeSelected = false; // Reset mode selection for final level // Reset bomb mechanics lastBombSpawn = 0; bombHoldTimer = 0; if (bombWarningText) { bombWarningText.destroy(); bombWarningText = null; } // Reset character state character.heldPackage = null; character.x = arenaX; character.y = arenaY; character.rotation = 0; character.targetRotation = 0; // Clear all packages for (var p = packages.length - 1; p >= 0; p--) { packages[p].destroy(); packages.splice(p, 1); } // Clear all confetti for (var c = confettiParticles.length - 1; c >= 0; c--) { confettiParticles[c].destroy(); confettiParticles.splice(c, 1); } // Show final level ready screen levelText = new Text2('FINAL LEVEL', { size: 120, fill: 0xFFD700 }); levelText.anchor.set(0.5, 0.5); levelText.x = gameWidth / 2; levelText.y = gameHeight / 2 - 250; game.addChild(levelText); readyButton = LK.getAsset('ready', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2, y: gameHeight / 2 }); readyButton.tint = 0x666666; // Dark grey passive effect readyButton.alpha = 0.5; // Additional passive effect game.addChild(readyButton); // Hell mode selection UI for final level normalModeButton = new Text2('NORMAL', { size: 120, fill: 0x00FF00 }); normalModeButton.anchor.set(0.5, 0.5); normalModeButton.x = gameWidth / 2 - 220; normalModeButton.y = gameHeight / 2 + 130; game.addChild(normalModeButton); hellModeButton = LK.getAsset('hell', { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2 + 220, y: gameHeight / 2 + 130, scaleX: 2, scaleY: 2 }); game.addChild(hellModeButton); return; } else { // Final level completed - you win with fireworks gameEnded = true; createFireworks(); LK.getSound('gameend').play(); LK.setTimeout(function () { LK.showYouWin(); }, 5000); return; } } // Hell mode and infinite mode bomb spawning for all levels if ((isHellMode || isInfiniteMode) && gameTime - lastBombSpawn >= bombSpawnInterval) { if (Math.random() < 0.25) { // 25% chance spawnBomb(); } lastBombSpawn = gameTime; } // Infinite mode game over condition when score reaches 0 if (isInfiniteMode && LK.getScore() <= 0) { gameEnded = true; LK.showGameOver(); return; } // Play bomb sound continuously when bomb is present in hell mode or infinite mode for all levels if ((isHellMode || isInfiniteMode) && character.heldPackage && character.heldPackage.isBomb) { if (!LK.getSound('bomb').isPlaying) { LK.getSound('bomb').play(); } } // Bomb holding penalty (10 points every 0.1 seconds = every 6 frames at 60fps) if (character.heldPackage && character.heldPackage.isBomb) { bombHoldTimer++; if (bombHoldTimer >= 6) { // 0.1 seconds at 60fps LK.setScore(LK.getScore() - 10); bombHoldTimer = 0; } // Show bomb warning if not already shown if (!bombWarningText) { bombWarningText = new Text2('BOMB! THROW AWAY!', { size: 60, fill: 0xFF0000 }); bombWarningText.anchor.set(0.5, 0.5); bombWarningText.x = gameWidth / 2; bombWarningText.y = 200; game.addChild(bombWarningText); // Flash effect tween(bombWarningText, { alpha: 0.3 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { if (bombWarningText) { tween(bombWarningText, { alpha: 1 }, { duration: 500, easing: tween.easeInOut }); } } }); } } else { bombHoldTimer = 0; // Clear bomb warning if not holding bomb if (bombWarningText && (!character.heldPackage || !character.heldPackage.isBomb)) { bombWarningText.destroy(); bombWarningText = null; } } // Spawn packages var currentColorSequence; if (isInfiniteMode) { // Infinite mode uses completely random colors var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; currentColorSequence = randomColors; } else if (currentLevel === 1) { currentColorSequence = colorSequenceLevel1; } else if (currentLevel === 2) { currentColorSequence = colorSequenceLevel2; } else { currentColorSequence = colorSequenceLevel3; } var currentSpawnInterval; if (isInfiniteMode) { // Infinite mode random intervals: 15% - 0.4s, 30% - 0.8s, 54% - 1.2s, 1% - 0.2s var randomValue = Math.random(); if (randomValue < 0.15) { currentSpawnInterval = 400; // 0.4 seconds } else if (randomValue < 0.45) { currentSpawnInterval = 800; // 0.8 seconds } else if (randomValue < 0.99) { currentSpawnInterval = 1200; // 1.2 seconds } else { currentSpawnInterval = 200; // 0.2 seconds } } else if (currentLevel === 1) { currentSpawnInterval = packageSpawnInterval; // Keep 2000ms for level 1 } else if (currentLevel === 2) { // Level 2 uses custom timing intervals if (currentTimingIndex < timingIntervalsLevel2.length) { currentSpawnInterval = timingIntervalsLevel2[currentTimingIndex]; } else { currentSpawnInterval = 1720; // Default fallback } } else if (currentLevel === 3) { // Level 3 uses custom timing intervals if (currentTimingIndex < timingIntervalsLevel3.length) { currentSpawnInterval = timingIntervalsLevel3[currentTimingIndex]; } else { currentSpawnInterval = 1720; // Default fallback } } else if (currentLevel === 4) { // Final level uses progressive timing based on game time var progressiveTiming = [1500, 1875]; // 0-30 seconds if (gameTime >= 30000 && gameTime < 60000) { progressiveTiming = [1125, 1500, 1875]; // 30-60 seconds } else if (gameTime >= 60000 && gameTime < 180000) { progressiveTiming = [1125, 1500]; // 60-180 seconds } else if (gameTime >= 180000) { progressiveTiming = [750, 1125, 1500]; // 180-240 seconds } currentSpawnInterval = progressiveTiming[Math.floor(Math.random() * progressiveTiming.length)]; } if (gameTime - lastPackageSpawn >= currentSpawnInterval) { // For final level, allow sequence to loop when exhausted if (currentLevel === 4 && currentSequenceIndex >= currentColorSequence.length) { currentSequenceIndex = 0; // Reset to beginning of sequence } // Only spawn if we have colors available or it's the final level or infinite mode if (currentSequenceIndex < currentColorSequence.length || currentLevel === 4 || isInfiniteMode) { spawnPackage(); if (currentLevel === 2 || currentLevel === 3) { currentTimingIndex++; } lastPackageSpawn = gameTime; } } // Update packages for (var i = packages.length - 1; i >= 0; i--) { var pkg = packages[i]; if (pkg.lastY === undefined) pkg.lastY = pkg.y; if (pkg.lastCaught === undefined) pkg.lastCaught = false; if (pkg.lastThrown === undefined) pkg.lastThrown = false; // Check if package reached character (catch) if (!pkg.caught && !pkg.thrown) { var distanceToCharacter = Math.sqrt(Math.pow(pkg.x - character.x, 2) + Math.pow(pkg.y - character.y, 2)); if (distanceToCharacter < 75) { if (!character.heldPackage) { pkg.caught = true; character.heldPackage = pkg; LK.getSound('catch').play(); } } } // Check if package fell off screen (drop penalty) if (!pkg.caught && !pkg.thrown && pkg.y > arenaY + arenaSize / 2 + 100) { LK.setScore(LK.getScore() - 150); pkg.destroy(); packages.splice(i, 1); continue; } // Check thrown package collisions with targets if (pkg.thrown) { var hitTarget = false; for (var j = 0; j < targets.length; j++) { var target = targets[j]; if (checkPackageTargetCollision(pkg, target)) { if (pkg.isBomb) { // Bomb hit target - create flame explosion effect and apply penalty LK.setScore(LK.getScore() - 500); createFireExplosion(target.x, target.y); LK.getSound('blast').play(); } else if (pkg.colorValue === target.colorValue) { // Correct color match LK.setScore(LK.getScore() + 100); createConfettiExplosion(target.x, target.y, pkg.colorValue); LK.getSound('hit').play(); } else { // Wrong color penalty LK.setScore(LK.getScore() - 60); LK.getSound('miss').play(); } hitTarget = true; break; } } if (hitTarget || pkg.x < 0 || pkg.x > gameWidth || pkg.y < 0 || pkg.y > gameHeight) { if (!hitTarget) { // Only apply missed throw penalty for non-bomb packages if (!pkg.isBomb) { LK.setScore(LK.getScore() - 60); LK.getSound('miss').play(); } } pkg.destroy(); packages.splice(i, 1); continue; } } pkg.lastY = pkg.y; pkg.lastCaught = pkg.caught; pkg.lastThrown = pkg.thrown; } // Update confetti for (var i = confettiParticles.length - 1; i >= 0; i--) { var confetti = confettiParticles[i]; if (confetti.alpha <= 0) { confetti.destroy(); confettiParticles.splice(i, 1); } } // Update score display scoreTxt.setText('Score: ' + LK.getScore()); }; // Infinite button animation - runs independently of game update var infiniteButtonQueue = []; // Queue to track buttons for delayed destruction LK.on('tick', function () { // Animate infinite button (only in level 1 before game starts) if (currentLevel === 1 && !gameStarted && infiniteButton && infiniteButton.parent) { infiniteAnimationTimer++; if (infiniteAnimationTimer >= infiniteAnimationSpeed) { currentInfiniteTextureIndex = (currentInfiniteTextureIndex + 1) % infiniteTextures.length; // Add current button to queue for delayed destruction infiniteButtonQueue.push(infiniteButton); // Create new button with next texture at 4.76x scale and fixed position infiniteButton = LK.getAsset(infiniteTextures[currentInfiniteTextureIndex], { anchorX: 0.5, anchorY: 0.5, x: gameWidth / 2, y: gameHeight / 2 + 430, scaleX: 4.76, scaleY: 4.76 }); game.addChild(infiniteButton); // If we have 3 or more buttons in queue, destroy the oldest one if (infiniteButtonQueue.length >= 3) { var buttonToDestroy = infiniteButtonQueue.shift(); // Remove first (oldest) button from queue // Fade out and destroy the oldest button with 0.1 second delay tween(buttonToDestroy, { alpha: 0 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { // Remove old button after fade out if (buttonToDestroy.parent) { game.removeChild(buttonToDestroy); } buttonToDestroy.destroy(); } }); } infiniteAnimationTimer = 0; } } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.isBomb = true;
self.speed = 172.5;
if (isInfiniteMode) {
self.speed = 172.5 * 3.6; // 3.6x faster than normal speed (20% increase)
} else if (isHellMode) {
self.speed *= 4; // 4x speed in hell mode
}
self.caught = false;
self.thrown = false;
self.throwVelocityX = 0;
self.throwVelocityY = 0;
self.update = function () {
if (self.thrown) {
self.x += self.throwVelocityX;
self.y += self.throwVelocityY;
} else if (!self.caught) {
self.y += self.speed * (1 / 60);
}
};
return self;
});
var Character = Container.expand(function () {
var self = Container.call(this);
var characterGraphics = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
});
self.heldPackage = null;
self.targetRotation = 0;
self.update = function () {
var rotationDiff = self.targetRotation - self.rotation;
while (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
while (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
self.rotation += rotationDiff * 0.1;
if (self.heldPackage) {
// Position package in front of character based on rotation
var packageDistance = 90; // Distance in front of character
var packageX = self.x + Math.cos(self.rotation) * packageDistance;
var packageY = self.y + Math.sin(self.rotation) * packageDistance;
// Smoothly tween package to new position for better visual flow
tween(self.heldPackage, {
x: packageX,
y: packageY,
rotation: self.rotation
}, {
duration: 100,
easing: tween.easeOut
});
}
};
return self;
});
var Confetti = Container.expand(function (colorValue) {
var self = Container.call(this);
var confettiGraphics = self.attachAsset('confetti', {
anchorX: 0.5,
anchorY: 0.5
});
confettiGraphics.tint = colorValue;
self.velocityX = (Math.random() - 0.5) * 400;
self.velocityY = (Math.random() - 0.5) * 400;
self.life = 60;
self.update = function () {
self.x += self.velocityX * (1 / 60);
self.y += self.velocityY * (1 / 60);
self.velocityY += 300 * (1 / 60);
self.life--;
if (self.life <= 0) {
self.alpha = 0;
}
};
return self;
});
var Package = Container.expand(function (colorValue) {
var self = Container.call(this);
var assetName = 'yellowpacket';
if (colorValue === 0x0066ff) assetName = 'bluepacket';else if (colorValue === 0xff0000) assetName = 'redpacket';else if (colorValue === 0xff8800) assetName = 'orangepacket';
var packageGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.colorValue = colorValue || 0xffffff;
self.speed = 172.5; // 150 * 1.15 = 15% increase
if (isInfiniteMode) {
self.speed = 172.5 * 3.6; // 3.6x faster than normal speed (20% increase)
} else if (isHellMode) {
self.speed *= 4; // 4x speed in hell mode
}
self.caught = false;
self.thrown = false;
self.throwVelocityX = 0;
self.throwVelocityY = 0;
self.update = function () {
if (self.thrown) {
self.x += self.throwVelocityX;
self.y += self.throwVelocityY;
} else if (!self.caught) {
self.y += self.speed * (1 / 60);
}
};
return self;
});
var TargetBox = Container.expand(function (colorValue) {
var self = Container.call(this);
var assetName = 'targetYellow';
if (colorValue === 0x0066ff) assetName = 'targetBlue';else if (colorValue === 0xff0000) assetName = 'targetRed';else if (colorValue === 0xff8800) assetName = 'targetOrange';
var targetGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.colorValue = colorValue;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var gameWidth = 2048;
var gameHeight = 2732;
var arenaSize = 1600;
var arenaX = gameWidth / 2;
var arenaY = gameHeight / 2;
// Game arena
var arena = game.addChild(LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5,
x: arenaX,
y: arenaY
}));
// Character
var character = game.addChild(new Character());
character.x = arenaX;
character.y = arenaY;
// Target boxes at corners
var targets = [];
var targetColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; // yellow, blue, red, orange
var targetPositions = [{
x: arenaX - arenaSize / 2 + 180,
y: arenaY - arenaSize / 2 + 180
},
// top-left
{
x: arenaX + arenaSize / 2 - 180,
y: arenaY - arenaSize / 2 + 180
},
// top-right
{
x: arenaX - arenaSize / 2 + 180,
y: arenaY + arenaSize / 2 - 180
},
// bottom-left
{
x: arenaX + arenaSize / 2 - 180,
y: arenaY + arenaSize / 2 - 180
} // bottom-right
];
for (var i = 0; i < 4; i++) {
var target = game.addChild(new TargetBox(targetColors[i]));
target.x = targetPositions[i].x;
target.y = targetPositions[i].y;
targets.push(target);
}
// Game state
var packages = [];
var confettiParticles = [];
var gameTime = 0;
var gameDuration = 30000; // 30 seconds for levels 1-3, 240000 for final level
var packageSpawnInterval = 2000; // 2 seconds
var lastPackageSpawn = 0;
var gameStarted = false;
var gameEnded = false;
var gameReady = false;
var currentLevel = 1;
var levelCompleted = false;
var finalLevelStarted = false;
var isHellMode = false;
var hellModeSelected = false;
var isInfiniteMode = false;
var infiniteModeSelected = false;
var infiniteMusicTimer = 0;
var infiniteMusicInterval = 108000; // 108 seconds in milliseconds
// Arena animation variables
var arenaAnimationTimes = [11, 23, 31, 41, 53, 63, 75, 85, 96, 107, 116, 128, 137, 149, 157, 169, 180, 190, 202, 214, 222, 232, 241, 253, 263, 271, 280, 290, 301, 310, 321, 329, 339, 349, 361, 370, 382, 394, 405, 414, 423, 435, 444, 452, 461, 471, 479, 490, 500, 510, 519, 527, 538, 546, 558, 567, 575, 584, 596, 608];
var currentArenaAnimationIndex = 0;
var isArenaAnimating = false;
var arenaAnimationStep = 0;
// Bomb mechanic variables
var lastBombSpawn = 0;
var bombSpawnInterval = 3000; // 3 seconds
var bombHoldTimer = 0;
var bombWarningText = null;
// Predetermined color sequence for level 1
var colorSequenceLevel1 = [0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xff0000, 0x0066ff, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff8800, 0xff0000, 0xffff00, 0x0066ff];
// Level 2 color sequence and timing intervals (in milliseconds)
var colorSequenceLevel2 = [0xff0000, 0xff8800, 0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xff0000, 0x0066ff, 0xffff00, 0xff8800, 0x0066ff, 0xffff00];
var timingIntervalsLevel2 = [1720, 2150, 1290, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1720, 1720, 1290, 1720, 1720, 1720, 1720, 1720, 2150, 1720, 1290];
// Level 3 color sequence and timing intervals (in milliseconds)
var colorSequenceLevel3 = [0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0x0066ff, 0xffff00];
var timingIntervalsLevel3 = [1720, 1720, 2150, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1290, 1720];
// Final level color sequence (longer sequence for 4 minutes)
var colorSequenceFinal = [0xff0000, 0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000];
var currentSequenceIndex = 0;
var currentTimingIndex = 0;
// UI
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var timeTxt = new Text2('Time: 30', {
size: 60,
fill: 0xFFFFFF
});
timeTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(timeTxt);
// Level display
var levelText = new Text2('LEVEL 1', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
// Ready button
var readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI
var normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
var hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
// Infinite button (only for level 1)
var infiniteButton = LK.getAsset('infinite1', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2 + 430,
scaleX: 4.76,
scaleY: 4.76
});
game.addChild(infiniteButton);
// Infinite button animation variables
var infiniteTextures = ['infinite1', 'infinite2', 'infinite3', 'infinite4', 'infinite5', 'infinite6', 'infinite7', 'infinite8'];
var currentInfiniteTextureIndex = 0;
var infiniteAnimationTimer = 0;
var infiniteAnimationSpeed = 4; // Change texture every 4 frames (about 15 fps at 60fps)
// Helper functions
function getColorName(colorValue) {
switch (colorValue) {
case 0xffff00:
return 'Yellow';
case 0x0066ff:
return 'Blue';
case 0xff0000:
return 'Red';
case 0xff8800:
return 'Orange';
default:
return 'Unknown';
}
}
function spawnPackage() {
var currentColorSequence;
var packageColor;
if (isInfiniteMode) {
// Infinite mode: completely random colors
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
packageColor = randomColors[Math.floor(Math.random() * randomColors.length)];
} else {
if (currentLevel === 1) {
currentColorSequence = colorSequenceLevel1;
} else if (currentLevel === 2) {
currentColorSequence = colorSequenceLevel2;
} else if (currentLevel === 3) {
currentColorSequence = colorSequenceLevel3;
} else {
currentColorSequence = colorSequenceFinal;
}
if (currentSequenceIndex >= currentColorSequence.length) return;
packageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
}
var newPackage = new Package(packageColor);
newPackage.x = character.x; // Spawn directly above character
newPackage.y = arenaY - arenaSize / 2 - 200; // Spawn higher above arena
packages.push(newPackage);
game.addChild(newPackage);
// Hell mode: 20% chance to spawn a second package 0.05 seconds later
if (isHellMode && Math.random() < 0.2) {
LK.setTimeout(function () {
// Get next color from sequence for the second package
var secondPackageColor;
if (currentSequenceIndex < currentColorSequence.length) {
secondPackageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
} else if (currentLevel === 4) {
// For final level, loop back to beginning
currentSequenceIndex = 0;
secondPackageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
} else {
// Use random color if sequence exhausted
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
secondPackageColor = randomColors[Math.floor(Math.random() * randomColors.length)];
}
var secondPackage = new Package(secondPackageColor);
secondPackage.x = character.x;
secondPackage.y = arenaY - arenaSize / 2 - 200;
packages.push(secondPackage);
game.addChild(secondPackage);
}, 150); // 0.15 seconds = 150ms
}
}
function spawnBomb() {
var newBomb = new Bomb();
newBomb.x = character.x; // Spawn directly above character
newBomb.y = arenaY - arenaSize / 2 - 200; // Spawn higher above arena
packages.push(newBomb);
game.addChild(newBomb);
}
function createConfettiExplosion(x, y, color) {
var allColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; // yellow, blue, red, orange
// Create more particles for bigger explosion (25 instead of 15)
for (var i = 0; i < 25; i++) {
var confettiColor;
if (i < 18) {
// Most particles (72%) use the main color
confettiColor = color;
} else {
// Some particles (28%) use random other colors
confettiColor = allColors[Math.floor(Math.random() * allColors.length)];
}
var confetti = new Confetti(confettiColor);
confetti.x = x;
confetti.y = y;
// Scale up confetti particles by 1.5x for bigger effect
tween(confetti, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 50,
easing: tween.easeOut
});
confettiParticles.push(confetti);
game.addChild(confetti);
}
}
function startArenaAnimation() {
if (isArenaAnimating) return;
isArenaAnimating = true;
arenaAnimationStep = 0;
// Start animation sequence
animateArenaStep();
}
function animateArenaStep() {
var arenaAssets = ['arena2', 'arena3', 'arena4'];
if (arenaAnimationStep < arenaAssets.length) {
// Change to next animation frame
var newArena = LK.getAsset(arenaAssets[arenaAnimationStep], {
anchorX: 0.5,
anchorY: 0.5,
x: arenaX,
y: arenaY,
scaleX: 12,
// Scale to match original arena size
scaleY: 12
});
// Add new arena behind all other objects (at index 1, after original arena)
game.addChildAt(newArena, 1);
// Auto-remove after 1.6 seconds
LK.setTimeout(function () {
newArena.destroy();
}, 1600);
arenaAnimationStep++;
// Schedule next step after 400ms
LK.setTimeout(function () {
animateArenaStep();
}, 400);
} else {
// Animation complete
isArenaAnimating = false;
}
}
function createFireExplosion(x, y) {
var flameColors = [0xFF4500, 0xFF6347, 0xFF0000, 0xFFA500, 0xFFD700]; // Orange, tomato, red, orange, gold
// Create 30 flame particles for explosion effect
for (var i = 0; i < 30; i++) {
var flameColor = flameColors[Math.floor(Math.random() * flameColors.length)];
var flame = new Confetti(flameColor);
flame.x = x;
flame.y = y;
// Enhanced velocities for explosive effect
flame.velocityX = (Math.random() - 0.5) * 600;
flame.velocityY = (Math.random() - 0.5) * 600;
flame.life = 90; // Medium life for flames
// Scale and fade effect using tween
tween(flame, {
scaleX: 2,
scaleY: 2
}, {
duration: 80,
easing: tween.easeOut
});
tween(flame, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut
});
confettiParticles.push(flame);
game.addChild(flame);
}
}
function createFireworks() {
var fireworksColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0x00ff00, 0xff00ff, 0x00ffff];
// Create multiple firework explosions across the screen
for (var f = 0; f < 8; f++) {
var fireworkX = Math.random() * gameWidth;
var fireworkY = Math.random() * (gameHeight * 0.6) + gameHeight * 0.2;
var fireworkColor = fireworksColors[Math.floor(Math.random() * fireworksColors.length)];
// Create 40 particles per firework for spectacular effect
for (var i = 0; i < 40; i++) {
var firework = new Confetti(fireworkColor);
firework.x = fireworkX;
firework.y = fireworkY;
// Enhanced velocities for bigger explosion
firework.velocityX = (Math.random() - 0.5) * 800;
firework.velocityY = (Math.random() - 0.5) * 600 - 200;
firework.life = 120; // Longer life for fireworks
// Scale and fade effect using tween
tween(firework, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 100,
easing: tween.easeOut
});
tween(firework, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut
});
confettiParticles.push(firework);
game.addChild(firework);
}
}
}
function checkPackageTargetCollision(packageObj, target) {
var dx = packageObj.x - target.x;
var dy = packageObj.y - target.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Orange target gets larger hitbox (50% larger than base 120 = 180)
if (target.colorValue === 0xff8800) {
return distance < 180; // Orange target gets bigger hitbox
}
return distance < 156; // Increased from 104 to 156 (1.5x scale) for other targets
}
function throwPackage(targetX, targetY) {
if (!character.heldPackage) {
// Empty click penalty
LK.setScore(LK.getScore() - 15);
LK.getSound('miss').play();
return;
}
var packageToThrow = character.heldPackage;
character.heldPackage = null;
// Clear bomb warning if it exists
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
var dx = targetX - packageToThrow.x;
var dy = targetY - packageToThrow.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var speed = 800;
packageToThrow.throwVelocityX = dx / distance * speed * (1 / 60);
packageToThrow.throwVelocityY = dy / distance * speed * (1 / 60);
packageToThrow.thrown = true;
LK.getSound('throw').play();
}
// Event handlers
game.move = function (x, y, obj) {
var dx = x - character.x;
var dy = y - character.y;
character.targetRotation = Math.atan2(dy, dx);
};
game.down = function (x, y, obj) {
if (!gameReady) {
// Check infinite button first (only for level 1)
if (currentLevel === 1) {
var infiniteButtonX = gameWidth / 2;
var infiniteButtonY = gameHeight / 2 + 430;
var infiniteButtonWidth = 476; // 4.76x scale of 200px width = 952, so half is 476
var infiniteButtonHeight = 110; // 4.76x scale of 46.1px height = 219, so half is 110
if (x >= infiniteButtonX - infiniteButtonWidth && x <= infiniteButtonX + infiniteButtonWidth && y >= infiniteButtonY - infiniteButtonHeight && y <= infiniteButtonY + infiniteButtonHeight) {
// Start infinite mode
isInfiniteMode = true;
infiniteModeSelected = true;
hellModeSelected = true; // Skip mode selection since infinite mode is already chosen
gameReady = true;
gameStarted = true;
gameTime = 0;
lastPackageSpawn = 0;
// Set score to 10000 for infinite mode
LK.setScore(10000);
// Clean up UI elements and stop animation
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
if (levelText && levelText.parent) {
levelText.destroy();
}
if (readyButton && readyButton.parent) {
readyButton.destroy();
}
if (normalModeButton && normalModeButton.parent) {
normalModeButton.destroy();
}
if (hellModeButton && hellModeButton.parent) {
hellModeButton.destroy();
}
// Start infinite mode music
LK.playMusic('bgmusicinfinite');
return;
}
}
// Check mode selection if not selected
if (!hellModeSelected) {
// Check normal mode button
var normalButtonX = gameWidth / 2 - 220;
var normalButtonY = gameHeight / 2 + 130;
var normalButtonWidth = 150;
var normalButtonHeight = 60;
if (x >= normalButtonX - normalButtonWidth && x <= normalButtonX + normalButtonWidth && y >= normalButtonY - normalButtonHeight && y <= normalButtonY + normalButtonHeight) {
isHellMode = false;
hellModeSelected = true;
normalModeButton.destroy();
hellModeButton.destroy();
// Hide infinite button when normal mode is selected
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
// Remove passive effect from ready button
readyButton.tint = 0xFFFFFF;
readyButton.alpha = 1;
return;
}
// Check hell mode button
var hellButtonX = gameWidth / 2 + 220;
var hellButtonY = gameHeight / 2 + 130;
var hellButtonWidth = 100;
var hellButtonHeight = 100;
if (x >= hellButtonX - hellButtonWidth && x <= hellButtonX + hellButtonWidth && y >= hellButtonY - hellButtonHeight && y <= hellButtonY + hellButtonHeight) {
isHellMode = true;
hellModeSelected = true;
normalModeButton.destroy();
hellModeButton.destroy();
// Hide infinite button when hell mode is selected
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
// Remove passive effect from ready button
readyButton.tint = 0xFFFFFF;
readyButton.alpha = 1;
return;
}
return;
}
// Check if clicked on ready button area (only after mode selected)
if (hellModeSelected) {
var buttonX = gameWidth / 2;
var buttonY = gameHeight / 2;
var buttonWidth = 200;
var buttonHeight = 100;
if (x >= buttonX - buttonWidth && x <= buttonX + buttonWidth && y >= buttonY - buttonHeight && y <= buttonY + buttonHeight) {
gameReady = true;
readyButton.destroy();
levelText.destroy();
}
}
return;
}
if (gameEnded) return;
throwPackage(x, y);
};
// Main game update
game.update = function () {
if (!gameReady) return;
if (!gameStarted) {
gameStarted = true;
gameTime = 0; // Reset game time when starting
lastPackageSpawn = 0; // Reset package spawn timer
// Remove infinite button when game starts (level 1 only)
if (currentLevel === 1 && infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
if (currentLevel === 1) {
LK.getSound('level1').play();
LK.playMusic('bgmusic');
} else if (currentLevel === 2) {
LK.getSound('level2').play();
LK.playMusic('bgmusic');
} else if (currentLevel === 3) {
LK.getSound('level3').play();
LK.playMusic('bgmusic3');
} else if (currentLevel === 4) {
LK.getSound('final').play();
LK.playMusic('bgmusicfinal');
gameDuration = 240000; // 4 minutes for final level
finalLevelStarted = true;
}
}
if (gameEnded) return;
// Arena animation trigger at predetermined times - works for all levels
var currentGameTimeInSeconds = Math.floor(gameTime / 1000);
if (currentArenaAnimationIndex < arenaAnimationTimes.length && currentGameTimeInSeconds >= arenaAnimationTimes[currentArenaAnimationIndex]) {
startArenaAnimation();
currentArenaAnimationIndex++;
}
// Check for insufficient score in final level
if (currentLevel === 4) {
if (LK.getScore() < -200) {
gameEnded = true;
LK.showGameOver();
return;
}
}
gameTime += 1000 / 60; // Increment game time only after ready
if (isInfiniteMode) {
var elapsedTime = Math.floor(gameTime / 1000);
timeTxt.setText('Time: ' + elapsedTime);
} else {
var remainingTime = Math.max(0, Math.ceil((gameDuration - gameTime) / 1000));
timeTxt.setText('Time: ' + remainingTime);
}
// Infinite mode music timer
if (isInfiniteMode) {
infiniteMusicTimer += 1000 / 60;
if (infiniteMusicTimer >= infiniteMusicInterval) {
LK.playMusic('bgmusicinfinite');
infiniteMusicTimer = 0;
}
}
// Check for level completion (skip for infinite mode)
if (!isInfiniteMode && gameTime >= gameDuration) {
if (currentLevel === 1) {
// Level 1 completed, prepare for level 2
currentLevel = 2;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for next level
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show level 2 ready screen
levelText = new Text2('LEVEL 2', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for level 2
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else if (currentLevel === 2) {
// Level 2 completed, prepare for level 3
currentLevel = 3;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for level 3
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show level 3 ready screen
levelText = new Text2('LEVEL 3', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for level 3
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else if (currentLevel === 3) {
// Level 3 completed, prepare for final level
currentLevel = 4;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for final level
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show final level ready screen
levelText = new Text2('FINAL LEVEL', {
size: 120,
fill: 0xFFD700
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for final level
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else {
// Final level completed - you win with fireworks
gameEnded = true;
createFireworks();
LK.getSound('gameend').play();
LK.setTimeout(function () {
LK.showYouWin();
}, 5000);
return;
}
}
// Hell mode and infinite mode bomb spawning for all levels
if ((isHellMode || isInfiniteMode) && gameTime - lastBombSpawn >= bombSpawnInterval) {
if (Math.random() < 0.25) {
// 25% chance
spawnBomb();
}
lastBombSpawn = gameTime;
}
// Infinite mode game over condition when score reaches 0
if (isInfiniteMode && LK.getScore() <= 0) {
gameEnded = true;
LK.showGameOver();
return;
}
// Play bomb sound continuously when bomb is present in hell mode or infinite mode for all levels
if ((isHellMode || isInfiniteMode) && character.heldPackage && character.heldPackage.isBomb) {
if (!LK.getSound('bomb').isPlaying) {
LK.getSound('bomb').play();
}
}
// Bomb holding penalty (10 points every 0.1 seconds = every 6 frames at 60fps)
if (character.heldPackage && character.heldPackage.isBomb) {
bombHoldTimer++;
if (bombHoldTimer >= 6) {
// 0.1 seconds at 60fps
LK.setScore(LK.getScore() - 10);
bombHoldTimer = 0;
}
// Show bomb warning if not already shown
if (!bombWarningText) {
bombWarningText = new Text2('BOMB! THROW AWAY!', {
size: 60,
fill: 0xFF0000
});
bombWarningText.anchor.set(0.5, 0.5);
bombWarningText.x = gameWidth / 2;
bombWarningText.y = 200;
game.addChild(bombWarningText);
// Flash effect
tween(bombWarningText, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (bombWarningText) {
tween(bombWarningText, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
} else {
bombHoldTimer = 0;
// Clear bomb warning if not holding bomb
if (bombWarningText && (!character.heldPackage || !character.heldPackage.isBomb)) {
bombWarningText.destroy();
bombWarningText = null;
}
}
// Spawn packages
var currentColorSequence;
if (isInfiniteMode) {
// Infinite mode uses completely random colors
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
currentColorSequence = randomColors;
} else if (currentLevel === 1) {
currentColorSequence = colorSequenceLevel1;
} else if (currentLevel === 2) {
currentColorSequence = colorSequenceLevel2;
} else {
currentColorSequence = colorSequenceLevel3;
}
var currentSpawnInterval;
if (isInfiniteMode) {
// Infinite mode random intervals: 15% - 0.4s, 30% - 0.8s, 54% - 1.2s, 1% - 0.2s
var randomValue = Math.random();
if (randomValue < 0.15) {
currentSpawnInterval = 400; // 0.4 seconds
} else if (randomValue < 0.45) {
currentSpawnInterval = 800; // 0.8 seconds
} else if (randomValue < 0.99) {
currentSpawnInterval = 1200; // 1.2 seconds
} else {
currentSpawnInterval = 200; // 0.2 seconds
}
} else if (currentLevel === 1) {
currentSpawnInterval = packageSpawnInterval; // Keep 2000ms for level 1
} else if (currentLevel === 2) {
// Level 2 uses custom timing intervals
if (currentTimingIndex < timingIntervalsLevel2.length) {
currentSpawnInterval = timingIntervalsLevel2[currentTimingIndex];
} else {
currentSpawnInterval = 1720; // Default fallback
}
} else if (currentLevel === 3) {
// Level 3 uses custom timing intervals
if (currentTimingIndex < timingIntervalsLevel3.length) {
currentSpawnInterval = timingIntervalsLevel3[currentTimingIndex];
} else {
currentSpawnInterval = 1720; // Default fallback
}
} else if (currentLevel === 4) {
// Final level uses progressive timing based on game time
var progressiveTiming = [1500, 1875]; // 0-30 seconds
if (gameTime >= 30000 && gameTime < 60000) {
progressiveTiming = [1125, 1500, 1875]; // 30-60 seconds
} else if (gameTime >= 60000 && gameTime < 180000) {
progressiveTiming = [1125, 1500]; // 60-180 seconds
} else if (gameTime >= 180000) {
progressiveTiming = [750, 1125, 1500]; // 180-240 seconds
}
currentSpawnInterval = progressiveTiming[Math.floor(Math.random() * progressiveTiming.length)];
}
if (gameTime - lastPackageSpawn >= currentSpawnInterval) {
// For final level, allow sequence to loop when exhausted
if (currentLevel === 4 && currentSequenceIndex >= currentColorSequence.length) {
currentSequenceIndex = 0; // Reset to beginning of sequence
}
// Only spawn if we have colors available or it's the final level or infinite mode
if (currentSequenceIndex < currentColorSequence.length || currentLevel === 4 || isInfiniteMode) {
spawnPackage();
if (currentLevel === 2 || currentLevel === 3) {
currentTimingIndex++;
}
lastPackageSpawn = gameTime;
}
}
// Update packages
for (var i = packages.length - 1; i >= 0; i--) {
var pkg = packages[i];
if (pkg.lastY === undefined) pkg.lastY = pkg.y;
if (pkg.lastCaught === undefined) pkg.lastCaught = false;
if (pkg.lastThrown === undefined) pkg.lastThrown = false;
// Check if package reached character (catch)
if (!pkg.caught && !pkg.thrown) {
var distanceToCharacter = Math.sqrt(Math.pow(pkg.x - character.x, 2) + Math.pow(pkg.y - character.y, 2));
if (distanceToCharacter < 75) {
if (!character.heldPackage) {
pkg.caught = true;
character.heldPackage = pkg;
LK.getSound('catch').play();
}
}
}
// Check if package fell off screen (drop penalty)
if (!pkg.caught && !pkg.thrown && pkg.y > arenaY + arenaSize / 2 + 100) {
LK.setScore(LK.getScore() - 150);
pkg.destroy();
packages.splice(i, 1);
continue;
}
// Check thrown package collisions with targets
if (pkg.thrown) {
var hitTarget = false;
for (var j = 0; j < targets.length; j++) {
var target = targets[j];
if (checkPackageTargetCollision(pkg, target)) {
if (pkg.isBomb) {
// Bomb hit target - create flame explosion effect and apply penalty
LK.setScore(LK.getScore() - 500);
createFireExplosion(target.x, target.y);
LK.getSound('blast').play();
} else if (pkg.colorValue === target.colorValue) {
// Correct color match
LK.setScore(LK.getScore() + 100);
createConfettiExplosion(target.x, target.y, pkg.colorValue);
LK.getSound('hit').play();
} else {
// Wrong color penalty
LK.setScore(LK.getScore() - 60);
LK.getSound('miss').play();
}
hitTarget = true;
break;
}
}
if (hitTarget || pkg.x < 0 || pkg.x > gameWidth || pkg.y < 0 || pkg.y > gameHeight) {
if (!hitTarget) {
// Only apply missed throw penalty for non-bomb packages
if (!pkg.isBomb) {
LK.setScore(LK.getScore() - 60);
LK.getSound('miss').play();
}
}
pkg.destroy();
packages.splice(i, 1);
continue;
}
}
pkg.lastY = pkg.y;
pkg.lastCaught = pkg.caught;
pkg.lastThrown = pkg.thrown;
}
// Update confetti
for (var i = confettiParticles.length - 1; i >= 0; i--) {
var confetti = confettiParticles[i];
if (confetti.alpha <= 0) {
confetti.destroy();
confettiParticles.splice(i, 1);
}
}
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
};
// Infinite button animation - runs independently of game update
var infiniteButtonQueue = []; // Queue to track buttons for delayed destruction
LK.on('tick', function () {
// Animate infinite button (only in level 1 before game starts)
if (currentLevel === 1 && !gameStarted && infiniteButton && infiniteButton.parent) {
infiniteAnimationTimer++;
if (infiniteAnimationTimer >= infiniteAnimationSpeed) {
currentInfiniteTextureIndex = (currentInfiniteTextureIndex + 1) % infiniteTextures.length;
// Add current button to queue for delayed destruction
infiniteButtonQueue.push(infiniteButton);
// Create new button with next texture at 4.76x scale and fixed position
infiniteButton = LK.getAsset(infiniteTextures[currentInfiniteTextureIndex], {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2 + 430,
scaleX: 4.76,
scaleY: 4.76
});
game.addChild(infiniteButton);
// If we have 3 or more buttons in queue, destroy the oldest one
if (infiniteButtonQueue.length >= 3) {
var buttonToDestroy = infiniteButtonQueue.shift(); // Remove first (oldest) button from queue
// Fade out and destroy the oldest button with 0.1 second delay
tween(buttonToDestroy, {
alpha: 0
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove old button after fade out
if (buttonToDestroy.parent) {
game.removeChild(buttonToDestroy);
}
buttonToDestroy.destroy();
}
});
}
infiniteAnimationTimer = 0;
}
}
});
bire bir aynı daha açık parlak sarı
birebir aynısı sarı-gri renkleri biraz daha koyu gri ile karıştır mavi-gri renklerin maviliğini biraz arttır. biraz da kırmızı-gri kareleri düzel
birebir aynısı. karelerin yerlerini değiştir dizilimi bozmadan
fitili ateşlenmiş parlak üzerinde yeşil tehlike işareti olan siyah bomba. In-Game asset. 2d. High contrast. No shadows
müzik temalı cool ready yazısı biraz da sprey boya punk tarzı. In-Game asset. 2d. High contrast. No shadows
dehşet saçan bir hell mode yazısı tek line olsun geniş kısa. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
açık mavi dj masası ekipmanı. In-Game asset. 2d. High contrast. No shadows
aynı stil daha parlak ve altınımsı mikrofon ve kulaklığı kaldır
orange cool dj keyboard rgb keys. In-Game asset. 2d. High contrast. No shadows
red cool dj headset rgp neon. In-Game asset. 2d. High contrast. No shadows