/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Enemy Cell Class var EnemyCell = Container.expand(function () { var self = Container.call(this); var cell = self.attachAsset('enemyCell', { anchorX: 0.5, anchorY: 0.5 }); self.radius = cell.width / 2; self.lastX = self.x; self.lastY = self.y; self.lastRadius = self.radius; self.setSize = function (diameter) { cell.width = diameter; cell.height = diameter; self.radius = diameter / 2; }; // AI movement target self.targetX = self.x; self.targetY = self.y; self.speed = (3 + 40 / (self.radius + 40)) * 0.3; // 70% slower movement for enemies self.update = function () { // Move towards target var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); self.lastX = self.x; self.lastY = self.y; self.lastRadius = self.radius; if (dist > self.speed) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } else { self.x = self.targetX; self.y = self.targetY; } }; return self; }); // Food Cell Class var FoodCell = Container.expand(function () { var self = Container.call(this); var cell = self.attachAsset('foodCell', { anchorX: 0.5, anchorY: 0.5 }); self.radius = cell.width / 2; self.setSize = function (diameter) { cell.width = diameter; cell.height = diameter; self.radius = diameter / 2; }; return self; }); // Player Cell Class var PlayerCell = Container.expand(function () { var self = Container.call(this); var cell = self.attachAsset('playerCell', { anchorX: 0.5, anchorY: 0.5 }); self.radius = cell.width / 2; self.lastX = self.x; self.lastY = self.y; self.lastRadius = self.radius; self.setSize = function (diameter) { cell.width = diameter; cell.height = diameter; self.radius = diameter / 2; }; // For smooth movement self.targetX = self.x; self.targetY = self.y; self.update = function () { self.lastX = self.x; self.lastY = self.y; self.lastRadius = self.radius; // Drag direction control: move in drag direction if dragging var speed = 4 + 40 / (self.radius + 40); // Slower movement for player, larger cells move slower if (typeof dragDirX !== "undefined" && (dragDirX !== 0 || dragDirY !== 0)) { // Move in drag direction self.x += dragDirX * speed; self.y += dragDirY * speed; } }; return self; }); // Spike Cell Class var SpikeCell = Container.expand(function () { var self = Container.call(this); var cell = self.attachAsset('spikeCell', { anchorX: 0.5, anchorY: 0.5 }); self.radius = cell.width / 2; self.setSize = function (diameter) { cell.width = diameter; cell.height = diameter; self.radius = diameter / 2; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Food cell: yellow ellipse // Enemy cell: blue ellipse // Player cell: green ellipse // Arena size (expanded for a much larger play area) var ARENA_WIDTH = 4096; var ARENA_HEIGHT = 5464; // Player cell var player = new PlayerCell(); player.setSize(180); player.x = ARENA_WIDTH / 2; player.y = ARENA_HEIGHT / 2; game.addChild(player); // Arrays for enemies and food var enemies = []; var foods = []; var spikes = []; // Score display var score = 0; var scoreTxt = new Text2('Score: 0', { size: 90, fill: 0x222222 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Helper: spawn food at random position function spawnFood() { var food = new FoodCell(); var size = 60 + Math.floor(Math.random() * 40); food.setSize(size); food.x = 100 + Math.random() * (ARENA_WIDTH - 200); food.y = 200 + Math.random() * (ARENA_HEIGHT - 400); foods.push(food); game.addChild(food); } // Helper: spawn enemy at random position and size function spawnEnemy() { var enemy = new EnemyCell(); var size = 120 + Math.floor(Math.random() * 120); enemy.setSize(size); // Avoid spawning too close to player var safe = false; while (!safe) { enemy.x = 100 + Math.random() * (ARENA_WIDTH - 200); enemy.y = 200 + Math.random() * (ARENA_HEIGHT - 400); var dx = enemy.x - player.x; var dy = enemy.y - player.y; if (Math.sqrt(dx * dx + dy * dy) > 400) safe = true; } // Set initial AI target enemy.targetX = enemy.x; enemy.targetY = enemy.y; enemies.push(enemy); game.addChild(enemy); } // Helper: spawn spike at random position function spawnSpike() { var spike = new SpikeCell(); var size = 140 + Math.floor(Math.random() * 40); spike.setSize(size); // Avoid spawning too close to player var safe = false; while (!safe) { spike.x = 100 + Math.random() * (ARENA_WIDTH - 200); spike.y = 200 + Math.random() * (ARENA_HEIGHT - 400); var dx = spike.x - player.x; var dy = spike.y - player.y; if (Math.sqrt(dx * dx + dy * dy) > 500) safe = true; } spikes.push(spike); game.addChild(spike); } // Initial spawn for (var i = 0; i < 18; i++) spawnFood(); for (var i = 0; i < 7; i++) spawnEnemy(); for (var i = 0; i < 5; i++) spawnSpike(); // Drag-to-move: swipe direction controls player movement var dragging = false; var dragStartX = 0; var dragStartY = 0; var dragLastX = 0; var dragLastY = 0; var dragDirX = 0; var dragDirY = 0; function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } game.down = function (x, y, obj) { dragging = true; dragStartX = x; dragStartY = y; dragLastX = x; dragLastY = y; dragDirX = 0; dragDirY = 0; }; game.move = function (x, y, obj) { if (dragging) { // Calculate drag direction vector dragDirX = x - dragStartX; dragDirY = y - dragStartY; dragLastX = x; dragLastY = y; // Normalize direction for smooth control var len = Math.sqrt(dragDirX * dragDirX + dragDirY * dragDirY); if (len > 20) { // Only if drag is significant dragDirX /= len; dragDirY /= len; } else { dragDirX = 0; dragDirY = 0; } } }; game.up = function (x, y, obj) { dragging = false; // Do not reset dragDirX/dragDirY here; keep last direction for inertia }; // Main update loop game.update = function () { // Update player player.update(); // Only update and check collisions for enemies and foods near the camera view for performance var camX = clamp(player.x, 1024, ARENA_WIDTH - 1024); var camY = clamp(player.y, 1366, ARENA_HEIGHT - 1366); var camLeft = camX - 1200, camRight = camX + 1200, camTop = camY - 1600, camBottom = camY + 1600; // Update enemies (only those near camera) for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Only update if near camera if (enemy.x > camLeft - 400 && enemy.x < camRight + 400 && enemy.y > camTop - 400 && enemy.y < camBottom + 400) { // AI: If player is smaller, chase; if bigger, flee; else random walk var dx = player.x - enemy.x; var dy = player.y - enemy.y; var dist = Math.sqrt(dx * dx + dy * dy); if (enemy.radius > player.radius * 1.15) { // Enemy is bigger: chase player if not too close to border if (dist < 900) { // Move towards player enemy.targetX = clamp(player.x, enemy.radius, ARENA_WIDTH - enemy.radius); enemy.targetY = clamp(player.y, enemy.radius, ARENA_HEIGHT - enemy.radius); } else { // Random walk if (LK.ticks % 120 === 0) { enemy.targetX = 100 + Math.random() * (ARENA_WIDTH - 200); enemy.targetY = 200 + Math.random() * (ARENA_HEIGHT - 400); } } } else if (enemy.radius < player.radius * 0.85) { // Enemy is smaller: flee from player if (dist < 700) { var angle = Math.atan2(dy, dx) + Math.PI; // Opposite direction var fleeDist = 300; enemy.targetX = clamp(enemy.x + Math.cos(angle) * fleeDist, enemy.radius, ARENA_WIDTH - enemy.radius); enemy.targetY = clamp(enemy.y + Math.sin(angle) * fleeDist, enemy.radius, ARENA_HEIGHT - enemy.radius); } else { // Random walk if (LK.ticks % 120 === 0) { enemy.targetX = 100 + Math.random() * (ARENA_WIDTH - 200); enemy.targetY = 200 + Math.random() * (ARENA_HEIGHT - 400); } } } else { // Similar size: random walk if (LK.ticks % 120 === 0) { enemy.targetX = 100 + Math.random() * (ARENA_WIDTH - 200); enemy.targetY = 200 + Math.random() * (ARENA_HEIGHT - 400); } } enemy.update(); } } // --- Collision: Player and spike interaction --- var playerHidingUnderSpike = false; for (var i = 0; i < spikes.length; i++) { var spike = spikes[i]; // Only check if spike is near player/camera if (spike.x > camLeft - 300 && spike.x < camRight + 300 && spike.y > camTop - 300 && spike.y < camBottom + 300) { var dx = player.x - spike.x; var dy = player.y - spike.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius + spike.radius * 0.7) { if (player.radius < spike.radius * 0.95) { // Player is small: hide under spike (draw under, no effect) playerHidingUnderSpike = true; // Move player visually under spike (z-order handled by addChild order, so do nothing) } else { // Player is big: split into 3-4 pieces and destroy spike (only once per spike) var splitCount = 3 + Math.floor(Math.random() * 2); // 3 or 4 for (var s = 0; s < splitCount; s++) { var food = new FoodCell(); var size = 60 + Math.floor(Math.random() * 30); food.setSize(size); var angle = Math.random() * Math.PI * 2; var distF = 60 + Math.random() * 40; food.x = player.x + Math.cos(angle) * distF; food.y = player.y + Math.sin(angle) * distF; foods.push(food); game.addChild(food); } // Shrink player drastically player.setSize(Math.max(120, player.radius * 0.4)); LK.effects.flashObject(player, 0x888888, 800); // Destroy spike and remove from array spike.destroy(); spikes.splice(i, 1); break; // Only trigger once per frame } } } } // --- Collision: Player eats food (only foods near player/camera) --- for (var i = foods.length - 1; i >= 0; i--) { var food = foods[i]; // Only check if food is near player/camera if (food.x > camLeft - 200 && food.x < camRight + 200 && food.y > camTop - 200 && food.y < camBottom + 200) { var dx = player.x - food.x; var dy = player.y - food.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius + food.radius * 0.7) { // Eat food: grow player by a fixed amount var growAmount = Math.max(12, food.radius * 0.6); var newDiameter = player.radius * 2 + growAmount; newDiameter = Math.min(newDiameter, 600); // Cap max size player.setSize(newDiameter); // Score score += 1; scoreTxt.setText('Score: ' + score); // Remove food food.destroy(); foods.splice(i, 1); // Spawn new food spawnFood(); } } } // --- Collision: Enemy eats food (only if both are near camera) --- for (var j = 0; j < enemies.length; j++) { var enemy = enemies[j]; if (enemy.x > camLeft - 400 && enemy.x < camRight + 400 && enemy.y > camTop - 400 && enemy.y < camBottom + 400) { for (var i = foods.length - 1; i >= 0; i--) { var food = foods[i]; if (food.x > camLeft - 200 && food.x < camRight + 200 && food.y > camTop - 200 && food.y < camBottom + 200) { var dx = enemy.x - food.x; var dy = enemy.y - food.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < enemy.radius + food.radius * 0.7) { // Enemy eats food var newSize = Math.min(enemy.radius * 2 + 12, 400); enemy.setSize(newSize); food.destroy(); foods.splice(i, 1); spawnFood(); } } } } } // --- Collision: Player eats enemy or is eaten --- for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; var dx = player.x - enemy.x; var dy = player.y - enemy.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius + enemy.radius * 0.7) { if (player.radius > enemy.radius * 1.15) { // Player eats enemy var newSize = Math.min(player.radius * 2 + enemy.radius * 2 * 0.7, 700); player.setSize(newSize); score += 5; scoreTxt.setText('Score: ' + score); enemy.destroy(); enemies.splice(i, 1); // After removing, ensure at least one enemy is always bigger than the player var hasBigger = false; for (var j = 0; j < enemies.length; j++) { if (enemies[j].radius > player.radius * 1.15) { hasBigger = true; break; } } if (!hasBigger) { // Spawn a new enemy that is always bigger than the player var bigEnemy = new EnemyCell(); var bigSize = Math.max(player.radius * 1.25, 220 + Math.random() * 80); bigEnemy.setSize(bigSize); // Place far from player var safe = false; while (!safe) { bigEnemy.x = 100 + Math.random() * (ARENA_WIDTH - 200); bigEnemy.y = 200 + Math.random() * (ARENA_HEIGHT - 400); var dx2 = bigEnemy.x - player.x; var dy2 = bigEnemy.y - player.y; if (Math.sqrt(dx2 * dx2 + dy2 * dy2) > 600) safe = true; } bigEnemy.targetX = bigEnemy.x; bigEnemy.targetY = bigEnemy.y; enemies.push(bigEnemy); game.addChild(bigEnemy); } else { spawnEnemy(); } } else if (enemy.radius > player.radius * 1.15) { // Player is eaten LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } } } // --- Enemy eats enemy --- for (var i = enemies.length - 1; i >= 0; i--) { var enemyA = enemies[i]; for (var j = enemies.length - 1; j >= 0; j--) { if (i === j) continue; var enemyB = enemies[j]; var dx = enemyA.x - enemyB.x; var dy = enemyA.y - enemyB.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < enemyA.radius + enemyB.radius * 0.7) { if (enemyA.radius > enemyB.radius * 1.15) { // A eats B var newSize = Math.min(enemyA.radius * 2 + enemyB.radius * 2 * 0.5, 500); enemyA.setSize(newSize); enemyB.destroy(); enemies.splice(j, 1); spawnEnemy(); if (j < i) i--; // Adjust index if needed } } } } // Clamp player inside arena player.x = clamp(player.x, player.radius, ARENA_WIDTH - player.radius); player.y = clamp(player.y, player.radius, ARENA_HEIGHT - player.radius); // Clamp enemies inside arena for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; enemy.x = clamp(enemy.x, enemy.radius, ARENA_WIDTH - enemy.radius); enemy.y = clamp(enemy.y, enemy.radius, ARENA_HEIGHT - enemy.radius); } // Camera follow: center the visible area on the player // LK.stage is the root container for all game objects // The visible area is 2048x2732 (game resolution) var camX = clamp(player.x, 1024, ARENA_WIDTH - 1024); var camY = clamp(player.y, 1366, ARENA_HEIGHT - 1366); game.x = 1024 - camX; game.y = 1366 - camY; };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy Cell Class
var EnemyCell = Container.expand(function () {
var self = Container.call(this);
var cell = self.attachAsset('enemyCell', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = cell.width / 2;
self.lastX = self.x;
self.lastY = self.y;
self.lastRadius = self.radius;
self.setSize = function (diameter) {
cell.width = diameter;
cell.height = diameter;
self.radius = diameter / 2;
};
// AI movement target
self.targetX = self.x;
self.targetY = self.y;
self.speed = (3 + 40 / (self.radius + 40)) * 0.3; // 70% slower movement for enemies
self.update = function () {
// Move towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
self.lastX = self.x;
self.lastY = self.y;
self.lastRadius = self.radius;
if (dist > self.speed) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
} else {
self.x = self.targetX;
self.y = self.targetY;
}
};
return self;
});
// Food Cell Class
var FoodCell = Container.expand(function () {
var self = Container.call(this);
var cell = self.attachAsset('foodCell', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = cell.width / 2;
self.setSize = function (diameter) {
cell.width = diameter;
cell.height = diameter;
self.radius = diameter / 2;
};
return self;
});
// Player Cell Class
var PlayerCell = Container.expand(function () {
var self = Container.call(this);
var cell = self.attachAsset('playerCell', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = cell.width / 2;
self.lastX = self.x;
self.lastY = self.y;
self.lastRadius = self.radius;
self.setSize = function (diameter) {
cell.width = diameter;
cell.height = diameter;
self.radius = diameter / 2;
};
// For smooth movement
self.targetX = self.x;
self.targetY = self.y;
self.update = function () {
self.lastX = self.x;
self.lastY = self.y;
self.lastRadius = self.radius;
// Drag direction control: move in drag direction if dragging
var speed = 4 + 40 / (self.radius + 40); // Slower movement for player, larger cells move slower
if (typeof dragDirX !== "undefined" && (dragDirX !== 0 || dragDirY !== 0)) {
// Move in drag direction
self.x += dragDirX * speed;
self.y += dragDirY * speed;
}
};
return self;
});
// Spike Cell Class
var SpikeCell = Container.expand(function () {
var self = Container.call(this);
var cell = self.attachAsset('spikeCell', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = cell.width / 2;
self.setSize = function (diameter) {
cell.width = diameter;
cell.height = diameter;
self.radius = diameter / 2;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Food cell: yellow ellipse
// Enemy cell: blue ellipse
// Player cell: green ellipse
// Arena size (expanded for a much larger play area)
var ARENA_WIDTH = 4096;
var ARENA_HEIGHT = 5464;
// Player cell
var player = new PlayerCell();
player.setSize(180);
player.x = ARENA_WIDTH / 2;
player.y = ARENA_HEIGHT / 2;
game.addChild(player);
// Arrays for enemies and food
var enemies = [];
var foods = [];
var spikes = [];
// Score display
var score = 0;
var scoreTxt = new Text2('Score: 0', {
size: 90,
fill: 0x222222
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Helper: spawn food at random position
function spawnFood() {
var food = new FoodCell();
var size = 60 + Math.floor(Math.random() * 40);
food.setSize(size);
food.x = 100 + Math.random() * (ARENA_WIDTH - 200);
food.y = 200 + Math.random() * (ARENA_HEIGHT - 400);
foods.push(food);
game.addChild(food);
}
// Helper: spawn enemy at random position and size
function spawnEnemy() {
var enemy = new EnemyCell();
var size = 120 + Math.floor(Math.random() * 120);
enemy.setSize(size);
// Avoid spawning too close to player
var safe = false;
while (!safe) {
enemy.x = 100 + Math.random() * (ARENA_WIDTH - 200);
enemy.y = 200 + Math.random() * (ARENA_HEIGHT - 400);
var dx = enemy.x - player.x;
var dy = enemy.y - player.y;
if (Math.sqrt(dx * dx + dy * dy) > 400) safe = true;
}
// Set initial AI target
enemy.targetX = enemy.x;
enemy.targetY = enemy.y;
enemies.push(enemy);
game.addChild(enemy);
}
// Helper: spawn spike at random position
function spawnSpike() {
var spike = new SpikeCell();
var size = 140 + Math.floor(Math.random() * 40);
spike.setSize(size);
// Avoid spawning too close to player
var safe = false;
while (!safe) {
spike.x = 100 + Math.random() * (ARENA_WIDTH - 200);
spike.y = 200 + Math.random() * (ARENA_HEIGHT - 400);
var dx = spike.x - player.x;
var dy = spike.y - player.y;
if (Math.sqrt(dx * dx + dy * dy) > 500) safe = true;
}
spikes.push(spike);
game.addChild(spike);
}
// Initial spawn
for (var i = 0; i < 18; i++) spawnFood();
for (var i = 0; i < 7; i++) spawnEnemy();
for (var i = 0; i < 5; i++) spawnSpike();
// Drag-to-move: swipe direction controls player movement
var dragging = false;
var dragStartX = 0;
var dragStartY = 0;
var dragLastX = 0;
var dragLastY = 0;
var dragDirX = 0;
var dragDirY = 0;
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
game.down = function (x, y, obj) {
dragging = true;
dragStartX = x;
dragStartY = y;
dragLastX = x;
dragLastY = y;
dragDirX = 0;
dragDirY = 0;
};
game.move = function (x, y, obj) {
if (dragging) {
// Calculate drag direction vector
dragDirX = x - dragStartX;
dragDirY = y - dragStartY;
dragLastX = x;
dragLastY = y;
// Normalize direction for smooth control
var len = Math.sqrt(dragDirX * dragDirX + dragDirY * dragDirY);
if (len > 20) {
// Only if drag is significant
dragDirX /= len;
dragDirY /= len;
} else {
dragDirX = 0;
dragDirY = 0;
}
}
};
game.up = function (x, y, obj) {
dragging = false;
// Do not reset dragDirX/dragDirY here; keep last direction for inertia
};
// Main update loop
game.update = function () {
// Update player
player.update();
// Only update and check collisions for enemies and foods near the camera view for performance
var camX = clamp(player.x, 1024, ARENA_WIDTH - 1024);
var camY = clamp(player.y, 1366, ARENA_HEIGHT - 1366);
var camLeft = camX - 1200,
camRight = camX + 1200,
camTop = camY - 1600,
camBottom = camY + 1600;
// Update enemies (only those near camera)
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Only update if near camera
if (enemy.x > camLeft - 400 && enemy.x < camRight + 400 && enemy.y > camTop - 400 && enemy.y < camBottom + 400) {
// AI: If player is smaller, chase; if bigger, flee; else random walk
var dx = player.x - enemy.x;
var dy = player.y - enemy.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (enemy.radius > player.radius * 1.15) {
// Enemy is bigger: chase player if not too close to border
if (dist < 900) {
// Move towards player
enemy.targetX = clamp(player.x, enemy.radius, ARENA_WIDTH - enemy.radius);
enemy.targetY = clamp(player.y, enemy.radius, ARENA_HEIGHT - enemy.radius);
} else {
// Random walk
if (LK.ticks % 120 === 0) {
enemy.targetX = 100 + Math.random() * (ARENA_WIDTH - 200);
enemy.targetY = 200 + Math.random() * (ARENA_HEIGHT - 400);
}
}
} else if (enemy.radius < player.radius * 0.85) {
// Enemy is smaller: flee from player
if (dist < 700) {
var angle = Math.atan2(dy, dx) + Math.PI; // Opposite direction
var fleeDist = 300;
enemy.targetX = clamp(enemy.x + Math.cos(angle) * fleeDist, enemy.radius, ARENA_WIDTH - enemy.radius);
enemy.targetY = clamp(enemy.y + Math.sin(angle) * fleeDist, enemy.radius, ARENA_HEIGHT - enemy.radius);
} else {
// Random walk
if (LK.ticks % 120 === 0) {
enemy.targetX = 100 + Math.random() * (ARENA_WIDTH - 200);
enemy.targetY = 200 + Math.random() * (ARENA_HEIGHT - 400);
}
}
} else {
// Similar size: random walk
if (LK.ticks % 120 === 0) {
enemy.targetX = 100 + Math.random() * (ARENA_WIDTH - 200);
enemy.targetY = 200 + Math.random() * (ARENA_HEIGHT - 400);
}
}
enemy.update();
}
}
// --- Collision: Player and spike interaction ---
var playerHidingUnderSpike = false;
for (var i = 0; i < spikes.length; i++) {
var spike = spikes[i];
// Only check if spike is near player/camera
if (spike.x > camLeft - 300 && spike.x < camRight + 300 && spike.y > camTop - 300 && spike.y < camBottom + 300) {
var dx = player.x - spike.x;
var dy = player.y - spike.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < player.radius + spike.radius * 0.7) {
if (player.radius < spike.radius * 0.95) {
// Player is small: hide under spike (draw under, no effect)
playerHidingUnderSpike = true;
// Move player visually under spike (z-order handled by addChild order, so do nothing)
} else {
// Player is big: split into 3-4 pieces and destroy spike (only once per spike)
var splitCount = 3 + Math.floor(Math.random() * 2); // 3 or 4
for (var s = 0; s < splitCount; s++) {
var food = new FoodCell();
var size = 60 + Math.floor(Math.random() * 30);
food.setSize(size);
var angle = Math.random() * Math.PI * 2;
var distF = 60 + Math.random() * 40;
food.x = player.x + Math.cos(angle) * distF;
food.y = player.y + Math.sin(angle) * distF;
foods.push(food);
game.addChild(food);
}
// Shrink player drastically
player.setSize(Math.max(120, player.radius * 0.4));
LK.effects.flashObject(player, 0x888888, 800);
// Destroy spike and remove from array
spike.destroy();
spikes.splice(i, 1);
break; // Only trigger once per frame
}
}
}
}
// --- Collision: Player eats food (only foods near player/camera) ---
for (var i = foods.length - 1; i >= 0; i--) {
var food = foods[i];
// Only check if food is near player/camera
if (food.x > camLeft - 200 && food.x < camRight + 200 && food.y > camTop - 200 && food.y < camBottom + 200) {
var dx = player.x - food.x;
var dy = player.y - food.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < player.radius + food.radius * 0.7) {
// Eat food: grow player by a fixed amount
var growAmount = Math.max(12, food.radius * 0.6);
var newDiameter = player.radius * 2 + growAmount;
newDiameter = Math.min(newDiameter, 600); // Cap max size
player.setSize(newDiameter);
// Score
score += 1;
scoreTxt.setText('Score: ' + score);
// Remove food
food.destroy();
foods.splice(i, 1);
// Spawn new food
spawnFood();
}
}
}
// --- Collision: Enemy eats food (only if both are near camera) ---
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
if (enemy.x > camLeft - 400 && enemy.x < camRight + 400 && enemy.y > camTop - 400 && enemy.y < camBottom + 400) {
for (var i = foods.length - 1; i >= 0; i--) {
var food = foods[i];
if (food.x > camLeft - 200 && food.x < camRight + 200 && food.y > camTop - 200 && food.y < camBottom + 200) {
var dx = enemy.x - food.x;
var dy = enemy.y - food.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < enemy.radius + food.radius * 0.7) {
// Enemy eats food
var newSize = Math.min(enemy.radius * 2 + 12, 400);
enemy.setSize(newSize);
food.destroy();
foods.splice(i, 1);
spawnFood();
}
}
}
}
}
// --- Collision: Player eats enemy or is eaten ---
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
var dx = player.x - enemy.x;
var dy = player.y - enemy.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < player.radius + enemy.radius * 0.7) {
if (player.radius > enemy.radius * 1.15) {
// Player eats enemy
var newSize = Math.min(player.radius * 2 + enemy.radius * 2 * 0.7, 700);
player.setSize(newSize);
score += 5;
scoreTxt.setText('Score: ' + score);
enemy.destroy();
enemies.splice(i, 1);
// After removing, ensure at least one enemy is always bigger than the player
var hasBigger = false;
for (var j = 0; j < enemies.length; j++) {
if (enemies[j].radius > player.radius * 1.15) {
hasBigger = true;
break;
}
}
if (!hasBigger) {
// Spawn a new enemy that is always bigger than the player
var bigEnemy = new EnemyCell();
var bigSize = Math.max(player.radius * 1.25, 220 + Math.random() * 80);
bigEnemy.setSize(bigSize);
// Place far from player
var safe = false;
while (!safe) {
bigEnemy.x = 100 + Math.random() * (ARENA_WIDTH - 200);
bigEnemy.y = 200 + Math.random() * (ARENA_HEIGHT - 400);
var dx2 = bigEnemy.x - player.x;
var dy2 = bigEnemy.y - player.y;
if (Math.sqrt(dx2 * dx2 + dy2 * dy2) > 600) safe = true;
}
bigEnemy.targetX = bigEnemy.x;
bigEnemy.targetY = bigEnemy.y;
enemies.push(bigEnemy);
game.addChild(bigEnemy);
} else {
spawnEnemy();
}
} else if (enemy.radius > player.radius * 1.15) {
// Player is eaten
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
// --- Enemy eats enemy ---
for (var i = enemies.length - 1; i >= 0; i--) {
var enemyA = enemies[i];
for (var j = enemies.length - 1; j >= 0; j--) {
if (i === j) continue;
var enemyB = enemies[j];
var dx = enemyA.x - enemyB.x;
var dy = enemyA.y - enemyB.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < enemyA.radius + enemyB.radius * 0.7) {
if (enemyA.radius > enemyB.radius * 1.15) {
// A eats B
var newSize = Math.min(enemyA.radius * 2 + enemyB.radius * 2 * 0.5, 500);
enemyA.setSize(newSize);
enemyB.destroy();
enemies.splice(j, 1);
spawnEnemy();
if (j < i) i--; // Adjust index if needed
}
}
}
}
// Clamp player inside arena
player.x = clamp(player.x, player.radius, ARENA_WIDTH - player.radius);
player.y = clamp(player.y, player.radius, ARENA_HEIGHT - player.radius);
// Clamp enemies inside arena
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
enemy.x = clamp(enemy.x, enemy.radius, ARENA_WIDTH - enemy.radius);
enemy.y = clamp(enemy.y, enemy.radius, ARENA_HEIGHT - enemy.radius);
}
// Camera follow: center the visible area on the player
// LK.stage is the root container for all game objects
// The visible area is 2048x2732 (game resolution)
var camX = clamp(player.x, 1024, ARENA_WIDTH - 1024);
var camY = clamp(player.y, 1366, ARENA_HEIGHT - 1366);
game.x = 1024 - camX;
game.y = 1366 - camY;
};