/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
var Bubble = Container.expand(function () {
	var self = Container.call(this);
	var bubbleGraphics = self.attachAsset('bubble', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = -5;
	self.collected = false;
	// Add a small bobbing motion to the bubble
	self.bobSpeed = 0.5 + Math.random() * 0.5;
	self.bobAmount = 10;
	self.bobOffset = Math.random() * Math.PI * 2;
	self.startY = 0;
	self.update = function () {
		self.x += self.speed;
		// Bob up and down
		if (self.startY) {
			self.y = self.startY + Math.sin(LK.ticks * 0.02 * self.bobSpeed + self.bobOffset) * self.bobAmount;
		} else {
			self.startY = self.y;
		}
		// Remove when off screen
		if (self.x < -50) {
			self.destroy();
			var index = game.bubbles.indexOf(self);
			if (index !== -1) {
				game.bubbles.splice(index, 1);
			}
		}
	};
	self.collect = function () {
		if (self.collected) {
			return;
		}
		self.collected = true;
		// Play collect sound
		LK.getSound('collect').play();
		// Add score
		game.score += 5;
		scoreTxt.setText(game.score.toString());
		// Show visual effect
		LK.effects.flashObject(self, 0xffffff, 200);
		// Animate bubble scaling up and fading out
		tween(self, {
			scaleX: 2,
			scaleY: 2,
			alpha: 0
		}, {
			duration: 300,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				self.destroy();
				var index = game.bubbles.indexOf(self);
				if (index !== -1) {
					game.bubbles.splice(index, 1);
				}
			}
		});
	};
	return self;
});
var Coral = Container.expand(function () {
	var self = Container.call(this);
	var coralGraphics = self.attachAsset('coral', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = -5;
	self.passed = false;
	self.update = function () {
		self.x += self.speed;
		// Mark as passed for scoring
		if (!self.passed && self.x < 600) {
			self.passed = true;
			if (!game.gameOver) {
				game.score++;
				scoreTxt.setText(game.score.toString());
			}
		}
		// Remove when off screen
		if (self.x < -100) {
			self.destroy();
			var index = game.obstacles.indexOf(self);
			if (index !== -1) {
				game.obstacles.splice(index, 1);
			}
		}
	};
	return self;
});
var Fish = Container.expand(function () {
	var self = Container.call(this);
	var fishGraphics = self.attachAsset('fish', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocity = 0;
	self.gravity = 0.4; // Reduced gravity for smoother falling
	self.jumpPower = -8; // Lower jump power for more controlled swimming
	self.rotation = 0;
	self.isDead = false;
	self.lastY = 0; // Track last position for collision detection
	self.jump = function () {
		if (self.isDead) {
			return;
		}
		// Apply jump/swim impulse
		self.velocity = self.jumpPower;
		// Play swimming sound
		LK.getSound('swim').play();
		// Add a slight upward animation using tween
		tween(fishGraphics, {
			scaleX: 1.2,
			scaleY: 0.8
		}, {
			duration: 100,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(fishGraphics, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200,
					easing: tween.easeOut
				});
			}
		});
	};
	self.update = function () {
		if (self.isDead) {
			return;
		}
		// Track last position for collision detection
		self.lastY = self.y;
		// Apply physics
		self.velocity += self.gravity;
		self.y += self.velocity;
		// Add water resistance (makes movement more fish-like)
		self.velocity *= 0.95;
		// Rotate fish based on velocity - smoother rotation for swimming feel
		var targetRotation = Math.min(Math.max(self.velocity * 0.04, -0.5), 0.5);
		self.rotation += (targetRotation - self.rotation) * 0.2;
		fishGraphics.rotation = self.rotation;
		// Keep fish within bounds
		if (self.y < 50) {
			self.y = 50;
			self.velocity = 0;
		}
		if (self.y > 2732 - 50) {
			self.y = 2732 - 50;
			self.velocity = 0;
			self.die();
		}
	};
	self.die = function () {
		if (self.isDead) {
			return;
		}
		self.isDead = true;
		LK.getSound('hit').play();
		LK.effects.flashObject(self, 0xff0000, 500);
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x0066aa
});
/**** 
* Game Code
****/ 
// Create background
var background = game.addChild(LK.getAsset('background', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0
}));
// Game variables
game.speed = 5;
game.score = 0;
game.gameOver = false;
game.obstacles = [];
game.bubbles = [];
game.difficulty = 1;
game.lastObstacleTime = 0;
game.lastBubbleTime = 0;
// Create score text
var scoreTxt = new Text2('0', {
	size: 100,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50;
// Create instructions text (disappears after game starts)
var instructionsTxt = new Text2('Tap to make the fish swim!', {
	size: 60,
	fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionsTxt);
// Create fish
var fish = new Fish();
fish.x = 400;
fish.y = 2732 / 2;
game.addChild(fish);
// Game has started flag
var gameStarted = false;
// Function to start game
function startGame() {
	if (gameStarted) {
		return;
	}
	gameStarted = true;
	instructionsTxt.visible = false;
	// Start background music
	LK.playMusic('underwater');
}
// Function to spawn a new coral obstacle
function spawnObstacle() {
	// Calculate gap size based on difficulty
	var gapSize = 500 - game.difficulty * 30;
	gapSize = Math.max(gapSize, 200); // Minimum gap size
	// Calculate random position for the gap
	var gapPosition = 300 + Math.random() * (2732 - 600);
	// Create top coral
	var topCoral = new Coral();
	topCoral.x = 2200;
	topCoral.y = gapPosition - gapSize / 2;
	topCoral.scaleY = (gapPosition - gapSize / 2) / 250; // Scale to reach from top to gap
	game.addChild(topCoral);
	game.obstacles.push(topCoral);
	// Create bottom coral
	var bottomCoral = new Coral();
	bottomCoral.x = 2200;
	bottomCoral.y = gapPosition + gapSize / 2 + 250;
	bottomCoral.scaleY = (2732 - (gapPosition + gapSize / 2)) / 250; // Scale to reach from gap to bottom
	game.addChild(bottomCoral);
	game.obstacles.push(bottomCoral);
	// Increase difficulty as game progresses
	game.difficulty += 0.1;
	// Increase speed slightly
	game.speed = Math.min(game.speed + 0.1, 12);
	// Update all obstacle speeds
	for (var i = 0; i < game.obstacles.length; i++) {
		game.obstacles[i].speed = -game.speed;
	}
	// Update all bubble speeds
	for (var i = 0; i < game.bubbles.length; i++) {
		game.bubbles[i].speed = -game.speed;
	}
}
// Function to spawn a bubble
function spawnBubble() {
	var bubble = new Bubble();
	bubble.x = 2200;
	bubble.y = 300 + Math.random() * (2732 - 600);
	bubble.speed = -game.speed;
	game.addChild(bubble);
	game.bubbles.push(bubble);
}
// Check for collisions
function checkCollisions() {
	if (fish.isDead) {
		return;
	}
	// Check collision with obstacles
	for (var i = 0; i < game.obstacles.length; i++) {
		if (fish.intersects(game.obstacles[i])) {
			fish.die();
			gameOver();
			return;
		}
	}
	// Check collision with bubbles
	for (var i = game.bubbles.length - 1; i >= 0; i--) {
		if (!game.bubbles[i].collected && fish.intersects(game.bubbles[i])) {
			game.bubbles[i].collect();
		}
	}
}
// Game over function
function gameOver() {
	game.gameOver = true;
	// Save high score
	var highScore = storage.highScore || 0;
	if (game.score > highScore) {
		storage.highScore = game.score;
	}
	// Show game over screen
	LK.setTimeout(function () {
		LK.showGameOver();
	}, 1000);
}
// Track if player is holding down for continuous swimming
var isHolding = false;
var holdTimer = 0;
var lastJumpTime = 0;
// Handle input down
game.down = function (x, y, obj) {
	if (!gameStarted) {
		startGame();
	}
	fish.jump();
	isHolding = true;
	lastJumpTime = LK.ticks;
};
// Handle input up
game.up = function (x, y, obj) {
	isHolding = false;
};
// Game update function
game.update = function () {
	if (!gameStarted) {
		return;
	}
	// Handle continuous swimming when holding
	if (isHolding && !fish.isDead) {
		holdTimer++;
		// Make the fish swim every 15 ticks while holding
		if (holdTimer >= 15) {
			fish.jump();
			holdTimer = 0;
		}
	} else {
		holdTimer = 0;
	}
	// Spawn obstacles
	if (LK.ticks - game.lastObstacleTime > 120) {
		game.lastObstacleTime = LK.ticks;
		spawnObstacle();
	}
	// Spawn bubbles
	if (LK.ticks - game.lastBubbleTime > 60) {
		game.lastBubbleTime = LK.ticks;
		if (Math.random() < 0.3) {
			// 30% chance to spawn a bubble
			spawnBubble();
		}
	}
	// Check collisions
	checkCollisions();
}; /**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/**** 
* Classes
****/ 
var Bubble = Container.expand(function () {
	var self = Container.call(this);
	var bubbleGraphics = self.attachAsset('bubble', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = -5;
	self.collected = false;
	// Add a small bobbing motion to the bubble
	self.bobSpeed = 0.5 + Math.random() * 0.5;
	self.bobAmount = 10;
	self.bobOffset = Math.random() * Math.PI * 2;
	self.startY = 0;
	self.update = function () {
		self.x += self.speed;
		// Bob up and down
		if (self.startY) {
			self.y = self.startY + Math.sin(LK.ticks * 0.02 * self.bobSpeed + self.bobOffset) * self.bobAmount;
		} else {
			self.startY = self.y;
		}
		// Remove when off screen
		if (self.x < -50) {
			self.destroy();
			var index = game.bubbles.indexOf(self);
			if (index !== -1) {
				game.bubbles.splice(index, 1);
			}
		}
	};
	self.collect = function () {
		if (self.collected) {
			return;
		}
		self.collected = true;
		// Play collect sound
		LK.getSound('collect').play();
		// Add score
		game.score += 5;
		scoreTxt.setText(game.score.toString());
		// Show visual effect
		LK.effects.flashObject(self, 0xffffff, 200);
		// Animate bubble scaling up and fading out
		tween(self, {
			scaleX: 2,
			scaleY: 2,
			alpha: 0
		}, {
			duration: 300,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				self.destroy();
				var index = game.bubbles.indexOf(self);
				if (index !== -1) {
					game.bubbles.splice(index, 1);
				}
			}
		});
	};
	return self;
});
var Coral = Container.expand(function () {
	var self = Container.call(this);
	var coralGraphics = self.attachAsset('coral', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.speed = -5;
	self.passed = false;
	self.update = function () {
		self.x += self.speed;
		// Mark as passed for scoring
		if (!self.passed && self.x < 600) {
			self.passed = true;
			if (!game.gameOver) {
				game.score++;
				scoreTxt.setText(game.score.toString());
			}
		}
		// Remove when off screen
		if (self.x < -100) {
			self.destroy();
			var index = game.obstacles.indexOf(self);
			if (index !== -1) {
				game.obstacles.splice(index, 1);
			}
		}
	};
	return self;
});
var Fish = Container.expand(function () {
	var self = Container.call(this);
	var fishGraphics = self.attachAsset('fish', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	self.velocity = 0;
	self.gravity = 0.4; // Reduced gravity for smoother falling
	self.jumpPower = -8; // Lower jump power for more controlled swimming
	self.rotation = 0;
	self.isDead = false;
	self.lastY = 0; // Track last position for collision detection
	self.jump = function () {
		if (self.isDead) {
			return;
		}
		// Apply jump/swim impulse
		self.velocity = self.jumpPower;
		// Play swimming sound
		LK.getSound('swim').play();
		// Add a slight upward animation using tween
		tween(fishGraphics, {
			scaleX: 1.2,
			scaleY: 0.8
		}, {
			duration: 100,
			easing: tween.easeOut,
			onFinish: function onFinish() {
				tween(fishGraphics, {
					scaleX: 1,
					scaleY: 1
				}, {
					duration: 200,
					easing: tween.easeOut
				});
			}
		});
	};
	self.update = function () {
		if (self.isDead) {
			return;
		}
		// Track last position for collision detection
		self.lastY = self.y;
		// Apply physics
		self.velocity += self.gravity;
		self.y += self.velocity;
		// Add water resistance (makes movement more fish-like)
		self.velocity *= 0.95;
		// Rotate fish based on velocity - smoother rotation for swimming feel
		var targetRotation = Math.min(Math.max(self.velocity * 0.04, -0.5), 0.5);
		self.rotation += (targetRotation - self.rotation) * 0.2;
		fishGraphics.rotation = self.rotation;
		// Keep fish within bounds
		if (self.y < 50) {
			self.y = 50;
			self.velocity = 0;
		}
		if (self.y > 2732 - 50) {
			self.y = 2732 - 50;
			self.velocity = 0;
			self.die();
		}
	};
	self.die = function () {
		if (self.isDead) {
			return;
		}
		self.isDead = true;
		LK.getSound('hit').play();
		LK.effects.flashObject(self, 0xff0000, 500);
	};
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x0066aa
});
/**** 
* Game Code
****/ 
// Create background
var background = game.addChild(LK.getAsset('background', {
	anchorX: 0,
	anchorY: 0,
	x: 0,
	y: 0
}));
// Game variables
game.speed = 5;
game.score = 0;
game.gameOver = false;
game.obstacles = [];
game.bubbles = [];
game.difficulty = 1;
game.lastObstacleTime = 0;
game.lastBubbleTime = 0;
// Create score text
var scoreTxt = new Text2('0', {
	size: 100,
	fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50;
// Create instructions text (disappears after game starts)
var instructionsTxt = new Text2('Tap to make the fish swim!', {
	size: 60,
	fill: 0xFFFFFF
});
instructionsTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionsTxt);
// Create fish
var fish = new Fish();
fish.x = 400;
fish.y = 2732 / 2;
game.addChild(fish);
// Game has started flag
var gameStarted = false;
// Function to start game
function startGame() {
	if (gameStarted) {
		return;
	}
	gameStarted = true;
	instructionsTxt.visible = false;
	// Start background music
	LK.playMusic('underwater');
}
// Function to spawn a new coral obstacle
function spawnObstacle() {
	// Calculate gap size based on difficulty
	var gapSize = 500 - game.difficulty * 30;
	gapSize = Math.max(gapSize, 200); // Minimum gap size
	// Calculate random position for the gap
	var gapPosition = 300 + Math.random() * (2732 - 600);
	// Create top coral
	var topCoral = new Coral();
	topCoral.x = 2200;
	topCoral.y = gapPosition - gapSize / 2;
	topCoral.scaleY = (gapPosition - gapSize / 2) / 250; // Scale to reach from top to gap
	game.addChild(topCoral);
	game.obstacles.push(topCoral);
	// Create bottom coral
	var bottomCoral = new Coral();
	bottomCoral.x = 2200;
	bottomCoral.y = gapPosition + gapSize / 2 + 250;
	bottomCoral.scaleY = (2732 - (gapPosition + gapSize / 2)) / 250; // Scale to reach from gap to bottom
	game.addChild(bottomCoral);
	game.obstacles.push(bottomCoral);
	// Increase difficulty as game progresses
	game.difficulty += 0.1;
	// Increase speed slightly
	game.speed = Math.min(game.speed + 0.1, 12);
	// Update all obstacle speeds
	for (var i = 0; i < game.obstacles.length; i++) {
		game.obstacles[i].speed = -game.speed;
	}
	// Update all bubble speeds
	for (var i = 0; i < game.bubbles.length; i++) {
		game.bubbles[i].speed = -game.speed;
	}
}
// Function to spawn a bubble
function spawnBubble() {
	var bubble = new Bubble();
	bubble.x = 2200;
	bubble.y = 300 + Math.random() * (2732 - 600);
	bubble.speed = -game.speed;
	game.addChild(bubble);
	game.bubbles.push(bubble);
}
// Check for collisions
function checkCollisions() {
	if (fish.isDead) {
		return;
	}
	// Check collision with obstacles
	for (var i = 0; i < game.obstacles.length; i++) {
		if (fish.intersects(game.obstacles[i])) {
			fish.die();
			gameOver();
			return;
		}
	}
	// Check collision with bubbles
	for (var i = game.bubbles.length - 1; i >= 0; i--) {
		if (!game.bubbles[i].collected && fish.intersects(game.bubbles[i])) {
			game.bubbles[i].collect();
		}
	}
}
// Game over function
function gameOver() {
	game.gameOver = true;
	// Save high score
	var highScore = storage.highScore || 0;
	if (game.score > highScore) {
		storage.highScore = game.score;
	}
	// Show game over screen
	LK.setTimeout(function () {
		LK.showGameOver();
	}, 1000);
}
// Track if player is holding down for continuous swimming
var isHolding = false;
var holdTimer = 0;
var lastJumpTime = 0;
// Handle input down
game.down = function (x, y, obj) {
	if (!gameStarted) {
		startGame();
	}
	fish.jump();
	isHolding = true;
	lastJumpTime = LK.ticks;
};
// Handle input up
game.up = function (x, y, obj) {
	isHolding = false;
};
// Game update function
game.update = function () {
	if (!gameStarted) {
		return;
	}
	// Handle continuous swimming when holding
	if (isHolding && !fish.isDead) {
		holdTimer++;
		// Make the fish swim every 15 ticks while holding
		if (holdTimer >= 15) {
			fish.jump();
			holdTimer = 0;
		}
	} else {
		holdTimer = 0;
	}
	// Spawn obstacles
	if (LK.ticks - game.lastObstacleTime > 120) {
		game.lastObstacleTime = LK.ticks;
		spawnObstacle();
	}
	// Spawn bubbles
	if (LK.ticks - game.lastBubbleTime > 60) {
		game.lastBubbleTime = LK.ticks;
		if (Math.random() < 0.3) {
			// 30% chance to spawn a bubble
			spawnBubble();
		}
	}
	// Check collisions
	checkCollisions();
};
 A bubble. Single Game Texture. In-Game asset. No shadows
 Blue coral. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows. Awesome. Cool
 Water. Single Game Texture. In-Game asset. 2d. High contrast. No shadows. Water
 Floppy Fish. Single Game Texture. In-Game asset. Blank background. High contrast. No shadows. 2d