/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boat = Container.expand(function () {
var self = Container.call(this);
var boatGraphics = self.attachAsset('boat', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.isCasting = false;
self.lineLength = 0;
self.maxLineLength = 2400; // Increased to reach bottom of screen (screen height is 2732, boat is at y=300)
self.lineSpeed = 10; // Increased from 6 to 10 for even faster fishing line movement
self.isExploding = false;
self.explodeScale = 1;
self.velocityX = 0; // Current horizontal velocity for smooth movement
self.maxVelocity = 30; // Maximum velocity
self.friction = 0.92; // Friction applied to slow down movement
self.targetX = null; // Target position to move toward
self.swipeStartTime = 0; // Track when swipe started for velocity calculation
var fishingLine = self.attachAsset('fishingLine', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 50,
visible: false,
height: 0
});
var fishHook = self.attachAsset('fishHook', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 50,
visible: false
});
self.castLine = function () {
if (!self.isCasting) {
self.isCasting = true;
fishingLine.visible = true;
fishHook.visible = true;
LK.getSound('cast').play();
}
};
self.reelIn = function () {
self.isCasting = false;
};
self.update = function () {
if (self.isCasting && self.lineLength < self.maxLineLength) {
self.lineLength += self.lineSpeed;
fishingLine.height = self.lineLength;
fishHook.y = 50 + self.lineLength;
} else if (!self.isCasting && self.lineLength > 0) {
self.lineLength -= self.lineSpeed * 2;
fishingLine.height = self.lineLength;
fishHook.y = 50 + self.lineLength;
}
if (self.lineLength <= 0 && !self.isCasting) {
fishingLine.visible = false;
fishHook.visible = false;
self.lineLength = 0;
}
// Smooth boat movement physics
if (self.targetX !== null) {
// Calculate distance to target
var distanceToTarget = self.targetX - self.x;
// Apply force based on distance
self.velocityX += distanceToTarget * 0.04;
// Limit maximum velocity
if (self.velocityX > self.maxVelocity) self.velocityX = self.maxVelocity;
if (self.velocityX < -self.maxVelocity) self.velocityX = -self.maxVelocity;
// If very close to target and moving slowly, just snap to position
if (Math.abs(distanceToTarget) < 1 && Math.abs(self.velocityX) < 0.5) {
self.x = self.targetX;
self.velocityX = 0;
self.targetX = null;
}
}
// Apply velocity with friction
if (Math.abs(self.velocityX) > 0.1) {
self.x += self.velocityX;
self.velocityX *= self.friction;
// Keep boat within bounds
if (self.x < 100) {
self.x = 100;
self.velocityX = 0;
} else if (self.x > 2048 - 100) {
self.x = 2048 - 100;
self.velocityX = 0;
}
} else {
self.velocityX = 0;
}
};
self.getHookPosition = function () {
return {
x: self.x,
y: self.y + 50 + self.lineLength
};
};
self.lastHookPosition = self.getHookPosition();
self.explode = function () {
if (self.isExploding) return;
self.isExploding = true;
boatGraphics.tint = 0xFF3300; // Fire color
function animateExplosion() {
self.explodeScale += 0.2;
boatGraphics.scale.set(self.explodeScale);
boatGraphics.alpha -= 0.1;
if (boatGraphics.alpha > 0) {
LK.setTimeout(animateExplosion, 30);
} else {
// Boat destroyed completely
self.visible = false;
isGameOver = true;
LK.showGameOver();
}
}
animateExplosion();
};
self.down = function (x, y, obj) {
self.swipeStartTime = Date.now(); // Track when swipe started
self.castLine();
};
self.up = function (x, y, obj) {
self.reelIn();
};
return self;
});
var Fish = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'normal';
var assetId, points;
switch (self.type) {
case 'rare':
assetId = 'rareFish';
points = 50;
self.speed = 3; // Increased speed for rare fish
break;
case 'legendary':
assetId = 'legendaryFish';
points = 100;
self.speed = 6; // Further increased speed for legendary fish
break;
default:
assetId = 'fish';
points = 10;
self.speed = 2.5;
// Increased speed for normal fish
}
self.points = points;
var fishGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Randomly set the direction (left or right)
self.direction = Math.random() > 0.5 ? 1 : -1;
// If moving right, flip the fish
if (self.direction > 0) {
fishGraphics.scaleX = -1;
}
self.update = function () {
// Only update position every other frame to reduce computational load
if (LK.ticks % 2 === 0) {
self.x += self.speed * self.direction;
// If fish goes off screen, remove it
if (self.direction > 0 && self.x > 2048 + fishGraphics.width || self.direction < 0 && self.x < -fishGraphics.width) {
self.shouldRemove = true;
}
// If boat is casting and fish is far from hook, adjust its path toward visible area
if (boat && boat.isCasting) {
var hookPos = boat.getHookPosition();
var verticalDistance = Math.abs(self.y - hookPos.y);
// If fish is too far from hook vertically, move it toward the hook
if (verticalDistance > 1000) {
// Move fish toward hook's vertical position
if (self.y > hookPos.y) {
self.y -= 1;
} else {
self.y += 1;
}
}
}
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
// Sea mine asset (round shape with spikes)
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x333333,
// Dark gray color for sea mines
scaleX: 0.7,
// Smaller size to reduce impact
scaleY: 0.7 // Smaller size to reduce impact
});
// Create spikes around the mine (using rotation) - reduced number of spikes
for (var i = 0; i < 4; i++) {
// Reduced from 8 to 4 spikes
var spike = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.15,
scaleY: 0.4,
tint: 0x333333
});
spike.rotation = i * Math.PI / 2; // 4 spikes at 90 degree intervals
spike.x = Math.cos(spike.rotation) * 15; // Reduced distance
spike.y = Math.sin(spike.rotation) * 15; // Reduced distance
}
self.speed = 0.08; // Even more reduced obstacle speed
// Add simplified pulsing animation effect
self.pulsePhase = Math.random() * Math.PI * 2; // Random starting phase
self.update = function () {
// Pulsing animation for sea mine - update less frequently to save CPU
if (LK.ticks % 3 === 0) {
// Only update animation every 3 frames
var pulseFactor = 0.08 * Math.sin(self.pulsePhase);
obstacleGraphics.scaleX = 0.7 + pulseFactor;
obstacleGraphics.scaleY = 0.7 + pulseFactor;
self.pulsePhase += 0.03; // Slower animation
}
self.y -= self.speed;
if (self.y < -obstacleGraphics.height) {
self.shouldRemove = true;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var boat;
var fishes = [];
var obstacles = [];
var score = 0;
var isGameOver = false;
var waterLevel = 0; // Used for difficulty progression only
var fishSpawnRate = 0.02;
var obstacleSpawnRate = 0.01;
var backgroundSpeed = 1;
var highScore = storage.highScore || 0;
// Create water background
var water = LK.getAsset('water', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(water);
// Initialize boat
boat = new Boat();
boat.x = 2048 / 2;
boat.y = 300;
game.addChild(boat);
// Create score text
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create high score text
var highScoreTxt = new Text2('High Score: ' + highScore, {
size: 50,
fill: 0xFFD700
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 150;
LK.gui.top.addChild(highScoreTxt);
// Depth indicator removed
// Handle touch/click events for the game area
var gameSwipeStartX = 0;
var gameSwipeStartY = 0;
var isSwipingInGame = false;
game.down = function (x, y, obj) {
if (!isGameOver) {
gameSwipeStartX = x;
gameSwipeStartY = y;
isSwipingInGame = true;
boat.swipeStartTime = Date.now(); // Track when swipe started for velocity calculation
}
};
game.up = function (x, y, obj) {
if (!isGameOver) {
// Calculate swipe distance
var deltaX = x - gameSwipeStartX;
var deltaY = y - gameSwipeStartY;
var swipeDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var swipeTime = Date.now() - boat.swipeStartTime;
if (swipeDistance < 20) {
// It's a tap, not a swipe - toggle fishing line
if (boat.isCasting) {
boat.reelIn();
} else {
boat.castLine();
}
} else {
// It was a swipe - apply momentum to the boat based on swipe speed
var swipeVelocity = deltaX / swipeTime * 10;
boat.velocityX += swipeVelocity;
// Limit initial velocity
if (boat.velocityX > boat.maxVelocity) boat.velocityX = boat.maxVelocity;
if (boat.velocityX < -boat.maxVelocity) boat.velocityX = -boat.maxVelocity;
// Calculate target position based on swipe length
boat.targetX = boat.x + deltaX * 0.6;
// Keep target within game bounds
boat.targetX = Math.max(boat.targetX, 100);
boat.targetX = Math.min(boat.targetX, 2048 - 100);
}
isSwipingInGame = false;
}
};
// Handle touch move for navigating the boat with swipe
var swipeStartX = 0;
var swipeStartY = 0;
var isDraggingBoat = false;
game.move = function (x, y, obj) {
if (isDraggingBoat) {
// Calculate horizontal distance moved since last position
var deltaX = x - swipeStartX;
// Set target position directly for smooth following
boat.targetX = boat.x + deltaX;
// Add velocity based on motion speed
var moveSpeed = deltaX;
boat.velocityX = moveSpeed * 0.8;
// Keep boat within game bounds
boat.targetX = Math.max(boat.targetX, 100);
boat.targetX = Math.min(boat.targetX, 2048 - 100);
// Update swipe start position for next move
swipeStartX = x;
}
// Handle swiping in general game area - adds another way to control the boat
if (isSwipingInGame) {
var dragDeltaX = x - gameSwipeStartX;
// Only update if there's enough movement to be considered a swipe
if (Math.abs(dragDeltaX) > 5) {
boat.targetX = boat.x + dragDeltaX * 0.5;
boat.targetX = Math.max(boat.targetX, 100);
boat.targetX = Math.min(boat.targetX, 2048 - 100);
// Update swipe start for next move
gameSwipeStartX = x;
}
}
};
// Start tracking swipe
boat.down = function (x, y, obj) {
isDraggingBoat = true;
swipeStartX = x;
swipeStartY = y;
boat.swipeStartTime = Date.now(); // Track when swipe started for velocity calculation
};
// End swipe and cast fishing line
boat.up = function (x, y, obj) {
isDraggingBoat = false;
// Only cast line if it was a tap (not a long swipe)
var deltaX = Math.abs(x - swipeStartX);
var deltaY = Math.abs(y - swipeStartY);
if (deltaX < 20 && deltaY < 20) {
boat.castLine();
}
};
// Function to spawn fish
function spawnFish() {
// Limit the maximum number of fish to prevent lag
if (fishes.length >= 10) return; // Don't create more than 10 fish at a time
// Significantly reduced spawn chance
if (Math.random() < fishSpawnRate * 0.3) {
var fishType;
var random = Math.random();
if (random < 0.05) {
fishType = 'legendary';
} else if (random < 0.2) {
fishType = 'rare';
} else {
fishType = 'normal';
}
var fish = new Fish(fishType);
// Random position on left or right side
if (fish.direction > 0) {
fish.x = -fish.width / 2;
} else {
fish.x = 2048 + fish.width / 2;
}
// Random depth within screen bounds
var minDepth = 400;
var maxDepth = 2732 - 100;
// If the boat is casting, ensure fish spawn near the hook
if (boat.isCasting) {
var hookPos = boat.getHookPosition();
// Calculate a reasonable vertical area around the hook (ensuring fish are on screen)
var maxDistance = 800; // Maximum distance from hook in pixels
minDepth = Math.max(400, hookPos.y - maxDistance);
maxDepth = Math.min(2732 - 100, hookPos.y + maxDistance);
}
fish.y = minDepth + Math.random() * (maxDepth - minDepth);
fishes.push(fish);
game.addChild(fish);
}
}
// Function to spawn obstacles
function spawnObstacle() {
// Limit the maximum number of obstacles to prevent lag
if (obstacles.length >= 5) return; // Don't create more than 5 obstacles at a time
if (Math.random() < obstacleSpawnRate) {
var obstacle = new Obstacle();
// Sea mines float at varying depths
obstacle.x = 100 + Math.random() * (2048 - 200);
obstacle.y = 2732 + obstacle.height;
// Add simple floating movement to sea mines
obstacle.floatDirection = Math.random() > 0.5 ? 1 : -1;
obstacle.floatSpeed = 0.1 + Math.random() * 0.1; // Reduced speed variation
obstacle.floatPhase = Math.random() * Math.PI * 2;
obstacles.push(obstacle);
game.addChild(obstacle);
}
}
// Function to check if hook caught a fish
function checkFishCatch() {
if (!boat.isCasting) return;
var hookPos = boat.getHookPosition();
for (var i = fishes.length - 1; i >= 0; i--) {
var fish = fishes[i];
var distance = Math.sqrt(Math.pow(hookPos.x - fish.x, 2) + Math.pow(hookPos.y - fish.y, 2));
if (distance < 40) {
// Hook caught a fish
// Add points based on fish type
score += fish.points;
LK.setScore(score);
scoreTxt.setText('Score: ' + score);
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
// Flash high score text when broken
LK.effects.flashObject(highScoreTxt, 0xFFD700, 1000);
}
// Play catch sound
LK.getSound('catch').play();
// Flash effect
LK.effects.flashObject(fish, 0xFFFFFF, 500);
// Remove the fish
fish.destroy();
fishes.splice(i, 1);
// Reel in after catching
boat.reelIn();
break;
}
}
}
// Function to check boat collision with obstacles
function checkObstacleCollisions() {
var _loop = function _loop() {
obstacle = obstacles[i];
if (boat.intersects(obstacle)) {
var _animateExplosion = function animateExplosion() {
explosionSize += 0.2;
obstacle.scale.set(explosionSize);
obstacle.alpha -= 0.1;
if (obstacle.alpha > 0) {
LK.setTimeout(_animateExplosion, 30);
} else {
// Remove the sea mine after explosion completes
obstacle.destroy();
obstacles.splice(i, 1);
}
};
// Hit a sea mine - create explosion effect
LK.effects.flashScreen(0xFF0000, 800);
LK.getSound('splash').play();
// Create explosion animation
explosionSize = 1;
obstacle.tint = 0xFF3300; // Explosion color
_animateExplosion();
// Make boat explode
boat.explode();
return 0; // break
}
// Also check if fishing line hits sea mine
if (boat.isCasting) {
hookPos = boat.getHookPosition();
distance = Math.sqrt(Math.pow(hookPos.x - obstacle.x, 2) + Math.pow(hookPos.y - obstacle.y, 2));
if (distance < 60) {
// Hook hit a sea mine
LK.effects.flashScreen(0xFF5500, 500);
LK.getSound('splash').play();
// Trigger mine explosion
obstacle.tint = 0xFF3300;
obstacle.scale.set(1.5);
LK.setTimeout(function () {
obstacle.destroy();
obstacles.splice(i, 1);
}, 300);
// Reel in after hitting mine
boat.reelIn();
return 0; // break
}
}
},
obstacle,
explosionSize,
hookPos,
distance,
_ret;
for (var i = obstacles.length - 1; i >= 0; i--) {
_ret = _loop();
if (_ret === 0) break;
}
}
// Update game loop
game.update = function () {
if (isGameOver || boat.isExploding) return;
// Increase difficulty over time
waterLevel += backgroundSpeed * 0.1;
// Increase difficulty based on time
fishSpawnRate = 0.005 + waterLevel / 20000; // Further reduced fish spawn rate
obstacleSpawnRate = 0.0001 + waterLevel / 100000; // Extremely reduced sea mine spawn rate to prevent lag
// Spawn game elements
spawnFish();
spawnObstacle();
// Keep track of current hook position
if (boat.isCasting) {
boat.lastHookPosition = boat.getHookPosition();
}
// Update all game objects
// Update fishes - only process a subset each frame if there are many
var fishesToProcess = Math.min(fishes.length, 5); // Process max 5 fish per frame
for (var i = fishes.length - 1; i >= Math.max(0, fishes.length - fishesToProcess); i--) {
var fish = fishes[i];
if (fish.shouldRemove) {
fish.destroy();
fishes.splice(i, 1);
}
}
// Update obstacles (sea mines)
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Add gentle floating motion to sea mines - less frequently
if (obstacle.floatPhase !== undefined && LK.ticks % 4 === 0) {
// Only update floating every 4 frames
obstacle.floatPhase += 0.01; // Slower movement
obstacle.x += Math.sin(obstacle.floatPhase) * obstacle.floatSpeed * obstacle.floatDirection * 0.5; // Reduced movement
}
if (obstacle.shouldRemove) {
obstacle.destroy();
obstacles.splice(i, 1);
}
}
// Check for fish catch
checkFishCatch();
// Check for obstacle collisions
checkObstacleCollisions();
// No winning condition - allow unlimited play
// Game continues indefinitely so players can achieve the highest score possible
};
// Play background music
LK.playMusic('backgroundNature', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boat = Container.expand(function () {
var self = Container.call(this);
var boatGraphics = self.attachAsset('boat', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.isCasting = false;
self.lineLength = 0;
self.maxLineLength = 2400; // Increased to reach bottom of screen (screen height is 2732, boat is at y=300)
self.lineSpeed = 10; // Increased from 6 to 10 for even faster fishing line movement
self.isExploding = false;
self.explodeScale = 1;
self.velocityX = 0; // Current horizontal velocity for smooth movement
self.maxVelocity = 30; // Maximum velocity
self.friction = 0.92; // Friction applied to slow down movement
self.targetX = null; // Target position to move toward
self.swipeStartTime = 0; // Track when swipe started for velocity calculation
var fishingLine = self.attachAsset('fishingLine', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 50,
visible: false,
height: 0
});
var fishHook = self.attachAsset('fishHook', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 50,
visible: false
});
self.castLine = function () {
if (!self.isCasting) {
self.isCasting = true;
fishingLine.visible = true;
fishHook.visible = true;
LK.getSound('cast').play();
}
};
self.reelIn = function () {
self.isCasting = false;
};
self.update = function () {
if (self.isCasting && self.lineLength < self.maxLineLength) {
self.lineLength += self.lineSpeed;
fishingLine.height = self.lineLength;
fishHook.y = 50 + self.lineLength;
} else if (!self.isCasting && self.lineLength > 0) {
self.lineLength -= self.lineSpeed * 2;
fishingLine.height = self.lineLength;
fishHook.y = 50 + self.lineLength;
}
if (self.lineLength <= 0 && !self.isCasting) {
fishingLine.visible = false;
fishHook.visible = false;
self.lineLength = 0;
}
// Smooth boat movement physics
if (self.targetX !== null) {
// Calculate distance to target
var distanceToTarget = self.targetX - self.x;
// Apply force based on distance
self.velocityX += distanceToTarget * 0.04;
// Limit maximum velocity
if (self.velocityX > self.maxVelocity) self.velocityX = self.maxVelocity;
if (self.velocityX < -self.maxVelocity) self.velocityX = -self.maxVelocity;
// If very close to target and moving slowly, just snap to position
if (Math.abs(distanceToTarget) < 1 && Math.abs(self.velocityX) < 0.5) {
self.x = self.targetX;
self.velocityX = 0;
self.targetX = null;
}
}
// Apply velocity with friction
if (Math.abs(self.velocityX) > 0.1) {
self.x += self.velocityX;
self.velocityX *= self.friction;
// Keep boat within bounds
if (self.x < 100) {
self.x = 100;
self.velocityX = 0;
} else if (self.x > 2048 - 100) {
self.x = 2048 - 100;
self.velocityX = 0;
}
} else {
self.velocityX = 0;
}
};
self.getHookPosition = function () {
return {
x: self.x,
y: self.y + 50 + self.lineLength
};
};
self.lastHookPosition = self.getHookPosition();
self.explode = function () {
if (self.isExploding) return;
self.isExploding = true;
boatGraphics.tint = 0xFF3300; // Fire color
function animateExplosion() {
self.explodeScale += 0.2;
boatGraphics.scale.set(self.explodeScale);
boatGraphics.alpha -= 0.1;
if (boatGraphics.alpha > 0) {
LK.setTimeout(animateExplosion, 30);
} else {
// Boat destroyed completely
self.visible = false;
isGameOver = true;
LK.showGameOver();
}
}
animateExplosion();
};
self.down = function (x, y, obj) {
self.swipeStartTime = Date.now(); // Track when swipe started
self.castLine();
};
self.up = function (x, y, obj) {
self.reelIn();
};
return self;
});
var Fish = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'normal';
var assetId, points;
switch (self.type) {
case 'rare':
assetId = 'rareFish';
points = 50;
self.speed = 3; // Increased speed for rare fish
break;
case 'legendary':
assetId = 'legendaryFish';
points = 100;
self.speed = 6; // Further increased speed for legendary fish
break;
default:
assetId = 'fish';
points = 10;
self.speed = 2.5;
// Increased speed for normal fish
}
self.points = points;
var fishGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Randomly set the direction (left or right)
self.direction = Math.random() > 0.5 ? 1 : -1;
// If moving right, flip the fish
if (self.direction > 0) {
fishGraphics.scaleX = -1;
}
self.update = function () {
// Only update position every other frame to reduce computational load
if (LK.ticks % 2 === 0) {
self.x += self.speed * self.direction;
// If fish goes off screen, remove it
if (self.direction > 0 && self.x > 2048 + fishGraphics.width || self.direction < 0 && self.x < -fishGraphics.width) {
self.shouldRemove = true;
}
// If boat is casting and fish is far from hook, adjust its path toward visible area
if (boat && boat.isCasting) {
var hookPos = boat.getHookPosition();
var verticalDistance = Math.abs(self.y - hookPos.y);
// If fish is too far from hook vertically, move it toward the hook
if (verticalDistance > 1000) {
// Move fish toward hook's vertical position
if (self.y > hookPos.y) {
self.y -= 1;
} else {
self.y += 1;
}
}
}
}
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
// Sea mine asset (round shape with spikes)
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x333333,
// Dark gray color for sea mines
scaleX: 0.7,
// Smaller size to reduce impact
scaleY: 0.7 // Smaller size to reduce impact
});
// Create spikes around the mine (using rotation) - reduced number of spikes
for (var i = 0; i < 4; i++) {
// Reduced from 8 to 4 spikes
var spike = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.15,
scaleY: 0.4,
tint: 0x333333
});
spike.rotation = i * Math.PI / 2; // 4 spikes at 90 degree intervals
spike.x = Math.cos(spike.rotation) * 15; // Reduced distance
spike.y = Math.sin(spike.rotation) * 15; // Reduced distance
}
self.speed = 0.08; // Even more reduced obstacle speed
// Add simplified pulsing animation effect
self.pulsePhase = Math.random() * Math.PI * 2; // Random starting phase
self.update = function () {
// Pulsing animation for sea mine - update less frequently to save CPU
if (LK.ticks % 3 === 0) {
// Only update animation every 3 frames
var pulseFactor = 0.08 * Math.sin(self.pulsePhase);
obstacleGraphics.scaleX = 0.7 + pulseFactor;
obstacleGraphics.scaleY = 0.7 + pulseFactor;
self.pulsePhase += 0.03; // Slower animation
}
self.y -= self.speed;
if (self.y < -obstacleGraphics.height) {
self.shouldRemove = true;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var boat;
var fishes = [];
var obstacles = [];
var score = 0;
var isGameOver = false;
var waterLevel = 0; // Used for difficulty progression only
var fishSpawnRate = 0.02;
var obstacleSpawnRate = 0.01;
var backgroundSpeed = 1;
var highScore = storage.highScore || 0;
// Create water background
var water = LK.getAsset('water', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(water);
// Initialize boat
boat = new Boat();
boat.x = 2048 / 2;
boat.y = 300;
game.addChild(boat);
// Create score text
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create high score text
var highScoreTxt = new Text2('High Score: ' + highScore, {
size: 50,
fill: 0xFFD700
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 150;
LK.gui.top.addChild(highScoreTxt);
// Depth indicator removed
// Handle touch/click events for the game area
var gameSwipeStartX = 0;
var gameSwipeStartY = 0;
var isSwipingInGame = false;
game.down = function (x, y, obj) {
if (!isGameOver) {
gameSwipeStartX = x;
gameSwipeStartY = y;
isSwipingInGame = true;
boat.swipeStartTime = Date.now(); // Track when swipe started for velocity calculation
}
};
game.up = function (x, y, obj) {
if (!isGameOver) {
// Calculate swipe distance
var deltaX = x - gameSwipeStartX;
var deltaY = y - gameSwipeStartY;
var swipeDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var swipeTime = Date.now() - boat.swipeStartTime;
if (swipeDistance < 20) {
// It's a tap, not a swipe - toggle fishing line
if (boat.isCasting) {
boat.reelIn();
} else {
boat.castLine();
}
} else {
// It was a swipe - apply momentum to the boat based on swipe speed
var swipeVelocity = deltaX / swipeTime * 10;
boat.velocityX += swipeVelocity;
// Limit initial velocity
if (boat.velocityX > boat.maxVelocity) boat.velocityX = boat.maxVelocity;
if (boat.velocityX < -boat.maxVelocity) boat.velocityX = -boat.maxVelocity;
// Calculate target position based on swipe length
boat.targetX = boat.x + deltaX * 0.6;
// Keep target within game bounds
boat.targetX = Math.max(boat.targetX, 100);
boat.targetX = Math.min(boat.targetX, 2048 - 100);
}
isSwipingInGame = false;
}
};
// Handle touch move for navigating the boat with swipe
var swipeStartX = 0;
var swipeStartY = 0;
var isDraggingBoat = false;
game.move = function (x, y, obj) {
if (isDraggingBoat) {
// Calculate horizontal distance moved since last position
var deltaX = x - swipeStartX;
// Set target position directly for smooth following
boat.targetX = boat.x + deltaX;
// Add velocity based on motion speed
var moveSpeed = deltaX;
boat.velocityX = moveSpeed * 0.8;
// Keep boat within game bounds
boat.targetX = Math.max(boat.targetX, 100);
boat.targetX = Math.min(boat.targetX, 2048 - 100);
// Update swipe start position for next move
swipeStartX = x;
}
// Handle swiping in general game area - adds another way to control the boat
if (isSwipingInGame) {
var dragDeltaX = x - gameSwipeStartX;
// Only update if there's enough movement to be considered a swipe
if (Math.abs(dragDeltaX) > 5) {
boat.targetX = boat.x + dragDeltaX * 0.5;
boat.targetX = Math.max(boat.targetX, 100);
boat.targetX = Math.min(boat.targetX, 2048 - 100);
// Update swipe start for next move
gameSwipeStartX = x;
}
}
};
// Start tracking swipe
boat.down = function (x, y, obj) {
isDraggingBoat = true;
swipeStartX = x;
swipeStartY = y;
boat.swipeStartTime = Date.now(); // Track when swipe started for velocity calculation
};
// End swipe and cast fishing line
boat.up = function (x, y, obj) {
isDraggingBoat = false;
// Only cast line if it was a tap (not a long swipe)
var deltaX = Math.abs(x - swipeStartX);
var deltaY = Math.abs(y - swipeStartY);
if (deltaX < 20 && deltaY < 20) {
boat.castLine();
}
};
// Function to spawn fish
function spawnFish() {
// Limit the maximum number of fish to prevent lag
if (fishes.length >= 10) return; // Don't create more than 10 fish at a time
// Significantly reduced spawn chance
if (Math.random() < fishSpawnRate * 0.3) {
var fishType;
var random = Math.random();
if (random < 0.05) {
fishType = 'legendary';
} else if (random < 0.2) {
fishType = 'rare';
} else {
fishType = 'normal';
}
var fish = new Fish(fishType);
// Random position on left or right side
if (fish.direction > 0) {
fish.x = -fish.width / 2;
} else {
fish.x = 2048 + fish.width / 2;
}
// Random depth within screen bounds
var minDepth = 400;
var maxDepth = 2732 - 100;
// If the boat is casting, ensure fish spawn near the hook
if (boat.isCasting) {
var hookPos = boat.getHookPosition();
// Calculate a reasonable vertical area around the hook (ensuring fish are on screen)
var maxDistance = 800; // Maximum distance from hook in pixels
minDepth = Math.max(400, hookPos.y - maxDistance);
maxDepth = Math.min(2732 - 100, hookPos.y + maxDistance);
}
fish.y = minDepth + Math.random() * (maxDepth - minDepth);
fishes.push(fish);
game.addChild(fish);
}
}
// Function to spawn obstacles
function spawnObstacle() {
// Limit the maximum number of obstacles to prevent lag
if (obstacles.length >= 5) return; // Don't create more than 5 obstacles at a time
if (Math.random() < obstacleSpawnRate) {
var obstacle = new Obstacle();
// Sea mines float at varying depths
obstacle.x = 100 + Math.random() * (2048 - 200);
obstacle.y = 2732 + obstacle.height;
// Add simple floating movement to sea mines
obstacle.floatDirection = Math.random() > 0.5 ? 1 : -1;
obstacle.floatSpeed = 0.1 + Math.random() * 0.1; // Reduced speed variation
obstacle.floatPhase = Math.random() * Math.PI * 2;
obstacles.push(obstacle);
game.addChild(obstacle);
}
}
// Function to check if hook caught a fish
function checkFishCatch() {
if (!boat.isCasting) return;
var hookPos = boat.getHookPosition();
for (var i = fishes.length - 1; i >= 0; i--) {
var fish = fishes[i];
var distance = Math.sqrt(Math.pow(hookPos.x - fish.x, 2) + Math.pow(hookPos.y - fish.y, 2));
if (distance < 40) {
// Hook caught a fish
// Add points based on fish type
score += fish.points;
LK.setScore(score);
scoreTxt.setText('Score: ' + score);
// Update high score if needed
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
// Flash high score text when broken
LK.effects.flashObject(highScoreTxt, 0xFFD700, 1000);
}
// Play catch sound
LK.getSound('catch').play();
// Flash effect
LK.effects.flashObject(fish, 0xFFFFFF, 500);
// Remove the fish
fish.destroy();
fishes.splice(i, 1);
// Reel in after catching
boat.reelIn();
break;
}
}
}
// Function to check boat collision with obstacles
function checkObstacleCollisions() {
var _loop = function _loop() {
obstacle = obstacles[i];
if (boat.intersects(obstacle)) {
var _animateExplosion = function animateExplosion() {
explosionSize += 0.2;
obstacle.scale.set(explosionSize);
obstacle.alpha -= 0.1;
if (obstacle.alpha > 0) {
LK.setTimeout(_animateExplosion, 30);
} else {
// Remove the sea mine after explosion completes
obstacle.destroy();
obstacles.splice(i, 1);
}
};
// Hit a sea mine - create explosion effect
LK.effects.flashScreen(0xFF0000, 800);
LK.getSound('splash').play();
// Create explosion animation
explosionSize = 1;
obstacle.tint = 0xFF3300; // Explosion color
_animateExplosion();
// Make boat explode
boat.explode();
return 0; // break
}
// Also check if fishing line hits sea mine
if (boat.isCasting) {
hookPos = boat.getHookPosition();
distance = Math.sqrt(Math.pow(hookPos.x - obstacle.x, 2) + Math.pow(hookPos.y - obstacle.y, 2));
if (distance < 60) {
// Hook hit a sea mine
LK.effects.flashScreen(0xFF5500, 500);
LK.getSound('splash').play();
// Trigger mine explosion
obstacle.tint = 0xFF3300;
obstacle.scale.set(1.5);
LK.setTimeout(function () {
obstacle.destroy();
obstacles.splice(i, 1);
}, 300);
// Reel in after hitting mine
boat.reelIn();
return 0; // break
}
}
},
obstacle,
explosionSize,
hookPos,
distance,
_ret;
for (var i = obstacles.length - 1; i >= 0; i--) {
_ret = _loop();
if (_ret === 0) break;
}
}
// Update game loop
game.update = function () {
if (isGameOver || boat.isExploding) return;
// Increase difficulty over time
waterLevel += backgroundSpeed * 0.1;
// Increase difficulty based on time
fishSpawnRate = 0.005 + waterLevel / 20000; // Further reduced fish spawn rate
obstacleSpawnRate = 0.0001 + waterLevel / 100000; // Extremely reduced sea mine spawn rate to prevent lag
// Spawn game elements
spawnFish();
spawnObstacle();
// Keep track of current hook position
if (boat.isCasting) {
boat.lastHookPosition = boat.getHookPosition();
}
// Update all game objects
// Update fishes - only process a subset each frame if there are many
var fishesToProcess = Math.min(fishes.length, 5); // Process max 5 fish per frame
for (var i = fishes.length - 1; i >= Math.max(0, fishes.length - fishesToProcess); i--) {
var fish = fishes[i];
if (fish.shouldRemove) {
fish.destroy();
fishes.splice(i, 1);
}
}
// Update obstacles (sea mines)
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Add gentle floating motion to sea mines - less frequently
if (obstacle.floatPhase !== undefined && LK.ticks % 4 === 0) {
// Only update floating every 4 frames
obstacle.floatPhase += 0.01; // Slower movement
obstacle.x += Math.sin(obstacle.floatPhase) * obstacle.floatSpeed * obstacle.floatDirection * 0.5; // Reduced movement
}
if (obstacle.shouldRemove) {
obstacle.destroy();
obstacles.splice(i, 1);
}
}
// Check for fish catch
checkFishCatch();
// Check for obstacle collisions
checkObstacleCollisions();
// No winning condition - allow unlimited play
// Game continues indefinitely so players can achieve the highest score possible
};
// Play background music
LK.playMusic('backgroundNature', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
horizontal image sea tuna. In-Game asset. 2d. High contrast. No shadows
horizontal image Snapper fish. In-Game asset. 2d. High contrast. No shadows
horizontal image blue marlin fish. In-Game asset. 2d. High contrast. No shadows
sea mine. In-Game asset. 2d. High contrast. No shadows
quite blue under water of sea
horizontal top down image submarine. In-Game asset. 2d. High contrast. No shadows
vertical harpoon head. In-Game asset. 2d. High contrast. No shadows