/**** * 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();
}
});