/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Arrow = Container.expand(function () { var self = Container.call(this); var arrowGraphics = self.attachAsset('flecha', { anchorX: 0.5, anchorY: 0.5 }); self.radius = 100; // Distance from player center return self; }); var Arrow2 = Container.expand(function () { var self = Container.call(this); var arrow2Graphics = self.attachAsset('flecha2', { anchorX: 0.5, anchorY: 0.5 }); self.radius = 100; // Distance from player center return self; }); var Battery = Container.expand(function () { var self = Container.call(this); var batteryGraphics = self.attachAsset('bateria', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemigo', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 50; // Enemy movement speed - increased for faster gameplay self.chaseDelay = 1000; // Time between chase movements self.chasePlayer = function () { var deltaX = player.x - self.x; var deltaY = player.y - self.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared > 2500) { // 50 * 50 - avoid sqrt when possible var distance = Math.sqrt(distanceSquared); // Calculate movement direction var directionX = deltaX / distance; var directionY = deltaY / distance; // Move continuously towards player with dynamic speed based on avoidance var baseSpeed = 4.5; // Base movement speed - reduced by 0.5 for slower movement var moveSpeed = baseSpeed; // Increase speed when avoiding trees for more responsive movement if (avoidanceForce > 0) { moveSpeed = baseSpeed * 2.5; // 150% speed increase when avoiding trees for better evasion } // Tree avoidance logic with increased responsiveness var avoidanceX = 0; var avoidanceY = 0; var lookAheadDistance = 150; // Further increased look-ahead distance for much earlier detection var avoidanceForce = 0; // Check for trees in movement path with multiple prediction points var futureX = self.x + directionX * lookAheadDistance; var futureY = self.y + directionY * lookAheadDistance; // Also check current position for immediate threats var immediateX = self.x + directionX * 30; var immediateY = self.y + directionY * 30; // Check nearby trees for potential collisions for (var t = 0; t < allTrees.length; t++) { var tree = allTrees[t]; // Check both future and immediate positions var treeDeltaX = tree.x - futureX; var treeDeltaY = tree.y - futureY; var treeDistanceSquared = treeDeltaX * treeDeltaX + treeDeltaY * treeDeltaY; var avoidanceRadius = 100; // Reduced avoidance radius for smaller avoidance area // Also check immediate threat var immediateDeltaX = tree.x - immediateX; var immediateDeltaY = tree.y - immediateY; var immediateDistanceSquared = immediateDeltaX * immediateDeltaX + immediateDeltaY * immediateDeltaY; var immediateRadius = 80; // Reduced immediate radius for smaller close-range avoidance // Use the closer threat for avoidance calculation var useImmediate = immediateDistanceSquared < immediateRadius * immediateRadius; var finalDeltaX = useImmediate ? immediateDeltaX : treeDeltaX; var finalDeltaY = useImmediate ? immediateDeltaY : treeDeltaY; var finalDistanceSquared = useImmediate ? immediateDistanceSquared : treeDistanceSquared; var finalRadius = useImmediate ? immediateRadius : avoidanceRadius; if (finalDistanceSquared < finalRadius * finalRadius) { var treeDistance = Math.sqrt(finalDistanceSquared); if (treeDistance > 0) { // Calculate avoidance direction (perpendicular to tree direction) var avoidanceStrength = (finalRadius - treeDistance) / finalRadius; // Increase avoidance strength for immediate threats if (useImmediate) { avoidanceStrength *= 2.5; // Much stronger avoidance for immediate threats } var treeAvoidX = finalDeltaX / treeDistance * avoidanceStrength; var treeAvoidY = finalDeltaY / treeDistance * avoidanceStrength; // Add perpendicular avoidance force with increased magnitude avoidanceX += treeAvoidY * 2.0; // Further increased avoidance force for better evasion avoidanceY -= treeAvoidX * 2.0; avoidanceForce += avoidanceStrength; } } } // Normalize avoidance force if (avoidanceForce > 0) { avoidanceX /= avoidanceForce; avoidanceY /= avoidanceForce; // Increased blend factor for stronger avoidance var blendFactor = Math.min(avoidanceForce, 0.95); // Increased from 0.8 to 0.95 directionX = directionX * (1 - blendFactor) + avoidanceX * blendFactor; directionY = directionY * (1 - blendFactor) + avoidanceY * blendFactor; // Renormalize direction var newDistance = Math.sqrt(directionX * directionX + directionY * directionY); if (newDistance > 0) { directionX /= newDistance; directionY /= newDistance; } } self.x += directionX * moveSpeed; self.y += directionY * moveSpeed; // Keep enemy within bounds self.x = Math.max(80, Math.min(1968, self.x)); self.y = Math.max(120, Math.min(2612, self.y)); // Rotate to face movement direction smoothly (only every 2 frames for performance) if (LK.ticks % 2 === 0) { var targetRotation = Math.atan2(directionY, directionX) + Math.PI / 2; var rotationDiff = targetRotation - self.rotation; // Normalize rotation difference to [-PI, PI] while (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI; while (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI; // Smooth rotation self.rotation += rotationDiff * 0.1; } } }; return self; }); var Factory = Container.expand(function () { var self = Container.call(this); var factoryGraphics = self.attachAsset('fabrica', { anchorX: 0.5, anchorY: 0.5 }); self.spawnTimer = 0; self.spawnDelay = 480; // Spawn enemy every 8 seconds (480 frames at 60fps) self.spawnEnemy = function () { var newEnemy = new Enemy(); newEnemy.x = self.x; newEnemy.y = self.y; enemies.push(newEnemy); camera.addChild(newEnemy); }; self.update = function () { self.spawnTimer++; if (self.spawnTimer >= self.spawnDelay) { self.spawnEnemy(); self.spawnTimer = 0; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var Shield = Container.expand(function () { var self = Container.call(this); var shieldGraphics = self.attachAsset('escudo', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var Wheel = Container.expand(function () { var self = Container.call(this); var wheelGraphics = self.attachAsset('rueda', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x4a7c59 }); /**** * Game Code ****/ // Create extended background that covers the entire visible area including camera zoom var extendedBackground = game.addChild(LK.getAsset('fondo', { anchorX: 0, anchorY: 0, scaleX: 40.96, // Double scale to cover zoomed view (2048*2/100) scaleY: 54.64, // Double scale to cover zoomed view (2732*2/100) x: -2048, // Offset to center the extended background y: -2732 // Offset to center the extended background })); // Create additional background for right side var rightBackground = game.addChild(LK.getAsset('fondo', { anchorX: 0, anchorY: 0, scaleX: 20.48, // Scale to cover additional right area scaleY: 27.32, // Scale to cover full height x: 2048, // Position to the right of main map y: 0 })); // Create additional background for bottom side var bottomBackground = game.addChild(LK.getAsset('fondo', { anchorX: 0, anchorY: 0, scaleX: 20.48, // Scale to cover full width scaleY: 27.32, // Scale to cover additional bottom area x: 0, y: 2732 // Position below main map })); // Create additional background for top-right corner var topRightBackground = game.addChild(LK.getAsset('fondo', { anchorX: 0, anchorY: 0, scaleX: 20.48, // Scale to cover corner area scaleY: 27.32, // Scale to cover corner area x: 2048, // Position to the right y: -2732 // Position above main map })); // Create additional background for bottom-right corner var bottomRightBackground = game.addChild(LK.getAsset('fondo', { anchorX: 0, anchorY: 0, scaleX: 20.48, // Scale to cover corner area scaleY: 27.32, // Scale to cover corner area x: 2048, // Position to the right y: 2732 // Position below main map })); // Create additional background for bottom-left corner var bottomLeftBackground = game.addChild(LK.getAsset('fondo', { anchorX: 0, anchorY: 0, scaleX: 20.48, // Scale to cover corner area scaleY: 27.32, // Scale to cover corner area x: -2048, // Position to the left y: 2732 // Position below main map })); // Create large map floor - scale the grass background to cover entire game area var mapFloor = game.addChild(LK.getAsset('fondo', { anchorX: 0, anchorY: 0, scaleX: 20.48, // Scale to cover 2048px width (2048/100) scaleY: 27.32, // Scale to cover 2732px height (2732/100) x: 0, y: 0 })); // Create trees around all borders var trees = []; // Top border trees for (var i = 0; i < 26; i++) { var topTree = game.addChild(LK.getAsset('arbol', { anchorX: 0.5, anchorY: 0.5, x: i * 80 + 40, y: 60 })); trees.push(topTree); } // Bottom border trees for (var i = 0; i < 26; i++) { var bottomTree = game.addChild(LK.getAsset('arbol', { anchorX: 0.5, anchorY: 0.5, x: i * 80 + 40, y: 2672 })); trees.push(bottomTree); } // Left border trees (excluding corners already covered) for (var i = 1; i < 33; i++) { var leftTree = game.addChild(LK.getAsset('arbol', { anchorX: 0.5, anchorY: 0.5, x: 40, y: i * 80 + 60 })); trees.push(leftTree); } // Right border trees (excluding corners already covered) for (var i = 1; i < 33; i++) { var rightTree = game.addChild(LK.getAsset('arbol', { anchorX: 0.5, anchorY: 0.5, x: 2008, y: i * 80 + 60 })); trees.push(rightTree); } // Create camera container for following player var camera = game.addChild(new Container()); camera.x = 1024; camera.y = 1366; // Move all game elements to camera container camera.addChild(extendedBackground); camera.addChild(rightBackground); camera.addChild(bottomBackground); camera.addChild(topRightBackground); camera.addChild(bottomRightBackground); camera.addChild(bottomLeftBackground); camera.addChild(mapFloor); for (var i = 0; i < trees.length; i++) { camera.addChild(trees[i]); } // Add random trees scattered throughout the map var randomTrees = []; // Define factory positions for exclusion zones var factoryPositions = [{ x: 400, y: 400 }, // top-left { x: 1648, y: 400 }, // top-right { x: 400, y: 2332 }, // bottom-left { x: 1648, y: 2332 } // bottom-right ]; var exclusionRadius = 400; // Distance to keep trees away from factories - increased for better separation for (var i = 0; i < 45; i++) { var validPosition = false; var randomX, randomY; var attempts = 0; var maxAttempts = 50; // Keep trying until we find a valid position away from factories while (!validPosition && attempts < maxAttempts) { randomX = Math.random() * (2048 - 120) + 60; // Closer to borders randomY = Math.random() * (2732 - 180) + 90; // Closer to borders validPosition = true; // Check distance from all factories for (var j = 0; j < factoryPositions.length; j++) { var factoryX = factoryPositions[j].x; var factoryY = factoryPositions[j].y; var deltaX = randomX - factoryX; var deltaY = randomY - factoryY; var distanceSquared = deltaX * deltaX + deltaY * deltaY; var exclusionRadiusSquared = exclusionRadius * exclusionRadius; if (distanceSquared < exclusionRadiusSquared) { validPosition = false; break; } } // Check distance from existing random trees to ensure separation var minTreeDistance = 200; // Minimum distance between trees var minTreeDistanceSquared = minTreeDistance * minTreeDistance; for (var k = 0; k < randomTrees.length; k++) { var existingTree = randomTrees[k]; var treeDeltaX = randomX - existingTree.x; var treeDeltaY = randomY - existingTree.y; var treeDistanceSquared = treeDeltaX * treeDeltaX + treeDeltaY * treeDeltaY; if (treeDistanceSquared < minTreeDistanceSquared) { validPosition = false; break; } } attempts++; } // Only create tree if we found a valid position if (validPosition) { var randomTree = camera.addChild(LK.getAsset('arbol', { anchorX: 0.5, anchorY: 0.5, x: randomX, y: randomY })); randomTrees.push(randomTree); } } // Cache all trees for optimized collision detection var allTrees = trees.concat(randomTrees); // Create player and add to camera var player = camera.addChild(new Player()); player.x = 1024; player.y = 1366; // Create arrow and add to camera var arrow = camera.addChild(new Arrow()); arrow.visible = false; // Initially hidden until battery spawns // Create arrow2 for special items and add to camera var arrow2 = camera.addChild(new Arrow2()); arrow2.visible = false; // Initially hidden until special item spawns // Create 3 lightning bolts in top-left corner of screen var lightning1 = LK.gui.topLeft.addChild(LK.getAsset('rayo', { anchorX: 0.5, anchorY: 0.5, x: 150, y: 100 })); var lightning2 = LK.gui.topLeft.addChild(LK.getAsset('rayo', { anchorX: 0.5, anchorY: 0.5, x: 250, y: 100 })); var lightning3 = LK.gui.topLeft.addChild(LK.getAsset('rayo', { anchorX: 0.5, anchorY: 0.5, x: 350, y: 100 })); // Lightning management var lightningBolts = [lightning1, lightning2, lightning3]; var lightningTimer = 0; var lightningDelay = 420; // 7 seconds at 60fps var activeLightning = 3; // Start with 3 active lightning bolts // Create timer display in top center of screen var timerText = new Text2('00:00', { size: 80, fill: 0xFFFFFF }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); // Timer variables var gameTimer = 0; // Timer in frames (60fps) // Create 5 lives in top-right corner of screen var life1 = LK.gui.topRight.addChild(LK.getAsset('vida', { anchorX: 0.5, anchorY: 0.5, x: -50, y: 100 })); var life2 = LK.gui.topRight.addChild(LK.getAsset('vida', { anchorX: 0.5, anchorY: 0.5, x: -150, y: 100 })); var life3 = LK.gui.topRight.addChild(LK.getAsset('vida', { anchorX: 0.5, anchorY: 0.5, x: -250, y: 100 })); var life4 = LK.gui.topRight.addChild(LK.getAsset('vida', { anchorX: 0.5, anchorY: 0.5, x: -350, y: 100 })); // Hearts management var hearts = [life1, life2, life3, life4]; var activeHearts = 4; // Start with 4 active hearts // Create enemies array var enemies = []; // Create batteries array var batteries = []; // Battery spawn timer var batterySpawnTimer = 600; // Start ready to spawn immediately var batterySpawnDelay = 600; // Spawn battery every 10 seconds (600 frames at 60fps) // Create special items arrays var wheels = []; var shields = []; // Special item spawn timer var itemSpawnTimer = 0; var itemSpawnMinDelay = 600; // 10 seconds minimum (600 frames at 60fps) var itemSpawnMaxDelay = 900; // 15 seconds maximum (900 frames at 60fps) var itemSpawnDelay = Math.random() * (itemSpawnMaxDelay - itemSpawnMinDelay) + itemSpawnMinDelay; // Item collection cooldown tracking var itemCollectionCooldown = 0; // Timer to prevent item spawning after collection var itemCollectionCooldownDelay = 720; // 12 seconds (720 frames at 60fps) // Create factories array var factories = []; // Create factory in top-left corner var topLeftFactory = camera.addChild(new Factory()); topLeftFactory.x = 400; topLeftFactory.y = 400; factories.push(topLeftFactory); // Create factory in top-right corner var topRightFactory = camera.addChild(new Factory()); topRightFactory.x = 1648; topRightFactory.y = 400; factories.push(topRightFactory); // Create factory in bottom-left corner var bottomLeftFactory = camera.addChild(new Factory()); bottomLeftFactory.x = 400; bottomLeftFactory.y = 2332; factories.push(bottomLeftFactory); // Create factory in bottom-right corner var bottomRightFactory = camera.addChild(new Factory()); bottomRightFactory.x = 1648; bottomRightFactory.y = 2332; factories.push(bottomRightFactory); // Set camera scale for closer view camera.scaleX = 2; camera.scaleY = 2; // Start playing background music LK.playMusic('si'); // Game controls - move player toward touch position var playerSpeed = 4; // Player movement speed var basePlayerSpeed = 4; // Store original speed for wheel effect var speedBoostActive = false; // Track if speed boost is active var shieldActive = false; // Track if shield immunity is active var targetX = player.x; var targetY = player.y; var isMoving = false; function updatePlayerMovement(x, y) { // Convert screen coordinates to world coordinates accounting for camera transform var worldX = (x - camera.x) / camera.scaleX; var worldY = (y - camera.y) / camera.scaleY; targetX = worldX; targetY = worldY; isMoving = true; } game.down = function (x, y, obj) { updatePlayerMovement(x, y); }; game.move = function (x, y, obj) { updatePlayerMovement(x, y); }; game.up = function (x, y, obj) { isMoving = false; }; // Main game update loop game.update = function () { // Update and display timer gameTimer++; var seconds = Math.floor(gameTimer / 60); var minutes = Math.floor(seconds / 60); seconds = seconds % 60; var timeString = (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds; timerText.setText(timeString); // Move player toward target position if moving if (isMoving) { var deltaX = targetX - player.x; var deltaY = targetY - player.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared > 25) { // 5 * 5 - avoid sqrt when possible // Only move if not close enough to target var distance = Math.sqrt(distanceSquared); // Calculate movement direction var directionX = deltaX / distance; var directionY = deltaY / distance; // Move player toward target player.x += directionX * playerSpeed; player.y += directionY * playerSpeed; // Rotate player to face movement direction var targetRotation = Math.atan2(deltaY, deltaX) + Math.PI / 2; var rotationDiff = targetRotation - player.rotation; // Normalize rotation difference to [-PI, PI] while (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI; while (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI; // Smooth rotation player.rotation += rotationDiff * 0.15; } } // Check player-tree collisions before finalizing movement var playerCollisionDetected = false; var playerRadius = 25; // Player collision radius var treeRadius = 40; // Tree collision radius var combinedRadius = playerRadius + treeRadius; var combinedRadiusSquared = combinedRadius * combinedRadius; // Check collision with all trees for (var t = 0; t < allTrees.length; t++) { var tree = allTrees[t]; var deltaX = player.x - tree.x; var deltaY = player.y - tree.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared < combinedRadiusSquared) { playerCollisionDetected = true; // Push player away from tree var distance = Math.sqrt(distanceSquared); if (distance > 0) { var pushDistance = combinedRadius - distance; var pushX = deltaX / distance * pushDistance; var pushY = deltaY / distance * pushDistance; player.x += pushX; player.y += pushY; } } } // Keep player within bounds, accounting for tree borders player.x = Math.max(80, Math.min(1968, player.x)); player.y = Math.max(120, Math.min(2612, player.y)); // Update camera position to follow player var targetCameraX = 1024 - player.x * camera.scaleX; var targetCameraY = 1366 - player.y * camera.scaleY; // Smooth camera following camera.x += (targetCameraX - camera.x) * 0.1; camera.y += (targetCameraY - camera.y) * 0.1; // Handle enemy collision detection - prevent enemies from overlapping (frame-skipped optimization) // Only run separation every 3 frames to reduce computational load if (LK.ticks % 3 === 0) { var maxChecks = Math.min(enemies.length * enemies.length / 4, 100); // Limit total checks var checksPerformed = 0; for (var i = 0; i < enemies.length && checksPerformed < maxChecks; i++) { for (var j = i + 1; j < enemies.length && checksPerformed < maxChecks; j++) { var enemy1 = enemies[i]; var enemy2 = enemies[j]; var deltaX = enemy2.x - enemy1.x; var deltaY = enemy2.y - enemy1.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; var minDistanceSquared = 6400; // 80 * 80 - avoid sqrt when possible if (distanceSquared < minDistanceSquared && distanceSquared > 0) { var distance = Math.sqrt(distanceSquared); // Calculate separation force var separationForce = (80 - distance) / 2; var separationX = deltaX / distance * separationForce; var separationY = deltaY / distance * separationForce; // Push enemies apart enemy1.x -= separationX; enemy1.y -= separationY; enemy2.x += separationX; enemy2.y += separationY; // Keep enemies within bounds after separation enemy1.x = Math.max(80, Math.min(1968, enemy1.x)); enemy1.y = Math.max(120, Math.min(2612, enemy1.y)); enemy2.x = Math.max(80, Math.min(1968, enemy2.x)); enemy2.y = Math.max(120, Math.min(2612, enemy2.y)); } checksPerformed++; } } } // Update factories for (var i = 0; i < factories.length; i++) { factories[i].update(); } // Handle battery spawning batterySpawnTimer++; if (batterySpawnTimer >= batterySpawnDelay && batteries.length < 5) { var validPosition = false; var batteryX, batteryY; var attempts = 0; var maxAttempts = 50; // Find valid position for battery spawn while (!validPosition && attempts < maxAttempts) { batteryX = Math.random() * (2048 - 200) + 100; // Keep away from borders batteryY = Math.random() * (2732 - 240) + 120; // Keep away from borders validPosition = true; // Check distance from factories - increased exclusion radius for (var f = 0; f < factories.length; f++) { var factory = factories[f]; var deltaX = batteryX - factory.x; var deltaY = batteryY - factory.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; var factoryExclusionRadius = 300; // Increased from 200px to 300px for better separation var factoryExclusionRadiusSquared = factoryExclusionRadius * factoryExclusionRadius; if (distanceSquared < factoryExclusionRadiusSquared) { validPosition = false; break; } } // Check distance from trees if (validPosition) { for (var t = 0; t < allTrees.length; t++) { var tree = allTrees[t]; var deltaX = batteryX - tree.x; var deltaY = batteryY - tree.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared < 10000) { // 100px minimum distance from trees validPosition = false; break; } } } attempts++; } // Spawn battery if valid position found if (validPosition) { var newBattery = camera.addChild(new Battery()); newBattery.x = batteryX; newBattery.y = batteryY; batteries.push(newBattery); } batterySpawnTimer = 0; } // Handle special item spawning (wheel or shield) itemSpawnTimer++; // Update item collection cooldown if (itemCollectionCooldown > 0) { itemCollectionCooldown--; } if (itemSpawnTimer >= itemSpawnDelay && itemCollectionCooldown <= 0 && wheels.length === 0 && shields.length === 0) { var validPosition = false; var itemX, itemY; var attempts = 0; var maxAttempts = 50; // Find valid position for item spawn while (!validPosition && attempts < maxAttempts) { itemX = Math.random() * (2048 - 200) + 100; // Keep away from borders itemY = Math.random() * (2732 - 240) + 120; // Keep away from borders validPosition = true; // Check distance from factories for (var f = 0; f < factories.length; f++) { var factory = factories[f]; var deltaX = itemX - factory.x; var deltaY = itemY - factory.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; var factoryExclusionRadius = 300; var factoryExclusionRadiusSquared = factoryExclusionRadius * factoryExclusionRadius; if (distanceSquared < factoryExclusionRadiusSquared) { validPosition = false; break; } } // Check distance from trees if (validPosition) { for (var t = 0; t < allTrees.length; t++) { var tree = allTrees[t]; var deltaX = itemX - tree.x; var deltaY = itemY - tree.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared < 10000) { // 100px minimum distance from trees validPosition = false; break; } } } attempts++; } // Spawn item if valid position found if (validPosition) { // Randomly choose between wheel (0) or shield (1) var itemType = Math.floor(Math.random() * 2); if (itemType === 0) { // Spawn wheel var newWheel = camera.addChild(new Wheel()); newWheel.x = itemX; newWheel.y = itemY; wheels.push(newWheel); } else { // Spawn shield var newShield = camera.addChild(new Shield()); newShield.x = itemX; newShield.y = itemY; shields.push(newShield); } } // Reset timer with new random delay itemSpawnTimer = 0; itemSpawnDelay = Math.random() * (itemSpawnMaxDelay - itemSpawnMinDelay) + itemSpawnMinDelay; } // Check player-wheel collisions and collection for (var w = wheels.length - 1; w >= 0; w--) { var wheel = wheels[w]; var deltaX = player.x - wheel.x; var deltaY = player.y - wheel.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; var collectionRadius = 50; // Collection radius for wheel var collectionRadiusSquared = collectionRadius * collectionRadius; if (distanceSquared < collectionRadiusSquared) { // Wheel collected - remove it wheel.destroy(); wheels.splice(w, 1); // Start item collection cooldown itemCollectionCooldown = itemCollectionCooldownDelay; // Apply speed boost for 10 seconds if (!speedBoostActive) { speedBoostActive = true; playerSpeed = basePlayerSpeed * 2; // Double the speed // Use tween to reset speed after 10 seconds var speedBoostTimer = { value: 1 }; tween(speedBoostTimer, { value: 0 }, { duration: 10000, // 10 seconds onFinish: function onFinish() { playerSpeed = basePlayerSpeed; // Reset to original speed speedBoostActive = false; } }); } } } // Check player-shield collisions and collection for (var s = shields.length - 1; s >= 0; s--) { var shield = shields[s]; var deltaX = player.x - shield.x; var deltaY = player.y - shield.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; var collectionRadius = 50; // Collection radius for shield var collectionRadiusSquared = collectionRadius * collectionRadius; if (distanceSquared < collectionRadiusSquared) { // Shield collected - remove it shield.destroy(); shields.splice(s, 1); // Start item collection cooldown itemCollectionCooldown = itemCollectionCooldownDelay; // Apply immunity for 10 seconds if (!shieldActive) { shieldActive = true; // Visual indicator - make player semi-transparent blue tween(player, { tint: 0x0099ff }, { duration: 200 }); // Use tween to reset immunity after 10 seconds var shieldTimer = { value: 1 }; tween(shieldTimer, { value: 0 }, { duration: 10000, // 10 seconds onFinish: function onFinish() { shieldActive = false; // Reset player color tween(player, { tint: 0xffffff }, { duration: 200 }); } }); } } } // Check player-battery collisions and collection for (var b = batteries.length - 1; b >= 0; b--) { var battery = batteries[b]; var deltaX = player.x - battery.x; var deltaY = player.y - battery.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; var collectionRadius = 50; // Collection radius for battery var collectionRadiusSquared = collectionRadius * collectionRadius; if (distanceSquared < collectionRadiusSquared) { // Battery collected - remove it battery.destroy(); batteries.splice(b, 1); // Add a lightning bolt back if we have less than 3 if (activeLightning < 3) { var lightningToAdd = lightningBolts[activeLightning]; lightningToAdd.visible = true; lightningToAdd.alpha = 1; lightningToAdd.scaleX = 1; lightningToAdd.scaleY = 1; activeLightning++; } // Immediately spawn a new battery when one is collected (only if less than 5) if (batteries.length < 5) { var validPosition = false; var batteryX, batteryY; var attempts = 0; var maxAttempts = 50; // Find valid position for new battery spawn while (!validPosition && attempts < maxAttempts) { batteryX = Math.random() * (2048 - 200) + 100; // Keep away from borders batteryY = Math.random() * (2732 - 240) + 120; // Keep away from borders validPosition = true; // Check distance from factories - increased exclusion radius for (var f = 0; f < factories.length; f++) { var factory = factories[f]; var deltaX = batteryX - factory.x; var deltaY = batteryY - factory.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; var factoryExclusionRadius = 300; // Increased from 200px to 300px for better separation var factoryExclusionRadiusSquared = factoryExclusionRadius * factoryExclusionRadius; if (distanceSquared < factoryExclusionRadiusSquared) { validPosition = false; break; } } // Check distance from trees if (validPosition) { for (var t = 0; t < allTrees.length; t++) { var tree = allTrees[t]; var deltaX = batteryX - tree.x; var deltaY = batteryY - tree.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared < 10000) { // 100px minimum distance from trees validPosition = false; break; } } } attempts++; } // Spawn new battery if valid position found if (validPosition) { var newBattery = camera.addChild(new Battery()); newBattery.x = batteryX; newBattery.y = batteryY; batteries.push(newBattery); } } } } // Update arrow to point to nearest battery if (batteries.length > 0) { arrow.visible = true; // Find nearest battery var nearestBattery = null; var nearestDistance = Infinity; for (var b = 0; b < batteries.length; b++) { var battery = batteries[b]; var deltaX = battery.x - player.x; var deltaY = battery.y - player.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared < nearestDistance) { nearestDistance = distanceSquared; nearestBattery = battery; } } // Position arrow around player pointing to nearest battery if (nearestBattery) { var batteryDeltaX = nearestBattery.x - player.x; var batteryDeltaY = nearestBattery.y - player.y; var batteryDistance = Math.sqrt(batteryDeltaX * batteryDeltaX + batteryDeltaY * batteryDeltaY); if (batteryDistance > 0) { // Normalize direction to battery var directionX = batteryDeltaX / batteryDistance; var directionY = batteryDeltaY / batteryDistance; // Position arrow around player at fixed radius arrow.x = player.x + directionX * arrow.radius; arrow.y = player.y + directionY * arrow.radius; // Rotate arrow to point toward battery arrow.rotation = Math.atan2(batteryDeltaY, batteryDeltaX) + Math.PI / 2; } } } else { arrow.visible = false; } // Update arrow2 to point to nearest special item (wheel or shield) var allSpecialItems = wheels.concat(shields); if (allSpecialItems.length > 0) { arrow2.visible = true; // Find nearest special item var nearestItem = null; var nearestDistance = Infinity; for (var i = 0; i < allSpecialItems.length; i++) { var item = allSpecialItems[i]; var deltaX = item.x - player.x; var deltaY = item.y - player.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared < nearestDistance) { nearestDistance = distanceSquared; nearestItem = item; } } // Position arrow2 around player pointing to nearest special item if (nearestItem) { var itemDeltaX = nearestItem.x - player.x; var itemDeltaY = nearestItem.y - player.y; var itemDistance = Math.sqrt(itemDeltaX * itemDeltaX + itemDeltaY * itemDeltaY); if (itemDistance > 0) { // Normalize direction to item var directionX = itemDeltaX / itemDistance; var directionY = itemDeltaY / itemDistance; // Position arrow2 around player at fixed radius arrow2.x = player.x + directionX * arrow2.radius; arrow2.y = player.y + directionY * arrow2.radius; // Rotate arrow2 to point toward item arrow2.rotation = Math.atan2(itemDeltaY, itemDeltaX) + Math.PI / 2; } } } else { arrow2.visible = false; } // Handle lightning bolt disappearance every 20 seconds lightningTimer++; if (lightningTimer >= lightningDelay && activeLightning > 0) { var lightningToRemove = lightningBolts[activeLightning - 1]; // Animate lightning bolt disappearing with fade and scale down tween(lightningToRemove, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { lightningToRemove.visible = false; } }); activeLightning--; lightningTimer = 0; // Check if all lightning bolts are consumed if (activeLightning <= 0) { // Make player disappear tween(player, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 500, easing: tween.easeOut }); // Hide arrow when player disappears arrow.visible = false; // Hide arrow2 when player disappears arrow2.visible = false; // Create explosion at player position var playerExplosion = camera.addChild(LK.getAsset('explocion', { anchorX: 0.5, anchorY: 0.5, x: player.x, y: player.y })); // Animate explosion tween(playerExplosion, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { playerExplosion.destroy(); } }); // Play explosion sound LK.getSound('explosion').play(); // Show "lost" image covering the entire screen after 1 second delay LK.setTimeout(function () { // Scale camera back to show full image tween(camera, { scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeOut }); // Calculate scale to fit camera dimensions (2048x2732) var cameraWidth = 2048; var cameraHeight = 2732; var lostImageWidth = 100; // Original asset width var lostImageHeight = 100; // Original asset height var scaleX = cameraWidth / lostImageWidth; var scaleY = cameraHeight / lostImageHeight; var lostImage = LK.gui.center.addChild(LK.getAsset('lost', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: scaleX, scaleY: scaleY })); }, 1000); // Show game over after a brief delay LK.setTimeout(function () { LK.showGameOver(); }, 2500); } } // Check enemy-player collisions using hitbox intersection for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; // Use intersects method to check if enemy hitbox touches player hitbox if (enemy.intersects(player) && activeHearts > 0 && !shieldActive) { // Enemy touched player - remove a heart var heartToRemove = hearts[activeHearts - 1]; // Animate heart disappearing with fade and scale down tween(heartToRemove, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { heartToRemove.visible = false; } }); activeHearts--; // Remove the enemy that hit the player enemy.destroy(); enemies.splice(i, 1); // Check if all hearts are gone if (activeHearts <= 0) { // Make player disappear tween(player, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 500, easing: tween.easeOut }); // Hide arrow when player disappears arrow.visible = false; // Hide arrow2 when player disappears arrow2.visible = false; // Create explosion at player position var playerExplosion = camera.addChild(LK.getAsset('explocion', { anchorX: 0.5, anchorY: 0.5, x: player.x, y: player.y })); // Animate explosion tween(playerExplosion, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { playerExplosion.destroy(); } }); // Play explosion sound LK.getSound('explosion').play(); // Show game over after a brief delay LK.setTimeout(function () { LK.showGameOver(); }, 1500); return; // Exit update loop early since game is over } continue; // Skip to next enemy since this one was removed } } // Check enemy-tree collisions with optimized distance-based pre-filtering for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; var collisionDetected = false; var collidedTree = null; // Pre-filter trees by distance before expensive intersection check for (var j = 0; j < allTrees.length; j++) { var tree = allTrees[j]; var deltaX = enemy.x - tree.x; var deltaY = enemy.y - tree.y; var distanceSquared = deltaX * deltaX + deltaY * deltaY; // Only check intersection if close enough (tree radius ~50 + enemy radius ~30) if (distanceSquared < 6400) { // 80 * 80 if (enemy.intersects(tree)) { collisionDetected = true; collidedTree = tree; break; } } } // Handle collision - create explosion and remove enemy if (collisionDetected) { // Create explosion effect at enemy position var explosion = camera.addChild(LK.getAsset('explocion', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y })); // Animate explosion with scaling and fading tween(explosion, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Play explosion sound LK.getSound('explosion').play(); // Remove enemy enemy.destroy(); enemies.splice(i, 1); } else { // Update enemy movement if no collision enemy.chasePlayer(); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Arrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('flecha', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 100; // Distance from player center
return self;
});
var Arrow2 = Container.expand(function () {
var self = Container.call(this);
var arrow2Graphics = self.attachAsset('flecha2', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 100; // Distance from player center
return self;
});
var Battery = Container.expand(function () {
var self = Container.call(this);
var batteryGraphics = self.attachAsset('bateria', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemigo', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 50; // Enemy movement speed - increased for faster gameplay
self.chaseDelay = 1000; // Time between chase movements
self.chasePlayer = function () {
var deltaX = player.x - self.x;
var deltaY = player.y - self.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared > 2500) {
// 50 * 50 - avoid sqrt when possible
var distance = Math.sqrt(distanceSquared);
// Calculate movement direction
var directionX = deltaX / distance;
var directionY = deltaY / distance;
// Move continuously towards player with dynamic speed based on avoidance
var baseSpeed = 4.5; // Base movement speed - reduced by 0.5 for slower movement
var moveSpeed = baseSpeed;
// Increase speed when avoiding trees for more responsive movement
if (avoidanceForce > 0) {
moveSpeed = baseSpeed * 2.5; // 150% speed increase when avoiding trees for better evasion
}
// Tree avoidance logic with increased responsiveness
var avoidanceX = 0;
var avoidanceY = 0;
var lookAheadDistance = 150; // Further increased look-ahead distance for much earlier detection
var avoidanceForce = 0;
// Check for trees in movement path with multiple prediction points
var futureX = self.x + directionX * lookAheadDistance;
var futureY = self.y + directionY * lookAheadDistance;
// Also check current position for immediate threats
var immediateX = self.x + directionX * 30;
var immediateY = self.y + directionY * 30;
// Check nearby trees for potential collisions
for (var t = 0; t < allTrees.length; t++) {
var tree = allTrees[t];
// Check both future and immediate positions
var treeDeltaX = tree.x - futureX;
var treeDeltaY = tree.y - futureY;
var treeDistanceSquared = treeDeltaX * treeDeltaX + treeDeltaY * treeDeltaY;
var avoidanceRadius = 100; // Reduced avoidance radius for smaller avoidance area
// Also check immediate threat
var immediateDeltaX = tree.x - immediateX;
var immediateDeltaY = tree.y - immediateY;
var immediateDistanceSquared = immediateDeltaX * immediateDeltaX + immediateDeltaY * immediateDeltaY;
var immediateRadius = 80; // Reduced immediate radius for smaller close-range avoidance
// Use the closer threat for avoidance calculation
var useImmediate = immediateDistanceSquared < immediateRadius * immediateRadius;
var finalDeltaX = useImmediate ? immediateDeltaX : treeDeltaX;
var finalDeltaY = useImmediate ? immediateDeltaY : treeDeltaY;
var finalDistanceSquared = useImmediate ? immediateDistanceSquared : treeDistanceSquared;
var finalRadius = useImmediate ? immediateRadius : avoidanceRadius;
if (finalDistanceSquared < finalRadius * finalRadius) {
var treeDistance = Math.sqrt(finalDistanceSquared);
if (treeDistance > 0) {
// Calculate avoidance direction (perpendicular to tree direction)
var avoidanceStrength = (finalRadius - treeDistance) / finalRadius;
// Increase avoidance strength for immediate threats
if (useImmediate) {
avoidanceStrength *= 2.5; // Much stronger avoidance for immediate threats
}
var treeAvoidX = finalDeltaX / treeDistance * avoidanceStrength;
var treeAvoidY = finalDeltaY / treeDistance * avoidanceStrength;
// Add perpendicular avoidance force with increased magnitude
avoidanceX += treeAvoidY * 2.0; // Further increased avoidance force for better evasion
avoidanceY -= treeAvoidX * 2.0;
avoidanceForce += avoidanceStrength;
}
}
}
// Normalize avoidance force
if (avoidanceForce > 0) {
avoidanceX /= avoidanceForce;
avoidanceY /= avoidanceForce;
// Increased blend factor for stronger avoidance
var blendFactor = Math.min(avoidanceForce, 0.95); // Increased from 0.8 to 0.95
directionX = directionX * (1 - blendFactor) + avoidanceX * blendFactor;
directionY = directionY * (1 - blendFactor) + avoidanceY * blendFactor;
// Renormalize direction
var newDistance = Math.sqrt(directionX * directionX + directionY * directionY);
if (newDistance > 0) {
directionX /= newDistance;
directionY /= newDistance;
}
}
self.x += directionX * moveSpeed;
self.y += directionY * moveSpeed;
// Keep enemy within bounds
self.x = Math.max(80, Math.min(1968, self.x));
self.y = Math.max(120, Math.min(2612, self.y));
// Rotate to face movement direction smoothly (only every 2 frames for performance)
if (LK.ticks % 2 === 0) {
var targetRotation = Math.atan2(directionY, directionX) + Math.PI / 2;
var rotationDiff = targetRotation - self.rotation;
// Normalize rotation difference to [-PI, PI]
while (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
while (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
// Smooth rotation
self.rotation += rotationDiff * 0.1;
}
}
};
return self;
});
var Factory = Container.expand(function () {
var self = Container.call(this);
var factoryGraphics = self.attachAsset('fabrica', {
anchorX: 0.5,
anchorY: 0.5
});
self.spawnTimer = 0;
self.spawnDelay = 480; // Spawn enemy every 8 seconds (480 frames at 60fps)
self.spawnEnemy = function () {
var newEnemy = new Enemy();
newEnemy.x = self.x;
newEnemy.y = self.y;
enemies.push(newEnemy);
camera.addChild(newEnemy);
};
self.update = function () {
self.spawnTimer++;
if (self.spawnTimer >= self.spawnDelay) {
self.spawnEnemy();
self.spawnTimer = 0;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldGraphics = self.attachAsset('escudo', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Wheel = Container.expand(function () {
var self = Container.call(this);
var wheelGraphics = self.attachAsset('rueda', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x4a7c59
});
/****
* Game Code
****/
// Create extended background that covers the entire visible area including camera zoom
var extendedBackground = game.addChild(LK.getAsset('fondo', {
anchorX: 0,
anchorY: 0,
scaleX: 40.96,
// Double scale to cover zoomed view (2048*2/100)
scaleY: 54.64,
// Double scale to cover zoomed view (2732*2/100)
x: -2048,
// Offset to center the extended background
y: -2732 // Offset to center the extended background
}));
// Create additional background for right side
var rightBackground = game.addChild(LK.getAsset('fondo', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
// Scale to cover additional right area
scaleY: 27.32,
// Scale to cover full height
x: 2048,
// Position to the right of main map
y: 0
}));
// Create additional background for bottom side
var bottomBackground = game.addChild(LK.getAsset('fondo', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
// Scale to cover full width
scaleY: 27.32,
// Scale to cover additional bottom area
x: 0,
y: 2732 // Position below main map
}));
// Create additional background for top-right corner
var topRightBackground = game.addChild(LK.getAsset('fondo', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
// Scale to cover corner area
scaleY: 27.32,
// Scale to cover corner area
x: 2048,
// Position to the right
y: -2732 // Position above main map
}));
// Create additional background for bottom-right corner
var bottomRightBackground = game.addChild(LK.getAsset('fondo', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
// Scale to cover corner area
scaleY: 27.32,
// Scale to cover corner area
x: 2048,
// Position to the right
y: 2732 // Position below main map
}));
// Create additional background for bottom-left corner
var bottomLeftBackground = game.addChild(LK.getAsset('fondo', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
// Scale to cover corner area
scaleY: 27.32,
// Scale to cover corner area
x: -2048,
// Position to the left
y: 2732 // Position below main map
}));
// Create large map floor - scale the grass background to cover entire game area
var mapFloor = game.addChild(LK.getAsset('fondo', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
// Scale to cover 2048px width (2048/100)
scaleY: 27.32,
// Scale to cover 2732px height (2732/100)
x: 0,
y: 0
}));
// Create trees around all borders
var trees = [];
// Top border trees
for (var i = 0; i < 26; i++) {
var topTree = game.addChild(LK.getAsset('arbol', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 80 + 40,
y: 60
}));
trees.push(topTree);
}
// Bottom border trees
for (var i = 0; i < 26; i++) {
var bottomTree = game.addChild(LK.getAsset('arbol', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 80 + 40,
y: 2672
}));
trees.push(bottomTree);
}
// Left border trees (excluding corners already covered)
for (var i = 1; i < 33; i++) {
var leftTree = game.addChild(LK.getAsset('arbol', {
anchorX: 0.5,
anchorY: 0.5,
x: 40,
y: i * 80 + 60
}));
trees.push(leftTree);
}
// Right border trees (excluding corners already covered)
for (var i = 1; i < 33; i++) {
var rightTree = game.addChild(LK.getAsset('arbol', {
anchorX: 0.5,
anchorY: 0.5,
x: 2008,
y: i * 80 + 60
}));
trees.push(rightTree);
}
// Create camera container for following player
var camera = game.addChild(new Container());
camera.x = 1024;
camera.y = 1366;
// Move all game elements to camera container
camera.addChild(extendedBackground);
camera.addChild(rightBackground);
camera.addChild(bottomBackground);
camera.addChild(topRightBackground);
camera.addChild(bottomRightBackground);
camera.addChild(bottomLeftBackground);
camera.addChild(mapFloor);
for (var i = 0; i < trees.length; i++) {
camera.addChild(trees[i]);
}
// Add random trees scattered throughout the map
var randomTrees = [];
// Define factory positions for exclusion zones
var factoryPositions = [{
x: 400,
y: 400
},
// top-left
{
x: 1648,
y: 400
},
// top-right
{
x: 400,
y: 2332
},
// bottom-left
{
x: 1648,
y: 2332
} // bottom-right
];
var exclusionRadius = 400; // Distance to keep trees away from factories - increased for better separation
for (var i = 0; i < 45; i++) {
var validPosition = false;
var randomX, randomY;
var attempts = 0;
var maxAttempts = 50;
// Keep trying until we find a valid position away from factories
while (!validPosition && attempts < maxAttempts) {
randomX = Math.random() * (2048 - 120) + 60; // Closer to borders
randomY = Math.random() * (2732 - 180) + 90; // Closer to borders
validPosition = true;
// Check distance from all factories
for (var j = 0; j < factoryPositions.length; j++) {
var factoryX = factoryPositions[j].x;
var factoryY = factoryPositions[j].y;
var deltaX = randomX - factoryX;
var deltaY = randomY - factoryY;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
var exclusionRadiusSquared = exclusionRadius * exclusionRadius;
if (distanceSquared < exclusionRadiusSquared) {
validPosition = false;
break;
}
}
// Check distance from existing random trees to ensure separation
var minTreeDistance = 200; // Minimum distance between trees
var minTreeDistanceSquared = minTreeDistance * minTreeDistance;
for (var k = 0; k < randomTrees.length; k++) {
var existingTree = randomTrees[k];
var treeDeltaX = randomX - existingTree.x;
var treeDeltaY = randomY - existingTree.y;
var treeDistanceSquared = treeDeltaX * treeDeltaX + treeDeltaY * treeDeltaY;
if (treeDistanceSquared < minTreeDistanceSquared) {
validPosition = false;
break;
}
}
attempts++;
}
// Only create tree if we found a valid position
if (validPosition) {
var randomTree = camera.addChild(LK.getAsset('arbol', {
anchorX: 0.5,
anchorY: 0.5,
x: randomX,
y: randomY
}));
randomTrees.push(randomTree);
}
}
// Cache all trees for optimized collision detection
var allTrees = trees.concat(randomTrees);
// Create player and add to camera
var player = camera.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Create arrow and add to camera
var arrow = camera.addChild(new Arrow());
arrow.visible = false; // Initially hidden until battery spawns
// Create arrow2 for special items and add to camera
var arrow2 = camera.addChild(new Arrow2());
arrow2.visible = false; // Initially hidden until special item spawns
// Create 3 lightning bolts in top-left corner of screen
var lightning1 = LK.gui.topLeft.addChild(LK.getAsset('rayo', {
anchorX: 0.5,
anchorY: 0.5,
x: 150,
y: 100
}));
var lightning2 = LK.gui.topLeft.addChild(LK.getAsset('rayo', {
anchorX: 0.5,
anchorY: 0.5,
x: 250,
y: 100
}));
var lightning3 = LK.gui.topLeft.addChild(LK.getAsset('rayo', {
anchorX: 0.5,
anchorY: 0.5,
x: 350,
y: 100
}));
// Lightning management
var lightningBolts = [lightning1, lightning2, lightning3];
var lightningTimer = 0;
var lightningDelay = 420; // 7 seconds at 60fps
var activeLightning = 3; // Start with 3 active lightning bolts
// Create timer display in top center of screen
var timerText = new Text2('00:00', {
size: 80,
fill: 0xFFFFFF
});
timerText.anchor.set(0.5, 0);
LK.gui.top.addChild(timerText);
// Timer variables
var gameTimer = 0; // Timer in frames (60fps)
// Create 5 lives in top-right corner of screen
var life1 = LK.gui.topRight.addChild(LK.getAsset('vida', {
anchorX: 0.5,
anchorY: 0.5,
x: -50,
y: 100
}));
var life2 = LK.gui.topRight.addChild(LK.getAsset('vida', {
anchorX: 0.5,
anchorY: 0.5,
x: -150,
y: 100
}));
var life3 = LK.gui.topRight.addChild(LK.getAsset('vida', {
anchorX: 0.5,
anchorY: 0.5,
x: -250,
y: 100
}));
var life4 = LK.gui.topRight.addChild(LK.getAsset('vida', {
anchorX: 0.5,
anchorY: 0.5,
x: -350,
y: 100
}));
// Hearts management
var hearts = [life1, life2, life3, life4];
var activeHearts = 4; // Start with 4 active hearts
// Create enemies array
var enemies = [];
// Create batteries array
var batteries = [];
// Battery spawn timer
var batterySpawnTimer = 600; // Start ready to spawn immediately
var batterySpawnDelay = 600; // Spawn battery every 10 seconds (600 frames at 60fps)
// Create special items arrays
var wheels = [];
var shields = [];
// Special item spawn timer
var itemSpawnTimer = 0;
var itemSpawnMinDelay = 600; // 10 seconds minimum (600 frames at 60fps)
var itemSpawnMaxDelay = 900; // 15 seconds maximum (900 frames at 60fps)
var itemSpawnDelay = Math.random() * (itemSpawnMaxDelay - itemSpawnMinDelay) + itemSpawnMinDelay;
// Item collection cooldown tracking
var itemCollectionCooldown = 0; // Timer to prevent item spawning after collection
var itemCollectionCooldownDelay = 720; // 12 seconds (720 frames at 60fps)
// Create factories array
var factories = [];
// Create factory in top-left corner
var topLeftFactory = camera.addChild(new Factory());
topLeftFactory.x = 400;
topLeftFactory.y = 400;
factories.push(topLeftFactory);
// Create factory in top-right corner
var topRightFactory = camera.addChild(new Factory());
topRightFactory.x = 1648;
topRightFactory.y = 400;
factories.push(topRightFactory);
// Create factory in bottom-left corner
var bottomLeftFactory = camera.addChild(new Factory());
bottomLeftFactory.x = 400;
bottomLeftFactory.y = 2332;
factories.push(bottomLeftFactory);
// Create factory in bottom-right corner
var bottomRightFactory = camera.addChild(new Factory());
bottomRightFactory.x = 1648;
bottomRightFactory.y = 2332;
factories.push(bottomRightFactory);
// Set camera scale for closer view
camera.scaleX = 2;
camera.scaleY = 2;
// Start playing background music
LK.playMusic('si');
// Game controls - move player toward touch position
var playerSpeed = 4; // Player movement speed
var basePlayerSpeed = 4; // Store original speed for wheel effect
var speedBoostActive = false; // Track if speed boost is active
var shieldActive = false; // Track if shield immunity is active
var targetX = player.x;
var targetY = player.y;
var isMoving = false;
function updatePlayerMovement(x, y) {
// Convert screen coordinates to world coordinates accounting for camera transform
var worldX = (x - camera.x) / camera.scaleX;
var worldY = (y - camera.y) / camera.scaleY;
targetX = worldX;
targetY = worldY;
isMoving = true;
}
game.down = function (x, y, obj) {
updatePlayerMovement(x, y);
};
game.move = function (x, y, obj) {
updatePlayerMovement(x, y);
};
game.up = function (x, y, obj) {
isMoving = false;
};
// Main game update loop
game.update = function () {
// Update and display timer
gameTimer++;
var seconds = Math.floor(gameTimer / 60);
var minutes = Math.floor(seconds / 60);
seconds = seconds % 60;
var timeString = (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
timerText.setText(timeString);
// Move player toward target position if moving
if (isMoving) {
var deltaX = targetX - player.x;
var deltaY = targetY - player.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared > 25) {
// 5 * 5 - avoid sqrt when possible
// Only move if not close enough to target
var distance = Math.sqrt(distanceSquared);
// Calculate movement direction
var directionX = deltaX / distance;
var directionY = deltaY / distance;
// Move player toward target
player.x += directionX * playerSpeed;
player.y += directionY * playerSpeed;
// Rotate player to face movement direction
var targetRotation = Math.atan2(deltaY, deltaX) + Math.PI / 2;
var rotationDiff = targetRotation - player.rotation;
// Normalize rotation difference to [-PI, PI]
while (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
while (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
// Smooth rotation
player.rotation += rotationDiff * 0.15;
}
}
// Check player-tree collisions before finalizing movement
var playerCollisionDetected = false;
var playerRadius = 25; // Player collision radius
var treeRadius = 40; // Tree collision radius
var combinedRadius = playerRadius + treeRadius;
var combinedRadiusSquared = combinedRadius * combinedRadius;
// Check collision with all trees
for (var t = 0; t < allTrees.length; t++) {
var tree = allTrees[t];
var deltaX = player.x - tree.x;
var deltaY = player.y - tree.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < combinedRadiusSquared) {
playerCollisionDetected = true;
// Push player away from tree
var distance = Math.sqrt(distanceSquared);
if (distance > 0) {
var pushDistance = combinedRadius - distance;
var pushX = deltaX / distance * pushDistance;
var pushY = deltaY / distance * pushDistance;
player.x += pushX;
player.y += pushY;
}
}
}
// Keep player within bounds, accounting for tree borders
player.x = Math.max(80, Math.min(1968, player.x));
player.y = Math.max(120, Math.min(2612, player.y));
// Update camera position to follow player
var targetCameraX = 1024 - player.x * camera.scaleX;
var targetCameraY = 1366 - player.y * camera.scaleY;
// Smooth camera following
camera.x += (targetCameraX - camera.x) * 0.1;
camera.y += (targetCameraY - camera.y) * 0.1;
// Handle enemy collision detection - prevent enemies from overlapping (frame-skipped optimization)
// Only run separation every 3 frames to reduce computational load
if (LK.ticks % 3 === 0) {
var maxChecks = Math.min(enemies.length * enemies.length / 4, 100); // Limit total checks
var checksPerformed = 0;
for (var i = 0; i < enemies.length && checksPerformed < maxChecks; i++) {
for (var j = i + 1; j < enemies.length && checksPerformed < maxChecks; j++) {
var enemy1 = enemies[i];
var enemy2 = enemies[j];
var deltaX = enemy2.x - enemy1.x;
var deltaY = enemy2.y - enemy1.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
var minDistanceSquared = 6400; // 80 * 80 - avoid sqrt when possible
if (distanceSquared < minDistanceSquared && distanceSquared > 0) {
var distance = Math.sqrt(distanceSquared);
// Calculate separation force
var separationForce = (80 - distance) / 2;
var separationX = deltaX / distance * separationForce;
var separationY = deltaY / distance * separationForce;
// Push enemies apart
enemy1.x -= separationX;
enemy1.y -= separationY;
enemy2.x += separationX;
enemy2.y += separationY;
// Keep enemies within bounds after separation
enemy1.x = Math.max(80, Math.min(1968, enemy1.x));
enemy1.y = Math.max(120, Math.min(2612, enemy1.y));
enemy2.x = Math.max(80, Math.min(1968, enemy2.x));
enemy2.y = Math.max(120, Math.min(2612, enemy2.y));
}
checksPerformed++;
}
}
}
// Update factories
for (var i = 0; i < factories.length; i++) {
factories[i].update();
}
// Handle battery spawning
batterySpawnTimer++;
if (batterySpawnTimer >= batterySpawnDelay && batteries.length < 5) {
var validPosition = false;
var batteryX, batteryY;
var attempts = 0;
var maxAttempts = 50;
// Find valid position for battery spawn
while (!validPosition && attempts < maxAttempts) {
batteryX = Math.random() * (2048 - 200) + 100; // Keep away from borders
batteryY = Math.random() * (2732 - 240) + 120; // Keep away from borders
validPosition = true;
// Check distance from factories - increased exclusion radius
for (var f = 0; f < factories.length; f++) {
var factory = factories[f];
var deltaX = batteryX - factory.x;
var deltaY = batteryY - factory.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
var factoryExclusionRadius = 300; // Increased from 200px to 300px for better separation
var factoryExclusionRadiusSquared = factoryExclusionRadius * factoryExclusionRadius;
if (distanceSquared < factoryExclusionRadiusSquared) {
validPosition = false;
break;
}
}
// Check distance from trees
if (validPosition) {
for (var t = 0; t < allTrees.length; t++) {
var tree = allTrees[t];
var deltaX = batteryX - tree.x;
var deltaY = batteryY - tree.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < 10000) {
// 100px minimum distance from trees
validPosition = false;
break;
}
}
}
attempts++;
}
// Spawn battery if valid position found
if (validPosition) {
var newBattery = camera.addChild(new Battery());
newBattery.x = batteryX;
newBattery.y = batteryY;
batteries.push(newBattery);
}
batterySpawnTimer = 0;
}
// Handle special item spawning (wheel or shield)
itemSpawnTimer++;
// Update item collection cooldown
if (itemCollectionCooldown > 0) {
itemCollectionCooldown--;
}
if (itemSpawnTimer >= itemSpawnDelay && itemCollectionCooldown <= 0 && wheels.length === 0 && shields.length === 0) {
var validPosition = false;
var itemX, itemY;
var attempts = 0;
var maxAttempts = 50;
// Find valid position for item spawn
while (!validPosition && attempts < maxAttempts) {
itemX = Math.random() * (2048 - 200) + 100; // Keep away from borders
itemY = Math.random() * (2732 - 240) + 120; // Keep away from borders
validPosition = true;
// Check distance from factories
for (var f = 0; f < factories.length; f++) {
var factory = factories[f];
var deltaX = itemX - factory.x;
var deltaY = itemY - factory.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
var factoryExclusionRadius = 300;
var factoryExclusionRadiusSquared = factoryExclusionRadius * factoryExclusionRadius;
if (distanceSquared < factoryExclusionRadiusSquared) {
validPosition = false;
break;
}
}
// Check distance from trees
if (validPosition) {
for (var t = 0; t < allTrees.length; t++) {
var tree = allTrees[t];
var deltaX = itemX - tree.x;
var deltaY = itemY - tree.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < 10000) {
// 100px minimum distance from trees
validPosition = false;
break;
}
}
}
attempts++;
}
// Spawn item if valid position found
if (validPosition) {
// Randomly choose between wheel (0) or shield (1)
var itemType = Math.floor(Math.random() * 2);
if (itemType === 0) {
// Spawn wheel
var newWheel = camera.addChild(new Wheel());
newWheel.x = itemX;
newWheel.y = itemY;
wheels.push(newWheel);
} else {
// Spawn shield
var newShield = camera.addChild(new Shield());
newShield.x = itemX;
newShield.y = itemY;
shields.push(newShield);
}
}
// Reset timer with new random delay
itemSpawnTimer = 0;
itemSpawnDelay = Math.random() * (itemSpawnMaxDelay - itemSpawnMinDelay) + itemSpawnMinDelay;
}
// Check player-wheel collisions and collection
for (var w = wheels.length - 1; w >= 0; w--) {
var wheel = wheels[w];
var deltaX = player.x - wheel.x;
var deltaY = player.y - wheel.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
var collectionRadius = 50; // Collection radius for wheel
var collectionRadiusSquared = collectionRadius * collectionRadius;
if (distanceSquared < collectionRadiusSquared) {
// Wheel collected - remove it
wheel.destroy();
wheels.splice(w, 1);
// Start item collection cooldown
itemCollectionCooldown = itemCollectionCooldownDelay;
// Apply speed boost for 10 seconds
if (!speedBoostActive) {
speedBoostActive = true;
playerSpeed = basePlayerSpeed * 2; // Double the speed
// Use tween to reset speed after 10 seconds
var speedBoostTimer = {
value: 1
};
tween(speedBoostTimer, {
value: 0
}, {
duration: 10000,
// 10 seconds
onFinish: function onFinish() {
playerSpeed = basePlayerSpeed; // Reset to original speed
speedBoostActive = false;
}
});
}
}
}
// Check player-shield collisions and collection
for (var s = shields.length - 1; s >= 0; s--) {
var shield = shields[s];
var deltaX = player.x - shield.x;
var deltaY = player.y - shield.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
var collectionRadius = 50; // Collection radius for shield
var collectionRadiusSquared = collectionRadius * collectionRadius;
if (distanceSquared < collectionRadiusSquared) {
// Shield collected - remove it
shield.destroy();
shields.splice(s, 1);
// Start item collection cooldown
itemCollectionCooldown = itemCollectionCooldownDelay;
// Apply immunity for 10 seconds
if (!shieldActive) {
shieldActive = true;
// Visual indicator - make player semi-transparent blue
tween(player, {
tint: 0x0099ff
}, {
duration: 200
});
// Use tween to reset immunity after 10 seconds
var shieldTimer = {
value: 1
};
tween(shieldTimer, {
value: 0
}, {
duration: 10000,
// 10 seconds
onFinish: function onFinish() {
shieldActive = false;
// Reset player color
tween(player, {
tint: 0xffffff
}, {
duration: 200
});
}
});
}
}
}
// Check player-battery collisions and collection
for (var b = batteries.length - 1; b >= 0; b--) {
var battery = batteries[b];
var deltaX = player.x - battery.x;
var deltaY = player.y - battery.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
var collectionRadius = 50; // Collection radius for battery
var collectionRadiusSquared = collectionRadius * collectionRadius;
if (distanceSquared < collectionRadiusSquared) {
// Battery collected - remove it
battery.destroy();
batteries.splice(b, 1);
// Add a lightning bolt back if we have less than 3
if (activeLightning < 3) {
var lightningToAdd = lightningBolts[activeLightning];
lightningToAdd.visible = true;
lightningToAdd.alpha = 1;
lightningToAdd.scaleX = 1;
lightningToAdd.scaleY = 1;
activeLightning++;
}
// Immediately spawn a new battery when one is collected (only if less than 5)
if (batteries.length < 5) {
var validPosition = false;
var batteryX, batteryY;
var attempts = 0;
var maxAttempts = 50;
// Find valid position for new battery spawn
while (!validPosition && attempts < maxAttempts) {
batteryX = Math.random() * (2048 - 200) + 100; // Keep away from borders
batteryY = Math.random() * (2732 - 240) + 120; // Keep away from borders
validPosition = true;
// Check distance from factories - increased exclusion radius
for (var f = 0; f < factories.length; f++) {
var factory = factories[f];
var deltaX = batteryX - factory.x;
var deltaY = batteryY - factory.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
var factoryExclusionRadius = 300; // Increased from 200px to 300px for better separation
var factoryExclusionRadiusSquared = factoryExclusionRadius * factoryExclusionRadius;
if (distanceSquared < factoryExclusionRadiusSquared) {
validPosition = false;
break;
}
}
// Check distance from trees
if (validPosition) {
for (var t = 0; t < allTrees.length; t++) {
var tree = allTrees[t];
var deltaX = batteryX - tree.x;
var deltaY = batteryY - tree.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < 10000) {
// 100px minimum distance from trees
validPosition = false;
break;
}
}
}
attempts++;
}
// Spawn new battery if valid position found
if (validPosition) {
var newBattery = camera.addChild(new Battery());
newBattery.x = batteryX;
newBattery.y = batteryY;
batteries.push(newBattery);
}
}
}
}
// Update arrow to point to nearest battery
if (batteries.length > 0) {
arrow.visible = true;
// Find nearest battery
var nearestBattery = null;
var nearestDistance = Infinity;
for (var b = 0; b < batteries.length; b++) {
var battery = batteries[b];
var deltaX = battery.x - player.x;
var deltaY = battery.y - player.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < nearestDistance) {
nearestDistance = distanceSquared;
nearestBattery = battery;
}
}
// Position arrow around player pointing to nearest battery
if (nearestBattery) {
var batteryDeltaX = nearestBattery.x - player.x;
var batteryDeltaY = nearestBattery.y - player.y;
var batteryDistance = Math.sqrt(batteryDeltaX * batteryDeltaX + batteryDeltaY * batteryDeltaY);
if (batteryDistance > 0) {
// Normalize direction to battery
var directionX = batteryDeltaX / batteryDistance;
var directionY = batteryDeltaY / batteryDistance;
// Position arrow around player at fixed radius
arrow.x = player.x + directionX * arrow.radius;
arrow.y = player.y + directionY * arrow.radius;
// Rotate arrow to point toward battery
arrow.rotation = Math.atan2(batteryDeltaY, batteryDeltaX) + Math.PI / 2;
}
}
} else {
arrow.visible = false;
}
// Update arrow2 to point to nearest special item (wheel or shield)
var allSpecialItems = wheels.concat(shields);
if (allSpecialItems.length > 0) {
arrow2.visible = true;
// Find nearest special item
var nearestItem = null;
var nearestDistance = Infinity;
for (var i = 0; i < allSpecialItems.length; i++) {
var item = allSpecialItems[i];
var deltaX = item.x - player.x;
var deltaY = item.y - player.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < nearestDistance) {
nearestDistance = distanceSquared;
nearestItem = item;
}
}
// Position arrow2 around player pointing to nearest special item
if (nearestItem) {
var itemDeltaX = nearestItem.x - player.x;
var itemDeltaY = nearestItem.y - player.y;
var itemDistance = Math.sqrt(itemDeltaX * itemDeltaX + itemDeltaY * itemDeltaY);
if (itemDistance > 0) {
// Normalize direction to item
var directionX = itemDeltaX / itemDistance;
var directionY = itemDeltaY / itemDistance;
// Position arrow2 around player at fixed radius
arrow2.x = player.x + directionX * arrow2.radius;
arrow2.y = player.y + directionY * arrow2.radius;
// Rotate arrow2 to point toward item
arrow2.rotation = Math.atan2(itemDeltaY, itemDeltaX) + Math.PI / 2;
}
}
} else {
arrow2.visible = false;
}
// Handle lightning bolt disappearance every 20 seconds
lightningTimer++;
if (lightningTimer >= lightningDelay && activeLightning > 0) {
var lightningToRemove = lightningBolts[activeLightning - 1];
// Animate lightning bolt disappearing with fade and scale down
tween(lightningToRemove, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
lightningToRemove.visible = false;
}
});
activeLightning--;
lightningTimer = 0;
// Check if all lightning bolts are consumed
if (activeLightning <= 0) {
// Make player disappear
tween(player, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeOut
});
// Hide arrow when player disappears
arrow.visible = false;
// Hide arrow2 when player disappears
arrow2.visible = false;
// Create explosion at player position
var playerExplosion = camera.addChild(LK.getAsset('explocion', {
anchorX: 0.5,
anchorY: 0.5,
x: player.x,
y: player.y
}));
// Animate explosion
tween(playerExplosion, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
playerExplosion.destroy();
}
});
// Play explosion sound
LK.getSound('explosion').play();
// Show "lost" image covering the entire screen after 1 second delay
LK.setTimeout(function () {
// Scale camera back to show full image
tween(camera, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut
});
// Calculate scale to fit camera dimensions (2048x2732)
var cameraWidth = 2048;
var cameraHeight = 2732;
var lostImageWidth = 100; // Original asset width
var lostImageHeight = 100; // Original asset height
var scaleX = cameraWidth / lostImageWidth;
var scaleY = cameraHeight / lostImageHeight;
var lostImage = LK.gui.center.addChild(LK.getAsset('lost', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: scaleX,
scaleY: scaleY
}));
}, 1000);
// Show game over after a brief delay
LK.setTimeout(function () {
LK.showGameOver();
}, 2500);
}
}
// Check enemy-player collisions using hitbox intersection
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Use intersects method to check if enemy hitbox touches player hitbox
if (enemy.intersects(player) && activeHearts > 0 && !shieldActive) {
// Enemy touched player - remove a heart
var heartToRemove = hearts[activeHearts - 1];
// Animate heart disappearing with fade and scale down
tween(heartToRemove, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
heartToRemove.visible = false;
}
});
activeHearts--;
// Remove the enemy that hit the player
enemy.destroy();
enemies.splice(i, 1);
// Check if all hearts are gone
if (activeHearts <= 0) {
// Make player disappear
tween(player, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeOut
});
// Hide arrow when player disappears
arrow.visible = false;
// Hide arrow2 when player disappears
arrow2.visible = false;
// Create explosion at player position
var playerExplosion = camera.addChild(LK.getAsset('explocion', {
anchorX: 0.5,
anchorY: 0.5,
x: player.x,
y: player.y
}));
// Animate explosion
tween(playerExplosion, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
playerExplosion.destroy();
}
});
// Play explosion sound
LK.getSound('explosion').play();
// Show game over after a brief delay
LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
return; // Exit update loop early since game is over
}
continue; // Skip to next enemy since this one was removed
}
}
// Check enemy-tree collisions with optimized distance-based pre-filtering
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
var collisionDetected = false;
var collidedTree = null;
// Pre-filter trees by distance before expensive intersection check
for (var j = 0; j < allTrees.length; j++) {
var tree = allTrees[j];
var deltaX = enemy.x - tree.x;
var deltaY = enemy.y - tree.y;
var distanceSquared = deltaX * deltaX + deltaY * deltaY;
// Only check intersection if close enough (tree radius ~50 + enemy radius ~30)
if (distanceSquared < 6400) {
// 80 * 80
if (enemy.intersects(tree)) {
collisionDetected = true;
collidedTree = tree;
break;
}
}
}
// Handle collision - create explosion and remove enemy
if (collisionDetected) {
// Create explosion effect at enemy position
var explosion = camera.addChild(LK.getAsset('explocion', {
anchorX: 0.5,
anchorY: 0.5,
x: enemy.x,
y: enemy.y
}));
// Animate explosion with scaling and fading
tween(explosion, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Play explosion sound
LK.getSound('explosion').play();
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
} else {
// Update enemy movement if no collision
enemy.chasePlayer();
}
}
};
robot estilo steampunk visto desde arriba totalmente no desde el frente con llantas. In-Game asset. 2d. High contrast. No shadows. vistasuperior
dame un fondo de color verde pasto. In-Game asset. 2d. High contrast. No shadows
has un robot negro tipo auto steampunk con aspecto desgastado que se mueva con llantas y este en una vista desde arriba In-Game asset. 2d. High contrast. No shadows
explosion realista. In-Game asset. 2d. High contrast. No shadows
haz un corazon con aspecto steampunk metalico desgastado. In-Game asset. 2d. High contrast. No shadows
una bateria energetica estilo steampunk. In-Game asset. 2d. High contrast. No shadows
has una flecha de señalizacion en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
has un rayo. In-Game asset. 2d. High contrast. No shadows
has que se vea en estilo steampunk
has una rueda en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
has un escudo en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
plateada
un 0 es estilo steampunk. In-Game asset. 2d. High contrast. No shadows
1 en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
numero 2 estilo steampunk. In-Game asset. 2d. High contrast. No shadows
numero 3 en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
numero 4 en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
numero 5 en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
numero 6 en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
numero 7 en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
numero 8 en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
numero 9 en estilo steampunk. In-Game asset. 2d. High contrast. No shadows
simbolo ":" en estilo steampunk. In-Game asset. 2d. High contrast. No shadows