Code edit (4 edits merged)
Please save this source code
User prompt
allow to define another x position for a wave spawn where the obstacles of that wave still keep the current shapw
User prompt
but make sure all obstacles of a wave keep their same random x positiion
User prompt
obstacle waves should spawn from different random x positions from outside the bottom of the screen
User prompt
have obstacles waves spawn in the center or either side of the bottom of the screen
User prompt
have obstacles waves move from one conrer of the screen to the other
User prompt
waves of enemys direction should be towards the dierr
User prompt
obstacle initial direction should be towards diver
Code edit (1 edits merged)
Please save this source code
User prompt
obstacles direction should be towards the player
Code edit (1 edits merged)
Please save this source code
User prompt
obstacle.x should be the same for the obstacles shape
User prompt
make obstacle.x position random to any position of the bottom of the screen
Code edit (15 edits merged)
Please save this source code
User prompt
Update spawnwave to starts waves from the bottom right or bottom left and move in obliquos direcio towards the player
User prompt
Spawm waves from the bottom roght corner an iniitial directio should be the divers location
User prompt
Enemies waves can also spanw from the bottom corners and move towars the player
User prompt
Enemies can spawn also from.the bottom corners. It should be random
User prompt
add a collisionbox to the diver
User prompt
reduce downward speed of power ups 20%
User prompt
make sure flippers are attached to player
User prompt
add a collisionbox in the player
Code edit (1 edits merged)
Please save this source code
Code edit (12 edits merged)
Please save this source code
User prompt
move flippers 100 pixels up
/**** 
* 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 = -2;
	// 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++) {
				if (self.intersects(spatialHash[hashKey][i])) {
					// Bring the bubble to the top of the z axis
					game.setChildIndex(self, game.children.length - 1);
				}
			}
		}
	};
});
// 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 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;
	};
	// 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: 0.7,
		scaleY: 0.7
	});
	// Set obstacle1 speed
	self.speed = -5;
	// This is automatically called every game tick, if the obstacle1 is attached!
	self.update = function () {
		// Check if the obstacle is colliding with the shield
		if (self.intersects(shield, {
			tolerance: 10
		})) {
			// Play bubble sound if at least 1 second has passed since the last play
			if (LK.ticks - lastBubbleSoundTime > 20) {
				LK.getSound('bubble').play();
				lastBubbleSoundTime = LK.ticks;
			}
			// 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;
		}
		// Check for collision with diver
		if (self.intersects(diver, {
			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.y += self.speed * speedMultiplier;
		self.rotation += 0.01; // Add small rotation to the obstacle
		// Add size change 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;
			}
		}
	};
});
// Obstacle2 class
var Obstacle2 = Container.expand(function () {
	var self = Container.call(this);
	var obstacle2Graphics = self.attachAsset('obstacle2', {
		anchorX: 0.5,
		anchorY: 0.5,
		scaleX: 0.7,
		scaleY: 0.7
	});
	// Set obstacle2 speed
	self.speed = -5;
	// 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
		})) {
			// Play bubble sound if at least 1 second has passed since the last play
			if (LK.ticks - lastBubbleSoundTime > 20) {
				LK.getSound('bubble').play();
				lastBubbleSoundTime = LK.ticks;
			}
			// 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;
		}
		// Check for collision with diver
		if (self.intersects(diver, {
			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.y += self.speed * speedMultiplier;
		// Add wiggly movement to the obstacle
		self.x += Math.sin(LK.ticks / 10) * 2;
		// Add size change 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: 0.7,
		scaleY: 0.7
	});
	// Set obstacle3 speed
	self.speed = -5;
	// 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
		})) {
			// Play bubble sound if at least 1 second has passed since the last play
			if (LK.ticks - lastBubbleSoundTime > 20) {
				LK.getSound('bubble').play();
				lastBubbleSoundTime = LK.ticks;
			}
			// 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;
		}
		// Check for collision with diver
		if (self.intersects(diver, {
			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.y += self.speed * speedMultiplier;
		// Add unique behavior to obstacle3
		self.rotation += 0.05; // Rotate faster than obstacle1
		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;
			}
		}
	};
});
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
		})) {
			// 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;
		}
		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;
				}, 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;
				}, 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
****/ 
// 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 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: '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];
	var obstacleType = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
	// Generate a single random x position for the entire wave
	var waveXPosition = Math.random() * 2048;
	layoutPositions.forEach(function (position) {
		var obstacle = game.addChild(new obstacleType());
		// Position the obstacles according to the layout, using the grid for positioning
		obstacle.x = waveXPosition; // Use the same random x position for all obstacles in the wave
		obstacle.y = 2732 + position.y * cellSize; // Spawn obstacles offscreen at the bottom
		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
	LK.setTimeout(function () {
		spawnWave();
	}, 5000); // 5 seconds wait between waves
}
// 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();
; ===================================================================
--- original.js
+++ change.js
@@ -649,12 +649,14 @@
 	});
 	// Randomly choose an obstacle type for the entire wave
 	var obstacleTypes = [Obstacle1, Obstacle2, Obstacle3];
 	var obstacleType = obstacleTypes[Math.floor(Math.random() * obstacleTypes.length)];
+	// Generate a single random x position for the entire wave
+	var waveXPosition = Math.random() * 2048;
 	layoutPositions.forEach(function (position) {
 		var obstacle = game.addChild(new obstacleType());
 		// Position the obstacles according to the layout, using the grid for positioning
-		obstacle.x = Math.random() * 2048; // Random x position
+		obstacle.x = waveXPosition; // Use the same random x position for all obstacles in the wave
 		obstacle.y = 2732 + position.y * cellSize; // Spawn obstacles offscreen at the bottom
 		obstacles.push(obstacle);
 	});
 	currentWave++;
:quality(85)/https://cdn.frvr.ai/668bb01272715dcfc88d88a4.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/668d055672715dcfc88d89a5.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/668d0d5172715dcfc88d89c9.png%3F3) 
 8bit. cartoon. jellyfish.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
:quality(85)/https://cdn.frvr.ai/668d0ea572715dcfc88d89f0.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/668d176772715dcfc88d8a95.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/668d2aadf389b5a8c22af7d1.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/668d2c2af389b5a8c22af7f0.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/668d2ef0f389b5a8c22af80a.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/6690e1c5b7c8eed252dc5cd7.png%3F3) 
 empty 8 bit cartoon white circle. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
:quality(85)/https://cdn.frvr.ai/6694d71bc5225bf8be2530af.png%3F3) 
 cartoon. 8-bit. octopus. colorful.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
:quality(85)/https://cdn.frvr.ai/6694d840c5225bf8be2530c0.png%3F3) 
 cartoon. 8-bit. sea urchin. colorful. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
:quality(85)/https://cdn.frvr.ai/6694df05c5225bf8be2530ea.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/66b5f62bfbc2197754e6a900.png%3F3) 
 cartoon 8bit stingray. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
:quality(85)/https://cdn.frvr.ai/66b5fbf1fbc2197754e6a948.png%3F3)