/**** * Classes ****/ // Background class var Background = Container.expand(function () { var self = Container.call(this); var backgroundGraphics = self.attachAsset('background', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732 }); backgroundGraphics.anchor.set(0, 0); // Add black overlay on top of the background var overlay = self.attachAsset('shape', { width: 2048, height: 2732, color: 0x000000, alpha: 0.0, shape: 'box' }); overlay.anchor.set(0, 0); // This is automatically called every game tick, if the background is attached! self.update = function () { if (overlay.alpha < 1) { overlay.alpha += 0.0001; // Increase alpha over time } }; }); // Bubble class var Bubble = Container.expand(function () { var self = Container.call(this); var bubbleGraphics = self.attachAsset('bubble', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); var scale = Math.random() * 0.5 + 0.5; bubbleGraphics.scale.set(scale, scale); // Set bubble speed self.speed = -3; // Initialize colliding property self.colliding = false; // This is automatically called every game tick, if the bubble is attached! self.update = function () { // Move the bubble upwards and sideways self.y += self.speed; self.x += Math.sin(LK.ticks / 10) * 0.1; // Reset the position of the bubble when it reaches the top if (self.y <= 0) { self.y = 2732; } // Check if the bubble is colliding with an obstacle using spatial hash var cellSize = 150; var hashX = Math.floor(self.x / cellSize); var hashY = Math.floor(self.y / cellSize); var hashKey = hashX + ',' + hashY; if (spatialHash[hashKey]) { for (var i = 0; i < spatialHash[hashKey].length; i++) { var obstacle = spatialHash[hashKey][i]; if (self.intersects(obstacle)) { // Bring the bubble to the top of the z axis game.setChildIndex(self, game.children.length - 1); self.colliding = true; } else { self.collidingWithShield = false; self.colliding = false; } } } else { self.colliding = false; } }; }); // Diver class var Diver = Container.expand(function () { var self = Container.call(this); var diverGraphics = self.attachAsset('diver', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); // Add an invisible hitbox with smaller dimensions var hitbox = self.attachAsset('shape', { width: 200, // Smaller width height: 260, // Increased height by 30% // Smaller height color: 0x000000, alpha: 0 // Invisible }); // Add a sideways movement to the diver self.movement = 0; self.direction = 1; self.update = function () { self.movement += self.direction * 0.03; if (self.movement > 0.5 || self.movement < -0.5) { self.direction *= -1; } self.x += self.movement; }; // Center the hitbox on the diver hitbox.anchor.set(0.5, 0.5); // Add flippers to the diver var leftFlipper = self.addChild(new Flipper()); leftFlipper.x = -50; leftFlipper.depth = -1; var rightFlipper = self.addChild(new Flipper()); rightFlipper.x = 60; rightFlipper.depth = -1; }); // Flipper class var Flipper = Container.expand(function () { var self = Container.call(this); var flipperGraphics = self.attachAsset('flippers', { anchorX: 0.5, anchorY: 1.0, scaleX: 0.7, scaleY: 0.7 }); // Set flipper movement self.movement = 0; self.direction = 1; // This is automatically called every game tick, if the flipper is attached! self.update = function () { // Move the flipper subtly to simulate flipping self.movement += self.direction * 0.03; if (self.movement > 0.5 || self.movement < -0.5) { self.direction *= -1; } self.rotation = self.movement; }; }); // Obstacle1 class var Obstacle1 = Container.expand(function () { var self = Container.call(this); var obstacle1Graphics = self.attachAsset('obstacle1', { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() < 0.5 ? 0.7 : -0.7, // Randomly mirror horizontally scaleY: 0.7 }); // Set obstacle1 speed self.speed = -5; self.originalSpeedX = self.speed; // This is automatically called every game tick, if the obstacle1 is attached! self.update = function () { // Rotate obstacle1 on its axis self.rotation += 0.05; // Check if the obstacle is colliding with the shield if (self.intersects(shield, { tolerance: 10 })) { if (!self.collidingWithShield) { LK.getSound('bubble').play(); self.collidingWithShield = true; } // Calculate the direction vector between the shield and the obstacle var dx = self.x - shield.x; var dy = self.y - shield.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Push the obstacle away from the shield self.x += dx * 5; self.y += dy * 5; } else { self.collidingWithShield = false; self.speed = self.originalSpeedX; } // Check for collision with diver if (self.intersects(diver.children.find(function (child) { return child.alpha === 0; }), { tolerance: 10 })) { // Flash screen red for 1 second (1000ms) to show game over LK.effects.flashScreen(0xff0000, 1000); // Show game over. The game will be automatically paused while game over is showing. LK.showGameOver(); } // Update spatial hash var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize); var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize); var newHashX = Math.floor(self.x / cellSize); var newHashY = Math.floor(self.y / cellSize); var oldHashKey = oldHashX + ',' + oldHashY; var newHashKey = newHashX + ',' + newHashY; if (oldHashKey !== newHashKey) { if (spatialHash[oldHashKey]) { var index = spatialHash[oldHashKey].indexOf(self); if (index > -1) { spatialHash[oldHashKey].splice(index, 1); } } if (!spatialHash[newHashKey]) { spatialHash[newHashKey] = []; } spatialHash[newHashKey].push(self); } // Continue moving upwards self.x += self.waveSpeedX; self.y += self.speed * speedMultiplier; self.scale.x = 1 + Math.sin(LK.ticks / 10) * 0.02; self.scale.y = 1 + Math.sin(LK.ticks / 10) * 0.02; if (self.y < 0) { self.destroy(); var index = obstacles.indexOf(self); if (index > -1) { obstacles.splice(index, 1); } } // Check if the obstacle is colliding with another obstacle for (var i = 0; i < obstacles.length; i++) { var otherObstacle = obstacles[i]; if (self !== otherObstacle && self.intersects(otherObstacle)) { // Calculate the direction vector between the two obstacles var dx = self.x - otherObstacle.x; var dy = self.y - otherObstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Increase the repulsion force and always push the obstacle to the outside self.x += dx * 10; self.y += dy * 10; } } }; }); // Obstacle2 class var Obstacle2 = Container.expand(function () { var self = Container.call(this); var obstacle2Graphics = self.attachAsset('obstacle2', { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() < 0.5 ? 0.7 : -0.7, // Randomly mirror horizontally scaleY: 0.7 }); // Set obstacle2 speed self.speed = -5; self.originalSpeedX = self.speed; // This is automatically called every game tick, if the obstacle2 is attached! self.update = function () { // Check if the obstacle is colliding with the shield if (self.intersects(shield, { tolerance: 10 })) { if (!self.collidingWithShield) { LK.getSound('bubble').play(); self.collidingWithShield = true; } // Calculate the direction vector between the shield and the obstacle var dx = self.x - shield.x; var dy = self.y - shield.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Push the obstacle away from the shield self.x += dx * 5; self.y += dy * 5; } else { self.collidingWithShield = false; self.speed = self.originalSpeedX; } // Check for collision with diver if (self.intersects(diver.children.find(function (child) { return child.alpha === 0; }), { tolerance: 10 })) { // Flash screen red for 1 second (1000ms) to show game over LK.effects.flashScreen(0xff0000, 1000); // Show game over. The game will be automatically paused while game over is showing. LK.showGameOver(); } // Update spatial hash var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize); var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize); var newHashX = Math.floor(self.x / cellSize); var newHashY = Math.floor(self.y / cellSize); var oldHashKey = oldHashX + ',' + oldHashY; var newHashKey = newHashX + ',' + newHashY; if (oldHashKey !== newHashKey) { if (spatialHash[oldHashKey]) { var index = spatialHash[oldHashKey].indexOf(self); if (index > -1) { spatialHash[oldHashKey].splice(index, 1); } } if (!spatialHash[newHashKey]) { spatialHash[newHashKey] = []; } spatialHash[newHashKey].push(self); } // Continue moving upwards self.x += self.waveSpeedX; self.y += self.speed * speedMultiplier; // Add wiggly movement to the obstacle self.scale.x = 1 + Math.sin(LK.ticks / 10) * 0.02; self.scale.y = 1 + Math.sin(LK.ticks / 10) * 0.02; if (self.y < 0) { self.destroy(); var index = obstacles.indexOf(self); if (index > -1) { obstacles.splice(index, 1); } } // Check if the obstacle is colliding with another obstacle for (var i = 0; i < obstacles.length; i++) { var otherObstacle = obstacles[i]; if (self !== otherObstacle && self.intersects(otherObstacle)) { // Calculate the direction vector between the two obstacles var dx = self.x - otherObstacle.x; var dy = self.y - otherObstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Increase the repulsion force and always push the obstacle to the outside self.x += dx * 10; self.y += dy * 10; } } }; }); // Obstacle3 class var Obstacle3 = Container.expand(function () { var self = Container.call(this); var obstacle3Graphics = self.attachAsset('obstacle3', { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() < 0.5 ? 0.7 : -0.7, // Randomly mirror horizontally scaleY: 0.7 }); // Set obstacle3 speed self.speed = -5; self.originalSpeedX = self.speed; // This is automatically called every game tick, if the obstacle3 is attached! self.update = function () { // Check if the obstacle is colliding with the shield if (self.intersects(shield, { tolerance: 10 })) { if (!self.collidingWithShield) { LK.getSound('bubble').play(); self.collidingWithShield = true; } // Calculate the direction vector between the shield and the obstacle var dx = self.x - shield.x; var dy = self.y - shield.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Push the obstacle away from the shield self.x += dx * 5; self.y += dy * 5; } else { self.collidingWithShield = false; self.speed = self.originalSpeedX; } // Check for collision with diver if (self.intersects(diver.children.find(function (child) { return child.alpha === 0; }), { tolerance: 10 })) { // Flash screen red for 1 second (1000ms) to show game over LK.effects.flashScreen(0xff0000, 1000); // Show game over. The game will be automatically paused while game over is showing. LK.showGameOver(); } // Update spatial hash var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize); var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize); var newHashX = Math.floor(self.x / cellSize); var newHashY = Math.floor(self.y / cellSize); var oldHashKey = oldHashX + ',' + oldHashY; var newHashKey = newHashX + ',' + newHashY; if (oldHashKey !== newHashKey) { if (spatialHash[oldHashKey]) { var index = spatialHash[oldHashKey].indexOf(self); if (index > -1) { spatialHash[oldHashKey].splice(index, 1); } } if (!spatialHash[newHashKey]) { spatialHash[newHashKey] = []; } spatialHash[newHashKey].push(self); } // Continue moving upwards self.x += self.waveSpeedX; self.y += self.speed * speedMultiplier; // Add slow spinning effect self.rotation += 0.01; // Add unique behavior to obstacle3 self.scale.x = 1 + Math.sin(LK.ticks / 5) * 0.05; // Larger size change self.scale.y = 1 + Math.sin(LK.ticks / 5) * 0.05; if (self.y < 0) { self.destroy(); var index = obstacles.indexOf(self); if (index > -1) { obstacles.splice(index, 1); } } // Check if the obstacle is colliding with another obstacle for (var i = 0; i < obstacles.length; i++) { var otherObstacle = obstacles[i]; if (self !== otherObstacle && self.intersects(otherObstacle)) { // Calculate the direction vector between the two obstacles var dx = self.x - otherObstacle.x; var dy = self.y - otherObstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Increase the repulsion force and always push the obstacle to the outside self.x += dx * 10; self.y += dy * 10; } } }; }); // Obstacle4 class var Obstacle4 = Container.expand(function () { var self = Container.call(this); var obstacle4Graphics = self.attachAsset('obstacle4', { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() < 0.5 ? 0.7 : -0.7, // Randomly mirror horizontally scaleY: 0.7 }); // Set obstacle4 speed self.speed = -5; self.originalSpeedX = self.speed; // This is automatically called every game tick, if the obstacle4 is attached! self.update = function () { // Add up and down movement self.y += Math.sin(LK.ticks / 20) * 2; // Check if the obstacle is colliding with the shield if (self.intersects(shield, { tolerance: 10 })) { if (!self.collidingWithShield) { LK.getSound('bubble').play(); self.collidingWithShield = true; } // Calculate the direction vector between the shield and the obstacle var dx = self.x - shield.x; var dy = self.y - shield.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Push the obstacle away from the shield self.x += dx * 5; self.y += dy * 5; } else { self.collidingWithShield = false; self.speed = self.originalSpeedX; } // Check for collision with diver if (self.intersects(diver.children.find(function (child) { return child.alpha === 0; }), { tolerance: 10 })) { // Flash screen red for 1 second (1000ms) to show game over LK.effects.flashScreen(0xff0000, 1000); // Show game over. The game will be automatically paused while game over is showing. LK.showGameOver(); } // Update spatial hash var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize); var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize); var newHashX = Math.floor(self.x / cellSize); var newHashY = Math.floor(self.y / cellSize); var oldHashKey = oldHashX + ',' + oldHashY; var newHashKey = newHashX + ',' + newHashY; if (oldHashKey !== newHashKey) { if (spatialHash[oldHashKey]) { var index = spatialHash[oldHashKey].indexOf(self); if (index > -1) { spatialHash[oldHashKey].splice(index, 1); } } if (!spatialHash[newHashKey]) { spatialHash[newHashKey] = []; } spatialHash[newHashKey].push(self); } // Continue moving upwards self.x += self.waveSpeedX; self.y += self.speed * speedMultiplier + Math.sin(LK.ticks / 20) * 2; self.scale.x = 1 + Math.sin(LK.ticks / 15) * 0.03; // Different size change self.scale.y = 1 + Math.sin(LK.ticks / 15) * 0.03; if (self.y < 0) { self.destroy(); var index = obstacles.indexOf(self); if (index > -1) { obstacles.splice(index, 1); } } // Check if the obstacle is colliding with another obstacle for (var i = 0; i < obstacles.length; i++) { var otherObstacle = obstacles[i]; if (self !== otherObstacle && self.intersects(otherObstacle)) { // Calculate the direction vector between the two obstacles var dx = self.x - otherObstacle.x; var dy = self.y - otherObstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Increase the repulsion force and always push the obstacle to the outside self.x += dx * 10; self.y += dy * 10; } } }; }); // Obstacle5 class var Obstacle5 = Container.expand(function () { var self = Container.call(this); var obstacle5Graphics = self.attachAsset('obstacle5', { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() < 0.5 ? 0.7 : -0.7, // Randomly mirror horizontally scaleY: 0.7 }); // Set obstacle5 speed self.speed = -5; self.originalSpeedX = self.speed; // This is automatically called every game tick, if the obstacle5 is attached! self.update = function () { // Add very small sideways movement self.x += Math.sin(LK.ticks / 100) * 0.5; // Check if the obstacle is colliding with the shield if (self.intersects(shield, { tolerance: 10 })) { if (!self.collidingWithShield) { LK.getSound('bubble').play(); self.collidingWithShield = true; } // Calculate the direction vector between the shield and the obstacle var dx = self.x - shield.x; var dy = self.y - shield.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Push the obstacle away from the shield self.x += dx * 5; self.y += dy * 5; } else { self.collidingWithShield = false; self.speed = self.originalSpeedX; } // Check for collision with diver if (self.intersects(diver.children.find(function (child) { return child.alpha === 0; }), { tolerance: 10 })) { // Flash screen red for 1 second (1000ms) to show game over LK.effects.flashScreen(0xff0000, 1000); // Show game over. The game will be automatically paused while game over is showing. LK.showGameOver(); } // Update spatial hash var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize); var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize); var newHashX = Math.floor(self.x / cellSize); var newHashY = Math.floor(self.y / cellSize); var oldHashKey = oldHashX + ',' + oldHashY; var newHashKey = newHashX + ',' + newHashY; if (oldHashKey !== newHashKey) { if (spatialHash[oldHashKey]) { var index = spatialHash[oldHashKey].indexOf(self); if (index > -1) { spatialHash[oldHashKey].splice(index, 1); } } if (!spatialHash[newHashKey]) { spatialHash[newHashKey] = []; } spatialHash[newHashKey].push(self); } // Continue moving upwards self.x += self.waveSpeedX; self.y += self.speed * speedMultiplier; // Add unique behavior to obstacle5 self.scale.x = 1 + Math.sin(LK.ticks / 20) * 0.1; // Increase size change amplitude self.scale.y = 1 + Math.sin(LK.ticks / 20) * 0.1; if (self.y < 0) { self.destroy(); var index = obstacles.indexOf(self); if (index > -1) { obstacles.splice(index, 1); } } // Check if the obstacle is colliding with another obstacle for (var i = 0; i < obstacles.length; i++) { var otherObstacle = obstacles[i]; if (self !== otherObstacle && self.intersects(otherObstacle)) { // Calculate the direction vector between the two obstacles var dx = self.x - otherObstacle.x; var dy = self.y - otherObstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Increase the repulsion force and always push the obstacle to the outside self.x += dx * 10; self.y += dy * 10; } } }; }); // Obstacle6 class var Obstacle6 = Container.expand(function () { var self = Container.call(this); var obstacle6Graphics = self.attachAsset('obstacle6', { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() < 0.5 ? 0.7 : -0.7, // Randomly mirror horizontally scaleY: 0.7 }); // Set obstacle6 speed self.speed = -5; self.originalSpeedX = self.speed; // This is automatically called every game tick, if the obstacle6 is attached! self.update = function () { // Flickering effect self.alpha = Math.random() * 0.5 + 0.5; // Random alpha between 0.5 and 1 // Fast side-to-side movement self.x += Math.sin(LK.ticks / 10) * 1; // Further reduced horizontal movement // Check if the obstacle is colliding with the shield if (self.intersects(shield, { tolerance: 10 })) { if (!self.collidingWithShield) { LK.getSound('bubble').play(); self.collidingWithShield = true; } // Calculate the direction vector between the shield and the obstacle var dx = self.x - shield.x; var dy = self.y - shield.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Push the obstacle away from the shield self.x += dx * 5; self.y += dy * 5; } else { self.collidingWithShield = false; self.speed = self.originalSpeedX; } // Check for collision with diver if (self.intersects(diver.children.find(function (child) { return child.alpha === 0; }), { tolerance: 10 })) { // Flash screen red for 1 second (1000ms) to show game over LK.effects.flashScreen(0xff0000, 1000); // Show game over. The game will be automatically paused while game over is showing. LK.showGameOver(); } // Update spatial hash var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize); var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize); var newHashX = Math.floor(self.x / cellSize); var newHashY = Math.floor(self.y / cellSize); var oldHashKey = oldHashX + ',' + oldHashY; var newHashKey = newHashX + ',' + newHashY; if (oldHashKey !== newHashKey) { if (spatialHash[oldHashKey]) { var index = spatialHash[oldHashKey].indexOf(self); if (index > -1) { spatialHash[oldHashKey].splice(index, 1); } } if (!spatialHash[newHashKey]) { spatialHash[newHashKey] = []; } spatialHash[newHashKey].push(self); } // Continue moving upwards self.x += self.waveSpeedX; self.y += self.speed * speedMultiplier; // Add unique behavior to obstacle6 self.scale.x = 1 + Math.sin(LK.ticks / 25) * 0.05; // Different size change self.scale.y = 1 + Math.sin(LK.ticks / 25) * 0.05; self.alpha = Math.random() * 0.5 + 0.5; // Random alpha between 0.5 and 1 self.x += Math.sin(LK.ticks / 5) * 10; // Fast horizontal movement if (self.y < 0) { self.destroy(); var index = obstacles.indexOf(self); if (index > -1) { obstacles.splice(index, 1); } } // Check if the obstacle is colliding with another obstacle for (var i = 0; i < obstacles.length; i++) { var otherObstacle = obstacles[i]; if (self !== otherObstacle && self.intersects(otherObstacle)) { // Calculate the direction vector between the two obstacles var dx = self.x - otherObstacle.x; var dy = self.y - otherObstacle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Increase the repulsion force and always push the obstacle to the outside self.x += dx * 10; self.y += dy * 10; } } }; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var powerUpGraphics = self.attachAsset('powerUp', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.y += 1.6; // Move power-up downwards (reduced by 20%) self.x += Math.sin(LK.ticks / 20) * 2; // Add pendular movement // Check if the power-up is colliding with the shield if (self.intersects(shield, { tolerance: 10 })) { if (!self.collidingWithShield) { LK.getSound('bubble').play(); self.collidingWithShield = true; } // Calculate the direction vector between the shield and the power-up var dx = self.x - shield.x; var dy = self.y - shield.y; var distance = Math.sqrt(dx * dx + dy * dy); // Normalize the direction vector dx /= distance; dy /= distance; // Push the power-up away from the shield self.x += dx * 5; self.y += dy * 5; } else { self.collidingWithShield = false; } if (self.intersects(diver)) { LK.getSound('powerup').play(); shield.scale.x *= 2; shield.scale.y *= 2; LK.setTimeout(function () { // Start blinking effect after 8 seconds var blinkInterval = LK.setInterval(function () { shield.alpha = shield.alpha === 1 ? 0.5 : 1; if (shield.alpha === 0.5 && !self.shieldOffPlayed) { LK.getSound('shieldoff').play(); self.shieldOffPlayed = true; } }, 60); // Blink every 500ms // Stop blinking and reset shield size after 10 seconds LK.setTimeout(function () { LK.clearInterval(blinkInterval); shield.alpha = 0.9; // Reset alpha to original value shield.scale.x /= 2; shield.scale.y /= 2; self.shieldOffPlayed = false; }, 2000); // Blink for 2 seconds }, 8000); // Start blinking after 8 seconds self.destroy(); } }; }); // Shield class var Shield = Container.expand(function () { var self = Container.call(this); var shieldGraphics = self.attachAsset('shield', { anchorX: 0.5, anchorY: 0.5, alpha: 0.75 }); self.originalAlpha = shieldGraphics.alpha; // Store the original alpha value self.down = function (x, y, obj) { dragNode = shield; }; }); /**** * Initialize Game ****/ // Create a diver instance //<Assets used in the game will automatically appear here> var game = new LK.Game({ backgroundColor: 0xADD8E6 //Init game with a softer blue background }); /**** * Game Code ****/ var wavePositionLabel; // Function to spawn power-ups every 20 seconds function spawnPowerUp() { if (!firstPowerUpSpawned) { if (LK.ticks < 300) { // 300 ticks = 5 seconds return; } firstPowerUpSpawned = true; } else if (LK.ticks - lastPowerUpSpawnTime < 1200) { // 1200 ticks = 20 seconds return; } var powerUp = game.addChild(new PowerUp()); lastPowerUpSpawnTime = LK.ticks; do { powerUp.x = Math.random() * 2048; } while (diver && Math.abs(powerUp.x - diver.x) < 200); powerUp.y = -100; // Start offscreen at the top // Schedule the next power-up spawn LK.setTimeout(spawnPowerUp, 20000); // 20 seconds } // Initial call to spawn the first power-up after 20 seconds LK.setTimeout(spawnPowerUp, 20000); var lastBubbleSoundTime = 0; var lastPowerUpSpawnTime = 0; var speedMultiplier = 1; var waveInterval = 6000; // Initial interval of 5 seconds between waves var firstPowerUpSpawned = false; // Declare and initialize backgroundMusic variable var backgroundMusic = LK.getSound('background'); // Play background music on loop when the game starts LK.on('tick', function () { if (!backgroundMusic.playing) { backgroundMusic.play(); } }); var background1 = game.addChild(new Background()); background1.y = 0; // Create a shield instance var shield = game.addChildAt(new Shield(), game.children.length); // Position the shield at the center of the screen shield.x = 2048 / 2; shield.y = 2732 / 2; // Create a diver instance var dragNode = null; var diver = new Diver(); diver.depth = 2; // Position the diver at the top center of the screen, 200 pixels down from the top diver.x = 2048 / 2; diver.y = 500; // Ensure diver's dimensions are fully initialized before positioning flippers diver.on('added', function () { // Position the flippers relative to the diver diver.children[0].y = diver.height / 2 - 20; // Left flipper diver.children[1].y = diver.height / 2 - 20; // Right flipper }); // Position the flippers relative to the diver diver.children[0].y = diver.height / 2 - 20; // Left flipper diver.children[0].y = diver.children[0].y; // Right flipper // Set diver to a higher depth than flippers diver.depth = 2; // Create an obstacle1 instance game.move = function (x, y, obj) { if (dragNode) { if (dragNode !== shield) { shield.x += x - shield.x; shield.y += y - shield.y; } else { dragNode.x = x; dragNode.y = y; } } if (dragNode === shield) { shield.x = x; shield.y = y; } }; var obstacles = []; var spatialHash = {}; game.addChild(diver); game.setChildIndex(diver, game.children.length - 1); // Create power-up instance var powerUp = game.addChild(new PowerUp()); do { powerUp.x = Math.random() * 2048; } while (Math.abs(powerUp.x - diver.x) < 200); powerUp.y = -100; // Start offscreen at the top // Create bubbles after obstacles var bubbles = []; for (var i = 0; i < 20; i++) { var bubble = new Bubble(); bubble.x = Math.random() * 2048; bubble.y = Math.random() * 2732; bubbles.push(bubble); game.addChildAt(bubble, game.children.length); } // Spawn the first wave of obstacles spawnWave(); ; // Initialize score var score = LK.getScore(); // Create score text var depthScoreText = new Text2('Depth:0m', { size: 70, fill: "#ffffff", stroke: "#000000", strokeThickness: 6, font: "monospace" // Change font to be more square }); depthScoreText.anchor.set(0.5, 0.5); depthScoreText.x = 400; depthScoreText.y = 100; LK.gui.top.addChild(depthScoreText); // Initialize a timer to update the score every second and increase speed multiplier var scoreUpdateInterval = LK.setInterval(function () { LK.setScore(LK.getScore() + 1); if (depthScoreText) { depthScoreText.setText('Depth:' + LK.getScore() + 'm'); } speedMultiplier += 0.01; // Increase speed multiplier by 0.02 every second }, 1000); game.up = function (x, y, obj) { dragNode = null; }; // Check if background music is playing, if not, play it if (!backgroundMusic.playing) { backgroundMusic.play(); } // Update background background1.update(); // Update bubbles for (var i = 0; i < bubbles.length; i++) { bubbles[i].update(); } // Define the current wave var currentWave = 0; var allWavesSpawnedOnce = false; // Spawn wave function function spawnWave() { // Define the layout of the obstacles for each wave var obstacleLayouts = [{ type: 'line', grid: ['.....', '.....', '.#.#.', '.....', '.....'] }, { type: 'triangle', grid: ['.....', '..#..', '.....', '.#.#.', '.....'] }, { type: 'square', grid: ['.....', '.#.#.', '.....', '.#.#.', '.....'] }, { type: 'circle', grid: ['.....', '..#..', '.#.#.', '.#.#.', '..#..'] }, { type: 'cross', grid: ['.....', '..#..', '.#.#.', '..#..', '.....'] }, { type: 'hexagon', grid: ['.....', '..#..', '.#.#.', '.#.#.', '..#..'] }, { type: 'heptagon', grid: ['..#..', '.....', '#...#', '.....', '..#..'] }, { type: 'octagon', grid: ['.....', '#####', '.....', '.....', '.....'] }, { type: 'nonagon', grid: ['#....', '.#....', '..#..', '...#.', '....#'] }, { type: 'nonagon2', grid: ['....#', '...#.', '..#..', '.#...', '#....'] }, { type: 'gon2', grid: ['#.#.#', '......', '..#..', '.....', '#.#.#'] }, { type: 'gonn3', grid: ['#.#.#', '......', '.....', '.....', '#.#.#'] }, { type: 'decagon', grid: ['.....', '..#..', '.#.#.', '.#.#.', '..#..'] }]; // Get the layout for the current wave var layout = obstacleLayouts[currentWave]; if (!layout) { console.error('Error: layout is undefined. Check the obstacleLayouts array.'); return; } var layoutPositions = []; layout.grid.forEach(function (row, rowIndex) { row.split('').forEach(function (cell, colIndex) { if (cell === '#') { layoutPositions.push({ x: colIndex, y: rowIndex }); } }); }); // Randomly choose an obstacle type for the entire wave var obstacleTypes = [Obstacle1, Obstacle2, Obstacle3, Obstacle4, Obstacle5, Obstacle6]; var obstacleType = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)]; // Randomly choose one of the three starting x positions for the entire wave var startXPositions = [{ position: 2048 / 4.5, label: 'left' }, { position: 2048 / 1.08, label: 'right' }, { position: 2048 / 1.9, label: 'center' }]; var randomStart = startXPositions[Math.floor(Math.random() * startXPositions.length)]; var randomStartX = randomStart.position; wavePositionLabel = randomStart.label; console.log('Wave starting position:', wavePositionLabel); console.log('Wave starting position:', wavePositionLabel); // Generate a random spacing multiplier between 1 and 2 for the current wave var spacingMultiplier = 1 + Math.random(); layoutPositions.forEach(function (position) { var obstacle = game.addChild(new obstacleType()); // Position the obstacles according to the layout, using the grid for positioning obstacle.x = randomStartX + (position.x - gridSize / 2) * cellSize * spacingMultiplier; obstacle.y = 2732 + position.y * cellSize * spacingMultiplier; // Spawn obstacles offscreen at the bottom obstacle.waveSpeedX = wavePositionLabel === 'right' ? -1.5 : wavePositionLabel === 'left' ? 1.5 : 0; obstacles.push(obstacle); }); currentWave++; if (currentWave >= obstacleLayouts.length) { allWavesSpawnedOnce = true; currentWave = 0; } if (allWavesSpawnedOnce) { currentWave = Math.floor(Math.random() * (obstacleLayouts.length - 3)) + 3; } // Add interval of wait between waves, decreasing over time var waveInterval = Math.max(1000, 5000 - LK.getScore() * 10); // Minimum interval of 1 second LK.setTimeout(function () { spawnWave(); }, waveInterval); } // Define the grid size and the size of each cell in the grid var gridSize = 5; var cellSize = 150; // Call the spawn wave function every 1 second // Initial call to spawn the first wave spawnWave(); ;
/****
* Classes
****/
// Background class
var Background = Container.expand(function () {
var self = Container.call(this);
var backgroundGraphics = self.attachAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732
});
backgroundGraphics.anchor.set(0, 0);
// Add black overlay on top of the background
var overlay = self.attachAsset('shape', {
width: 2048,
height: 2732,
color: 0x000000,
alpha: 0.0,
shape: 'box'
});
overlay.anchor.set(0, 0);
// This is automatically called every game tick, if the background is attached!
self.update = function () {
if (overlay.alpha < 1) {
overlay.alpha += 0.0001; // Increase alpha over time
}
};
});
// Bubble class
var Bubble = Container.expand(function () {
var self = Container.call(this);
var bubbleGraphics = self.attachAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
var scale = Math.random() * 0.5 + 0.5;
bubbleGraphics.scale.set(scale, scale);
// Set bubble speed
self.speed = -3;
// Initialize colliding property
self.colliding = false;
// This is automatically called every game tick, if the bubble is attached!
self.update = function () {
// Move the bubble upwards and sideways
self.y += self.speed;
self.x += Math.sin(LK.ticks / 10) * 0.1;
// Reset the position of the bubble when it reaches the top
if (self.y <= 0) {
self.y = 2732;
}
// Check if the bubble is colliding with an obstacle using spatial hash
var cellSize = 150;
var hashX = Math.floor(self.x / cellSize);
var hashY = Math.floor(self.y / cellSize);
var hashKey = hashX + ',' + hashY;
if (spatialHash[hashKey]) {
for (var i = 0; i < spatialHash[hashKey].length; i++) {
var obstacle = spatialHash[hashKey][i];
if (self.intersects(obstacle)) {
// Bring the bubble to the top of the z axis
game.setChildIndex(self, game.children.length - 1);
self.colliding = true;
} else {
self.collidingWithShield = false;
self.colliding = false;
}
}
} else {
self.colliding = false;
}
};
});
// Diver class
var Diver = Container.expand(function () {
var self = Container.call(this);
var diverGraphics = self.attachAsset('diver', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
// Add an invisible hitbox with smaller dimensions
var hitbox = self.attachAsset('shape', {
width: 200,
// Smaller width
height: 260,
// Increased height by 30%
// Smaller height
color: 0x000000,
alpha: 0 // Invisible
});
// Add a sideways movement to the diver
self.movement = 0;
self.direction = 1;
self.update = function () {
self.movement += self.direction * 0.03;
if (self.movement > 0.5 || self.movement < -0.5) {
self.direction *= -1;
}
self.x += self.movement;
};
// Center the hitbox on the diver
hitbox.anchor.set(0.5, 0.5);
// Add flippers to the diver
var leftFlipper = self.addChild(new Flipper());
leftFlipper.x = -50;
leftFlipper.depth = -1;
var rightFlipper = self.addChild(new Flipper());
rightFlipper.x = 60;
rightFlipper.depth = -1;
});
// Flipper class
var Flipper = Container.expand(function () {
var self = Container.call(this);
var flipperGraphics = self.attachAsset('flippers', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.7,
scaleY: 0.7
});
// Set flipper movement
self.movement = 0;
self.direction = 1;
// This is automatically called every game tick, if the flipper is attached!
self.update = function () {
// Move the flipper subtly to simulate flipping
self.movement += self.direction * 0.03;
if (self.movement > 0.5 || self.movement < -0.5) {
self.direction *= -1;
}
self.rotation = self.movement;
};
});
// Obstacle1 class
var Obstacle1 = Container.expand(function () {
var self = Container.call(this);
var obstacle1Graphics = self.attachAsset('obstacle1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() < 0.5 ? 0.7 : -0.7,
// Randomly mirror horizontally
scaleY: 0.7
});
// Set obstacle1 speed
self.speed = -5;
self.originalSpeedX = self.speed;
// This is automatically called every game tick, if the obstacle1 is attached!
self.update = function () {
// Rotate obstacle1 on its axis
self.rotation += 0.05;
// Check if the obstacle is colliding with the shield
if (self.intersects(shield, {
tolerance: 10
})) {
if (!self.collidingWithShield) {
LK.getSound('bubble').play();
self.collidingWithShield = true;
}
// Calculate the direction vector between the shield and the obstacle
var dx = self.x - shield.x;
var dy = self.y - shield.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Push the obstacle away from the shield
self.x += dx * 5;
self.y += dy * 5;
} else {
self.collidingWithShield = false;
self.speed = self.originalSpeedX;
}
// Check for collision with diver
if (self.intersects(diver.children.find(function (child) {
return child.alpha === 0;
}), {
tolerance: 10
})) {
// Flash screen red for 1 second (1000ms) to show game over
LK.effects.flashScreen(0xff0000, 1000);
// Show game over. The game will be automatically paused while game over is showing.
LK.showGameOver();
}
// Update spatial hash
var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize);
var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize);
var newHashX = Math.floor(self.x / cellSize);
var newHashY = Math.floor(self.y / cellSize);
var oldHashKey = oldHashX + ',' + oldHashY;
var newHashKey = newHashX + ',' + newHashY;
if (oldHashKey !== newHashKey) {
if (spatialHash[oldHashKey]) {
var index = spatialHash[oldHashKey].indexOf(self);
if (index > -1) {
spatialHash[oldHashKey].splice(index, 1);
}
}
if (!spatialHash[newHashKey]) {
spatialHash[newHashKey] = [];
}
spatialHash[newHashKey].push(self);
}
// Continue moving upwards
self.x += self.waveSpeedX;
self.y += self.speed * speedMultiplier;
self.scale.x = 1 + Math.sin(LK.ticks / 10) * 0.02;
self.scale.y = 1 + Math.sin(LK.ticks / 10) * 0.02;
if (self.y < 0) {
self.destroy();
var index = obstacles.indexOf(self);
if (index > -1) {
obstacles.splice(index, 1);
}
}
// Check if the obstacle is colliding with another obstacle
for (var i = 0; i < obstacles.length; i++) {
var otherObstacle = obstacles[i];
if (self !== otherObstacle && self.intersects(otherObstacle)) {
// Calculate the direction vector between the two obstacles
var dx = self.x - otherObstacle.x;
var dy = self.y - otherObstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Increase the repulsion force and always push the obstacle to the outside
self.x += dx * 10;
self.y += dy * 10;
}
}
};
});
// Obstacle2 class
var Obstacle2 = Container.expand(function () {
var self = Container.call(this);
var obstacle2Graphics = self.attachAsset('obstacle2', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() < 0.5 ? 0.7 : -0.7,
// Randomly mirror horizontally
scaleY: 0.7
});
// Set obstacle2 speed
self.speed = -5;
self.originalSpeedX = self.speed;
// This is automatically called every game tick, if the obstacle2 is attached!
self.update = function () {
// Check if the obstacle is colliding with the shield
if (self.intersects(shield, {
tolerance: 10
})) {
if (!self.collidingWithShield) {
LK.getSound('bubble').play();
self.collidingWithShield = true;
}
// Calculate the direction vector between the shield and the obstacle
var dx = self.x - shield.x;
var dy = self.y - shield.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Push the obstacle away from the shield
self.x += dx * 5;
self.y += dy * 5;
} else {
self.collidingWithShield = false;
self.speed = self.originalSpeedX;
}
// Check for collision with diver
if (self.intersects(diver.children.find(function (child) {
return child.alpha === 0;
}), {
tolerance: 10
})) {
// Flash screen red for 1 second (1000ms) to show game over
LK.effects.flashScreen(0xff0000, 1000);
// Show game over. The game will be automatically paused while game over is showing.
LK.showGameOver();
}
// Update spatial hash
var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize);
var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize);
var newHashX = Math.floor(self.x / cellSize);
var newHashY = Math.floor(self.y / cellSize);
var oldHashKey = oldHashX + ',' + oldHashY;
var newHashKey = newHashX + ',' + newHashY;
if (oldHashKey !== newHashKey) {
if (spatialHash[oldHashKey]) {
var index = spatialHash[oldHashKey].indexOf(self);
if (index > -1) {
spatialHash[oldHashKey].splice(index, 1);
}
}
if (!spatialHash[newHashKey]) {
spatialHash[newHashKey] = [];
}
spatialHash[newHashKey].push(self);
}
// Continue moving upwards
self.x += self.waveSpeedX;
self.y += self.speed * speedMultiplier;
// Add wiggly movement to the obstacle
self.scale.x = 1 + Math.sin(LK.ticks / 10) * 0.02;
self.scale.y = 1 + Math.sin(LK.ticks / 10) * 0.02;
if (self.y < 0) {
self.destroy();
var index = obstacles.indexOf(self);
if (index > -1) {
obstacles.splice(index, 1);
}
}
// Check if the obstacle is colliding with another obstacle
for (var i = 0; i < obstacles.length; i++) {
var otherObstacle = obstacles[i];
if (self !== otherObstacle && self.intersects(otherObstacle)) {
// Calculate the direction vector between the two obstacles
var dx = self.x - otherObstacle.x;
var dy = self.y - otherObstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Increase the repulsion force and always push the obstacle to the outside
self.x += dx * 10;
self.y += dy * 10;
}
}
};
});
// Obstacle3 class
var Obstacle3 = Container.expand(function () {
var self = Container.call(this);
var obstacle3Graphics = self.attachAsset('obstacle3', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() < 0.5 ? 0.7 : -0.7,
// Randomly mirror horizontally
scaleY: 0.7
});
// Set obstacle3 speed
self.speed = -5;
self.originalSpeedX = self.speed;
// This is automatically called every game tick, if the obstacle3 is attached!
self.update = function () {
// Check if the obstacle is colliding with the shield
if (self.intersects(shield, {
tolerance: 10
})) {
if (!self.collidingWithShield) {
LK.getSound('bubble').play();
self.collidingWithShield = true;
}
// Calculate the direction vector between the shield and the obstacle
var dx = self.x - shield.x;
var dy = self.y - shield.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Push the obstacle away from the shield
self.x += dx * 5;
self.y += dy * 5;
} else {
self.collidingWithShield = false;
self.speed = self.originalSpeedX;
}
// Check for collision with diver
if (self.intersects(diver.children.find(function (child) {
return child.alpha === 0;
}), {
tolerance: 10
})) {
// Flash screen red for 1 second (1000ms) to show game over
LK.effects.flashScreen(0xff0000, 1000);
// Show game over. The game will be automatically paused while game over is showing.
LK.showGameOver();
}
// Update spatial hash
var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize);
var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize);
var newHashX = Math.floor(self.x / cellSize);
var newHashY = Math.floor(self.y / cellSize);
var oldHashKey = oldHashX + ',' + oldHashY;
var newHashKey = newHashX + ',' + newHashY;
if (oldHashKey !== newHashKey) {
if (spatialHash[oldHashKey]) {
var index = spatialHash[oldHashKey].indexOf(self);
if (index > -1) {
spatialHash[oldHashKey].splice(index, 1);
}
}
if (!spatialHash[newHashKey]) {
spatialHash[newHashKey] = [];
}
spatialHash[newHashKey].push(self);
}
// Continue moving upwards
self.x += self.waveSpeedX;
self.y += self.speed * speedMultiplier;
// Add slow spinning effect
self.rotation += 0.01;
// Add unique behavior to obstacle3
self.scale.x = 1 + Math.sin(LK.ticks / 5) * 0.05; // Larger size change
self.scale.y = 1 + Math.sin(LK.ticks / 5) * 0.05;
if (self.y < 0) {
self.destroy();
var index = obstacles.indexOf(self);
if (index > -1) {
obstacles.splice(index, 1);
}
}
// Check if the obstacle is colliding with another obstacle
for (var i = 0; i < obstacles.length; i++) {
var otherObstacle = obstacles[i];
if (self !== otherObstacle && self.intersects(otherObstacle)) {
// Calculate the direction vector between the two obstacles
var dx = self.x - otherObstacle.x;
var dy = self.y - otherObstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Increase the repulsion force and always push the obstacle to the outside
self.x += dx * 10;
self.y += dy * 10;
}
}
};
});
// Obstacle4 class
var Obstacle4 = Container.expand(function () {
var self = Container.call(this);
var obstacle4Graphics = self.attachAsset('obstacle4', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() < 0.5 ? 0.7 : -0.7,
// Randomly mirror horizontally
scaleY: 0.7
});
// Set obstacle4 speed
self.speed = -5;
self.originalSpeedX = self.speed;
// This is automatically called every game tick, if the obstacle4 is attached!
self.update = function () {
// Add up and down movement
self.y += Math.sin(LK.ticks / 20) * 2;
// Check if the obstacle is colliding with the shield
if (self.intersects(shield, {
tolerance: 10
})) {
if (!self.collidingWithShield) {
LK.getSound('bubble').play();
self.collidingWithShield = true;
}
// Calculate the direction vector between the shield and the obstacle
var dx = self.x - shield.x;
var dy = self.y - shield.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Push the obstacle away from the shield
self.x += dx * 5;
self.y += dy * 5;
} else {
self.collidingWithShield = false;
self.speed = self.originalSpeedX;
}
// Check for collision with diver
if (self.intersects(diver.children.find(function (child) {
return child.alpha === 0;
}), {
tolerance: 10
})) {
// Flash screen red for 1 second (1000ms) to show game over
LK.effects.flashScreen(0xff0000, 1000);
// Show game over. The game will be automatically paused while game over is showing.
LK.showGameOver();
}
// Update spatial hash
var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize);
var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize);
var newHashX = Math.floor(self.x / cellSize);
var newHashY = Math.floor(self.y / cellSize);
var oldHashKey = oldHashX + ',' + oldHashY;
var newHashKey = newHashX + ',' + newHashY;
if (oldHashKey !== newHashKey) {
if (spatialHash[oldHashKey]) {
var index = spatialHash[oldHashKey].indexOf(self);
if (index > -1) {
spatialHash[oldHashKey].splice(index, 1);
}
}
if (!spatialHash[newHashKey]) {
spatialHash[newHashKey] = [];
}
spatialHash[newHashKey].push(self);
}
// Continue moving upwards
self.x += self.waveSpeedX;
self.y += self.speed * speedMultiplier + Math.sin(LK.ticks / 20) * 2;
self.scale.x = 1 + Math.sin(LK.ticks / 15) * 0.03; // Different size change
self.scale.y = 1 + Math.sin(LK.ticks / 15) * 0.03;
if (self.y < 0) {
self.destroy();
var index = obstacles.indexOf(self);
if (index > -1) {
obstacles.splice(index, 1);
}
}
// Check if the obstacle is colliding with another obstacle
for (var i = 0; i < obstacles.length; i++) {
var otherObstacle = obstacles[i];
if (self !== otherObstacle && self.intersects(otherObstacle)) {
// Calculate the direction vector between the two obstacles
var dx = self.x - otherObstacle.x;
var dy = self.y - otherObstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Increase the repulsion force and always push the obstacle to the outside
self.x += dx * 10;
self.y += dy * 10;
}
}
};
});
// Obstacle5 class
var Obstacle5 = Container.expand(function () {
var self = Container.call(this);
var obstacle5Graphics = self.attachAsset('obstacle5', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() < 0.5 ? 0.7 : -0.7,
// Randomly mirror horizontally
scaleY: 0.7
});
// Set obstacle5 speed
self.speed = -5;
self.originalSpeedX = self.speed;
// This is automatically called every game tick, if the obstacle5 is attached!
self.update = function () {
// Add very small sideways movement
self.x += Math.sin(LK.ticks / 100) * 0.5;
// Check if the obstacle is colliding with the shield
if (self.intersects(shield, {
tolerance: 10
})) {
if (!self.collidingWithShield) {
LK.getSound('bubble').play();
self.collidingWithShield = true;
}
// Calculate the direction vector between the shield and the obstacle
var dx = self.x - shield.x;
var dy = self.y - shield.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Push the obstacle away from the shield
self.x += dx * 5;
self.y += dy * 5;
} else {
self.collidingWithShield = false;
self.speed = self.originalSpeedX;
}
// Check for collision with diver
if (self.intersects(diver.children.find(function (child) {
return child.alpha === 0;
}), {
tolerance: 10
})) {
// Flash screen red for 1 second (1000ms) to show game over
LK.effects.flashScreen(0xff0000, 1000);
// Show game over. The game will be automatically paused while game over is showing.
LK.showGameOver();
}
// Update spatial hash
var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize);
var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize);
var newHashX = Math.floor(self.x / cellSize);
var newHashY = Math.floor(self.y / cellSize);
var oldHashKey = oldHashX + ',' + oldHashY;
var newHashKey = newHashX + ',' + newHashY;
if (oldHashKey !== newHashKey) {
if (spatialHash[oldHashKey]) {
var index = spatialHash[oldHashKey].indexOf(self);
if (index > -1) {
spatialHash[oldHashKey].splice(index, 1);
}
}
if (!spatialHash[newHashKey]) {
spatialHash[newHashKey] = [];
}
spatialHash[newHashKey].push(self);
}
// Continue moving upwards
self.x += self.waveSpeedX;
self.y += self.speed * speedMultiplier;
// Add unique behavior to obstacle5
self.scale.x = 1 + Math.sin(LK.ticks / 20) * 0.1; // Increase size change amplitude
self.scale.y = 1 + Math.sin(LK.ticks / 20) * 0.1;
if (self.y < 0) {
self.destroy();
var index = obstacles.indexOf(self);
if (index > -1) {
obstacles.splice(index, 1);
}
}
// Check if the obstacle is colliding with another obstacle
for (var i = 0; i < obstacles.length; i++) {
var otherObstacle = obstacles[i];
if (self !== otherObstacle && self.intersects(otherObstacle)) {
// Calculate the direction vector between the two obstacles
var dx = self.x - otherObstacle.x;
var dy = self.y - otherObstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Increase the repulsion force and always push the obstacle to the outside
self.x += dx * 10;
self.y += dy * 10;
}
}
};
});
// Obstacle6 class
var Obstacle6 = Container.expand(function () {
var self = Container.call(this);
var obstacle6Graphics = self.attachAsset('obstacle6', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() < 0.5 ? 0.7 : -0.7,
// Randomly mirror horizontally
scaleY: 0.7
});
// Set obstacle6 speed
self.speed = -5;
self.originalSpeedX = self.speed;
// This is automatically called every game tick, if the obstacle6 is attached!
self.update = function () {
// Flickering effect
self.alpha = Math.random() * 0.5 + 0.5; // Random alpha between 0.5 and 1
// Fast side-to-side movement
self.x += Math.sin(LK.ticks / 10) * 1; // Further reduced horizontal movement
// Check if the obstacle is colliding with the shield
if (self.intersects(shield, {
tolerance: 10
})) {
if (!self.collidingWithShield) {
LK.getSound('bubble').play();
self.collidingWithShield = true;
}
// Calculate the direction vector between the shield and the obstacle
var dx = self.x - shield.x;
var dy = self.y - shield.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Push the obstacle away from the shield
self.x += dx * 5;
self.y += dy * 5;
} else {
self.collidingWithShield = false;
self.speed = self.originalSpeedX;
}
// Check for collision with diver
if (self.intersects(diver.children.find(function (child) {
return child.alpha === 0;
}), {
tolerance: 10
})) {
// Flash screen red for 1 second (1000ms) to show game over
LK.effects.flashScreen(0xff0000, 1000);
// Show game over. The game will be automatically paused while game over is showing.
LK.showGameOver();
}
// Update spatial hash
var oldHashX = Math.floor((self.x - self.speed * speedMultiplier) / cellSize);
var oldHashY = Math.floor((self.y - self.speed * speedMultiplier) / cellSize);
var newHashX = Math.floor(self.x / cellSize);
var newHashY = Math.floor(self.y / cellSize);
var oldHashKey = oldHashX + ',' + oldHashY;
var newHashKey = newHashX + ',' + newHashY;
if (oldHashKey !== newHashKey) {
if (spatialHash[oldHashKey]) {
var index = spatialHash[oldHashKey].indexOf(self);
if (index > -1) {
spatialHash[oldHashKey].splice(index, 1);
}
}
if (!spatialHash[newHashKey]) {
spatialHash[newHashKey] = [];
}
spatialHash[newHashKey].push(self);
}
// Continue moving upwards
self.x += self.waveSpeedX;
self.y += self.speed * speedMultiplier;
// Add unique behavior to obstacle6
self.scale.x = 1 + Math.sin(LK.ticks / 25) * 0.05; // Different size change
self.scale.y = 1 + Math.sin(LK.ticks / 25) * 0.05;
self.alpha = Math.random() * 0.5 + 0.5; // Random alpha between 0.5 and 1
self.x += Math.sin(LK.ticks / 5) * 10; // Fast horizontal movement
if (self.y < 0) {
self.destroy();
var index = obstacles.indexOf(self);
if (index > -1) {
obstacles.splice(index, 1);
}
}
// Check if the obstacle is colliding with another obstacle
for (var i = 0; i < obstacles.length; i++) {
var otherObstacle = obstacles[i];
if (self !== otherObstacle && self.intersects(otherObstacle)) {
// Calculate the direction vector between the two obstacles
var dx = self.x - otherObstacle.x;
var dy = self.y - otherObstacle.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Increase the repulsion force and always push the obstacle to the outside
self.x += dx * 10;
self.y += dy * 10;
}
}
};
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.y += 1.6; // Move power-up downwards (reduced by 20%)
self.x += Math.sin(LK.ticks / 20) * 2; // Add pendular movement
// Check if the power-up is colliding with the shield
if (self.intersects(shield, {
tolerance: 10
})) {
if (!self.collidingWithShield) {
LK.getSound('bubble').play();
self.collidingWithShield = true;
}
// Calculate the direction vector between the shield and the power-up
var dx = self.x - shield.x;
var dy = self.y - shield.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize the direction vector
dx /= distance;
dy /= distance;
// Push the power-up away from the shield
self.x += dx * 5;
self.y += dy * 5;
} else {
self.collidingWithShield = false;
}
if (self.intersects(diver)) {
LK.getSound('powerup').play();
shield.scale.x *= 2;
shield.scale.y *= 2;
LK.setTimeout(function () {
// Start blinking effect after 8 seconds
var blinkInterval = LK.setInterval(function () {
shield.alpha = shield.alpha === 1 ? 0.5 : 1;
if (shield.alpha === 0.5 && !self.shieldOffPlayed) {
LK.getSound('shieldoff').play();
self.shieldOffPlayed = true;
}
}, 60); // Blink every 500ms
// Stop blinking and reset shield size after 10 seconds
LK.setTimeout(function () {
LK.clearInterval(blinkInterval);
shield.alpha = 0.9; // Reset alpha to original value
shield.scale.x /= 2;
shield.scale.y /= 2;
self.shieldOffPlayed = false;
}, 2000); // Blink for 2 seconds
}, 8000); // Start blinking after 8 seconds
self.destroy();
}
};
});
// Shield class
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldGraphics = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.75
});
self.originalAlpha = shieldGraphics.alpha; // Store the original alpha value
self.down = function (x, y, obj) {
dragNode = shield;
};
});
/****
* Initialize Game
****/
// Create a diver instance
//<Assets used in the game will automatically appear here>
var game = new LK.Game({
backgroundColor: 0xADD8E6 //Init game with a softer blue background
});
/****
* Game Code
****/
var wavePositionLabel;
// Function to spawn power-ups every 20 seconds
function spawnPowerUp() {
if (!firstPowerUpSpawned) {
if (LK.ticks < 300) {
// 300 ticks = 5 seconds
return;
}
firstPowerUpSpawned = true;
} else if (LK.ticks - lastPowerUpSpawnTime < 1200) {
// 1200 ticks = 20 seconds
return;
}
var powerUp = game.addChild(new PowerUp());
lastPowerUpSpawnTime = LK.ticks;
do {
powerUp.x = Math.random() * 2048;
} while (diver && Math.abs(powerUp.x - diver.x) < 200);
powerUp.y = -100; // Start offscreen at the top
// Schedule the next power-up spawn
LK.setTimeout(spawnPowerUp, 20000); // 20 seconds
}
// Initial call to spawn the first power-up after 20 seconds
LK.setTimeout(spawnPowerUp, 20000);
var lastBubbleSoundTime = 0;
var lastPowerUpSpawnTime = 0;
var speedMultiplier = 1;
var waveInterval = 6000; // Initial interval of 5 seconds between waves
var firstPowerUpSpawned = false;
// Declare and initialize backgroundMusic variable
var backgroundMusic = LK.getSound('background');
// Play background music on loop when the game starts
LK.on('tick', function () {
if (!backgroundMusic.playing) {
backgroundMusic.play();
}
});
var background1 = game.addChild(new Background());
background1.y = 0;
// Create a shield instance
var shield = game.addChildAt(new Shield(), game.children.length);
// Position the shield at the center of the screen
shield.x = 2048 / 2;
shield.y = 2732 / 2;
// Create a diver instance
var dragNode = null;
var diver = new Diver();
diver.depth = 2;
// Position the diver at the top center of the screen, 200 pixels down from the top
diver.x = 2048 / 2;
diver.y = 500;
// Ensure diver's dimensions are fully initialized before positioning flippers
diver.on('added', function () {
// Position the flippers relative to the diver
diver.children[0].y = diver.height / 2 - 20; // Left flipper
diver.children[1].y = diver.height / 2 - 20; // Right flipper
});
// Position the flippers relative to the diver
diver.children[0].y = diver.height / 2 - 20; // Left flipper
diver.children[0].y = diver.children[0].y; // Right flipper
// Set diver to a higher depth than flippers
diver.depth = 2;
// Create an obstacle1 instance
game.move = function (x, y, obj) {
if (dragNode) {
if (dragNode !== shield) {
shield.x += x - shield.x;
shield.y += y - shield.y;
} else {
dragNode.x = x;
dragNode.y = y;
}
}
if (dragNode === shield) {
shield.x = x;
shield.y = y;
}
};
var obstacles = [];
var spatialHash = {};
game.addChild(diver);
game.setChildIndex(diver, game.children.length - 1);
// Create power-up instance
var powerUp = game.addChild(new PowerUp());
do {
powerUp.x = Math.random() * 2048;
} while (Math.abs(powerUp.x - diver.x) < 200);
powerUp.y = -100; // Start offscreen at the top
// Create bubbles after obstacles
var bubbles = [];
for (var i = 0; i < 20; i++) {
var bubble = new Bubble();
bubble.x = Math.random() * 2048;
bubble.y = Math.random() * 2732;
bubbles.push(bubble);
game.addChildAt(bubble, game.children.length);
}
// Spawn the first wave of obstacles
spawnWave();
;
// Initialize score
var score = LK.getScore();
// Create score text
var depthScoreText = new Text2('Depth:0m', {
size: 70,
fill: "#ffffff",
stroke: "#000000",
strokeThickness: 6,
font: "monospace" // Change font to be more square
});
depthScoreText.anchor.set(0.5, 0.5);
depthScoreText.x = 400;
depthScoreText.y = 100;
LK.gui.top.addChild(depthScoreText);
// Initialize a timer to update the score every second and increase speed multiplier
var scoreUpdateInterval = LK.setInterval(function () {
LK.setScore(LK.getScore() + 1);
if (depthScoreText) {
depthScoreText.setText('Depth:' + LK.getScore() + 'm');
}
speedMultiplier += 0.01; // Increase speed multiplier by 0.02 every second
}, 1000);
game.up = function (x, y, obj) {
dragNode = null;
};
// Check if background music is playing, if not, play it
if (!backgroundMusic.playing) {
backgroundMusic.play();
}
// Update background
background1.update();
// Update bubbles
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].update();
}
// Define the current wave
var currentWave = 0;
var allWavesSpawnedOnce = false;
// Spawn wave function
function spawnWave() {
// Define the layout of the obstacles for each wave
var obstacleLayouts = [{
type: 'line',
grid: ['.....', '.....', '.#.#.', '.....', '.....']
}, {
type: 'triangle',
grid: ['.....', '..#..', '.....', '.#.#.', '.....']
}, {
type: 'square',
grid: ['.....', '.#.#.', '.....', '.#.#.', '.....']
}, {
type: 'circle',
grid: ['.....', '..#..', '.#.#.', '.#.#.', '..#..']
}, {
type: 'cross',
grid: ['.....', '..#..', '.#.#.', '..#..', '.....']
}, {
type: 'hexagon',
grid: ['.....', '..#..', '.#.#.', '.#.#.', '..#..']
}, {
type: 'heptagon',
grid: ['..#..', '.....', '#...#', '.....', '..#..']
}, {
type: 'octagon',
grid: ['.....', '#####', '.....', '.....', '.....']
}, {
type: 'nonagon',
grid: ['#....', '.#....', '..#..', '...#.', '....#']
}, {
type: 'nonagon2',
grid: ['....#', '...#.', '..#..', '.#...', '#....']
}, {
type: 'gon2',
grid: ['#.#.#', '......', '..#..', '.....', '#.#.#']
}, {
type: 'gonn3',
grid: ['#.#.#', '......', '.....', '.....', '#.#.#']
}, {
type: 'decagon',
grid: ['.....', '..#..', '.#.#.', '.#.#.', '..#..']
}];
// Get the layout for the current wave
var layout = obstacleLayouts[currentWave];
if (!layout) {
console.error('Error: layout is undefined. Check the obstacleLayouts array.');
return;
}
var layoutPositions = [];
layout.grid.forEach(function (row, rowIndex) {
row.split('').forEach(function (cell, colIndex) {
if (cell === '#') {
layoutPositions.push({
x: colIndex,
y: rowIndex
});
}
});
});
// Randomly choose an obstacle type for the entire wave
var obstacleTypes = [Obstacle1, Obstacle2, Obstacle3, Obstacle4, Obstacle5, Obstacle6];
var obstacleType = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
// Randomly choose one of the three starting x positions for the entire wave
var startXPositions = [{
position: 2048 / 4.5,
label: 'left'
}, {
position: 2048 / 1.08,
label: 'right'
}, {
position: 2048 / 1.9,
label: 'center'
}];
var randomStart = startXPositions[Math.floor(Math.random() * startXPositions.length)];
var randomStartX = randomStart.position;
wavePositionLabel = randomStart.label;
console.log('Wave starting position:', wavePositionLabel);
console.log('Wave starting position:', wavePositionLabel);
// Generate a random spacing multiplier between 1 and 2 for the current wave
var spacingMultiplier = 1 + Math.random();
layoutPositions.forEach(function (position) {
var obstacle = game.addChild(new obstacleType());
// Position the obstacles according to the layout, using the grid for positioning
obstacle.x = randomStartX + (position.x - gridSize / 2) * cellSize * spacingMultiplier;
obstacle.y = 2732 + position.y * cellSize * spacingMultiplier; // Spawn obstacles offscreen at the bottom
obstacle.waveSpeedX = wavePositionLabel === 'right' ? -1.5 : wavePositionLabel === 'left' ? 1.5 : 0;
obstacles.push(obstacle);
});
currentWave++;
if (currentWave >= obstacleLayouts.length) {
allWavesSpawnedOnce = true;
currentWave = 0;
}
if (allWavesSpawnedOnce) {
currentWave = Math.floor(Math.random() * (obstacleLayouts.length - 3)) + 3;
}
// Add interval of wait between waves, decreasing over time
var waveInterval = Math.max(1000, 5000 - LK.getScore() * 10); // Minimum interval of 1 second
LK.setTimeout(function () {
spawnWave();
}, waveInterval);
}
// Define the grid size and the size of each cell in the grid
var gridSize = 5;
var cellSize = 150;
// Call the spawn wave function every 1 second
// Initial call to spawn the first wave
spawnWave();
;
8bit. cartoon. jellyfish.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
empty 8 bit cartoon white circle. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
cartoon. 8-bit. octopus. colorful.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
cartoon. 8-bit. sea urchin. colorful. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
cartoon 8bit stingray. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.