/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Enemy = Container.expand(function (speed, lifetime) { var self = Container.call(this); // Create eye white var eyeWhite = self.attachAsset('eye', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); // Create pupil var pupil = self.attachAsset('pupil', { anchorX: 0.5, anchorY: 0.5 }); // Create hit indicator (initially invisible) var hitCircle = self.attachAsset('hitCircle', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); // Set properties self.speed = speed || 1; self.vx = (Math.random() - 0.5) * self.speed * 3; self.vy = (Math.random() - 0.5) * self.speed * 3; self.targetTime = lifetime || 2000; // Time needed to hold to destroy (in ms) self.holdTime = 0; // Current time being held self.isHeld = false; self.isDying = false; self.value = Math.floor(self.targetTime / 500); // Score value based on difficulty // Looking animation self.lookTimer = 0; self.lookInterval = Math.random() * 1000 + 500; self.lookAt = function (x, y) { // Calculate angle to look at cursor var dx = x - self.x; var dy = y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Limit pupil movement within eye var maxDistance = eyeWhite.width * 0.3; var scale = distance > maxDistance ? maxDistance / distance : 1; // Move pupil pupil.x = dx * scale * 0.3; pupil.y = dy * scale * 0.3; }; // Hold and destroy functionality self.hold = function (deltaTime) { if (self.isDying) return; if (!self.isHeld) { self.isHeld = true; hitCircle.alpha = 0.7; hitCircle.scaleX = 0; hitCircle.scaleY = 0; tween(hitCircle, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); } self.holdTime += deltaTime; // Update visual feedback var progress = Math.min(self.holdTime / self.targetTime, 1); hitCircle.alpha = 0.3 + progress * 0.7; eyeWhite.scaleX = eyeWhite.scaleY = 1 + progress * 0.2; // Check if enemy is destroyed if (self.holdTime >= self.targetTime) { self.destroy(); return true; } return false; }; self.release = function () { if (self.isHeld && !self.isDying) { self.isHeld = false; // Shrink hit circle tween(hitCircle, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 200, easing: tween.easeIn }); // Shrink eye back to normal tween(eyeWhite, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); // Decay hold time when released self.holdTime = Math.max(0, self.holdTime - 500); } }; self.destroy = function () { if (self.isDying) return; self.isDying = true; LK.getSound('destroy').play(); // Play destroy animation tween(self, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Remove from parent after animation if (self.parent) { self.parent.removeChild(self); } } }); }; self.update = function (cursorX, cursorY) { if (self.isDying) return; // Move the enemy self.x += self.vx; self.y += self.vy; // Bounce off edges if (self.x < 50 || self.x > 2048 - 50) { self.vx *= -1; } if (self.y < 50 || self.y > 2732 - 50) { self.vy *= -1; } // Look at cursor if provided if (cursorX !== undefined && cursorY !== undefined) { self.lookAt(cursorX, cursorY); } else { // Random eye movement when cursor not provided self.lookTimer += 16.67; // Approx 1 frame at 60fps if (self.lookTimer > self.lookInterval) { pupil.x = (Math.random() - 0.5) * 15; pupil.y = (Math.random() - 0.5) * 15; self.lookTimer = 0; self.lookInterval = Math.random() * 1000 + 500; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 }); /**** * Game Code ****/ // Background var background = game.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0 })); // Game variables var enemies = []; var score = 0; var level = 1; var lives = 3; var spawnTimer = 0; var spawnInterval = 3000; // Initial spawn interval var minSpawnInterval = 500; // Minimum spawn interval at highest difficulty var maxEnemies = 5 + level * 2; // Max enemies on screen var heldEnemy = null; // Kept for compatibility but no longer used var cursorX = 1024; var cursorY = 1366; var gameRunning = true; // Initialize score text var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 40; LK.gui.top.addChild(scoreTxt); // Initialize level text var levelTxt = new Text2('Level: 1', { size: 50, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); levelTxt.x = 50; levelTxt.y = 40; LK.gui.topRight.addChild(levelTxt); // Initialize lives text var livesTxt = new Text2('Lives: 3', { size: 50, fill: 0xFFFFFF }); livesTxt.anchor.set(1, 0); livesTxt.x = -50; livesTxt.y = 40; LK.gui.topRight.addChild(livesTxt); // Helper function to spawn an enemy function spawnEnemy() { if (enemies.length >= maxEnemies || !gameRunning) return; // Create enemy with speed and lifetime based on level var enemySpeed = 1 + level * 0.3; var lifetime = 2000 - level * 100; lifetime = Math.max(lifetime, 800); // Minimum 800ms to destroy var enemy = new Enemy(enemySpeed, lifetime); // Position randomly, keeping away from edges enemy.x = Math.random() * (2048 - 200) + 100; enemy.y = Math.random() * (2732 - 200) + 100; // Add to game and array game.addChild(enemy); enemies.push(enemy); // Reset spawn timer spawnTimer = 0; } // Increase difficulty level function levelUp() { level++; // Increase difficulty parameters maxEnemies = 5 + level * 2; spawnInterval = Math.max(minSpawnInterval, 3000 - level * 200); // Update level display levelTxt.setText('Level: ' + level); // Play level up sound LK.getSound('levelup').play(); // Visual feedback LK.effects.flashScreen(0x6666FF, 500); } // Check for level up condition function checkLevelUp() { // Level up every 10 score points if (score > 0 && score % 10 === 0) { levelUp(); } } // Update score function updateScore(points) { var oldScore = score; score += points; scoreTxt.setText('Score: ' + score); // Check if we should level up if (Math.floor(score / 10) > Math.floor(oldScore / 10)) { levelUp(); } } // Lose a life function loseLife() { lives--; livesTxt.setText('Lives: ' + lives); // Visual feedback LK.effects.flashScreen(0xFF0000, 500); // Check for game over if (lives <= 0) { gameOver(); } } // Game over function gameOver() { gameRunning = false; LK.setScore(score); LK.showGameOver(); } // Mouse/touch down event game.down = function (x, y) { if (!gameRunning) return; cursorX = x; cursorY = y; // Check if we hit an enemy for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Simple distance check (could be replaced with intersects()) var dx = enemy.x - x; var dy = enemy.y - y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 50) { // Instead of holding, destroy enemy immediately enemy.isDying = true; updateScore(enemy.value); enemy.destroy(); // Remove from array enemies.splice(i, 1); LK.getSound('catch').play(); break; } } }; // Mouse/touch move event game.move = function (x, y) { cursorX = x; cursorY = y; }; // Mouse/touch up event is no longer needed for tap-to-kill game.up = function () {}; // Main game update loop game.update = function () { if (!gameRunning) return; // Spawn enemies spawnTimer += 16.67; // Approx 1 frame at 60fps if (spawnTimer >= spawnInterval) { spawnEnemy(); } // Check for escaped enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; // Check if enemy is completely off screen if (enemy.x < -100 || enemy.x > 2148 || enemy.y < -100 || enemy.y > 2832) { // Remove enemy and lose a life enemy.parent.removeChild(enemy); enemies.splice(i, 1); loseLife(); continue; } // Update enemy enemy.update(cursorX, cursorY); } // We no longer need to handle held enemies as they are destroyed immediately on tap // Keeping empty block to maintain line identifiers }; // Start game music LK.playMusic('gameBgm', { fade: { start: 0, end: 0.4, duration: 1000 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function (speed, lifetime) {
var self = Container.call(this);
// Create eye white
var eyeWhite = self.attachAsset('eye', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
// Create pupil
var pupil = self.attachAsset('pupil', {
anchorX: 0.5,
anchorY: 0.5
});
// Create hit indicator (initially invisible)
var hitCircle = self.attachAsset('hitCircle', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Set properties
self.speed = speed || 1;
self.vx = (Math.random() - 0.5) * self.speed * 3;
self.vy = (Math.random() - 0.5) * self.speed * 3;
self.targetTime = lifetime || 2000; // Time needed to hold to destroy (in ms)
self.holdTime = 0; // Current time being held
self.isHeld = false;
self.isDying = false;
self.value = Math.floor(self.targetTime / 500); // Score value based on difficulty
// Looking animation
self.lookTimer = 0;
self.lookInterval = Math.random() * 1000 + 500;
self.lookAt = function (x, y) {
// Calculate angle to look at cursor
var dx = x - self.x;
var dy = y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Limit pupil movement within eye
var maxDistance = eyeWhite.width * 0.3;
var scale = distance > maxDistance ? maxDistance / distance : 1;
// Move pupil
pupil.x = dx * scale * 0.3;
pupil.y = dy * scale * 0.3;
};
// Hold and destroy functionality
self.hold = function (deltaTime) {
if (self.isDying) return;
if (!self.isHeld) {
self.isHeld = true;
hitCircle.alpha = 0.7;
hitCircle.scaleX = 0;
hitCircle.scaleY = 0;
tween(hitCircle, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
self.holdTime += deltaTime;
// Update visual feedback
var progress = Math.min(self.holdTime / self.targetTime, 1);
hitCircle.alpha = 0.3 + progress * 0.7;
eyeWhite.scaleX = eyeWhite.scaleY = 1 + progress * 0.2;
// Check if enemy is destroyed
if (self.holdTime >= self.targetTime) {
self.destroy();
return true;
}
return false;
};
self.release = function () {
if (self.isHeld && !self.isDying) {
self.isHeld = false;
// Shrink hit circle
tween(hitCircle, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 200,
easing: tween.easeIn
});
// Shrink eye back to normal
tween(eyeWhite, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeOut
});
// Decay hold time when released
self.holdTime = Math.max(0, self.holdTime - 500);
}
};
self.destroy = function () {
if (self.isDying) return;
self.isDying = true;
LK.getSound('destroy').play();
// Play destroy animation
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove from parent after animation
if (self.parent) {
self.parent.removeChild(self);
}
}
});
};
self.update = function (cursorX, cursorY) {
if (self.isDying) return;
// Move the enemy
self.x += self.vx;
self.y += self.vy;
// Bounce off edges
if (self.x < 50 || self.x > 2048 - 50) {
self.vx *= -1;
}
if (self.y < 50 || self.y > 2732 - 50) {
self.vy *= -1;
}
// Look at cursor if provided
if (cursorX !== undefined && cursorY !== undefined) {
self.lookAt(cursorX, cursorY);
} else {
// Random eye movement when cursor not provided
self.lookTimer += 16.67; // Approx 1 frame at 60fps
if (self.lookTimer > self.lookInterval) {
pupil.x = (Math.random() - 0.5) * 15;
pupil.y = (Math.random() - 0.5) * 15;
self.lookTimer = 0;
self.lookInterval = Math.random() * 1000 + 500;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111
});
/****
* Game Code
****/
// Background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
// Game variables
var enemies = [];
var score = 0;
var level = 1;
var lives = 3;
var spawnTimer = 0;
var spawnInterval = 3000; // Initial spawn interval
var minSpawnInterval = 500; // Minimum spawn interval at highest difficulty
var maxEnemies = 5 + level * 2; // Max enemies on screen
var heldEnemy = null; // Kept for compatibility but no longer used
var cursorX = 1024;
var cursorY = 1366;
var gameRunning = true;
// Initialize score text
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 40;
LK.gui.top.addChild(scoreTxt);
// Initialize level text
var levelTxt = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
levelTxt.x = 50;
levelTxt.y = 40;
LK.gui.topRight.addChild(levelTxt);
// Initialize lives text
var livesTxt = new Text2('Lives: 3', {
size: 50,
fill: 0xFFFFFF
});
livesTxt.anchor.set(1, 0);
livesTxt.x = -50;
livesTxt.y = 40;
LK.gui.topRight.addChild(livesTxt);
// Helper function to spawn an enemy
function spawnEnemy() {
if (enemies.length >= maxEnemies || !gameRunning) return;
// Create enemy with speed and lifetime based on level
var enemySpeed = 1 + level * 0.3;
var lifetime = 2000 - level * 100;
lifetime = Math.max(lifetime, 800); // Minimum 800ms to destroy
var enemy = new Enemy(enemySpeed, lifetime);
// Position randomly, keeping away from edges
enemy.x = Math.random() * (2048 - 200) + 100;
enemy.y = Math.random() * (2732 - 200) + 100;
// Add to game and array
game.addChild(enemy);
enemies.push(enemy);
// Reset spawn timer
spawnTimer = 0;
}
// Increase difficulty level
function levelUp() {
level++;
// Increase difficulty parameters
maxEnemies = 5 + level * 2;
spawnInterval = Math.max(minSpawnInterval, 3000 - level * 200);
// Update level display
levelTxt.setText('Level: ' + level);
// Play level up sound
LK.getSound('levelup').play();
// Visual feedback
LK.effects.flashScreen(0x6666FF, 500);
}
// Check for level up condition
function checkLevelUp() {
// Level up every 10 score points
if (score > 0 && score % 10 === 0) {
levelUp();
}
}
// Update score
function updateScore(points) {
var oldScore = score;
score += points;
scoreTxt.setText('Score: ' + score);
// Check if we should level up
if (Math.floor(score / 10) > Math.floor(oldScore / 10)) {
levelUp();
}
}
// Lose a life
function loseLife() {
lives--;
livesTxt.setText('Lives: ' + lives);
// Visual feedback
LK.effects.flashScreen(0xFF0000, 500);
// Check for game over
if (lives <= 0) {
gameOver();
}
}
// Game over
function gameOver() {
gameRunning = false;
LK.setScore(score);
LK.showGameOver();
}
// Mouse/touch down event
game.down = function (x, y) {
if (!gameRunning) return;
cursorX = x;
cursorY = y;
// Check if we hit an enemy
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Simple distance check (could be replaced with intersects())
var dx = enemy.x - x;
var dy = enemy.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 50) {
// Instead of holding, destroy enemy immediately
enemy.isDying = true;
updateScore(enemy.value);
enemy.destroy();
// Remove from array
enemies.splice(i, 1);
LK.getSound('catch').play();
break;
}
}
};
// Mouse/touch move event
game.move = function (x, y) {
cursorX = x;
cursorY = y;
};
// Mouse/touch up event is no longer needed for tap-to-kill
game.up = function () {};
// Main game update loop
game.update = function () {
if (!gameRunning) return;
// Spawn enemies
spawnTimer += 16.67; // Approx 1 frame at 60fps
if (spawnTimer >= spawnInterval) {
spawnEnemy();
}
// Check for escaped enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Check if enemy is completely off screen
if (enemy.x < -100 || enemy.x > 2148 || enemy.y < -100 || enemy.y > 2832) {
// Remove enemy and lose a life
enemy.parent.removeChild(enemy);
enemies.splice(i, 1);
loseLife();
continue;
}
// Update enemy
enemy.update(cursorX, cursorY);
}
// We no longer need to handle held enemies as they are destroyed immediately on tap
// Keeping empty block to maintain line identifiers
};
// Start game music
LK.playMusic('gameBgm', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});