/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { level: 1, highScore: 0 }); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); sprite.tint = 0x00ffff; // Cyan for player bullets self.speed = 15; self.direction = { x: 0, y: -1 }; // Default moving upward self.isEnemyBullet = false; self.lastPosition = { x: 0, y: 0 }; self.setDirection = function (dirX, dirY) { var length = Math.sqrt(dirX * dirX + dirY * dirY); if (length > 0) { self.direction.x = dirX / length; self.direction.y = dirY / length; } }; self.setEnemyBullet = function (isEnemy) { self.isEnemyBullet = isEnemy; if (isEnemy) { sprite.tint = 0xff0000; // Red for enemy bullets } }; self.update = function () { self.lastPosition.x = self.x; self.lastPosition.y = self.y; // Apply time slowing effect - slow to 0.25x speed when time is frozen var actualSpeed = timeIsFrozen ? self.speed * 0.25 : self.speed; self.x += self.direction.x * actualSpeed; self.y += self.direction.y * actualSpeed; // Remove if out of bounds if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.shouldRemove = true; } }; return self; }); var Collectible = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('collectible', { anchorX: 0.5, anchorY: 0.5 }); self.value = 1; self.type = "standard"; // can be "standard" or "timeRecharge" self.setValue = function (value) { self.value = value; }; self.setType = function (type) { self.type = type; if (type === "timeRecharge") { sprite.tint = 0x3498db; // Blue for time recharge } else if (type === "shield") { sprite.tint = 0xE74C3C; // Red for shield power-up } }; self.update = function () { // Animate collectible if (!timeIsFrozen) { sprite.rotation += 0.02; } }; return self; }); var DirectionalButton = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('pauseBarBackground', { anchorX: 0.5, anchorY: 0.5 }); sprite.width = 150; sprite.height = 150; sprite.alpha = 0.5; var arrow = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); arrow.tint = 0xFFFFFF; self.direction = { x: 0, y: 0 }; self.isPressed = false; self.setDirection = function (dirX, dirY) { self.direction.x = dirX; self.direction.y = dirY; // Rotate arrow based on direction if (dirX === 1) { arrow.rotation = Math.PI / 2; } // Right else if (dirX === -1) { arrow.rotation = -Math.PI / 2; } // Left else if (dirY === 1) { arrow.rotation = Math.PI; } // Down else if (dirY === -1) { arrow.rotation = 0; } // Up }; self.down = function (x, y, obj) { self.isPressed = true; sprite.alpha = 0.8; }; self.up = function (x, y, obj) { self.isPressed = false; sprite.alpha = 0.5; }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.target = null; self.movementPattern = "chase"; // can be "chase", "patrol", or "random" self.patrolPoints = []; self.currentPatrolIndex = 0; self.randomMoveCounter = 0; self.setTarget = function (target) { self.target = target; }; self.setPatrolPoints = function (points) { self.patrolPoints = points; }; self.setMovementPattern = function (pattern) { self.movementPattern = pattern; }; self.health = 3; // Enemy health self.damaged = false; self.damagedTimer = 0; self.damagedMoveSpeed = 20; self.damagedDirection = { x: 0, y: 0 }; self.takeDamage = function () { self.health--; if (self.health <= 0) { return true; // Enemy is dead } // Set damaged state self.damaged = true; self.damagedTimer = 20; // Frames to stay in damaged state // Set random direction to move when damaged var angle = Math.random() * Math.PI * 2; self.damagedDirection = { x: Math.cos(angle), y: Math.sin(angle) }; // Flash enemy red sprite.tint = 0xFF0000; LK.setTimeout(function () { sprite.tint = 0xFFFFFF; }, 200); return false; // Enemy still alive }; self.update = function () { // Process damaged movement first if (self.damaged) { self.x += self.damagedDirection.x * self.damagedMoveSpeed; self.y += self.damagedDirection.y * self.damagedMoveSpeed; // Bounce off walls if (self.x <= sprite.width / 2 || self.x >= 2048 - sprite.width / 2) { self.damagedDirection.x *= -1; } if (self.y <= sprite.height / 2 || self.y >= 2732 - sprite.height / 2) { self.damagedDirection.y *= -1; } // Keep in bounds if (self.x < sprite.width / 2) { self.x = sprite.width / 2; } if (self.x > 2048 - sprite.width / 2) { self.x = 2048 - sprite.width / 2; } if (self.y < sprite.height / 2) { self.y = sprite.height / 2; } if (self.y > 2732 - sprite.height / 2) { self.y = 2732 - sprite.height / 2; } self.damagedTimer--; if (self.damagedTimer <= 0) { self.damaged = false; } return; } // Normal movement - now works during time freeze but at reduced speed var actualSpeed = timeIsFrozen ? self.speed * 0.25 : self.speed; if (self.movementPattern === "chase" && self.target) { // Chase player var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.x += dx / dist * actualSpeed; self.y += dy / dist * actualSpeed; } } else if (self.movementPattern === "patrol" && self.patrolPoints.length > 0) { // Patrol between points var targetPoint = self.patrolPoints[self.currentPatrolIndex]; var dx = targetPoint.x - self.x; var dy = targetPoint.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 10) { // Reached the point, move to next patrol point self.currentPatrolIndex = (self.currentPatrolIndex + 1) % self.patrolPoints.length; } else { self.x += dx / dist * actualSpeed; self.y += dy / dist * actualSpeed; } } else if (self.movementPattern === "random") { // Random movement self.randomMoveCounter--; if (self.randomMoveCounter <= 0) { self.vx = Math.random() * actualSpeed * 2 - actualSpeed; self.vy = Math.random() * actualSpeed * 2 - actualSpeed; self.randomMoveCounter = Math.floor(Math.random() * 60) + 30; } self.x += self.vx; self.y += self.vy; // Bounce off walls if (self.x <= sprite.width / 2 || self.x >= 2048 - sprite.width / 2) { self.vx *= -1; } if (self.y <= sprite.height / 2 || self.y >= 2732 - sprite.height / 2) { self.vy *= -1; } } }; return self; }); // Joystick class removed in favor of directional buttons var LevelEnd = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('levelEnd', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // Animate the level end gate if (!timeIsFrozen) { sprite.rotation += 0.01; } }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); self.vx = 0; self.vy = 0; self.update = function () { if (!timeIsFrozen) { self.x += self.vx; self.y += self.vy; // Bounce off walls if (self.x <= sprite.width / 2 || self.x >= 2048 - sprite.width / 2) { self.vx *= -1; } if (self.y <= sprite.height / 2 || self.y >= 2732 - sprite.height / 2) { self.vy *= -1; } } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.isDragging = false; self.isInvulnerable = false; self.lastPosition = { x: 0, y: 0 }; self.setDragging = function (isDragging) { self.isDragging = isDragging; }; self.move = function (x, y) { if (self.isDragging && !timeIsFrozen) { self.x = x; self.y = y; } }; self.update = function () { // Store last position self.lastPosition.x = self.x; self.lastPosition.y = self.y; // Process movement from directional buttons if (directionButtons) { var moveX = 0; var moveY = 0; // Calculate movement based on all pressed buttons for (var i = 0; i < directionButtons.length; i++) { var button = directionButtons[i]; if (button.isPressed) { moveX += button.direction.x; moveY += button.direction.y; } } // Normalize diagonal movement if (moveX !== 0 && moveY !== 0) { var length = Math.sqrt(moveX * moveX + moveY * moveY); moveX /= length; moveY /= length; } // Apply movement with or without time slowing var speed = timeIsFrozen ? self.speed * 0.7 : self.speed; self.x += moveX * speed; self.y += moveY * speed; } // Constrain player to game boundaries if (self.x < sprite.width / 2) { self.x = sprite.width / 2; } if (self.x > 2048 - sprite.width / 2) { self.x = 2048 - sprite.width / 2; } if (self.y < sprite.height / 2) { self.y = sprite.height / 2; } if (self.y > 2732 - sprite.height / 2) { self.y = 2732 - sprite.height / 2; } // Update shield effect if it exists if (self.shieldEffect) { self.shieldEffect.update(); } }; return self; }); var ShieldEffect = Container.expand(function () { var self = Container.call(this); // Create shield circle var shield = self.attachAsset('shield', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, alpha: 0.6 }); // Set initial shield properties shield.tint = 0x3498db; // Blue shield self.update = function () { // Rotate shield for visual effect shield.rotation += 0.02; // Pulsing effect if (self.pulseDirection) { shield.alpha += 0.01; if (shield.alpha >= 0.7) { self.pulseDirection = false; } } else { shield.alpha -= 0.01; if (shield.alpha <= 0.3) { self.pulseDirection = true; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x34495e }); /**** * Game Code ****/ // Game state variables var currentLevel = storage.level || 1; var score = 0; var timeIsFrozen = false; var pauseEnergy = 100; var pauseEnergyMax = 100; var pauseEnergyDrainRate = 0.5; var levelComplete = false; var player; var obstacles = []; var enemies = []; var collectibles = []; var bullets = []; var enemyBullets = []; var levelEnd; var freezeEffect; var directionButtons; var shootingTimer = 0; var enemyShootingTimer = {}; // UI elements var scoreTxt; var levelTxt; var pauseBarBg; var pauseBar; var instructionsTxt; function initGame() { // Clear existing objects if any obstacles = []; enemies = []; collectibles = []; bullets = []; enemyBullets = []; // Reset game state pauseEnergy = pauseEnergyMax; timeIsFrozen = false; levelComplete = false; score = LK.getScore(); shootingTimer = 0; enemyShootingTimer = {}; // Create UI createUI(); // Create player player = new Player(); player.x = 1024; // Center of screen player.y = 2200; // Near bottom game.addChild(player); // Apply shield effect for 1.5 seconds player.isInvulnerable = true; // Create and add shield effect var shieldEffect = new ShieldEffect(); shieldEffect.x = 0; shieldEffect.y = 0; player.addChild(shieldEffect); player.shieldEffect = shieldEffect; // Create pulse effect with tween tween(player, { alpha: 0.9 }, { duration: 750, easing: tween.sinceOut, repeat: true, yoyo: true }); // Remove shield effect after 1.5 seconds LK.setTimeout(function () { player.isInvulnerable = false; player.removeChild(shieldEffect); player.shieldEffect = null; tween(player, { alpha: 1.0 }, { duration: 300 }); }, 1500); // Create freeze effect (initially invisible) freezeEffect = LK.getAsset('freezeEffect', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); freezeEffect.x = 2048 / 2; freezeEffect.y = 2732 / 2; game.addChild(freezeEffect); // Create directional buttons var buttonSize = 300; var buttonSpacing = 160; var baseX = 300; var baseY = 2500; // Create up, down, left, right buttons var upButton = new DirectionalButton(); upButton.x = baseX; upButton.y = baseY - buttonSpacing; upButton.setDirection(0, -1); game.addChild(upButton); var downButton = new DirectionalButton(); downButton.x = baseX; downButton.y = baseY + buttonSpacing; downButton.setDirection(0, 1); game.addChild(downButton); var leftButton = new DirectionalButton(); leftButton.x = baseX - buttonSpacing; leftButton.y = baseY; leftButton.setDirection(-1, 0); game.addChild(leftButton); var rightButton = new DirectionalButton(); rightButton.x = baseX + buttonSpacing; rightButton.y = baseY; rightButton.setDirection(1, 0); game.addChild(rightButton); // Create button references array directionButtons = [upButton, downButton, leftButton, rightButton]; // Create level based on current level createLevel(currentLevel); // Start background music LK.playMusic('bgmusic'); } function createUI() { // Score text scoreTxt = new Text2('Score: 0', { size: 70, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); scoreTxt.x = 120; scoreTxt.y = 30; LK.gui.addChild(scoreTxt); // Level text levelTxt = new Text2('Level: ' + currentLevel, { size: 70, fill: 0xFFFFFF }); levelTxt.anchor.set(1, 0); levelTxt.x = 2048 - 120; levelTxt.y = 30; LK.gui.addChild(levelTxt); // Pause energy bar background pauseBarBg = LK.getAsset('pauseBarBackground', { anchorX: 0.5, anchorY: 0.5 }); pauseBarBg.x = 2048 / 2; pauseBarBg.y = 100; LK.gui.addChild(pauseBarBg); // Pause energy bar pauseBar = LK.getAsset('pauseBar', { anchorX: 0, anchorY: 0.5 }); pauseBar.x = pauseBarBg.x - pauseBarBg.width / 2; pauseBar.y = pauseBarBg.y; pauseBar.width = pauseBarBg.width * (pauseEnergy / pauseEnergyMax); LK.gui.addChild(pauseBar); // Instructions instructionsTxt = new Text2('Use directional buttons to move. Tap screen to slow time (enemies move at 0.25x speed).\nShoot automatically every 1.5 seconds. Enemies shoot every 2 seconds.\nDamage enemies to make them move. Defeat enemies to spawn new ones.', { size: 40, fill: 0xFFFFFF }); instructionsTxt.anchor.set(0.5, 0); instructionsTxt.x = 2048 / 2; instructionsTxt.y = 150; LK.gui.addChild(instructionsTxt); // Auto-hide instructions after 5 seconds LK.setTimeout(function () { tween(instructionsTxt, { alpha: 0 }, { duration: 1000 }); }, 5000); } function createLevel(level) { // Each level has a different configuration switch (level) { case 1: // Level 1: Simple introduction createObstacle(600, 500, 3, 0); createObstacle(1200, 800, -2, 2); createObstacle(1600, 1200, 0, 4); createCollectible(800, 600); createCollectible(1400, 1000); createCollectible(1700, 1800); createCollectible(500, 1500, "timeRecharge"); createCollectible(900, 1200, "shield"); // Make level end harder to reach by adding guards createObstacle(1600, 2200, 3, 1); createObstacle(2000, 2300, -2, 2); // Add enemy guarding level end var guardEnemy = createEnemy(1700, 2200, "patrol"); guardEnemy.setPatrolPoints([{ x: 1600, y: 2200 }, { x: 2000, y: 2200 }, { x: 2000, y: 2500 }, { x: 1600, y: 2500 }]); // Level end levelEnd = new LevelEnd(); levelEnd.x = 1800; levelEnd.y = 2400; game.addChild(levelEnd); break; case 2: // Level 2: Introduce enemies createObstacle(600, 500, 4, 0); createObstacle(1200, 800, -3, 3); createObstacle(1600, 1200, 0, 5); createObstacle(900, 1600, 4, -2); var enemy = createEnemy(1000, 1000, "chase"); enemy.setTarget(player); createCollectible(800, 600); createCollectible(1400, 1000); createCollectible(1700, 1800); createCollectible(500, 1500); createCollectible(1200, 2200, "timeRecharge"); // Make level end harder to reach by adding more guards createObstacle(1600, 2200, 4, 2); createObstacle(2000, 2300, -3, 3); createObstacle(1800, 2100, 2, -2); // Add multiple enemies guarding level end var guardEnemy1 = createEnemy(1700, 2200, "chase"); guardEnemy1.setTarget(player); var guardEnemy2 = createEnemy(1900, 2300, "patrol"); guardEnemy2.setPatrolPoints([{ x: 1700, y: 2200 }, { x: 1900, y: 2200 }, { x: 1900, y: 2500 }, { x: 1700, y: 2500 }]); // Level end levelEnd = new LevelEnd(); levelEnd.x = 1800; levelEnd.y = 2400; game.addChild(levelEnd); break; case 3: // Level 3: More complex with patrol enemies createObstacle(600, 500, 5, 0); createObstacle(1200, 800, -4, 3); createObstacle(1600, 1200, 0, 6); createObstacle(900, 1600, 5, -3); createObstacle(1400, 2000, -3, -3); var enemy1 = createEnemy(1000, 1000, "chase"); enemy1.setTarget(player); var enemy2 = createEnemy(1500, 1500, "patrol"); enemy2.setPatrolPoints([{ x: 1500, y: 1500 }, { x: 1500, y: 2000 }, { x: 1000, y: 2000 }, { x: 1000, y: 1500 }]); createCollectible(800, 600); createCollectible(1400, 1000); createCollectible(1700, 1800); createCollectible(500, 1500); createCollectible(1200, 2200); createCollectible(1800, 1300, "timeRecharge"); // Add a ring of obstacles around the level end var obstacleCount = 8; var radius = 250; for (var i = 0; i < obstacleCount; i++) { var angle = i / obstacleCount * Math.PI * 2; var obsX = 1800 + Math.cos(angle) * radius; var obsY = 2400 + Math.sin(angle) * radius; var vx = Math.cos(angle + Math.PI / 2) * 3; var vy = Math.sin(angle + Math.PI / 2) * 3; createObstacle(obsX, obsY, vx, vy); } // Add multiple enemies guarding level end var guardEnemy1 = createEnemy(1700, 2200, "chase"); guardEnemy1.setTarget(player); var guardEnemy2 = createEnemy(1900, 2300, "chase"); guardEnemy2.setTarget(player); var guardEnemy3 = createEnemy(1800, 2100, "patrol"); guardEnemy3.setPatrolPoints([{ x: 1600, y: 2100 }, { x: 2000, y: 2100 }, { x: 2000, y: 2600 }, { x: 1600, y: 2600 }]); // Level end levelEnd = new LevelEnd(); levelEnd.x = 1800; levelEnd.y = 2400; game.addChild(levelEnd); break; default: // Higher levels: Procedurally generate increasingly difficult layouts var obstacleCount = 3 + Math.min(level, 7); var enemyCount = 1 + Math.min(level - 2, 5); var collectibleCount = 4 + Math.min(level, 6); // Create obstacles for (var i = 0; i < obstacleCount; i++) { var x = 400 + Math.random() * 1600; var y = 400 + Math.random() * 1800; var vx = (Math.random() * 6 + 2) * (Math.random() > 0.5 ? 1 : -1); var vy = (Math.random() * 6 + 2) * (Math.random() > 0.5 ? 1 : -1); createObstacle(x, y, vx, vy); } // Create enemies for (var i = 0; i < enemyCount; i++) { var x = 400 + Math.random() * 1600; var y = 400 + Math.random() * 1800; var patternType = Math.random(); if (patternType < 0.4) { var enemy = createEnemy(x, y, "chase"); enemy.setTarget(player); } else if (patternType < 0.7) { var enemy = createEnemy(x, y, "patrol"); var patrolRadius = 300 + Math.random() * 300; var patrolPoints = []; var patrolPointCount = 3 + Math.floor(Math.random() * 3); for (var j = 0; j < patrolPointCount; j++) { var angle = j / patrolPointCount * Math.PI * 2; patrolPoints.push({ x: x + Math.cos(angle) * patrolRadius, y: y + Math.sin(angle) * patrolRadius }); } enemy.setPatrolPoints(patrolPoints); } else { createEnemy(x, y, "random"); } } // Create collectibles for (var i = 0; i < collectibleCount; i++) { var x = 300 + Math.random() * 1700; var y = 300 + Math.random() * 2000; if (i === collectibleCount - 1) { createCollectible(x, y, "timeRecharge"); } else { createCollectible(x, y); } } // Add a complex ring of obstacles and enemies around the level end var levelEndX = 1700 + Math.random() * 200; var levelEndY = 2200 + Math.random() * 300; // Add a ring of obstacles around the level end var obstacleCount = 8 + Math.min(level, 4); var radius = 200 + Math.min(level * 10, 100); for (var i = 0; i < obstacleCount; i++) { var angle = i / obstacleCount * Math.PI * 2; var obsX = levelEndX + Math.cos(angle) * radius; var obsY = levelEndY + Math.sin(angle) * radius; var vx = Math.cos(angle + Math.PI / 2) * (2 + Math.min(level * 0.5, 4)); var vy = Math.sin(angle + Math.PI / 2) * (2 + Math.min(level * 0.5, 4)); createObstacle(obsX, obsY, vx, vy); } // Add guardian enemies based on level var guardCount = Math.min(level, 5); for (var i = 0; i < guardCount; i++) { var angle = i / guardCount * Math.PI * 2; var enemyX = levelEndX + Math.cos(angle) * (radius - 50); var enemyY = levelEndY + Math.sin(angle) * (radius - 50); if (i % 3 === 0) { var enemy = createEnemy(enemyX, enemyY, "chase"); enemy.setTarget(player); } else if (i % 3 === 1) { var enemy = createEnemy(enemyX, enemyY, "patrol"); var patrolRadius = radius * 0.6; var patrolPoints = []; var patrolPointCount = 4; for (var j = 0; j < patrolPointCount; j++) { var patrolAngle = j / patrolPointCount * Math.PI * 2; patrolPoints.push({ x: levelEndX + Math.cos(patrolAngle) * patrolRadius, y: levelEndY + Math.sin(patrolAngle) * patrolRadius }); } enemy.setPatrolPoints(patrolPoints); } else { createEnemy(enemyX, enemyY, "random"); } } // Level end levelEnd = new LevelEnd(); levelEnd.x = levelEndX; levelEnd.y = levelEndY; game.addChild(levelEnd); break; } } function createObstacle(x, y, vx, vy) { var obstacle = new Obstacle(); obstacle.x = x; obstacle.y = y; obstacle.vx = vx; obstacle.vy = vy; obstacles.push(obstacle); game.addChild(obstacle); return obstacle; } function createEnemy(x, y, movementPattern) { var enemy = new Enemy(); enemy.x = x; enemy.y = y; enemy.setMovementPattern(movementPattern); enemies.push(enemy); game.addChild(enemy); return enemy; } function createCollectible(x, y, type) { var collectible = new Collectible(); collectible.x = x; collectible.y = y; if (type === "timeRecharge") { collectible.setType("timeRecharge"); } else if (type === "shield") { collectible.setType("shield"); collectible.setValue(5); // Shield worth more points var sprite = collectible.getChildAt(0); if (sprite) { sprite.tint = 0xE74C3C; // Red for shield power-up } } collectibles.push(collectible); game.addChild(collectible); return collectible; } function createPlayerBullet() { var bullet = new Bullet(); bullet.x = player.x; bullet.y = player.y - 30; bullet.setDirection(0, -1); // Shoot upward bullets.push(bullet); game.addChild(bullet); return bullet; } function createEnemyBullet(enemy) { var bullet = new Bullet(); bullet.x = enemy.x; bullet.y = enemy.y + 30; // Direction toward player var dx = player.x - enemy.x; var dy = player.y - enemy.y; bullet.setDirection(dx, dy); bullet.setEnemyBullet(true); enemyBullets.push(bullet); game.addChild(bullet); return bullet; } function toggleFreezeTime() { if (pauseEnergy <= 0 && timeIsFrozen) { // Can't stay frozen with no energy unfreezeTime(); return; } if (!timeIsFrozen && pauseEnergy > 0) { // Slow time (not completely freeze) timeIsFrozen = true; tween(freezeEffect, { alpha: 0.2 }, { duration: 300 }); LK.getSound('freeze').play(); } else if (timeIsFrozen) { // Unfreeze time unfreezeTime(); } } function unfreezeTime() { timeIsFrozen = false; tween(freezeEffect, { alpha: 0 }, { duration: 300 }); LK.getSound('unfreeze').play(); } function updatePauseEnergyBar() { pauseBar.width = pauseBarBg.width * (pauseEnergy / pauseEnergyMax); } function checkCollisions() { // Check player-obstacle collisions for (var i = 0; i < obstacles.length; i++) { if (player.intersects(obstacles[i]) && !player.isInvulnerable) { handlePlayerHit(); return; } } // Check player-enemy collisions for (var i = 0; i < enemies.length; i++) { if (player.intersects(enemies[i]) && !player.isInvulnerable) { handlePlayerHit(); return; } } // Check player-collectible collisions for (var i = collectibles.length - 1; i >= 0; i--) { if (player.intersects(collectibles[i])) { // Collect the item if (collectibles[i].type === "timeRecharge") { pauseEnergy = Math.min(pauseEnergyMax, pauseEnergy + 50); updatePauseEnergyBar(); } else if (collectibles[i].type === "shield") { // Apply shield effect player.isInvulnerable = true; if (!player.shieldEffect) { // Create new shield effect var shieldEffect = new ShieldEffect(); shieldEffect.x = 0; shieldEffect.y = 0; player.addChild(shieldEffect); player.shieldEffect = shieldEffect; // Flash player LK.effects.flashObject(player, 0xE74C3C, 300); // Remove shield after 5 seconds LK.setTimeout(function () { if (player.shieldEffect) { player.removeChild(player.shieldEffect); player.shieldEffect = null; player.isInvulnerable = false; } }, 5000); } score += collectibles[i].value; LK.setScore(score); scoreTxt.setText('Score: ' + score); } else { score += collectibles[i].value; LK.setScore(score); scoreTxt.setText('Score: ' + score); } LK.getSound('collect').play(); collectibles[i].destroy(); collectibles.splice(i, 1); } } // Check if player reached the level end if (levelEnd && player.intersects(levelEnd)) { completeLevel(); } } function handlePlayerHit() { if (!timeIsFrozen) { // Flash the screen red LK.effects.flashScreen(0xff0000, 500); LK.getSound('hit').play(); // Show game over LK.showGameOver(); } } function completeLevel() { if (!levelComplete) { levelComplete = true; // Save progress currentLevel++; storage.level = currentLevel; // Save high score if (score > storage.highScore) { storage.highScore = score; } // Play win sound LK.getSound('win').play(); // Show level complete LK.showYouWin(); } } // Game event handlers game.down = function (x, y, obj) { // Check if any directional button was clicked var buttonPressed = false; if (directionButtons) { for (var i = 0; i < directionButtons.length; i++) { var button = directionButtons[i]; var dx = x - button.x; var dy = y - button.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= 75) { // Button radius button.down(x, y, obj); buttonPressed = true; break; } } } if (!buttonPressed) { // If no button was pressed, handle time slowing if (!timeIsFrozen && pauseEnergy > 0) { // Slow time when clicking anywhere else (if we have energy) toggleFreezeTime(); } else if (timeIsFrozen) { // Unfreeze time when clicking anywhere else if already frozen toggleFreezeTime(); } } }; game.up = function (x, y, obj) { // Release all directional buttons if (directionButtons) { for (var i = 0; i < directionButtons.length; i++) { var button = directionButtons[i]; var dx = x - button.x; var dy = y - button.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= 75) { button.up(x, y, obj); } } } }; game.move = function (x, y, obj) { // Handle touch/mouse movement for buttons // Detect if we've moved over or out of any buttons if (directionButtons) { for (var i = 0; i < directionButtons.length; i++) { var button = directionButtons[i]; var dx = x - button.x; var dy = y - button.y; var distance = Math.sqrt(dx * dx + dy * dy); // If finger/pointer moved outside a pressed button, release it if (button.isPressed && distance > 75) { button.up(x, y, obj); } } } }; // Game update loop game.update = function () { // Check if game is initialized if (!player) { initGame(); return; } // Drain pause energy when time is frozen if (timeIsFrozen) { pauseEnergy = Math.max(0, pauseEnergy - pauseEnergyDrainRate); updatePauseEnergyBar(); // Automatically unfreeze when energy is depleted if (pauseEnergy <= 0) { unfreezeTime(); } } else if (pauseEnergy < pauseEnergyMax) { // Slowly recharge when not frozen pauseEnergy = Math.min(pauseEnergyMax, pauseEnergy + 0.1); updatePauseEnergyBar(); } // Handle player shooting (every 1.5 seconds) shootingTimer++; var shootingInterval = timeIsFrozen ? 90 * 1.5 : 90; // 90 ticks = 1.5 seconds, slower when time is frozen if (shootingTimer >= shootingInterval) { createPlayerBullet(); shootingTimer = 0; } // Handle enemy shooting (every 2 seconds) for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (!enemyShootingTimer[i]) { enemyShootingTimer[i] = 0; } enemyShootingTimer[i]++; var enemyShootInterval = timeIsFrozen ? 120 * 1.5 : 120; // 120 ticks = 2 seconds, slower when time is frozen if (enemyShootingTimer[i] >= enemyShootInterval) { createEnemyBullet(enemy); enemyShootingTimer[i] = 0; } } // Update game objects player.update(); // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { bullets[i].update(); if (bullets[i].shouldRemove) { bullets[i].destroy(); bullets.splice(i, 1); } } // Update enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { enemyBullets[i].update(); if (enemyBullets[i].shouldRemove) { enemyBullets[i].destroy(); enemyBullets.splice(i, 1); } } for (var i = 0; i < obstacles.length; i++) { obstacles[i].update(); } for (var i = 0; i < enemies.length; i++) { enemies[i].update(); } for (var i = 0; i < collectibles.length; i++) { collectibles[i].update(); } if (levelEnd) { levelEnd.update(); } // Check for collisions checkCollisions(); // Check bullet collisions with enemies for (var i = bullets.length - 1; i >= 0; i--) { for (var j = enemies.length - 1; j >= 0; j--) { if (bullets[i] && bullets[i].intersects(enemies[j])) { // Enemy hit by player bullet var enemyDead = enemies[j].takeDamage(); if (enemyDead) { // Spawn a new enemy at a random position when one is destroyed var spawnX = Math.random() < 0.5 ? 200 + Math.random() * 200 : 1848 - Math.random() * 200; var spawnY = Math.random() < 0.5 ? 200 + Math.random() * 200 : 2532 - Math.random() * 200; // Choose a random movement pattern with increased difficulty var patterns = ["chase", "patrol", "random"]; var patternIndex = Math.floor(Math.random() * patterns.length); var newEnemy = createEnemy(spawnX, spawnY, patterns[patternIndex]); // Make new enemies faster and more challenging newEnemy.speed = enemies[j].speed + 0.5; if (patterns[patternIndex] === "chase") { newEnemy.setTarget(player); } else if (patterns[patternIndex] === "patrol") { var patrolRadius = 300 + Math.random() * 300; var patrolPoints = []; var patrolPointCount = 3 + Math.floor(Math.random() * 3); for (var k = 0; k < patrolPointCount; k++) { var angle = k / patrolPointCount * Math.PI * 2; patrolPoints.push({ x: spawnX + Math.cos(angle) * patrolRadius, y: spawnY + Math.sin(angle) * patrolRadius }); } newEnemy.setPatrolPoints(patrolPoints); } // Destroy the old enemy enemies[j].destroy(); enemies.splice(j, 1); // Update score score += 10; LK.setScore(score); scoreTxt.setText('Score: ' + score); } // Always destroy the bullet that hit bullets[i].destroy(); bullets.splice(i, 1); break; } } } // Check enemy bullet collisions with player for (var i = enemyBullets.length - 1; i >= 0; i--) { if (enemyBullets[i] && player.intersects(enemyBullets[i])) { // Player hit by enemy bullet if (!timeIsFrozen && !player.isInvulnerable) { handlePlayerHit(); } else { // Just destroy the bullet if time is frozen or player is invulnerable enemyBullets[i].destroy(); enemyBullets.splice(i, 1); } } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
level: 1,
highScore: 0
});
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
sprite.tint = 0x00ffff; // Cyan for player bullets
self.speed = 15;
self.direction = {
x: 0,
y: -1
}; // Default moving upward
self.isEnemyBullet = false;
self.lastPosition = {
x: 0,
y: 0
};
self.setDirection = function (dirX, dirY) {
var length = Math.sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
self.direction.x = dirX / length;
self.direction.y = dirY / length;
}
};
self.setEnemyBullet = function (isEnemy) {
self.isEnemyBullet = isEnemy;
if (isEnemy) {
sprite.tint = 0xff0000; // Red for enemy bullets
}
};
self.update = function () {
self.lastPosition.x = self.x;
self.lastPosition.y = self.y;
// Apply time slowing effect - slow to 0.25x speed when time is frozen
var actualSpeed = timeIsFrozen ? self.speed * 0.25 : self.speed;
self.x += self.direction.x * actualSpeed;
self.y += self.direction.y * actualSpeed;
// Remove if out of bounds
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.shouldRemove = true;
}
};
return self;
});
var Collectible = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('collectible', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 1;
self.type = "standard"; // can be "standard" or "timeRecharge"
self.setValue = function (value) {
self.value = value;
};
self.setType = function (type) {
self.type = type;
if (type === "timeRecharge") {
sprite.tint = 0x3498db; // Blue for time recharge
} else if (type === "shield") {
sprite.tint = 0xE74C3C; // Red for shield power-up
}
};
self.update = function () {
// Animate collectible
if (!timeIsFrozen) {
sprite.rotation += 0.02;
}
};
return self;
});
var DirectionalButton = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('pauseBarBackground', {
anchorX: 0.5,
anchorY: 0.5
});
sprite.width = 150;
sprite.height = 150;
sprite.alpha = 0.5;
var arrow = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
arrow.tint = 0xFFFFFF;
self.direction = {
x: 0,
y: 0
};
self.isPressed = false;
self.setDirection = function (dirX, dirY) {
self.direction.x = dirX;
self.direction.y = dirY;
// Rotate arrow based on direction
if (dirX === 1) {
arrow.rotation = Math.PI / 2;
} // Right
else if (dirX === -1) {
arrow.rotation = -Math.PI / 2;
} // Left
else if (dirY === 1) {
arrow.rotation = Math.PI;
} // Down
else if (dirY === -1) {
arrow.rotation = 0;
} // Up
};
self.down = function (x, y, obj) {
self.isPressed = true;
sprite.alpha = 0.8;
};
self.up = function (x, y, obj) {
self.isPressed = false;
sprite.alpha = 0.5;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.target = null;
self.movementPattern = "chase"; // can be "chase", "patrol", or "random"
self.patrolPoints = [];
self.currentPatrolIndex = 0;
self.randomMoveCounter = 0;
self.setTarget = function (target) {
self.target = target;
};
self.setPatrolPoints = function (points) {
self.patrolPoints = points;
};
self.setMovementPattern = function (pattern) {
self.movementPattern = pattern;
};
self.health = 3; // Enemy health
self.damaged = false;
self.damagedTimer = 0;
self.damagedMoveSpeed = 20;
self.damagedDirection = {
x: 0,
y: 0
};
self.takeDamage = function () {
self.health--;
if (self.health <= 0) {
return true; // Enemy is dead
}
// Set damaged state
self.damaged = true;
self.damagedTimer = 20; // Frames to stay in damaged state
// Set random direction to move when damaged
var angle = Math.random() * Math.PI * 2;
self.damagedDirection = {
x: Math.cos(angle),
y: Math.sin(angle)
};
// Flash enemy red
sprite.tint = 0xFF0000;
LK.setTimeout(function () {
sprite.tint = 0xFFFFFF;
}, 200);
return false; // Enemy still alive
};
self.update = function () {
// Process damaged movement first
if (self.damaged) {
self.x += self.damagedDirection.x * self.damagedMoveSpeed;
self.y += self.damagedDirection.y * self.damagedMoveSpeed;
// Bounce off walls
if (self.x <= sprite.width / 2 || self.x >= 2048 - sprite.width / 2) {
self.damagedDirection.x *= -1;
}
if (self.y <= sprite.height / 2 || self.y >= 2732 - sprite.height / 2) {
self.damagedDirection.y *= -1;
}
// Keep in bounds
if (self.x < sprite.width / 2) {
self.x = sprite.width / 2;
}
if (self.x > 2048 - sprite.width / 2) {
self.x = 2048 - sprite.width / 2;
}
if (self.y < sprite.height / 2) {
self.y = sprite.height / 2;
}
if (self.y > 2732 - sprite.height / 2) {
self.y = 2732 - sprite.height / 2;
}
self.damagedTimer--;
if (self.damagedTimer <= 0) {
self.damaged = false;
}
return;
}
// Normal movement - now works during time freeze but at reduced speed
var actualSpeed = timeIsFrozen ? self.speed * 0.25 : self.speed;
if (self.movementPattern === "chase" && self.target) {
// Chase player
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += dx / dist * actualSpeed;
self.y += dy / dist * actualSpeed;
}
} else if (self.movementPattern === "patrol" && self.patrolPoints.length > 0) {
// Patrol between points
var targetPoint = self.patrolPoints[self.currentPatrolIndex];
var dx = targetPoint.x - self.x;
var dy = targetPoint.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 10) {
// Reached the point, move to next patrol point
self.currentPatrolIndex = (self.currentPatrolIndex + 1) % self.patrolPoints.length;
} else {
self.x += dx / dist * actualSpeed;
self.y += dy / dist * actualSpeed;
}
} else if (self.movementPattern === "random") {
// Random movement
self.randomMoveCounter--;
if (self.randomMoveCounter <= 0) {
self.vx = Math.random() * actualSpeed * 2 - actualSpeed;
self.vy = Math.random() * actualSpeed * 2 - actualSpeed;
self.randomMoveCounter = Math.floor(Math.random() * 60) + 30;
}
self.x += self.vx;
self.y += self.vy;
// Bounce off walls
if (self.x <= sprite.width / 2 || self.x >= 2048 - sprite.width / 2) {
self.vx *= -1;
}
if (self.y <= sprite.height / 2 || self.y >= 2732 - sprite.height / 2) {
self.vy *= -1;
}
}
};
return self;
});
// Joystick class removed in favor of directional buttons
var LevelEnd = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('levelEnd', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Animate the level end gate
if (!timeIsFrozen) {
sprite.rotation += 0.01;
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.update = function () {
if (!timeIsFrozen) {
self.x += self.vx;
self.y += self.vy;
// Bounce off walls
if (self.x <= sprite.width / 2 || self.x >= 2048 - sprite.width / 2) {
self.vx *= -1;
}
if (self.y <= sprite.height / 2 || self.y >= 2732 - sprite.height / 2) {
self.vy *= -1;
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.isDragging = false;
self.isInvulnerable = false;
self.lastPosition = {
x: 0,
y: 0
};
self.setDragging = function (isDragging) {
self.isDragging = isDragging;
};
self.move = function (x, y) {
if (self.isDragging && !timeIsFrozen) {
self.x = x;
self.y = y;
}
};
self.update = function () {
// Store last position
self.lastPosition.x = self.x;
self.lastPosition.y = self.y;
// Process movement from directional buttons
if (directionButtons) {
var moveX = 0;
var moveY = 0;
// Calculate movement based on all pressed buttons
for (var i = 0; i < directionButtons.length; i++) {
var button = directionButtons[i];
if (button.isPressed) {
moveX += button.direction.x;
moveY += button.direction.y;
}
}
// Normalize diagonal movement
if (moveX !== 0 && moveY !== 0) {
var length = Math.sqrt(moveX * moveX + moveY * moveY);
moveX /= length;
moveY /= length;
}
// Apply movement with or without time slowing
var speed = timeIsFrozen ? self.speed * 0.7 : self.speed;
self.x += moveX * speed;
self.y += moveY * speed;
}
// Constrain player to game boundaries
if (self.x < sprite.width / 2) {
self.x = sprite.width / 2;
}
if (self.x > 2048 - sprite.width / 2) {
self.x = 2048 - sprite.width / 2;
}
if (self.y < sprite.height / 2) {
self.y = sprite.height / 2;
}
if (self.y > 2732 - sprite.height / 2) {
self.y = 2732 - sprite.height / 2;
}
// Update shield effect if it exists
if (self.shieldEffect) {
self.shieldEffect.update();
}
};
return self;
});
var ShieldEffect = Container.expand(function () {
var self = Container.call(this);
// Create shield circle
var shield = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
alpha: 0.6
});
// Set initial shield properties
shield.tint = 0x3498db; // Blue shield
self.update = function () {
// Rotate shield for visual effect
shield.rotation += 0.02;
// Pulsing effect
if (self.pulseDirection) {
shield.alpha += 0.01;
if (shield.alpha >= 0.7) {
self.pulseDirection = false;
}
} else {
shield.alpha -= 0.01;
if (shield.alpha <= 0.3) {
self.pulseDirection = true;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x34495e
});
/****
* Game Code
****/
// Game state variables
var currentLevel = storage.level || 1;
var score = 0;
var timeIsFrozen = false;
var pauseEnergy = 100;
var pauseEnergyMax = 100;
var pauseEnergyDrainRate = 0.5;
var levelComplete = false;
var player;
var obstacles = [];
var enemies = [];
var collectibles = [];
var bullets = [];
var enemyBullets = [];
var levelEnd;
var freezeEffect;
var directionButtons;
var shootingTimer = 0;
var enemyShootingTimer = {};
// UI elements
var scoreTxt;
var levelTxt;
var pauseBarBg;
var pauseBar;
var instructionsTxt;
function initGame() {
// Clear existing objects if any
obstacles = [];
enemies = [];
collectibles = [];
bullets = [];
enemyBullets = [];
// Reset game state
pauseEnergy = pauseEnergyMax;
timeIsFrozen = false;
levelComplete = false;
score = LK.getScore();
shootingTimer = 0;
enemyShootingTimer = {};
// Create UI
createUI();
// Create player
player = new Player();
player.x = 1024; // Center of screen
player.y = 2200; // Near bottom
game.addChild(player);
// Apply shield effect for 1.5 seconds
player.isInvulnerable = true;
// Create and add shield effect
var shieldEffect = new ShieldEffect();
shieldEffect.x = 0;
shieldEffect.y = 0;
player.addChild(shieldEffect);
player.shieldEffect = shieldEffect;
// Create pulse effect with tween
tween(player, {
alpha: 0.9
}, {
duration: 750,
easing: tween.sinceOut,
repeat: true,
yoyo: true
});
// Remove shield effect after 1.5 seconds
LK.setTimeout(function () {
player.isInvulnerable = false;
player.removeChild(shieldEffect);
player.shieldEffect = null;
tween(player, {
alpha: 1.0
}, {
duration: 300
});
}, 1500);
// Create freeze effect (initially invisible)
freezeEffect = LK.getAsset('freezeEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
freezeEffect.x = 2048 / 2;
freezeEffect.y = 2732 / 2;
game.addChild(freezeEffect);
// Create directional buttons
var buttonSize = 300;
var buttonSpacing = 160;
var baseX = 300;
var baseY = 2500;
// Create up, down, left, right buttons
var upButton = new DirectionalButton();
upButton.x = baseX;
upButton.y = baseY - buttonSpacing;
upButton.setDirection(0, -1);
game.addChild(upButton);
var downButton = new DirectionalButton();
downButton.x = baseX;
downButton.y = baseY + buttonSpacing;
downButton.setDirection(0, 1);
game.addChild(downButton);
var leftButton = new DirectionalButton();
leftButton.x = baseX - buttonSpacing;
leftButton.y = baseY;
leftButton.setDirection(-1, 0);
game.addChild(leftButton);
var rightButton = new DirectionalButton();
rightButton.x = baseX + buttonSpacing;
rightButton.y = baseY;
rightButton.setDirection(1, 0);
game.addChild(rightButton);
// Create button references array
directionButtons = [upButton, downButton, leftButton, rightButton];
// Create level based on current level
createLevel(currentLevel);
// Start background music
LK.playMusic('bgmusic');
}
function createUI() {
// Score text
scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 120;
scoreTxt.y = 30;
LK.gui.addChild(scoreTxt);
// Level text
levelTxt = new Text2('Level: ' + currentLevel, {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
levelTxt.x = 2048 - 120;
levelTxt.y = 30;
LK.gui.addChild(levelTxt);
// Pause energy bar background
pauseBarBg = LK.getAsset('pauseBarBackground', {
anchorX: 0.5,
anchorY: 0.5
});
pauseBarBg.x = 2048 / 2;
pauseBarBg.y = 100;
LK.gui.addChild(pauseBarBg);
// Pause energy bar
pauseBar = LK.getAsset('pauseBar', {
anchorX: 0,
anchorY: 0.5
});
pauseBar.x = pauseBarBg.x - pauseBarBg.width / 2;
pauseBar.y = pauseBarBg.y;
pauseBar.width = pauseBarBg.width * (pauseEnergy / pauseEnergyMax);
LK.gui.addChild(pauseBar);
// Instructions
instructionsTxt = new Text2('Use directional buttons to move. Tap screen to slow time (enemies move at 0.25x speed).\nShoot automatically every 1.5 seconds. Enemies shoot every 2 seconds.\nDamage enemies to make them move. Defeat enemies to spawn new ones.', {
size: 40,
fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0);
instructionsTxt.x = 2048 / 2;
instructionsTxt.y = 150;
LK.gui.addChild(instructionsTxt);
// Auto-hide instructions after 5 seconds
LK.setTimeout(function () {
tween(instructionsTxt, {
alpha: 0
}, {
duration: 1000
});
}, 5000);
}
function createLevel(level) {
// Each level has a different configuration
switch (level) {
case 1:
// Level 1: Simple introduction
createObstacle(600, 500, 3, 0);
createObstacle(1200, 800, -2, 2);
createObstacle(1600, 1200, 0, 4);
createCollectible(800, 600);
createCollectible(1400, 1000);
createCollectible(1700, 1800);
createCollectible(500, 1500, "timeRecharge");
createCollectible(900, 1200, "shield");
// Make level end harder to reach by adding guards
createObstacle(1600, 2200, 3, 1);
createObstacle(2000, 2300, -2, 2);
// Add enemy guarding level end
var guardEnemy = createEnemy(1700, 2200, "patrol");
guardEnemy.setPatrolPoints([{
x: 1600,
y: 2200
}, {
x: 2000,
y: 2200
}, {
x: 2000,
y: 2500
}, {
x: 1600,
y: 2500
}]);
// Level end
levelEnd = new LevelEnd();
levelEnd.x = 1800;
levelEnd.y = 2400;
game.addChild(levelEnd);
break;
case 2:
// Level 2: Introduce enemies
createObstacle(600, 500, 4, 0);
createObstacle(1200, 800, -3, 3);
createObstacle(1600, 1200, 0, 5);
createObstacle(900, 1600, 4, -2);
var enemy = createEnemy(1000, 1000, "chase");
enemy.setTarget(player);
createCollectible(800, 600);
createCollectible(1400, 1000);
createCollectible(1700, 1800);
createCollectible(500, 1500);
createCollectible(1200, 2200, "timeRecharge");
// Make level end harder to reach by adding more guards
createObstacle(1600, 2200, 4, 2);
createObstacle(2000, 2300, -3, 3);
createObstacle(1800, 2100, 2, -2);
// Add multiple enemies guarding level end
var guardEnemy1 = createEnemy(1700, 2200, "chase");
guardEnemy1.setTarget(player);
var guardEnemy2 = createEnemy(1900, 2300, "patrol");
guardEnemy2.setPatrolPoints([{
x: 1700,
y: 2200
}, {
x: 1900,
y: 2200
}, {
x: 1900,
y: 2500
}, {
x: 1700,
y: 2500
}]);
// Level end
levelEnd = new LevelEnd();
levelEnd.x = 1800;
levelEnd.y = 2400;
game.addChild(levelEnd);
break;
case 3:
// Level 3: More complex with patrol enemies
createObstacle(600, 500, 5, 0);
createObstacle(1200, 800, -4, 3);
createObstacle(1600, 1200, 0, 6);
createObstacle(900, 1600, 5, -3);
createObstacle(1400, 2000, -3, -3);
var enemy1 = createEnemy(1000, 1000, "chase");
enemy1.setTarget(player);
var enemy2 = createEnemy(1500, 1500, "patrol");
enemy2.setPatrolPoints([{
x: 1500,
y: 1500
}, {
x: 1500,
y: 2000
}, {
x: 1000,
y: 2000
}, {
x: 1000,
y: 1500
}]);
createCollectible(800, 600);
createCollectible(1400, 1000);
createCollectible(1700, 1800);
createCollectible(500, 1500);
createCollectible(1200, 2200);
createCollectible(1800, 1300, "timeRecharge");
// Add a ring of obstacles around the level end
var obstacleCount = 8;
var radius = 250;
for (var i = 0; i < obstacleCount; i++) {
var angle = i / obstacleCount * Math.PI * 2;
var obsX = 1800 + Math.cos(angle) * radius;
var obsY = 2400 + Math.sin(angle) * radius;
var vx = Math.cos(angle + Math.PI / 2) * 3;
var vy = Math.sin(angle + Math.PI / 2) * 3;
createObstacle(obsX, obsY, vx, vy);
}
// Add multiple enemies guarding level end
var guardEnemy1 = createEnemy(1700, 2200, "chase");
guardEnemy1.setTarget(player);
var guardEnemy2 = createEnemy(1900, 2300, "chase");
guardEnemy2.setTarget(player);
var guardEnemy3 = createEnemy(1800, 2100, "patrol");
guardEnemy3.setPatrolPoints([{
x: 1600,
y: 2100
}, {
x: 2000,
y: 2100
}, {
x: 2000,
y: 2600
}, {
x: 1600,
y: 2600
}]);
// Level end
levelEnd = new LevelEnd();
levelEnd.x = 1800;
levelEnd.y = 2400;
game.addChild(levelEnd);
break;
default:
// Higher levels: Procedurally generate increasingly difficult layouts
var obstacleCount = 3 + Math.min(level, 7);
var enemyCount = 1 + Math.min(level - 2, 5);
var collectibleCount = 4 + Math.min(level, 6);
// Create obstacles
for (var i = 0; i < obstacleCount; i++) {
var x = 400 + Math.random() * 1600;
var y = 400 + Math.random() * 1800;
var vx = (Math.random() * 6 + 2) * (Math.random() > 0.5 ? 1 : -1);
var vy = (Math.random() * 6 + 2) * (Math.random() > 0.5 ? 1 : -1);
createObstacle(x, y, vx, vy);
}
// Create enemies
for (var i = 0; i < enemyCount; i++) {
var x = 400 + Math.random() * 1600;
var y = 400 + Math.random() * 1800;
var patternType = Math.random();
if (patternType < 0.4) {
var enemy = createEnemy(x, y, "chase");
enemy.setTarget(player);
} else if (patternType < 0.7) {
var enemy = createEnemy(x, y, "patrol");
var patrolRadius = 300 + Math.random() * 300;
var patrolPoints = [];
var patrolPointCount = 3 + Math.floor(Math.random() * 3);
for (var j = 0; j < patrolPointCount; j++) {
var angle = j / patrolPointCount * Math.PI * 2;
patrolPoints.push({
x: x + Math.cos(angle) * patrolRadius,
y: y + Math.sin(angle) * patrolRadius
});
}
enemy.setPatrolPoints(patrolPoints);
} else {
createEnemy(x, y, "random");
}
}
// Create collectibles
for (var i = 0; i < collectibleCount; i++) {
var x = 300 + Math.random() * 1700;
var y = 300 + Math.random() * 2000;
if (i === collectibleCount - 1) {
createCollectible(x, y, "timeRecharge");
} else {
createCollectible(x, y);
}
}
// Add a complex ring of obstacles and enemies around the level end
var levelEndX = 1700 + Math.random() * 200;
var levelEndY = 2200 + Math.random() * 300;
// Add a ring of obstacles around the level end
var obstacleCount = 8 + Math.min(level, 4);
var radius = 200 + Math.min(level * 10, 100);
for (var i = 0; i < obstacleCount; i++) {
var angle = i / obstacleCount * Math.PI * 2;
var obsX = levelEndX + Math.cos(angle) * radius;
var obsY = levelEndY + Math.sin(angle) * radius;
var vx = Math.cos(angle + Math.PI / 2) * (2 + Math.min(level * 0.5, 4));
var vy = Math.sin(angle + Math.PI / 2) * (2 + Math.min(level * 0.5, 4));
createObstacle(obsX, obsY, vx, vy);
}
// Add guardian enemies based on level
var guardCount = Math.min(level, 5);
for (var i = 0; i < guardCount; i++) {
var angle = i / guardCount * Math.PI * 2;
var enemyX = levelEndX + Math.cos(angle) * (radius - 50);
var enemyY = levelEndY + Math.sin(angle) * (radius - 50);
if (i % 3 === 0) {
var enemy = createEnemy(enemyX, enemyY, "chase");
enemy.setTarget(player);
} else if (i % 3 === 1) {
var enemy = createEnemy(enemyX, enemyY, "patrol");
var patrolRadius = radius * 0.6;
var patrolPoints = [];
var patrolPointCount = 4;
for (var j = 0; j < patrolPointCount; j++) {
var patrolAngle = j / patrolPointCount * Math.PI * 2;
patrolPoints.push({
x: levelEndX + Math.cos(patrolAngle) * patrolRadius,
y: levelEndY + Math.sin(patrolAngle) * patrolRadius
});
}
enemy.setPatrolPoints(patrolPoints);
} else {
createEnemy(enemyX, enemyY, "random");
}
}
// Level end
levelEnd = new LevelEnd();
levelEnd.x = levelEndX;
levelEnd.y = levelEndY;
game.addChild(levelEnd);
break;
}
}
function createObstacle(x, y, vx, vy) {
var obstacle = new Obstacle();
obstacle.x = x;
obstacle.y = y;
obstacle.vx = vx;
obstacle.vy = vy;
obstacles.push(obstacle);
game.addChild(obstacle);
return obstacle;
}
function createEnemy(x, y, movementPattern) {
var enemy = new Enemy();
enemy.x = x;
enemy.y = y;
enemy.setMovementPattern(movementPattern);
enemies.push(enemy);
game.addChild(enemy);
return enemy;
}
function createCollectible(x, y, type) {
var collectible = new Collectible();
collectible.x = x;
collectible.y = y;
if (type === "timeRecharge") {
collectible.setType("timeRecharge");
} else if (type === "shield") {
collectible.setType("shield");
collectible.setValue(5); // Shield worth more points
var sprite = collectible.getChildAt(0);
if (sprite) {
sprite.tint = 0xE74C3C; // Red for shield power-up
}
}
collectibles.push(collectible);
game.addChild(collectible);
return collectible;
}
function createPlayerBullet() {
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y - 30;
bullet.setDirection(0, -1); // Shoot upward
bullets.push(bullet);
game.addChild(bullet);
return bullet;
}
function createEnemyBullet(enemy) {
var bullet = new Bullet();
bullet.x = enemy.x;
bullet.y = enemy.y + 30;
// Direction toward player
var dx = player.x - enemy.x;
var dy = player.y - enemy.y;
bullet.setDirection(dx, dy);
bullet.setEnemyBullet(true);
enemyBullets.push(bullet);
game.addChild(bullet);
return bullet;
}
function toggleFreezeTime() {
if (pauseEnergy <= 0 && timeIsFrozen) {
// Can't stay frozen with no energy
unfreezeTime();
return;
}
if (!timeIsFrozen && pauseEnergy > 0) {
// Slow time (not completely freeze)
timeIsFrozen = true;
tween(freezeEffect, {
alpha: 0.2
}, {
duration: 300
});
LK.getSound('freeze').play();
} else if (timeIsFrozen) {
// Unfreeze time
unfreezeTime();
}
}
function unfreezeTime() {
timeIsFrozen = false;
tween(freezeEffect, {
alpha: 0
}, {
duration: 300
});
LK.getSound('unfreeze').play();
}
function updatePauseEnergyBar() {
pauseBar.width = pauseBarBg.width * (pauseEnergy / pauseEnergyMax);
}
function checkCollisions() {
// Check player-obstacle collisions
for (var i = 0; i < obstacles.length; i++) {
if (player.intersects(obstacles[i]) && !player.isInvulnerable) {
handlePlayerHit();
return;
}
}
// Check player-enemy collisions
for (var i = 0; i < enemies.length; i++) {
if (player.intersects(enemies[i]) && !player.isInvulnerable) {
handlePlayerHit();
return;
}
}
// Check player-collectible collisions
for (var i = collectibles.length - 1; i >= 0; i--) {
if (player.intersects(collectibles[i])) {
// Collect the item
if (collectibles[i].type === "timeRecharge") {
pauseEnergy = Math.min(pauseEnergyMax, pauseEnergy + 50);
updatePauseEnergyBar();
} else if (collectibles[i].type === "shield") {
// Apply shield effect
player.isInvulnerable = true;
if (!player.shieldEffect) {
// Create new shield effect
var shieldEffect = new ShieldEffect();
shieldEffect.x = 0;
shieldEffect.y = 0;
player.addChild(shieldEffect);
player.shieldEffect = shieldEffect;
// Flash player
LK.effects.flashObject(player, 0xE74C3C, 300);
// Remove shield after 5 seconds
LK.setTimeout(function () {
if (player.shieldEffect) {
player.removeChild(player.shieldEffect);
player.shieldEffect = null;
player.isInvulnerable = false;
}
}, 5000);
}
score += collectibles[i].value;
LK.setScore(score);
scoreTxt.setText('Score: ' + score);
} else {
score += collectibles[i].value;
LK.setScore(score);
scoreTxt.setText('Score: ' + score);
}
LK.getSound('collect').play();
collectibles[i].destroy();
collectibles.splice(i, 1);
}
}
// Check if player reached the level end
if (levelEnd && player.intersects(levelEnd)) {
completeLevel();
}
}
function handlePlayerHit() {
if (!timeIsFrozen) {
// Flash the screen red
LK.effects.flashScreen(0xff0000, 500);
LK.getSound('hit').play();
// Show game over
LK.showGameOver();
}
}
function completeLevel() {
if (!levelComplete) {
levelComplete = true;
// Save progress
currentLevel++;
storage.level = currentLevel;
// Save high score
if (score > storage.highScore) {
storage.highScore = score;
}
// Play win sound
LK.getSound('win').play();
// Show level complete
LK.showYouWin();
}
}
// Game event handlers
game.down = function (x, y, obj) {
// Check if any directional button was clicked
var buttonPressed = false;
if (directionButtons) {
for (var i = 0; i < directionButtons.length; i++) {
var button = directionButtons[i];
var dx = x - button.x;
var dy = y - button.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 75) {
// Button radius
button.down(x, y, obj);
buttonPressed = true;
break;
}
}
}
if (!buttonPressed) {
// If no button was pressed, handle time slowing
if (!timeIsFrozen && pauseEnergy > 0) {
// Slow time when clicking anywhere else (if we have energy)
toggleFreezeTime();
} else if (timeIsFrozen) {
// Unfreeze time when clicking anywhere else if already frozen
toggleFreezeTime();
}
}
};
game.up = function (x, y, obj) {
// Release all directional buttons
if (directionButtons) {
for (var i = 0; i < directionButtons.length; i++) {
var button = directionButtons[i];
var dx = x - button.x;
var dy = y - button.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 75) {
button.up(x, y, obj);
}
}
}
};
game.move = function (x, y, obj) {
// Handle touch/mouse movement for buttons
// Detect if we've moved over or out of any buttons
if (directionButtons) {
for (var i = 0; i < directionButtons.length; i++) {
var button = directionButtons[i];
var dx = x - button.x;
var dy = y - button.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If finger/pointer moved outside a pressed button, release it
if (button.isPressed && distance > 75) {
button.up(x, y, obj);
}
}
}
};
// Game update loop
game.update = function () {
// Check if game is initialized
if (!player) {
initGame();
return;
}
// Drain pause energy when time is frozen
if (timeIsFrozen) {
pauseEnergy = Math.max(0, pauseEnergy - pauseEnergyDrainRate);
updatePauseEnergyBar();
// Automatically unfreeze when energy is depleted
if (pauseEnergy <= 0) {
unfreezeTime();
}
} else if (pauseEnergy < pauseEnergyMax) {
// Slowly recharge when not frozen
pauseEnergy = Math.min(pauseEnergyMax, pauseEnergy + 0.1);
updatePauseEnergyBar();
}
// Handle player shooting (every 1.5 seconds)
shootingTimer++;
var shootingInterval = timeIsFrozen ? 90 * 1.5 : 90; // 90 ticks = 1.5 seconds, slower when time is frozen
if (shootingTimer >= shootingInterval) {
createPlayerBullet();
shootingTimer = 0;
}
// Handle enemy shooting (every 2 seconds)
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemyShootingTimer[i]) {
enemyShootingTimer[i] = 0;
}
enemyShootingTimer[i]++;
var enemyShootInterval = timeIsFrozen ? 120 * 1.5 : 120; // 120 ticks = 2 seconds, slower when time is frozen
if (enemyShootingTimer[i] >= enemyShootInterval) {
createEnemyBullet(enemy);
enemyShootingTimer[i] = 0;
}
}
// Update game objects
player.update();
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].update();
if (bullets[i].shouldRemove) {
bullets[i].destroy();
bullets.splice(i, 1);
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
enemyBullets[i].update();
if (enemyBullets[i].shouldRemove) {
enemyBullets[i].destroy();
enemyBullets.splice(i, 1);
}
}
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].update();
}
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
}
for (var i = 0; i < collectibles.length; i++) {
collectibles[i].update();
}
if (levelEnd) {
levelEnd.update();
}
// Check for collisions
checkCollisions();
// Check bullet collisions with enemies
for (var i = bullets.length - 1; i >= 0; i--) {
for (var j = enemies.length - 1; j >= 0; j--) {
if (bullets[i] && bullets[i].intersects(enemies[j])) {
// Enemy hit by player bullet
var enemyDead = enemies[j].takeDamage();
if (enemyDead) {
// Spawn a new enemy at a random position when one is destroyed
var spawnX = Math.random() < 0.5 ? 200 + Math.random() * 200 : 1848 - Math.random() * 200;
var spawnY = Math.random() < 0.5 ? 200 + Math.random() * 200 : 2532 - Math.random() * 200;
// Choose a random movement pattern with increased difficulty
var patterns = ["chase", "patrol", "random"];
var patternIndex = Math.floor(Math.random() * patterns.length);
var newEnemy = createEnemy(spawnX, spawnY, patterns[patternIndex]);
// Make new enemies faster and more challenging
newEnemy.speed = enemies[j].speed + 0.5;
if (patterns[patternIndex] === "chase") {
newEnemy.setTarget(player);
} else if (patterns[patternIndex] === "patrol") {
var patrolRadius = 300 + Math.random() * 300;
var patrolPoints = [];
var patrolPointCount = 3 + Math.floor(Math.random() * 3);
for (var k = 0; k < patrolPointCount; k++) {
var angle = k / patrolPointCount * Math.PI * 2;
patrolPoints.push({
x: spawnX + Math.cos(angle) * patrolRadius,
y: spawnY + Math.sin(angle) * patrolRadius
});
}
newEnemy.setPatrolPoints(patrolPoints);
}
// Destroy the old enemy
enemies[j].destroy();
enemies.splice(j, 1);
// Update score
score += 10;
LK.setScore(score);
scoreTxt.setText('Score: ' + score);
}
// Always destroy the bullet that hit
bullets[i].destroy();
bullets.splice(i, 1);
break;
}
}
}
// Check enemy bullet collisions with player
for (var i = enemyBullets.length - 1; i >= 0; i--) {
if (enemyBullets[i] && player.intersects(enemyBullets[i])) {
// Player hit by enemy bullet
if (!timeIsFrozen && !player.isInvulnerable) {
handlePlayerHit();
} else {
// Just destroy the bullet if time is frozen or player is invulnerable
enemyBullets[i].destroy();
enemyBullets.splice(i, 1);
}
}
}
};
Modern App Store icon, high definition, square with rounded corners, for a game titled "Time Freeze" and with the description "A puzzle-action game where pausing is a superpower. Strategically freeze time to navigate hazards, avoid enemies, and collect items while managing your limited pause energy. Plan your moves carefully in this time-bending adventure where stopping the clock is your greatest tool for success.". No text on icon!
Monster 2d
A big red flag with the word end on it. In-Game asset. 2d. High contrast. No shadows
2d electronic visible coin. In-Game asset. 2d. High contrast. No shadows
2d dangerous robot bomb. In-Game asset. 2d. High contrast. No shadows
2d beam bullet. In-Game asset. 2d. High contrast. No shadows
2d shield magic. In-Game asset. 2d. High contrast. No shadows