/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Lucky Cat: can jump up on tap
var LuckyCat = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('luckycat', {
anchorX: 0.5,
anchorY: 0.5
});
self.abilityCooldown = 0;
self.isJumping = false;
self.jumpTimer = 0;
self.defaultY = 0;
self.ability = function () {
if (self.abilityCooldown <= 0 && !self.isJumping) {
self.isJumping = true;
self.jumpTimer = 24; // 0.4s
self.abilityCooldown = 60;
tween(self, {
y: self.defaultY - 180
}, {
duration: 200,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self, {
y: self.defaultY
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
self.update = function () {
if (self.abilityCooldown > 0) self.abilityCooldown--;
// Paw wave animation
body.rotation = Math.sin(LK.ticks / 10) * 0.1;
};
return self;
});
// Orca: chases the trio, animates lunges
var Orca = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('orcaBody', {
anchorX: 0.5,
anchorY: 0.5
});
var belly = self.attachAsset('orcaBelly', {
anchorX: 0.5,
anchorY: 0.5,
y: 180
});
self.lungeTimer = 0;
self.lungeTarget = null;
self.lungeProgress = 0;
self.baseY = 0;
self.lungeSpeed = 6; // pixels per tick
self.lungeCooldown = 0;
self.lunge = function (target) {
if (self.lungeCooldown <= 0) {
self.lungeTarget = target;
self.lungeTimer = 30; // 0.5s lunge
self.lungeProgress = 0;
self.lungeCooldown = 60 + Math.floor(Math.random() * 30); // 1-1.5s cooldown
}
};
self.update = function () {
if (self.lungeCooldown > 0) self.lungeCooldown--;
// Idle bobbing
if (self.lungeTimer <= 0) {
self.y = self.baseY + Math.sin(LK.ticks / 20) * 18;
}
// Lunge logic
if (self.lungeTimer > 0 && self.lungeTarget) {
self.lungeProgress++;
// Move towards target
var dx = self.lungeTarget.x - self.x;
var dy = self.lungeTarget.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 1) {
self.x += dx / dist * self.lungeSpeed;
self.y += dy / dist * self.lungeSpeed;
}
self.lungeTimer--;
// After lunge, return to base position
if (self.lungeTimer === 0) {
tween(self, {
x: 1024,
y: self.baseY
}, {
duration: 400,
easing: tween.cubicOut
});
}
}
};
return self;
});
// Shark: can dash forward on tap
var Shark = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('shark', {
anchorX: 0.5,
anchorY: 0.5
});
self.abilityCooldown = 0; // ticks
self.isDashing = false;
self.dashTimer = 0;
self.defaultY = 0;
self.ability = function () {
if (self.abilityCooldown <= 0 && !self.isDashing) {
self.isDashing = true;
self.dashTimer = 18; // 0.3s at 60fps
self.abilityCooldown = 60; // 1s cooldown
tween(self, {
x: self.x + 180
}, {
duration: 250,
easing: tween.cubicOut,
onFinish: function onFinish() {
// Dash ends, return to normal
self.isDashing = false;
}
});
}
};
self.update = function () {
if (self.abilityCooldown > 0) self.abilityCooldown--;
if (self.isDashing) {
// Slight up-down wiggle for animation
self.y = self.defaultY + Math.sin(LK.ticks / 2) * 10;
} else {
self.y = self.defaultY;
}
};
return self;
});
// Tiger: can roll sideways on swipe
var Tiger = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('tiger', {
anchorX: 0.5,
anchorY: 0.5
});
self.abilityCooldown = 0;
self.isRolling = false;
self.rollDir = 0;
self.defaultX = 0;
self.ability = function (dir) {
if (self.abilityCooldown <= 0 && !self.isRolling) {
self.isRolling = true;
self.rollDir = dir;
self.abilityCooldown = 60;
var targetX = self.x + dir * 200;
if (targetX < 400) targetX = 400;
if (targetX > 1648) targetX = 1648;
tween(self, {
x: targetX
}, {
duration: 250,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.isRolling = false;
}
});
}
};
self.update = function () {
if (self.abilityCooldown > 0) self.abilityCooldown--;
// Tail wiggle
body.rotation = Math.sin(LK.ticks / 8) * 0.08;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x6ec6f7 // light blue, ocean-like
});
/****
* Game Code
****/
/*
We need simple shapes for the MVP.
- Shark: blue ellipse
- Lucky Cat: gold ellipse
- Tiger: orange ellipse
- Orca: large black ellipse with white belly
- Tap/Swipe indicators: small white ellipses
- Background: none (default)
- Sound/Music: none for MVP
*/
// Game state
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Place orca in center, lower part of screen
var orca = new Orca();
orca.x = 1024;
orca.baseY = 1800;
orca.y = orca.baseY;
game.addChild(orca);
// Place trio in arc above orca
var shark = new Shark();
shark.x = 824;
shark.y = 1200;
shark.defaultY = shark.y;
game.addChild(shark);
var luckycat = new LuckyCat();
luckycat.x = 1024;
luckycat.y = 1100;
luckycat.defaultY = luckycat.y;
game.addChild(luckycat);
var tiger = new Tiger();
tiger.x = 1224;
tiger.y = 1200;
tiger.defaultX = tiger.x;
game.addChild(tiger);
// For swipe detection
var swipeStart = null;
var swipeChar = null;
// Tap indicators (show briefly on tap)
var tapIndicators = [];
// Helper: show tap indicator at (x, y)
function showTapIndicator(x, y) {
var ind = LK.getAsset('tapIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
alpha: 0.7
});
tapIndicators.push({
node: ind,
timer: 18
});
game.addChild(ind);
}
// Helper: update tap indicators
function updateTapIndicators() {
for (var i = tapIndicators.length - 1; i >= 0; i--) {
tapIndicators[i].timer--;
tapIndicators[i].node.alpha -= 0.04;
if (tapIndicators[i].timer <= 0) {
tapIndicators[i].node.destroy();
tapIndicators.splice(i, 1);
}
}
}
// Helper: check if orca catches any character
function checkCaught() {
// If orca's body intersects any character, game over
if (orca.intersects(shark) || orca.intersects(luckycat) || orca.intersects(tiger)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
}
// Helper: update score
function updateScore() {
score++;
scoreTxt.setText(score);
}
// Main game update
game.update = function () {
shark.update();
luckycat.update();
tiger.update();
orca.update();
updateTapIndicators();
// Orca AI: every 60 ticks, lunge at random character
if (LK.ticks % 60 === 0 && orca.lungeTimer <= 0) {
var targets = [shark, luckycat, tiger];
var idx = Math.floor(Math.random() * 3);
orca.lunge(targets[idx]);
}
// Score increases every 30 ticks (0.5s)
if (LK.ticks % 30 === 0) {
updateScore();
}
checkCaught();
};
// Touch/tap/swipe controls
game.down = function (x, y, obj) {
// Convert to game coordinates
// Check which character is tapped
var localShark = shark.toLocal({
x: x,
y: y
});
var localCat = luckycat.toLocal({
x: x,
y: y
});
var localTiger = tiger.toLocal({
x: x,
y: y
});
var hit = null;
if (Math.abs(localShark.x) < 70 && Math.abs(localShark.y) < 50) {
shark.ability();
showTapIndicator(shark.x, shark.y);
hit = 'shark';
} else if (Math.abs(localCat.x) < 60 && Math.abs(localCat.y) < 60) {
luckycat.ability();
showTapIndicator(luckycat.x, luckycat.y);
hit = 'luckycat';
} else if (Math.abs(localTiger.x) < 70 && Math.abs(localTiger.y) < 50) {
// For tiger, start swipe
swipeStart = {
x: x,
y: y
};
swipeChar = tiger;
hit = 'tiger';
}
// If not on tiger, clear swipe
if (hit !== 'tiger') {
swipeStart = null;
swipeChar = null;
}
};
game.move = function (x, y, obj) {
// If swiping tiger
if (swipeStart && swipeChar) {
var dx = x - swipeStart.x;
var dy = y - swipeStart.y;
if (Math.abs(dx) > 80 && Math.abs(dx) > Math.abs(dy)) {
// Left or right swipe
var dir = dx > 0 ? 1 : -1;
tiger.ability(dir);
showTapIndicator(tiger.x, tiger.y);
swipeStart = null;
swipeChar = null;
}
}
};
game.up = function (x, y, obj) {
swipeStart = null;
swipeChar = null;
};
// Instructions text (centered, fades out after 2s)
var instructions = new Text2("Tap Shark or Lucky Cat to use their moves.\nSwipe Tiger left/right to roll.\nSurvive the orca's chase!", {
size: 80,
fill: "#fff",
align: "center"
});
instructions.anchor.set(0.5, 0.5);
instructions.x = 1024;
instructions.y = 400;
LK.gui.center.addChild(instructions);
tween(instructions, {
alpha: 0
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
instructions.destroy();
}
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Lucky Cat: can jump up on tap
var LuckyCat = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('luckycat', {
anchorX: 0.5,
anchorY: 0.5
});
self.abilityCooldown = 0;
self.isJumping = false;
self.jumpTimer = 0;
self.defaultY = 0;
self.ability = function () {
if (self.abilityCooldown <= 0 && !self.isJumping) {
self.isJumping = true;
self.jumpTimer = 24; // 0.4s
self.abilityCooldown = 60;
tween(self, {
y: self.defaultY - 180
}, {
duration: 200,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self, {
y: self.defaultY
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
self.update = function () {
if (self.abilityCooldown > 0) self.abilityCooldown--;
// Paw wave animation
body.rotation = Math.sin(LK.ticks / 10) * 0.1;
};
return self;
});
// Orca: chases the trio, animates lunges
var Orca = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('orcaBody', {
anchorX: 0.5,
anchorY: 0.5
});
var belly = self.attachAsset('orcaBelly', {
anchorX: 0.5,
anchorY: 0.5,
y: 180
});
self.lungeTimer = 0;
self.lungeTarget = null;
self.lungeProgress = 0;
self.baseY = 0;
self.lungeSpeed = 6; // pixels per tick
self.lungeCooldown = 0;
self.lunge = function (target) {
if (self.lungeCooldown <= 0) {
self.lungeTarget = target;
self.lungeTimer = 30; // 0.5s lunge
self.lungeProgress = 0;
self.lungeCooldown = 60 + Math.floor(Math.random() * 30); // 1-1.5s cooldown
}
};
self.update = function () {
if (self.lungeCooldown > 0) self.lungeCooldown--;
// Idle bobbing
if (self.lungeTimer <= 0) {
self.y = self.baseY + Math.sin(LK.ticks / 20) * 18;
}
// Lunge logic
if (self.lungeTimer > 0 && self.lungeTarget) {
self.lungeProgress++;
// Move towards target
var dx = self.lungeTarget.x - self.x;
var dy = self.lungeTarget.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 1) {
self.x += dx / dist * self.lungeSpeed;
self.y += dy / dist * self.lungeSpeed;
}
self.lungeTimer--;
// After lunge, return to base position
if (self.lungeTimer === 0) {
tween(self, {
x: 1024,
y: self.baseY
}, {
duration: 400,
easing: tween.cubicOut
});
}
}
};
return self;
});
// Shark: can dash forward on tap
var Shark = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('shark', {
anchorX: 0.5,
anchorY: 0.5
});
self.abilityCooldown = 0; // ticks
self.isDashing = false;
self.dashTimer = 0;
self.defaultY = 0;
self.ability = function () {
if (self.abilityCooldown <= 0 && !self.isDashing) {
self.isDashing = true;
self.dashTimer = 18; // 0.3s at 60fps
self.abilityCooldown = 60; // 1s cooldown
tween(self, {
x: self.x + 180
}, {
duration: 250,
easing: tween.cubicOut,
onFinish: function onFinish() {
// Dash ends, return to normal
self.isDashing = false;
}
});
}
};
self.update = function () {
if (self.abilityCooldown > 0) self.abilityCooldown--;
if (self.isDashing) {
// Slight up-down wiggle for animation
self.y = self.defaultY + Math.sin(LK.ticks / 2) * 10;
} else {
self.y = self.defaultY;
}
};
return self;
});
// Tiger: can roll sideways on swipe
var Tiger = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('tiger', {
anchorX: 0.5,
anchorY: 0.5
});
self.abilityCooldown = 0;
self.isRolling = false;
self.rollDir = 0;
self.defaultX = 0;
self.ability = function (dir) {
if (self.abilityCooldown <= 0 && !self.isRolling) {
self.isRolling = true;
self.rollDir = dir;
self.abilityCooldown = 60;
var targetX = self.x + dir * 200;
if (targetX < 400) targetX = 400;
if (targetX > 1648) targetX = 1648;
tween(self, {
x: targetX
}, {
duration: 250,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.isRolling = false;
}
});
}
};
self.update = function () {
if (self.abilityCooldown > 0) self.abilityCooldown--;
// Tail wiggle
body.rotation = Math.sin(LK.ticks / 8) * 0.08;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x6ec6f7 // light blue, ocean-like
});
/****
* Game Code
****/
/*
We need simple shapes for the MVP.
- Shark: blue ellipse
- Lucky Cat: gold ellipse
- Tiger: orange ellipse
- Orca: large black ellipse with white belly
- Tap/Swipe indicators: small white ellipses
- Background: none (default)
- Sound/Music: none for MVP
*/
// Game state
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Place orca in center, lower part of screen
var orca = new Orca();
orca.x = 1024;
orca.baseY = 1800;
orca.y = orca.baseY;
game.addChild(orca);
// Place trio in arc above orca
var shark = new Shark();
shark.x = 824;
shark.y = 1200;
shark.defaultY = shark.y;
game.addChild(shark);
var luckycat = new LuckyCat();
luckycat.x = 1024;
luckycat.y = 1100;
luckycat.defaultY = luckycat.y;
game.addChild(luckycat);
var tiger = new Tiger();
tiger.x = 1224;
tiger.y = 1200;
tiger.defaultX = tiger.x;
game.addChild(tiger);
// For swipe detection
var swipeStart = null;
var swipeChar = null;
// Tap indicators (show briefly on tap)
var tapIndicators = [];
// Helper: show tap indicator at (x, y)
function showTapIndicator(x, y) {
var ind = LK.getAsset('tapIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
alpha: 0.7
});
tapIndicators.push({
node: ind,
timer: 18
});
game.addChild(ind);
}
// Helper: update tap indicators
function updateTapIndicators() {
for (var i = tapIndicators.length - 1; i >= 0; i--) {
tapIndicators[i].timer--;
tapIndicators[i].node.alpha -= 0.04;
if (tapIndicators[i].timer <= 0) {
tapIndicators[i].node.destroy();
tapIndicators.splice(i, 1);
}
}
}
// Helper: check if orca catches any character
function checkCaught() {
// If orca's body intersects any character, game over
if (orca.intersects(shark) || orca.intersects(luckycat) || orca.intersects(tiger)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
}
// Helper: update score
function updateScore() {
score++;
scoreTxt.setText(score);
}
// Main game update
game.update = function () {
shark.update();
luckycat.update();
tiger.update();
orca.update();
updateTapIndicators();
// Orca AI: every 60 ticks, lunge at random character
if (LK.ticks % 60 === 0 && orca.lungeTimer <= 0) {
var targets = [shark, luckycat, tiger];
var idx = Math.floor(Math.random() * 3);
orca.lunge(targets[idx]);
}
// Score increases every 30 ticks (0.5s)
if (LK.ticks % 30 === 0) {
updateScore();
}
checkCaught();
};
// Touch/tap/swipe controls
game.down = function (x, y, obj) {
// Convert to game coordinates
// Check which character is tapped
var localShark = shark.toLocal({
x: x,
y: y
});
var localCat = luckycat.toLocal({
x: x,
y: y
});
var localTiger = tiger.toLocal({
x: x,
y: y
});
var hit = null;
if (Math.abs(localShark.x) < 70 && Math.abs(localShark.y) < 50) {
shark.ability();
showTapIndicator(shark.x, shark.y);
hit = 'shark';
} else if (Math.abs(localCat.x) < 60 && Math.abs(localCat.y) < 60) {
luckycat.ability();
showTapIndicator(luckycat.x, luckycat.y);
hit = 'luckycat';
} else if (Math.abs(localTiger.x) < 70 && Math.abs(localTiger.y) < 50) {
// For tiger, start swipe
swipeStart = {
x: x,
y: y
};
swipeChar = tiger;
hit = 'tiger';
}
// If not on tiger, clear swipe
if (hit !== 'tiger') {
swipeStart = null;
swipeChar = null;
}
};
game.move = function (x, y, obj) {
// If swiping tiger
if (swipeStart && swipeChar) {
var dx = x - swipeStart.x;
var dy = y - swipeStart.y;
if (Math.abs(dx) > 80 && Math.abs(dx) > Math.abs(dy)) {
// Left or right swipe
var dir = dx > 0 ? 1 : -1;
tiger.ability(dir);
showTapIndicator(tiger.x, tiger.y);
swipeStart = null;
swipeChar = null;
}
}
};
game.up = function (x, y, obj) {
swipeStart = null;
swipeChar = null;
};
// Instructions text (centered, fades out after 2s)
var instructions = new Text2("Tap Shark or Lucky Cat to use their moves.\nSwipe Tiger left/right to roll.\nSurvive the orca's chase!", {
size: 80,
fill: "#fff",
align: "center"
});
instructions.anchor.set(0.5, 0.5);
instructions.x = 1024;
instructions.y = 400;
LK.gui.center.addChild(instructions);
tween(instructions, {
alpha: 0
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
instructions.destroy();
}
});