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