/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var AntSwarm = Container.expand(function () {
var self = Container.call(this);
// Create individual ants in the swarm
self.ants = [];
var antCount = 15;
for (var i = 0; i < antCount; i++) {
var ant = self.attachAsset('ant', {
anchorX: 0.5,
anchorY: 0.5
});
ant.tint = 0x8B0000; // Dark red color for poisonous ants
ant.x = Math.random() * 200 - 100; // Spread ants in formation
ant.y = Math.random() * 150 - 75;
ant.scaleX = 0.7;
ant.scaleY = 0.7;
ant.alpha = 0.9;
ant.rotation = 0; // Face right (default orientation)
ant.lastX = ant.x;
ant.lastY = ant.y;
self.ants.push(ant);
}
self.speed = 2;
self.isActive = false;
self.duration = 0;
self.maxDuration = 1800; // 30 seconds at 60fps - stay until crossing screen
self.activate = function (yPosition) {
self.y = yPosition;
self.x = -200; // Start off-screen left
self.isActive = true;
self.duration = 0;
};
self.update = function () {
if (self.isActive) {
// Store old position for collision checking
var oldX = self.x;
var oldY = self.y;
// Move ant swarm from left to right
self.x += self.speed;
self.duration++;
// Check collision with stones for the entire swarm
var swarmHitStone = false;
for (var s = 0; s < stones.length; s++) {
var stone = stones[s];
var swarmCenterX = self.x;
var swarmCenterY = self.y;
var dx = swarmCenterX - stone.x;
var dy = swarmCenterY - stone.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < stone.radius + 150) {
// Swarm collision radius
swarmHitStone = true;
// Move swarm around the stone
if (swarmCenterY < stone.y) {
// Go above the stone
self.y -= 3;
} else {
// Go below the stone
self.y += 3;
}
break;
}
}
// Keep ants in formation without circular movement
for (var i = 0; i < self.ants.length; i++) {
var ant = self.ants[i];
// No circular movement - ants maintain their formation positions
}
// Deactivate when completely off-screen right
if (self.x > 2248) {
// 2048 + 200 buffer
self.isActive = false;
self.x = -200;
}
}
};
return self;
});
var Bug = Container.expand(function (bugType) {
var self = Container.call(this);
var type = bugType || 'fly';
var bugGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.bugType = type;
self.speed = type === 'fly' ? 6 : type === 'butterfly' ? 5 : 4;
self.velocityX = (Math.random() - 0.5) * self.speed;
self.velocityY = (Math.random() - 0.5) * self.speed;
self.changeDirectionTimer = 0;
self.isCaught = false;
self.isNewborn = true; // New bugs are newborn
self.newbornTimer = 180; // 3 seconds at 60fps
self.isInvulnerable = true; // Invulnerable during newborn period
self.blinkTween = null; // Track blinking animation
// Start blinking animation for newborn bugs
self.startBlinking = function () {
var _blinkCycle = function blinkCycle() {
self.blinkTween = tween(bugGraphics, {
alpha: 0.3
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.blinkTween = tween(bugGraphics, {
alpha: 1
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.isNewborn) {
_blinkCycle(); // Continue blinking while newborn
}
}
});
}
});
};
_blinkCycle();
};
// Stop blinking animation
self.stopBlinking = function () {
if (self.blinkTween) {
tween.stop(bugGraphics, {
alpha: true
});
self.blinkTween = null;
}
bugGraphics.alpha = 1; // Ensure full opacity
};
// Start blinking for new bugs
self.startBlinking();
self.update = function () {
if (!self.isCaught) {
// Handle newborn timer and invulnerability
if (self.isNewborn) {
self.newbornTimer--;
if (self.newbornTimer <= 0) {
self.isNewborn = false;
self.isInvulnerable = false;
self.stopBlinking();
}
}
// Store old position for collision checking
var oldX = self.x;
var oldY = self.y;
// Move bug randomly
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with stones
for (var s = 0; s < stones.length; s++) {
var stone = stones[s];
var dx = self.x - stone.x;
var dy = self.y - stone.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < stone.radius + 30) {
// Bug collision radius
// Collision detected - bounce off stone
self.x = oldX;
self.y = oldY;
// Reverse velocity and add some randomness
self.velocityX = -self.velocityX + (Math.random() - 0.5) * 2;
self.velocityY = -self.velocityY + (Math.random() - 0.5) * 2;
break;
}
}
self.changeDirectionTimer++;
// Change direction randomly
if (self.changeDirectionTimer > 20 + Math.random() * 30) {
self.velocityX = (Math.random() - 0.5) * self.speed;
self.velocityY = (Math.random() - 0.5) * self.speed;
self.changeDirectionTimer = 0;
}
// Keep bugs within bounds
if (self.x <= 50 || self.x >= 2048 - 50) {
self.velocityX = -self.velocityX;
}
if (self.y <= 50 || self.y >= 2732 - 50) {
self.velocityY = -self.velocityY;
}
// Prevent bugs from entering safe zone (bottom left corner - 400x400 area)
if (self.x <= 400 && self.y >= 2332) {
// Bug is trying to enter safe zone - bounce it away
if (self.x <= 400) {
self.velocityX = Math.abs(self.velocityX); // Force rightward movement
self.x = 401; // Push bug out of safe zone immediately
}
if (self.y >= 2332) {
self.velocityY = -Math.abs(self.velocityY); // Force upward movement
self.y = 2331; // Push bug out of safe zone immediately
}
}
// Add floating animation
bugGraphics.y = Math.sin(LK.ticks * 0.1) * 3;
}
};
return self;
});
var Predator = Container.expand(function (predatorType) {
var self = Container.call(this);
self.predatorType = predatorType || 'snake';
var predatorGraphics = self.attachAsset(self.predatorType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6; // Same speed as spider
self.isActive = false;
self.canMove = true; // Allow movement by default
self.targetX = 0;
self.targetY = 0;
self.lastX = 0;
self.lastY = 0;
self.activate = function (startCorner) {
self.isActive = true;
// Spawn from corners (top-left, top-right)
if (startCorner === 'topLeft') {
self.x = 150; // Avoid the menu area
self.y = 150;
} else {
// topRight
self.x = 2048 - 150;
self.y = 150;
}
self.lastX = self.x;
self.lastY = self.y;
// Add delay and alarm for lizard and snake predators
if (self.predatorType === 'lizard' || self.predatorType === 'snake') {
self.canMove = false;
// Play alarm sound 1 second before predator can move
if (LK.getSound('alarm')) {
LK.getSound('alarm').play();
}
// Allow movement after 1 second delay
LK.setTimeout(function () {
self.canMove = true;
}, 1000);
} else {
self.canMove = true;
}
};
self.deactivate = function () {
self.isActive = false;
self.x = -200; // Move off screen
self.y = -200;
};
self.update = function () {
if (self.isActive && self.canMove) {
self.lastX = self.x;
self.lastY = self.y;
// Chase the spider
var dx = spider.x - self.x;
var dy = spider.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Rotate to face spider
var angle = Math.atan2(dy, dx);
predatorGraphics.rotation = angle;
}
// Keep predator within bounds - reverse direction when hitting boundaries
if (self.x <= 60) {
self.x = 60;
// Reverse direction by moving back towards last position
var backDx = self.lastX - self.x;
var backDy = self.lastY - self.y;
var backDistance = Math.sqrt(backDx * backDx + backDy * backDy);
if (backDistance > 5) {
self.x += backDx / backDistance * self.speed;
self.y += backDy / backDistance * self.speed;
}
}
if (self.x >= 2048 - 60) {
self.x = 2048 - 60;
// Reverse direction by moving back towards last position
var backDx = self.lastX - self.x;
var backDy = self.lastY - self.y;
var backDistance = Math.sqrt(backDx * backDx + backDy * backDy);
if (backDistance > 5) {
self.x += backDx / backDistance * self.speed;
self.y += backDy / backDistance * self.speed;
}
}
if (self.y <= 60) {
self.y = 60;
// Reverse direction by moving back towards last position
var backDx = self.lastX - self.x;
var backDy = self.lastY - self.y;
var backDistance = Math.sqrt(backDx * backDx + backDy * backDy);
if (backDistance > 5) {
self.x += backDx / backDistance * self.speed;
self.y += backDy / backDistance * self.speed;
}
}
if (self.y >= 2732 - 60) {
self.y = 2732 - 60;
// Reverse direction by moving back towards last position
var backDx = self.lastX - self.x;
var backDy = self.lastY - self.y;
var backDistance = Math.sqrt(backDx * backDx + backDy * backDy);
if (backDistance > 5) {
self.x += backDx / backDistance * self.speed;
self.y += backDy / backDistance * self.speed;
}
}
}
};
return self;
});
var Spider = Container.expand(function () {
var self = Container.call(this);
var spiderGraphics = self.attachAsset('spider', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.velocityX = 0;
self.velocityY = 0;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.webNodes = [];
self.isInvulnerable = false;
self.invulnerabilityTimer = 0;
self.moveToTarget = function (targetX, targetY) {
self.targetX = targetX;
self.targetY = targetY;
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
self.isMoving = true;
// Rotate spider to face movement direction
var angle = Math.atan2(dy, dx);
tween(spiderGraphics, {
rotation: angle
}, {
duration: 100
});
}
};
self.createWebNode = function () {
if (currentWebs >= maxWebs) {
// Cannot create more webs
return;
}
// Check if a web node already exists at this position
for (var i = 0; i < self.webNodes.length; i++) {
var existingNode = self.webNodes[i];
var dx = existingNode.x - self.x;
var dy = existingNode.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 30) {
// Web node already exists too close to this position
return;
}
}
// Remove oldest web node if we've reached the limit
if (self.webNodes.length >= maxWebNodes) {
var oldestNode = self.webNodes.shift(); // Remove first (oldest) element
if (oldestNode && oldestNode.parent) {
oldestNode.destroy();
}
}
var webNode = LK.getAsset('web_node', {
anchorX: 0.5,
anchorY: 0.5
});
webNode.x = self.x;
webNode.y = self.y;
self.webNodes.push(webNode);
game.addChild(webNode);
currentWebs++;
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
// Play web creation sound
if (LK.getSound('web_create')) {
LK.getSound('web_create').play();
}
};
self.update = function () {
// Handle invulnerability timer
if (self.isInvulnerable) {
self.invulnerabilityTimer--;
if (self.invulnerabilityTimer <= 0) {
self.isInvulnerable = false;
}
// Add visual feedback for spider invulnerability
spiderGraphics.alpha = 0.5 + 0.5 * Math.sin(LK.ticks * 0.3);
} else {
spiderGraphics.alpha = 1;
}
if (self.isMoving) {
// Store old position for collision checking
var oldX = self.x;
var oldY = self.y;
// Move spider towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 5) {
self.isMoving = false;
self.velocityX = 0;
self.velocityY = 0;
} else {
self.x += self.velocityX;
self.y += self.velocityY;
}
// Check collision with stones
for (var s = 0; s < stones.length; s++) {
var stone = stones[s];
var dx = self.x - stone.x;
var dy = self.y - stone.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < stone.radius + 60) {
// Spider collision radius
// Collision detected - revert to old position and stop movement
self.x = oldX;
self.y = oldY;
self.isMoving = false;
self.velocityX = 0;
self.velocityY = 0;
break;
}
}
// Keep spider within bounds
if (self.x <= 60) {
self.x = 60;
self.isMoving = false;
}
if (self.x >= 2048 - 60) {
self.x = 2048 - 60;
self.isMoving = false;
}
if (self.y <= 60) {
self.y = 60;
self.isMoving = false;
}
if (self.y >= 2732 - 60) {
self.y = 2732 - 60;
self.isMoving = false;
}
}
};
return self;
});
var Stone = Container.expand(function () {
var self = Container.call(this);
// Random stone size between 80-150 pixels
var stoneSize = 80 + Math.random() * 70;
var stoneGraphics = LK.getAsset('stone_shape', {
width: stoneSize,
height: stoneSize,
color: 0x606060,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(stoneGraphics);
// Different shades of gray for stones
var grayShades = [0x404040, 0x505050, 0x606060, 0x707070, 0x808080, 0x909090, 0xa0a0a0, 0xb0b0b0];
stoneGraphics.tint = grayShades[Math.floor(Math.random() * grayShades.length)];
self.radius = stoneSize / 2;
return self;
});
var WebTriangle = Container.expand(function (node1, node2, node3) {
var self = Container.call(this);
self.nodes = [node1, node2, node3];
self.isComplete = false;
self.checkIfComplete = function () {
if (self.nodes.length === 3) {
self.isComplete = true;
return true;
}
return false;
};
self.containsBug = function (bug) {
if (!self.isComplete) return false;
// Simple point-in-triangle check using barycentric coordinates
var x1 = self.nodes[0].x,
y1 = self.nodes[0].y;
var x2 = self.nodes[1].x,
y2 = self.nodes[1].y;
var x3 = self.nodes[2].x,
y3 = self.nodes[2].y;
var px = bug.x,
py = bug.y;
var denom = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3);
var a = ((y2 - y3) * (px - x3) + (x3 - x2) * (py - y3)) / denom;
var b = ((y3 - y1) * (px - x3) + (x1 - x3) * (py - y3)) / denom;
var c = 1 - a - b;
return a >= 0 && b >= 0 && c >= 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c1810 // Dark brown for indoor environment
});
/****
* Game Code
****/
// Set garden environment background color
game.setBackgroundColor(0x654321);
// Create garden ground as single asset
var gardenGround = LK.getAsset('garden_ground', {
anchorX: 0,
anchorY: 0
});
gardenGround.x = 0;
gardenGround.y = 0;
gardenGround.alpha = 0.8;
game.addChild(gardenGround);
// Game variables
var spider;
var stones = [];
var bugs = [];
var webTriangles = [];
var score = 0;
var gameStarted = false;
var doubleClickTimer = 0;
var lastClickTime = 0;
var maxWebs = 30;
var currentWebs = 0;
var maxWebNodes = 30; // Maximum number of web nodes to keep on screen
// Level system variables
var currentLevel = 1;
var baseStoneCount = 2; // Start with 2 stones
var stonesPerLevel = 2; // Add 2 stones per level
var maxStones = 30; // Maximum 30 stones
var levelTargets = {
1: {
type: 'butterfly',
count: 3,
required: 3
},
2: {
type: 'fly',
count: 5,
required: 5
},
3: {
type: 'beetle',
count: 4,
required: 4
},
4: {
type: 'butterfly',
count: 6,
required: 6
},
5: {
type: 'fly',
count: 8,
required: 8
}
};
// Function to generate infinite levels
function getLevelTarget(level) {
if (levelTargets[level]) {
return levelTargets[level];
}
// Generate infinite levels beyond level 5
var bugTypes = ['fly', 'butterfly', 'beetle'];
var typeIndex = (level - 1) % bugTypes.length;
var difficultyMultiplier = Math.floor((level - 1) / 3) + 1;
return {
type: bugTypes[typeIndex],
count: 3 + difficultyMultiplier * 2,
required: 3 + difficultyMultiplier * 2
};
}
var levelProgress = {
butterfly: 0,
fly: 0,
beetle: 0
};
// Level timer variables
var levelStartTime = 0;
var levelTimeLimit = 0;
var levelTimerStarted = false;
var timeSpentOutsideSafeZone = 0; // Track actual time spent outside safe zone for level timer
// Safe zone and predator chase system variables
var safeZone = {
x: 0,
y: 2332,
width: 400,
height: 400
};
var timeOutsideSafeZone = 0;
var predatorSpawnTime = 0; // Random time between 15-35 seconds
var isInSafeZone = false;
var lastIsInSafeZone = false;
var predators = [];
var activePredators = [];
var countdownStarted = false; // Track if countdown has started
var countdownTime = 0; // Actual countdown time
var alarmSoundId = null; // Track continuous alarm sound
var safeZoneBlinkTween = null; // Track continuous safe zone blinking
// Ant swarm system variables
var antSwarms = [];
var antSwarmTimer = 0;
var antSwarmInterval = 1200; // 20 seconds at 60fps
var nextAntSwarmTime = antSwarmInterval;
// Create safe zone visual as a single asset
var safeZoneVisual = LK.getAsset('safe_zone', {
anchorX: 0,
anchorY: 0
});
safeZoneVisual.x = safeZone.x;
safeZoneVisual.y = safeZone.y;
safeZoneVisual.tint = 0xffffff;
safeZoneVisual.alpha = 0.6;
safeZoneVisual.scaleX = 2;
safeZoneVisual.scaleY = 2;
game.addChild(safeZoneVisual);
// Create ant swarms
for (var i = 0; i < 3; i++) {
var antSwarm = new AntSwarm();
antSwarms.push(antSwarm);
game.addChild(antSwarm);
}
// Create predators
for (var i = 0; i < 2; i++) {
var snake = new Predator('snake');
var lizard = new Predator('lizard');
predators.push(snake);
predators.push(lizard);
game.addChild(snake);
game.addChild(lizard);
}
// Initialize random predator spawn time (15-35 seconds)
predatorSpawnTime = (15 + Math.random() * 20) * 60; // 15-35 seconds at 60fps
// Create spider at center of room in safe zone
spider = game.addChild(new Spider());
spider.x = 100; // Start in safe zone
spider.y = 2632; // Start in safe zone
// Create instruction text
var instructionTxt = new Text2('Tap to move spider continuously, double-tap to shoot web while moving!', {
size: 60,
fill: 0xffffff
});
instructionTxt.anchor.set(0.5, 0.5);
instructionTxt.x = 1024;
instructionTxt.y = 1800; // Moved down from center
game.addChild(instructionTxt);
// Score display with star icon
var starIcon = new Text2('⭐', {
size: 80,
fill: 0xffffff
});
starIcon.anchor.set(1, 0);
starIcon.x = -10;
LK.gui.top.addChild(starIcon);
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xffffff
});
scoreTxt.anchor.set(0, 0);
LK.gui.top.addChild(scoreTxt);
// Web count display with web icon next to score
var webIcon = new Text2('🕸️', {
size: 80,
fill: 0xffffff
});
webIcon.anchor.set(0, 0);
webIcon.x = 120; // Position after score text
LK.gui.top.addChild(webIcon);
var webCountDisplayTxt = new Text2('30', {
size: 80,
fill: 0xffffff
});
webCountDisplayTxt.anchor.set(0, 0);
webCountDisplayTxt.x = 200; // Position after web icon
LK.gui.top.addChild(webCountDisplayTxt);
// Target bug display - start at center then animate to top-right corner
var initialTarget = getLevelTarget(currentLevel);
var targetBugImage = LK.getAsset(initialTarget.type, {
anchorX: 0.5,
anchorY: 0.5
});
targetBugImage.scaleX = 2.0; // Start large
targetBugImage.scaleY = 2.0; // Start large
targetBugImage.x = 1024; // Start at center
targetBugImage.y = 1366; // Start at center vertically (2732/2)
game.addChild(targetBugImage);
var targetCountTxt = new Text2(initialTarget.required.toString(), {
size: 120,
fill: 0xffffff
});
targetCountTxt.anchor.set(0.5, 0.5);
targetCountTxt.x = 1124; // Start at center, next to bug image
targetCountTxt.y = 1366; // Start at center
game.addChild(targetCountTxt);
// Animate target bug icon and count to top-right corner
LK.setTimeout(function () {
tween(targetBugImage, {
x: 1968,
y: 60,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetBugImage);
LK.gui.topRight.addChild(targetBugImage);
targetBugImage.x = -80;
targetBugImage.y = 60;
}
});
tween(targetCountTxt, {
x: 2028,
y: 60,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetCountTxt);
LK.gui.topRight.addChild(targetCountTxt);
targetCountTxt.x = -20;
targetCountTxt.y = 60;
}
});
}, 2000); // Show at center for 2 seconds
// Timer display
var timerTxt = new Text2('', {
size: 40,
fill: 0x00ff00
});
timerTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(timerTxt);
// Level timer display
var levelTimerTxt = new Text2('Level Time: 90s', {
size: 45,
fill: 0x00ffff
});
levelTimerTxt.anchor.set(1, 1);
LK.gui.bottomRight.addChild(levelTimerTxt);
// Create random stone obstacles
function createStones() {
// Clear existing stones
for (var k = 0; k < stones.length; k++) {
if (stones[k] && stones[k].destroy) {
stones[k].destroy();
}
}
stones = [];
// Create stones based on current level (start with 2, increase each level, max 30)
var stoneCount = Math.min(maxStones, baseStoneCount + (currentLevel - 1) * stonesPerLevel);
for (var i = 0; i < stoneCount; i++) {
var stone = new Stone();
var validPosition = false;
var attempts = 0;
// Try to find a valid position that doesn't overlap with safe zone or other obstacles
while (!validPosition && attempts < 50) {
stone.x = 100 + Math.random() * 1848; // Keep stones away from edges
stone.y = 100 + Math.random() * 2532;
// Check if stone is not in safe zone (bottom left corner)
if (stone.x > 500 || stone.y < 2232) {
// Check distance from other stones and obstacles
var tooClose = false;
// Check distance from other stones
for (var j = 0; j < stones.length; j++) {
var dx = stone.x - stones[j].x;
var dy = stone.y - stones[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < stone.radius + stones[j].radius + 50) {
tooClose = true;
break;
}
}
if (!tooClose) {
validPosition = true;
}
}
attempts++;
}
if (validPosition) {
stones.push(stone);
game.addChild(stone);
}
}
}
// Create stones
createStones();
// Function to spawn bugs
function spawnBug() {
var bugTypes = ['fly', 'butterfly', 'beetle'];
var randomType = bugTypes[Math.floor(Math.random() * bugTypes.length)];
var bug = new Bug(randomType);
// Spawn in middle areas of screen, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Find valid position for the bug
var validPosition = false;
var attempts = 0;
do {
bug.x = minX + Math.random() * (maxX - minX);
bug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = bug.x - spider.x;
var dy = bug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(bug.x <= 500 && bug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
bugs.push(bug);
game.addChild(bug);
}
// Spawn initial bugs
for (var i = 0; i < 5; i++) {
spawnBug();
}
// Touch input - move spider or create web
game.down = function (x, y, obj) {
var currentTime = LK.ticks;
var timeDiff = currentTime - lastClickTime;
if (timeDiff < 30) {
// Double click detected (within 0.5 seconds at 60fps)
// Create web node at double-click location and continue moving there
spider.createWebNode();
spider.moveToTarget(x, y);
if (LK.getSound('spider_move')) {
LK.getSound('spider_move').play();
}
} else {
// Single click - move spider to tap location
spider.moveToTarget(x, y);
if (LK.getSound('spider_move')) {
LK.getSound('spider_move').play();
}
}
lastClickTime = currentTime;
// Hide instruction text after first tap
if (instructionTxt && instructionTxt.parent) {
tween(instructionTxt, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
instructionTxt.destroy();
}
});
}
gameStarted = true;
// Initialize level timer
if (!levelTimerStarted) {
levelStartTime = LK.ticks;
levelTimeLimit = getLevelTarget(currentLevel).required * 30 * 60; // 30 seconds per bug × 60 fps
timeSpentOutsideSafeZone = 0; // Reset time counter
levelTimerStarted = true;
}
};
// Game update loop
game.update = function () {
// Check if spider is in safe zone
isInSafeZone = spider.x >= safeZone.x && spider.x <= safeZone.x + safeZone.width && spider.y >= safeZone.y && spider.y <= safeZone.y + safeZone.height;
// Handle predator chase system
if (lastIsInSafeZone && !isInSafeZone) {
// Just left safe zone - start countdown
countdownStarted = true;
countdownTime = (15 + Math.random() * 20) * 60; // New random time 15-35 seconds
}
if (!isInSafeZone && countdownStarted) {
countdownTime--;
// Update timer display when predators are not active
if (activePredators.length === 0) {
// Don't show countdown timer
}
// Check if countdown is finished and no predators are active
if (countdownTime <= 0 && activePredators.length === 0) {
// Spawn only one predator randomly
var availablePredators = [];
for (var p = 0; p < predators.length; p++) {
if (!predators[p].isActive) {
availablePredators.push(predators[p]);
}
}
if (availablePredators.length > 0) {
// Start continuous safe zone blinking
var _startSafeZoneBlinking = function startSafeZoneBlinking() {
safeZoneBlinkTween = tween(safeZoneVisual, {
alpha: 0.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
safeZoneBlinkTween = tween(safeZoneVisual, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (activePredators.length > 0) {
_startSafeZoneBlinking(); // Continue blinking while predators are active
}
}
});
}
});
};
// Choose random predator type and corner
var randomPredator = availablePredators[Math.floor(Math.random() * availablePredators.length)];
var randomCorner = Math.random() < 0.5 ? 'topLeft' : 'topRight';
randomPredator.activate(randomCorner);
activePredators.push(randomPredator);
// Start continuous alarm sound
if (LK.getSound('alarm')) {
alarmSoundId = LK.setInterval(function () {
LK.getSound('alarm').play();
}, 1000); // Play every second
}
_startSafeZoneBlinking();
// Don't show predator hunting text
}
}
} else if (isInSafeZone && activePredators.length > 0) {
// Back in safe zone - make predators retreat
for (var p = 0; p < activePredators.length; p++) {
var predator = activePredators[p];
// Move predator back to spawn corner
if (predator.x < 1024) {
// Was from topLeft, move back there
predator.x -= predator.speed * 2;
predator.y -= predator.speed * 2;
if (predator.x <= 50 || predator.y <= 50) {
predator.deactivate();
}
} else {
// Was from topRight, move back there
predator.x += predator.speed * 2;
predator.y -= predator.speed * 2;
if (predator.x >= 1998 || predator.y <= 50) {
predator.deactivate();
}
}
}
// Remove deactivated predators from active list
for (var p = activePredators.length - 1; p >= 0; p--) {
if (!activePredators[p].isActive) {
activePredators.splice(p, 1);
}
}
// Reset countdown when all predators are gone
if (activePredators.length === 0) {
countdownStarted = false;
timerTxt.setText('');
timerTxt.alpha = 0; // Hide timer when in safe zone
safeZoneVisual.alpha = 0.6; // Reset safe zone visual alpha
// Stop continuous alarm sound
if (alarmSoundId) {
LK.clearInterval(alarmSoundId);
alarmSoundId = null;
}
// Stop safe zone blinking
if (safeZoneBlinkTween) {
tween.stop(safeZoneVisual);
safeZoneBlinkTween = null;
}
}
} else if (isInSafeZone) {
// In safe zone with no active predators
timerTxt.setText('');
timerTxt.alpha = 0; // Hide timer when in safe zone
safeZoneVisual.alpha = 0.6; // Reset safe zone visual alpha
// Stop continuous alarm sound
if (alarmSoundId) {
LK.clearInterval(alarmSoundId);
alarmSoundId = null;
}
// Stop safe zone blinking
if (safeZoneBlinkTween) {
tween.stop(safeZoneVisual);
safeZoneBlinkTween = null;
}
}
// Update last safe zone state
lastIsInSafeZone = isInSafeZone;
// Handle level timer - only decrease when outside safe zone
if (levelTimerStarted && gameStarted) {
if (!isInSafeZone) {
timeSpentOutsideSafeZone++;
}
var levelTimeRemaining = levelTimeLimit - timeSpentOutsideSafeZone;
var levelSecondsLeft = Math.ceil(levelTimeRemaining / 60);
if (levelTimeRemaining <= 0) {
// Level time's up - game over immediately
LK.showGameOver();
return;
}
// Update level timer display
if (isInSafeZone) {
levelTimerTxt.setText('Level Time: ' + levelSecondsLeft + 's (PAUSED)');
levelTimerTxt.fill = 0x00ff00; // Green when paused in safe zone
levelTimerTxt.alpha = 1;
} else {
levelTimerTxt.setText('Level Time: ' + levelSecondsLeft + 's');
if (levelSecondsLeft <= 10) {
levelTimerTxt.fill = 0xff0000; // Red when 10 seconds left
// Blinking effect for last 10 seconds
if (LK.ticks % 30 < 15) {
levelTimerTxt.alpha = 1;
} else {
levelTimerTxt.alpha = 0.3;
}
} else if (levelSecondsLeft <= 30) {
levelTimerTxt.fill = 0xffff00; // Yellow when 30 seconds left
levelTimerTxt.alpha = 1;
} else {
levelTimerTxt.fill = 0x00ffff; // Cyan for normal time
levelTimerTxt.alpha = 1;
}
}
}
// Handle ant swarm system
antSwarmTimer++;
if (antSwarmTimer >= nextAntSwarmTime) {
// Spawn a new ant swarm
for (var d = 0; d < antSwarms.length; d++) {
var antSwarm = antSwarms[d];
if (!antSwarm.isActive) {
// Random Y position between 200 and 2300, but avoid safe zone area
var randomY;
do {
randomY = 200 + Math.random() * 2100;
} while (randomY >= safeZone.y - 100 && randomY <= safeZone.y + safeZone.height + 100);
antSwarm.activate(randomY);
break;
}
}
// Reset timer with some randomness (18-22 seconds)
antSwarmTimer = 0;
nextAntSwarmTime = 1080 + Math.random() * 240;
}
// Check spider collision with active ant swarms
for (var d = 0; d < antSwarms.length; d++) {
var antSwarm = antSwarms[d];
if (antSwarm.isActive) {
// Check if spider intersects with any ant in the swarm
for (var a = 0; a < antSwarm.ants.length; a++) {
var ant = antSwarm.ants[a];
var antWorldX = antSwarm.x + ant.x;
var antWorldY = antSwarm.y + ant.y;
var dx = spider.x - antWorldX;
var dy = spider.y - antWorldY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 50) {
// Spider hit poisonous ant - game over immediately
LK.showGameOver();
return;
}
}
}
}
// Check spider collision with active predators
for (var p = 0; p < activePredators.length; p++) {
var predator = activePredators[p];
if (predator.isActive) {
var dx = spider.x - predator.x;
var dy = spider.y - predator.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 60) {
// Spider caught by predator - game over immediately
LK.showGameOver();
return;
}
}
}
// Count bugs by type
var bugCounts = {
fly: 0,
butterfly: 0,
beetle: 0
};
for (var b = 0; b < bugs.length; b++) {
var bug = bugs[b];
if (!bug.isCaught) {
bugCounts[bug.bugType]++;
}
}
// Maintain minimum 2 bugs of each type
var bugTypes = ['fly', 'butterfly', 'beetle'];
for (var t = 0; t < bugTypes.length; t++) {
var bugType = bugTypes[t];
while (bugCounts[bugType] < 2) {
var newBug = new Bug(bugType);
// Spawn in middle areas of screen, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Avoid safe zone area (bottom left) and spider's current position
var validPosition = false;
var attempts = 0;
do {
newBug.x = minX + Math.random() * (maxX - minX);
newBug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = newBug.x - spider.x;
var dy = newBug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(newBug.x <= 500 && newBug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
bugs.push(newBug);
game.addChild(newBug);
bugCounts[bugType]++;
}
}
// Spawn new bugs occasionally if total count is low
if (LK.ticks % 180 === 0 && bugs.length < 12) {
// Every 3 seconds, spawn up to 12 total bugs
spawnBug();
}
// Check web triangles for caught bugs
for (var t = 0; t < webTriangles.length; t++) {
var triangle = webTriangles[t];
if (triangle.isComplete) {
for (var b = 0; b < bugs.length; b++) {
var bug = bugs[b];
if (!bug.isCaught && !bug.isInvulnerable && triangle.containsBug(bug)) {
// Bug caught!
bug.isCaught = true;
score += bug.bugType === 'fly' ? 10 : bug.bugType === 'butterfly' ? 20 : 30;
// Track level progress
levelProgress[bug.bugType]++;
var currentTarget = getLevelTarget(currentLevel);
if (currentTarget && levelProgress[currentTarget.type] >= currentTarget.required) {
// Level completed!
// Play level completion sound
if (LK.getSound('level_complete')) {
LK.getSound('level_complete').play();
}
currentLevel++;
storage.currentLevel = currentLevel;
var nextTarget = getLevelTarget(currentLevel);
if (nextTarget) {
// Clear all web nodes first before anything else
for (var w = 0; w < spider.webNodes.length; w++) {
if (spider.webNodes[w] && spider.webNodes[w].destroy) {
spider.webNodes[w].destroy();
}
}
spider.webNodes = [];
webTriangles = [];
currentWebs = 0;
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
// Regenerate map for new level
createStones();
// Destroy all existing bugs first
for (var b = bugs.length - 1; b >= 0; b--) {
var bug = bugs[b];
if (bug && bug.destroy) {
bug.destroy();
}
}
bugs = [];
// Spawn sufficient bugs for new level - all will be newborn and invulnerable
for (var i = 0; i < 10; i++) {
spawnBug();
}
// Make spider invulnerable for level start
spider.isInvulnerable = true;
spider.invulnerabilityTimer = 180; // 3 seconds at 60fps
// Reset spider position to safe zone after clearing webs
spider.x = 100;
spider.y = 2632;
spider.isMoving = false;
spider.velocityX = 0;
spider.velocityY = 0;
// Update target bug display for new level
var newTarget = getLevelTarget(currentLevel);
if (targetBugImage.parent) {
targetBugImage.destroy();
}
targetBugImage = LK.getAsset(newTarget.type, {
anchorX: 0.5,
anchorY: 0.5
});
targetBugImage.scaleX = 2.0; // Start large
targetBugImage.scaleY = 2.0; // Start large
targetBugImage.x = 1024; // Start at center
targetBugImage.y = 1366; // Start at center vertically (2732/2)
game.addChild(targetBugImage);
if (targetCountTxt.parent) {
game.removeChild(targetCountTxt);
}
targetCountTxt.setText(newTarget.required.toString());
targetCountTxt.scaleX = 1.0; // Reset scale
targetCountTxt.scaleY = 1.0; // Reset scale
targetCountTxt.x = 1124; // Start at center, next to bug image
targetCountTxt.y = 1366; // Start at center
game.addChild(targetCountTxt);
// Animate target bug icon and count to top-right corner
LK.setTimeout(function () {
tween(targetBugImage, {
x: 1968,
y: 60,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetBugImage);
LK.gui.topRight.addChild(targetBugImage);
targetBugImage.x = -80;
targetBugImage.y = 60;
}
});
tween(targetCountTxt, {
x: 2028,
y: 60,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetCountTxt);
LK.gui.topRight.addChild(targetCountTxt);
targetCountTxt.x = -20;
targetCountTxt.y = 60;
}
});
}, 1000); // Show at center for 1 second
// Reset level timer for new level
levelStartTime = LK.ticks;
levelTimeLimit = getLevelTarget(currentLevel).required * 30 * 60; // 30 seconds per bug × 60 fps
timeSpentOutsideSafeZone = 0; // Reset time counter for new level
}
} else if (currentTarget) {
targetCountTxt.setText((currentTarget.required - levelProgress[currentTarget.type]).toString());
}
// Give +10 web credits for catching bug
if (currentWebs > 0) {
currentWebs = Math.max(0, currentWebs - 10);
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
}
scoreTxt.setText(score.toString());
LK.setScore(score);
if (LK.getSound('bug_catch')) {
LK.getSound('bug_catch').play();
}
// Remove bug from array immediately
bugs.splice(b, 1);
// Create star animation
var bugSize = bug.bugType === 'fly' ? 50 : bug.bugType === 'butterfly' ? 70 : 60;
var starSize = bugSize * 3; // 3x bigger than bug
var star = new Text2('⭐', {
size: starSize,
fill: 0xFFD700
});
star.anchor.set(0.5, 0.5);
star.x = bug.x;
star.y = bug.y;
game.addChild(star);
// Play collect sound
if (LK.getSound('collect')) {
LK.getSound('collect').play();
}
// Display star for 500ms then animate to score
LK.setTimeout(function () {
// Get score icon world position
var scoreIconPos = starIcon.toGlobal({
x: 0,
y: 0
});
var scoreWorldPos = game.toLocal(scoreIconPos);
// Animate star to score position
tween(star, {
x: scoreWorldPos.x,
y: scoreWorldPos.y,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (star.parent) {
star.destroy();
}
}
});
}, 500);
// Hide original bug immediately
tween(bug, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (bug.parent) {
var caughtBugType = bug.bugType; // Store the bug type before destroying
bug.destroy();
// Spawn a new bug of the same type to replace the caught one
var newBug = new Bug(caughtBugType);
// Spawn in middle areas of screen, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Avoid safe zone area (bottom left) and spider's current position
var validPosition = false;
var attempts = 0;
do {
newBug.x = minX + Math.random() * (maxX - minX);
newBug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = newBug.x - spider.x;
var dy = newBug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(newBug.x <= 500 && newBug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
bugs.push(newBug);
game.addChild(newBug);
}
}
});
}
}
}
}
// Check web nodes for bug collision (kill bugs that touch web nodes)
for (var n = 0; n < spider.webNodes.length; n++) {
var webNode = spider.webNodes[n];
for (var b = 0; b < bugs.length; b++) {
var bug = bugs[b];
if (!bug.isCaught && !bug.isInvulnerable) {
var dx = bug.x - webNode.x;
var dy = bug.y - webNode.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 50) {
// Collision distance
// Bug touched web node - kill it
bug.isCaught = true;
score += bug.bugType === 'fly' ? 5 : bug.bugType === 'butterfly' ? 10 : 15;
// Track level progress
levelProgress[bug.bugType]++;
var currentTarget = getLevelTarget(currentLevel);
if (currentTarget && levelProgress[currentTarget.type] >= currentTarget.required) {
// Level completed!
// Play level completion sound
if (LK.getSound('level_complete')) {
LK.getSound('level_complete').play();
}
currentLevel++;
storage.currentLevel = currentLevel;
var nextTarget = getLevelTarget(currentLevel);
if (nextTarget) {
// Clear all web nodes first before anything else
for (var w = 0; w < spider.webNodes.length; w++) {
if (spider.webNodes[w] && spider.webNodes[w].destroy) {
spider.webNodes[w].destroy();
}
}
spider.webNodes = [];
webTriangles = [];
currentWebs = 0;
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
// Regenerate map for new level
createStones();
// Destroy all existing bugs first
for (var b = bugs.length - 1; b >= 0; b--) {
var bug = bugs[b];
if (bug && bug.destroy) {
bug.destroy();
}
}
bugs = [];
// Spawn sufficient bugs for new level - all will be newborn and invulnerable
for (var i = 0; i < 10; i++) {
spawnBug();
}
// Make spider invulnerable for level start
spider.isInvulnerable = true;
spider.invulnerabilityTimer = 180; // 3 seconds at 60fps
// Reset spider position to safe zone after clearing webs
spider.x = 100;
spider.y = 2632;
spider.isMoving = false;
spider.velocityX = 0;
spider.velocityY = 0;
// Update target bug display for new level
var newTarget = getLevelTarget(currentLevel);
if (targetBugImage.parent) {
targetBugImage.destroy();
}
targetBugImage = LK.getAsset(newTarget.type, {
anchorX: 0.5,
anchorY: 0.5
});
targetBugImage.scaleX = 2.0; // Start large
targetBugImage.scaleY = 2.0; // Start large
targetBugImage.x = 1024; // Start at center
targetBugImage.y = 1366; // Start at center vertically (2732/2)
game.addChild(targetBugImage);
if (targetCountTxt.parent) {
game.removeChild(targetCountTxt);
}
targetCountTxt.setText(newTarget.required.toString());
targetCountTxt.scaleX = 1.0; // Reset scale
targetCountTxt.scaleY = 1.0; // Reset scale
targetCountTxt.x = 1124; // Start at center, next to bug image
targetCountTxt.y = 1366; // Start at center
game.addChild(targetCountTxt);
// Animate target bug icon and count to top-right corner
LK.setTimeout(function () {
tween(targetBugImage, {
x: 1968,
y: 60,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetBugImage);
LK.gui.topRight.addChild(targetBugImage);
targetBugImage.x = -80;
targetBugImage.y = 60;
}
});
tween(targetCountTxt, {
x: 2028,
y: 60,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetCountTxt);
LK.gui.topRight.addChild(targetCountTxt);
targetCountTxt.x = -20;
targetCountTxt.y = 60;
}
});
}, 1000); // Show at center for 1 second
// Reset level timer for new level
levelStartTime = LK.ticks;
levelTimeLimit = getLevelTarget(currentLevel).required * 30 * 60; // 30 seconds per bug × 60 fps
timeSpentOutsideSafeZone = 0; // Reset time counter for new level
}
} else if (currentTarget) {
targetCountTxt.setText((currentTarget.required - levelProgress[currentTarget.type]).toString());
}
// Give +10 web credits for catching bug
if (currentWebs > 0) {
currentWebs = Math.max(0, currentWebs - 10);
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
}
scoreTxt.setText(score.toString());
LK.setScore(score);
if (LK.getSound('bug_catch')) {
LK.getSound('bug_catch').play();
}
// Remove bug from array immediately
bugs.splice(b, 1);
// Create star animation
var bugSize = bug.bugType === 'fly' ? 50 : bug.bugType === 'butterfly' ? 70 : 60;
var starSize = bugSize * 3; // 3x bigger than bug
var star = new Text2('⭐', {
size: starSize,
fill: 0xFFD700
});
star.anchor.set(0.5, 0.5);
star.x = bug.x;
star.y = bug.y;
game.addChild(star);
// Play collect sound
if (LK.getSound('collect')) {
LK.getSound('collect').play();
}
// Display star for 500ms then animate to score
LK.setTimeout(function () {
// Get score icon world position
var scoreIconPos = starIcon.toGlobal({
x: 0,
y: 0
});
var scoreWorldPos = game.toLocal(scoreIconPos);
// Animate star to score position
tween(star, {
x: scoreWorldPos.x,
y: scoreWorldPos.y,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (star.parent) {
star.destroy();
}
}
});
}, 500);
// Hide original bug immediately
tween(bug, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 150,
onFinish: function onFinish() {
if (bug.parent) {
var killedBugType = bug.bugType; // Store the bug type before destroying
bug.destroy();
// Spawn a new bug of the same type to replace the killed one
var newBug = new Bug(killedBugType);
// Spawn in middle areas of screen, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Avoid safe zone area (bottom left) and spider's current position
var validPosition = false;
var attempts = 0;
do {
newBug.x = minX + Math.random() * (maxX - minX);
newBug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = newBug.x - spider.x;
var dy = newBug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(newBug.x <= 500 && newBug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
bugs.push(newBug);
game.addChild(newBug);
}
}
});
break; // Exit web node loop for this bug
}
}
}
}
// Create web triangles from spider's web nodes
if (spider.webNodes.length >= 3) {
// Check if we can form triangles from the last 3 nodes
var nodeCount = spider.webNodes.length;
if (nodeCount % 3 === 0) {
var lastThreeNodes = spider.webNodes.slice(-3);
var newTriangle = new WebTriangle(lastThreeNodes[0], lastThreeNodes[1], lastThreeNodes[2]);
if (newTriangle.checkIfComplete()) {
webTriangles.push(newTriangle);
}
}
}
// Reset bugs that go off screen instead of removing them
for (var b = bugs.length - 1; b >= 0; b--) {
var bug = bugs[b];
if (bug.x < -50 || bug.x > 2098 || bug.y < -50 || bug.y > 2782) {
// Reset bug position to middle areas, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Avoid safe zone area (bottom left) and spider's current position
var validPosition = false;
var attempts = 0;
do {
bug.x = minX + Math.random() * (maxX - minX);
bug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = bug.x - spider.x;
var dy = bug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(bug.x <= 500 && bug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
}
}
// Check if spider touches any bug (game over condition)
for (var b = 0; b < bugs.length; b++) {
var bug = bugs[b];
// More defensive collision check - ensure bug is fully vulnerable and spider is not invulnerable
if (!bug.isCaught && !bug.isInvulnerable && !bug.isNewborn && bug.newbornTimer <= 0 && !spider.isInvulnerable) {
var dx = bug.x - spider.x;
var dy = bug.y - spider.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
// Spider touched bug - game over immediately
LK.showGameOver();
return;
}
}
}
};
// Start ambient nature sounds
LK.playMusic('ambient_nature', {
loop: true
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var AntSwarm = Container.expand(function () {
var self = Container.call(this);
// Create individual ants in the swarm
self.ants = [];
var antCount = 15;
for (var i = 0; i < antCount; i++) {
var ant = self.attachAsset('ant', {
anchorX: 0.5,
anchorY: 0.5
});
ant.tint = 0x8B0000; // Dark red color for poisonous ants
ant.x = Math.random() * 200 - 100; // Spread ants in formation
ant.y = Math.random() * 150 - 75;
ant.scaleX = 0.7;
ant.scaleY = 0.7;
ant.alpha = 0.9;
ant.rotation = 0; // Face right (default orientation)
ant.lastX = ant.x;
ant.lastY = ant.y;
self.ants.push(ant);
}
self.speed = 2;
self.isActive = false;
self.duration = 0;
self.maxDuration = 1800; // 30 seconds at 60fps - stay until crossing screen
self.activate = function (yPosition) {
self.y = yPosition;
self.x = -200; // Start off-screen left
self.isActive = true;
self.duration = 0;
};
self.update = function () {
if (self.isActive) {
// Store old position for collision checking
var oldX = self.x;
var oldY = self.y;
// Move ant swarm from left to right
self.x += self.speed;
self.duration++;
// Check collision with stones for the entire swarm
var swarmHitStone = false;
for (var s = 0; s < stones.length; s++) {
var stone = stones[s];
var swarmCenterX = self.x;
var swarmCenterY = self.y;
var dx = swarmCenterX - stone.x;
var dy = swarmCenterY - stone.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < stone.radius + 150) {
// Swarm collision radius
swarmHitStone = true;
// Move swarm around the stone
if (swarmCenterY < stone.y) {
// Go above the stone
self.y -= 3;
} else {
// Go below the stone
self.y += 3;
}
break;
}
}
// Keep ants in formation without circular movement
for (var i = 0; i < self.ants.length; i++) {
var ant = self.ants[i];
// No circular movement - ants maintain their formation positions
}
// Deactivate when completely off-screen right
if (self.x > 2248) {
// 2048 + 200 buffer
self.isActive = false;
self.x = -200;
}
}
};
return self;
});
var Bug = Container.expand(function (bugType) {
var self = Container.call(this);
var type = bugType || 'fly';
var bugGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.bugType = type;
self.speed = type === 'fly' ? 6 : type === 'butterfly' ? 5 : 4;
self.velocityX = (Math.random() - 0.5) * self.speed;
self.velocityY = (Math.random() - 0.5) * self.speed;
self.changeDirectionTimer = 0;
self.isCaught = false;
self.isNewborn = true; // New bugs are newborn
self.newbornTimer = 180; // 3 seconds at 60fps
self.isInvulnerable = true; // Invulnerable during newborn period
self.blinkTween = null; // Track blinking animation
// Start blinking animation for newborn bugs
self.startBlinking = function () {
var _blinkCycle = function blinkCycle() {
self.blinkTween = tween(bugGraphics, {
alpha: 0.3
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.blinkTween = tween(bugGraphics, {
alpha: 1
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.isNewborn) {
_blinkCycle(); // Continue blinking while newborn
}
}
});
}
});
};
_blinkCycle();
};
// Stop blinking animation
self.stopBlinking = function () {
if (self.blinkTween) {
tween.stop(bugGraphics, {
alpha: true
});
self.blinkTween = null;
}
bugGraphics.alpha = 1; // Ensure full opacity
};
// Start blinking for new bugs
self.startBlinking();
self.update = function () {
if (!self.isCaught) {
// Handle newborn timer and invulnerability
if (self.isNewborn) {
self.newbornTimer--;
if (self.newbornTimer <= 0) {
self.isNewborn = false;
self.isInvulnerable = false;
self.stopBlinking();
}
}
// Store old position for collision checking
var oldX = self.x;
var oldY = self.y;
// Move bug randomly
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with stones
for (var s = 0; s < stones.length; s++) {
var stone = stones[s];
var dx = self.x - stone.x;
var dy = self.y - stone.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < stone.radius + 30) {
// Bug collision radius
// Collision detected - bounce off stone
self.x = oldX;
self.y = oldY;
// Reverse velocity and add some randomness
self.velocityX = -self.velocityX + (Math.random() - 0.5) * 2;
self.velocityY = -self.velocityY + (Math.random() - 0.5) * 2;
break;
}
}
self.changeDirectionTimer++;
// Change direction randomly
if (self.changeDirectionTimer > 20 + Math.random() * 30) {
self.velocityX = (Math.random() - 0.5) * self.speed;
self.velocityY = (Math.random() - 0.5) * self.speed;
self.changeDirectionTimer = 0;
}
// Keep bugs within bounds
if (self.x <= 50 || self.x >= 2048 - 50) {
self.velocityX = -self.velocityX;
}
if (self.y <= 50 || self.y >= 2732 - 50) {
self.velocityY = -self.velocityY;
}
// Prevent bugs from entering safe zone (bottom left corner - 400x400 area)
if (self.x <= 400 && self.y >= 2332) {
// Bug is trying to enter safe zone - bounce it away
if (self.x <= 400) {
self.velocityX = Math.abs(self.velocityX); // Force rightward movement
self.x = 401; // Push bug out of safe zone immediately
}
if (self.y >= 2332) {
self.velocityY = -Math.abs(self.velocityY); // Force upward movement
self.y = 2331; // Push bug out of safe zone immediately
}
}
// Add floating animation
bugGraphics.y = Math.sin(LK.ticks * 0.1) * 3;
}
};
return self;
});
var Predator = Container.expand(function (predatorType) {
var self = Container.call(this);
self.predatorType = predatorType || 'snake';
var predatorGraphics = self.attachAsset(self.predatorType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6; // Same speed as spider
self.isActive = false;
self.canMove = true; // Allow movement by default
self.targetX = 0;
self.targetY = 0;
self.lastX = 0;
self.lastY = 0;
self.activate = function (startCorner) {
self.isActive = true;
// Spawn from corners (top-left, top-right)
if (startCorner === 'topLeft') {
self.x = 150; // Avoid the menu area
self.y = 150;
} else {
// topRight
self.x = 2048 - 150;
self.y = 150;
}
self.lastX = self.x;
self.lastY = self.y;
// Add delay and alarm for lizard and snake predators
if (self.predatorType === 'lizard' || self.predatorType === 'snake') {
self.canMove = false;
// Play alarm sound 1 second before predator can move
if (LK.getSound('alarm')) {
LK.getSound('alarm').play();
}
// Allow movement after 1 second delay
LK.setTimeout(function () {
self.canMove = true;
}, 1000);
} else {
self.canMove = true;
}
};
self.deactivate = function () {
self.isActive = false;
self.x = -200; // Move off screen
self.y = -200;
};
self.update = function () {
if (self.isActive && self.canMove) {
self.lastX = self.x;
self.lastY = self.y;
// Chase the spider
var dx = spider.x - self.x;
var dy = spider.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Rotate to face spider
var angle = Math.atan2(dy, dx);
predatorGraphics.rotation = angle;
}
// Keep predator within bounds - reverse direction when hitting boundaries
if (self.x <= 60) {
self.x = 60;
// Reverse direction by moving back towards last position
var backDx = self.lastX - self.x;
var backDy = self.lastY - self.y;
var backDistance = Math.sqrt(backDx * backDx + backDy * backDy);
if (backDistance > 5) {
self.x += backDx / backDistance * self.speed;
self.y += backDy / backDistance * self.speed;
}
}
if (self.x >= 2048 - 60) {
self.x = 2048 - 60;
// Reverse direction by moving back towards last position
var backDx = self.lastX - self.x;
var backDy = self.lastY - self.y;
var backDistance = Math.sqrt(backDx * backDx + backDy * backDy);
if (backDistance > 5) {
self.x += backDx / backDistance * self.speed;
self.y += backDy / backDistance * self.speed;
}
}
if (self.y <= 60) {
self.y = 60;
// Reverse direction by moving back towards last position
var backDx = self.lastX - self.x;
var backDy = self.lastY - self.y;
var backDistance = Math.sqrt(backDx * backDx + backDy * backDy);
if (backDistance > 5) {
self.x += backDx / backDistance * self.speed;
self.y += backDy / backDistance * self.speed;
}
}
if (self.y >= 2732 - 60) {
self.y = 2732 - 60;
// Reverse direction by moving back towards last position
var backDx = self.lastX - self.x;
var backDy = self.lastY - self.y;
var backDistance = Math.sqrt(backDx * backDx + backDy * backDy);
if (backDistance > 5) {
self.x += backDx / backDistance * self.speed;
self.y += backDy / backDistance * self.speed;
}
}
}
};
return self;
});
var Spider = Container.expand(function () {
var self = Container.call(this);
var spiderGraphics = self.attachAsset('spider', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.velocityX = 0;
self.velocityY = 0;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.webNodes = [];
self.isInvulnerable = false;
self.invulnerabilityTimer = 0;
self.moveToTarget = function (targetX, targetY) {
self.targetX = targetX;
self.targetY = targetY;
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
self.isMoving = true;
// Rotate spider to face movement direction
var angle = Math.atan2(dy, dx);
tween(spiderGraphics, {
rotation: angle
}, {
duration: 100
});
}
};
self.createWebNode = function () {
if (currentWebs >= maxWebs) {
// Cannot create more webs
return;
}
// Check if a web node already exists at this position
for (var i = 0; i < self.webNodes.length; i++) {
var existingNode = self.webNodes[i];
var dx = existingNode.x - self.x;
var dy = existingNode.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 30) {
// Web node already exists too close to this position
return;
}
}
// Remove oldest web node if we've reached the limit
if (self.webNodes.length >= maxWebNodes) {
var oldestNode = self.webNodes.shift(); // Remove first (oldest) element
if (oldestNode && oldestNode.parent) {
oldestNode.destroy();
}
}
var webNode = LK.getAsset('web_node', {
anchorX: 0.5,
anchorY: 0.5
});
webNode.x = self.x;
webNode.y = self.y;
self.webNodes.push(webNode);
game.addChild(webNode);
currentWebs++;
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
// Play web creation sound
if (LK.getSound('web_create')) {
LK.getSound('web_create').play();
}
};
self.update = function () {
// Handle invulnerability timer
if (self.isInvulnerable) {
self.invulnerabilityTimer--;
if (self.invulnerabilityTimer <= 0) {
self.isInvulnerable = false;
}
// Add visual feedback for spider invulnerability
spiderGraphics.alpha = 0.5 + 0.5 * Math.sin(LK.ticks * 0.3);
} else {
spiderGraphics.alpha = 1;
}
if (self.isMoving) {
// Store old position for collision checking
var oldX = self.x;
var oldY = self.y;
// Move spider towards target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 5) {
self.isMoving = false;
self.velocityX = 0;
self.velocityY = 0;
} else {
self.x += self.velocityX;
self.y += self.velocityY;
}
// Check collision with stones
for (var s = 0; s < stones.length; s++) {
var stone = stones[s];
var dx = self.x - stone.x;
var dy = self.y - stone.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < stone.radius + 60) {
// Spider collision radius
// Collision detected - revert to old position and stop movement
self.x = oldX;
self.y = oldY;
self.isMoving = false;
self.velocityX = 0;
self.velocityY = 0;
break;
}
}
// Keep spider within bounds
if (self.x <= 60) {
self.x = 60;
self.isMoving = false;
}
if (self.x >= 2048 - 60) {
self.x = 2048 - 60;
self.isMoving = false;
}
if (self.y <= 60) {
self.y = 60;
self.isMoving = false;
}
if (self.y >= 2732 - 60) {
self.y = 2732 - 60;
self.isMoving = false;
}
}
};
return self;
});
var Stone = Container.expand(function () {
var self = Container.call(this);
// Random stone size between 80-150 pixels
var stoneSize = 80 + Math.random() * 70;
var stoneGraphics = LK.getAsset('stone_shape', {
width: stoneSize,
height: stoneSize,
color: 0x606060,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(stoneGraphics);
// Different shades of gray for stones
var grayShades = [0x404040, 0x505050, 0x606060, 0x707070, 0x808080, 0x909090, 0xa0a0a0, 0xb0b0b0];
stoneGraphics.tint = grayShades[Math.floor(Math.random() * grayShades.length)];
self.radius = stoneSize / 2;
return self;
});
var WebTriangle = Container.expand(function (node1, node2, node3) {
var self = Container.call(this);
self.nodes = [node1, node2, node3];
self.isComplete = false;
self.checkIfComplete = function () {
if (self.nodes.length === 3) {
self.isComplete = true;
return true;
}
return false;
};
self.containsBug = function (bug) {
if (!self.isComplete) return false;
// Simple point-in-triangle check using barycentric coordinates
var x1 = self.nodes[0].x,
y1 = self.nodes[0].y;
var x2 = self.nodes[1].x,
y2 = self.nodes[1].y;
var x3 = self.nodes[2].x,
y3 = self.nodes[2].y;
var px = bug.x,
py = bug.y;
var denom = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3);
var a = ((y2 - y3) * (px - x3) + (x3 - x2) * (py - y3)) / denom;
var b = ((y3 - y1) * (px - x3) + (x1 - x3) * (py - y3)) / denom;
var c = 1 - a - b;
return a >= 0 && b >= 0 && c >= 0;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c1810 // Dark brown for indoor environment
});
/****
* Game Code
****/
// Set garden environment background color
game.setBackgroundColor(0x654321);
// Create garden ground as single asset
var gardenGround = LK.getAsset('garden_ground', {
anchorX: 0,
anchorY: 0
});
gardenGround.x = 0;
gardenGround.y = 0;
gardenGround.alpha = 0.8;
game.addChild(gardenGround);
// Game variables
var spider;
var stones = [];
var bugs = [];
var webTriangles = [];
var score = 0;
var gameStarted = false;
var doubleClickTimer = 0;
var lastClickTime = 0;
var maxWebs = 30;
var currentWebs = 0;
var maxWebNodes = 30; // Maximum number of web nodes to keep on screen
// Level system variables
var currentLevel = 1;
var baseStoneCount = 2; // Start with 2 stones
var stonesPerLevel = 2; // Add 2 stones per level
var maxStones = 30; // Maximum 30 stones
var levelTargets = {
1: {
type: 'butterfly',
count: 3,
required: 3
},
2: {
type: 'fly',
count: 5,
required: 5
},
3: {
type: 'beetle',
count: 4,
required: 4
},
4: {
type: 'butterfly',
count: 6,
required: 6
},
5: {
type: 'fly',
count: 8,
required: 8
}
};
// Function to generate infinite levels
function getLevelTarget(level) {
if (levelTargets[level]) {
return levelTargets[level];
}
// Generate infinite levels beyond level 5
var bugTypes = ['fly', 'butterfly', 'beetle'];
var typeIndex = (level - 1) % bugTypes.length;
var difficultyMultiplier = Math.floor((level - 1) / 3) + 1;
return {
type: bugTypes[typeIndex],
count: 3 + difficultyMultiplier * 2,
required: 3 + difficultyMultiplier * 2
};
}
var levelProgress = {
butterfly: 0,
fly: 0,
beetle: 0
};
// Level timer variables
var levelStartTime = 0;
var levelTimeLimit = 0;
var levelTimerStarted = false;
var timeSpentOutsideSafeZone = 0; // Track actual time spent outside safe zone for level timer
// Safe zone and predator chase system variables
var safeZone = {
x: 0,
y: 2332,
width: 400,
height: 400
};
var timeOutsideSafeZone = 0;
var predatorSpawnTime = 0; // Random time between 15-35 seconds
var isInSafeZone = false;
var lastIsInSafeZone = false;
var predators = [];
var activePredators = [];
var countdownStarted = false; // Track if countdown has started
var countdownTime = 0; // Actual countdown time
var alarmSoundId = null; // Track continuous alarm sound
var safeZoneBlinkTween = null; // Track continuous safe zone blinking
// Ant swarm system variables
var antSwarms = [];
var antSwarmTimer = 0;
var antSwarmInterval = 1200; // 20 seconds at 60fps
var nextAntSwarmTime = antSwarmInterval;
// Create safe zone visual as a single asset
var safeZoneVisual = LK.getAsset('safe_zone', {
anchorX: 0,
anchorY: 0
});
safeZoneVisual.x = safeZone.x;
safeZoneVisual.y = safeZone.y;
safeZoneVisual.tint = 0xffffff;
safeZoneVisual.alpha = 0.6;
safeZoneVisual.scaleX = 2;
safeZoneVisual.scaleY = 2;
game.addChild(safeZoneVisual);
// Create ant swarms
for (var i = 0; i < 3; i++) {
var antSwarm = new AntSwarm();
antSwarms.push(antSwarm);
game.addChild(antSwarm);
}
// Create predators
for (var i = 0; i < 2; i++) {
var snake = new Predator('snake');
var lizard = new Predator('lizard');
predators.push(snake);
predators.push(lizard);
game.addChild(snake);
game.addChild(lizard);
}
// Initialize random predator spawn time (15-35 seconds)
predatorSpawnTime = (15 + Math.random() * 20) * 60; // 15-35 seconds at 60fps
// Create spider at center of room in safe zone
spider = game.addChild(new Spider());
spider.x = 100; // Start in safe zone
spider.y = 2632; // Start in safe zone
// Create instruction text
var instructionTxt = new Text2('Tap to move spider continuously, double-tap to shoot web while moving!', {
size: 60,
fill: 0xffffff
});
instructionTxt.anchor.set(0.5, 0.5);
instructionTxt.x = 1024;
instructionTxt.y = 1800; // Moved down from center
game.addChild(instructionTxt);
// Score display with star icon
var starIcon = new Text2('⭐', {
size: 80,
fill: 0xffffff
});
starIcon.anchor.set(1, 0);
starIcon.x = -10;
LK.gui.top.addChild(starIcon);
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xffffff
});
scoreTxt.anchor.set(0, 0);
LK.gui.top.addChild(scoreTxt);
// Web count display with web icon next to score
var webIcon = new Text2('🕸️', {
size: 80,
fill: 0xffffff
});
webIcon.anchor.set(0, 0);
webIcon.x = 120; // Position after score text
LK.gui.top.addChild(webIcon);
var webCountDisplayTxt = new Text2('30', {
size: 80,
fill: 0xffffff
});
webCountDisplayTxt.anchor.set(0, 0);
webCountDisplayTxt.x = 200; // Position after web icon
LK.gui.top.addChild(webCountDisplayTxt);
// Target bug display - start at center then animate to top-right corner
var initialTarget = getLevelTarget(currentLevel);
var targetBugImage = LK.getAsset(initialTarget.type, {
anchorX: 0.5,
anchorY: 0.5
});
targetBugImage.scaleX = 2.0; // Start large
targetBugImage.scaleY = 2.0; // Start large
targetBugImage.x = 1024; // Start at center
targetBugImage.y = 1366; // Start at center vertically (2732/2)
game.addChild(targetBugImage);
var targetCountTxt = new Text2(initialTarget.required.toString(), {
size: 120,
fill: 0xffffff
});
targetCountTxt.anchor.set(0.5, 0.5);
targetCountTxt.x = 1124; // Start at center, next to bug image
targetCountTxt.y = 1366; // Start at center
game.addChild(targetCountTxt);
// Animate target bug icon and count to top-right corner
LK.setTimeout(function () {
tween(targetBugImage, {
x: 1968,
y: 60,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetBugImage);
LK.gui.topRight.addChild(targetBugImage);
targetBugImage.x = -80;
targetBugImage.y = 60;
}
});
tween(targetCountTxt, {
x: 2028,
y: 60,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetCountTxt);
LK.gui.topRight.addChild(targetCountTxt);
targetCountTxt.x = -20;
targetCountTxt.y = 60;
}
});
}, 2000); // Show at center for 2 seconds
// Timer display
var timerTxt = new Text2('', {
size: 40,
fill: 0x00ff00
});
timerTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(timerTxt);
// Level timer display
var levelTimerTxt = new Text2('Level Time: 90s', {
size: 45,
fill: 0x00ffff
});
levelTimerTxt.anchor.set(1, 1);
LK.gui.bottomRight.addChild(levelTimerTxt);
// Create random stone obstacles
function createStones() {
// Clear existing stones
for (var k = 0; k < stones.length; k++) {
if (stones[k] && stones[k].destroy) {
stones[k].destroy();
}
}
stones = [];
// Create stones based on current level (start with 2, increase each level, max 30)
var stoneCount = Math.min(maxStones, baseStoneCount + (currentLevel - 1) * stonesPerLevel);
for (var i = 0; i < stoneCount; i++) {
var stone = new Stone();
var validPosition = false;
var attempts = 0;
// Try to find a valid position that doesn't overlap with safe zone or other obstacles
while (!validPosition && attempts < 50) {
stone.x = 100 + Math.random() * 1848; // Keep stones away from edges
stone.y = 100 + Math.random() * 2532;
// Check if stone is not in safe zone (bottom left corner)
if (stone.x > 500 || stone.y < 2232) {
// Check distance from other stones and obstacles
var tooClose = false;
// Check distance from other stones
for (var j = 0; j < stones.length; j++) {
var dx = stone.x - stones[j].x;
var dy = stone.y - stones[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < stone.radius + stones[j].radius + 50) {
tooClose = true;
break;
}
}
if (!tooClose) {
validPosition = true;
}
}
attempts++;
}
if (validPosition) {
stones.push(stone);
game.addChild(stone);
}
}
}
// Create stones
createStones();
// Function to spawn bugs
function spawnBug() {
var bugTypes = ['fly', 'butterfly', 'beetle'];
var randomType = bugTypes[Math.floor(Math.random() * bugTypes.length)];
var bug = new Bug(randomType);
// Spawn in middle areas of screen, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Find valid position for the bug
var validPosition = false;
var attempts = 0;
do {
bug.x = minX + Math.random() * (maxX - minX);
bug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = bug.x - spider.x;
var dy = bug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(bug.x <= 500 && bug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
bugs.push(bug);
game.addChild(bug);
}
// Spawn initial bugs
for (var i = 0; i < 5; i++) {
spawnBug();
}
// Touch input - move spider or create web
game.down = function (x, y, obj) {
var currentTime = LK.ticks;
var timeDiff = currentTime - lastClickTime;
if (timeDiff < 30) {
// Double click detected (within 0.5 seconds at 60fps)
// Create web node at double-click location and continue moving there
spider.createWebNode();
spider.moveToTarget(x, y);
if (LK.getSound('spider_move')) {
LK.getSound('spider_move').play();
}
} else {
// Single click - move spider to tap location
spider.moveToTarget(x, y);
if (LK.getSound('spider_move')) {
LK.getSound('spider_move').play();
}
}
lastClickTime = currentTime;
// Hide instruction text after first tap
if (instructionTxt && instructionTxt.parent) {
tween(instructionTxt, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
instructionTxt.destroy();
}
});
}
gameStarted = true;
// Initialize level timer
if (!levelTimerStarted) {
levelStartTime = LK.ticks;
levelTimeLimit = getLevelTarget(currentLevel).required * 30 * 60; // 30 seconds per bug × 60 fps
timeSpentOutsideSafeZone = 0; // Reset time counter
levelTimerStarted = true;
}
};
// Game update loop
game.update = function () {
// Check if spider is in safe zone
isInSafeZone = spider.x >= safeZone.x && spider.x <= safeZone.x + safeZone.width && spider.y >= safeZone.y && spider.y <= safeZone.y + safeZone.height;
// Handle predator chase system
if (lastIsInSafeZone && !isInSafeZone) {
// Just left safe zone - start countdown
countdownStarted = true;
countdownTime = (15 + Math.random() * 20) * 60; // New random time 15-35 seconds
}
if (!isInSafeZone && countdownStarted) {
countdownTime--;
// Update timer display when predators are not active
if (activePredators.length === 0) {
// Don't show countdown timer
}
// Check if countdown is finished and no predators are active
if (countdownTime <= 0 && activePredators.length === 0) {
// Spawn only one predator randomly
var availablePredators = [];
for (var p = 0; p < predators.length; p++) {
if (!predators[p].isActive) {
availablePredators.push(predators[p]);
}
}
if (availablePredators.length > 0) {
// Start continuous safe zone blinking
var _startSafeZoneBlinking = function startSafeZoneBlinking() {
safeZoneBlinkTween = tween(safeZoneVisual, {
alpha: 0.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
safeZoneBlinkTween = tween(safeZoneVisual, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (activePredators.length > 0) {
_startSafeZoneBlinking(); // Continue blinking while predators are active
}
}
});
}
});
};
// Choose random predator type and corner
var randomPredator = availablePredators[Math.floor(Math.random() * availablePredators.length)];
var randomCorner = Math.random() < 0.5 ? 'topLeft' : 'topRight';
randomPredator.activate(randomCorner);
activePredators.push(randomPredator);
// Start continuous alarm sound
if (LK.getSound('alarm')) {
alarmSoundId = LK.setInterval(function () {
LK.getSound('alarm').play();
}, 1000); // Play every second
}
_startSafeZoneBlinking();
// Don't show predator hunting text
}
}
} else if (isInSafeZone && activePredators.length > 0) {
// Back in safe zone - make predators retreat
for (var p = 0; p < activePredators.length; p++) {
var predator = activePredators[p];
// Move predator back to spawn corner
if (predator.x < 1024) {
// Was from topLeft, move back there
predator.x -= predator.speed * 2;
predator.y -= predator.speed * 2;
if (predator.x <= 50 || predator.y <= 50) {
predator.deactivate();
}
} else {
// Was from topRight, move back there
predator.x += predator.speed * 2;
predator.y -= predator.speed * 2;
if (predator.x >= 1998 || predator.y <= 50) {
predator.deactivate();
}
}
}
// Remove deactivated predators from active list
for (var p = activePredators.length - 1; p >= 0; p--) {
if (!activePredators[p].isActive) {
activePredators.splice(p, 1);
}
}
// Reset countdown when all predators are gone
if (activePredators.length === 0) {
countdownStarted = false;
timerTxt.setText('');
timerTxt.alpha = 0; // Hide timer when in safe zone
safeZoneVisual.alpha = 0.6; // Reset safe zone visual alpha
// Stop continuous alarm sound
if (alarmSoundId) {
LK.clearInterval(alarmSoundId);
alarmSoundId = null;
}
// Stop safe zone blinking
if (safeZoneBlinkTween) {
tween.stop(safeZoneVisual);
safeZoneBlinkTween = null;
}
}
} else if (isInSafeZone) {
// In safe zone with no active predators
timerTxt.setText('');
timerTxt.alpha = 0; // Hide timer when in safe zone
safeZoneVisual.alpha = 0.6; // Reset safe zone visual alpha
// Stop continuous alarm sound
if (alarmSoundId) {
LK.clearInterval(alarmSoundId);
alarmSoundId = null;
}
// Stop safe zone blinking
if (safeZoneBlinkTween) {
tween.stop(safeZoneVisual);
safeZoneBlinkTween = null;
}
}
// Update last safe zone state
lastIsInSafeZone = isInSafeZone;
// Handle level timer - only decrease when outside safe zone
if (levelTimerStarted && gameStarted) {
if (!isInSafeZone) {
timeSpentOutsideSafeZone++;
}
var levelTimeRemaining = levelTimeLimit - timeSpentOutsideSafeZone;
var levelSecondsLeft = Math.ceil(levelTimeRemaining / 60);
if (levelTimeRemaining <= 0) {
// Level time's up - game over immediately
LK.showGameOver();
return;
}
// Update level timer display
if (isInSafeZone) {
levelTimerTxt.setText('Level Time: ' + levelSecondsLeft + 's (PAUSED)');
levelTimerTxt.fill = 0x00ff00; // Green when paused in safe zone
levelTimerTxt.alpha = 1;
} else {
levelTimerTxt.setText('Level Time: ' + levelSecondsLeft + 's');
if (levelSecondsLeft <= 10) {
levelTimerTxt.fill = 0xff0000; // Red when 10 seconds left
// Blinking effect for last 10 seconds
if (LK.ticks % 30 < 15) {
levelTimerTxt.alpha = 1;
} else {
levelTimerTxt.alpha = 0.3;
}
} else if (levelSecondsLeft <= 30) {
levelTimerTxt.fill = 0xffff00; // Yellow when 30 seconds left
levelTimerTxt.alpha = 1;
} else {
levelTimerTxt.fill = 0x00ffff; // Cyan for normal time
levelTimerTxt.alpha = 1;
}
}
}
// Handle ant swarm system
antSwarmTimer++;
if (antSwarmTimer >= nextAntSwarmTime) {
// Spawn a new ant swarm
for (var d = 0; d < antSwarms.length; d++) {
var antSwarm = antSwarms[d];
if (!antSwarm.isActive) {
// Random Y position between 200 and 2300, but avoid safe zone area
var randomY;
do {
randomY = 200 + Math.random() * 2100;
} while (randomY >= safeZone.y - 100 && randomY <= safeZone.y + safeZone.height + 100);
antSwarm.activate(randomY);
break;
}
}
// Reset timer with some randomness (18-22 seconds)
antSwarmTimer = 0;
nextAntSwarmTime = 1080 + Math.random() * 240;
}
// Check spider collision with active ant swarms
for (var d = 0; d < antSwarms.length; d++) {
var antSwarm = antSwarms[d];
if (antSwarm.isActive) {
// Check if spider intersects with any ant in the swarm
for (var a = 0; a < antSwarm.ants.length; a++) {
var ant = antSwarm.ants[a];
var antWorldX = antSwarm.x + ant.x;
var antWorldY = antSwarm.y + ant.y;
var dx = spider.x - antWorldX;
var dy = spider.y - antWorldY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 50) {
// Spider hit poisonous ant - game over immediately
LK.showGameOver();
return;
}
}
}
}
// Check spider collision with active predators
for (var p = 0; p < activePredators.length; p++) {
var predator = activePredators[p];
if (predator.isActive) {
var dx = spider.x - predator.x;
var dy = spider.y - predator.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 60) {
// Spider caught by predator - game over immediately
LK.showGameOver();
return;
}
}
}
// Count bugs by type
var bugCounts = {
fly: 0,
butterfly: 0,
beetle: 0
};
for (var b = 0; b < bugs.length; b++) {
var bug = bugs[b];
if (!bug.isCaught) {
bugCounts[bug.bugType]++;
}
}
// Maintain minimum 2 bugs of each type
var bugTypes = ['fly', 'butterfly', 'beetle'];
for (var t = 0; t < bugTypes.length; t++) {
var bugType = bugTypes[t];
while (bugCounts[bugType] < 2) {
var newBug = new Bug(bugType);
// Spawn in middle areas of screen, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Avoid safe zone area (bottom left) and spider's current position
var validPosition = false;
var attempts = 0;
do {
newBug.x = minX + Math.random() * (maxX - minX);
newBug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = newBug.x - spider.x;
var dy = newBug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(newBug.x <= 500 && newBug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
bugs.push(newBug);
game.addChild(newBug);
bugCounts[bugType]++;
}
}
// Spawn new bugs occasionally if total count is low
if (LK.ticks % 180 === 0 && bugs.length < 12) {
// Every 3 seconds, spawn up to 12 total bugs
spawnBug();
}
// Check web triangles for caught bugs
for (var t = 0; t < webTriangles.length; t++) {
var triangle = webTriangles[t];
if (triangle.isComplete) {
for (var b = 0; b < bugs.length; b++) {
var bug = bugs[b];
if (!bug.isCaught && !bug.isInvulnerable && triangle.containsBug(bug)) {
// Bug caught!
bug.isCaught = true;
score += bug.bugType === 'fly' ? 10 : bug.bugType === 'butterfly' ? 20 : 30;
// Track level progress
levelProgress[bug.bugType]++;
var currentTarget = getLevelTarget(currentLevel);
if (currentTarget && levelProgress[currentTarget.type] >= currentTarget.required) {
// Level completed!
// Play level completion sound
if (LK.getSound('level_complete')) {
LK.getSound('level_complete').play();
}
currentLevel++;
storage.currentLevel = currentLevel;
var nextTarget = getLevelTarget(currentLevel);
if (nextTarget) {
// Clear all web nodes first before anything else
for (var w = 0; w < spider.webNodes.length; w++) {
if (spider.webNodes[w] && spider.webNodes[w].destroy) {
spider.webNodes[w].destroy();
}
}
spider.webNodes = [];
webTriangles = [];
currentWebs = 0;
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
// Regenerate map for new level
createStones();
// Destroy all existing bugs first
for (var b = bugs.length - 1; b >= 0; b--) {
var bug = bugs[b];
if (bug && bug.destroy) {
bug.destroy();
}
}
bugs = [];
// Spawn sufficient bugs for new level - all will be newborn and invulnerable
for (var i = 0; i < 10; i++) {
spawnBug();
}
// Make spider invulnerable for level start
spider.isInvulnerable = true;
spider.invulnerabilityTimer = 180; // 3 seconds at 60fps
// Reset spider position to safe zone after clearing webs
spider.x = 100;
spider.y = 2632;
spider.isMoving = false;
spider.velocityX = 0;
spider.velocityY = 0;
// Update target bug display for new level
var newTarget = getLevelTarget(currentLevel);
if (targetBugImage.parent) {
targetBugImage.destroy();
}
targetBugImage = LK.getAsset(newTarget.type, {
anchorX: 0.5,
anchorY: 0.5
});
targetBugImage.scaleX = 2.0; // Start large
targetBugImage.scaleY = 2.0; // Start large
targetBugImage.x = 1024; // Start at center
targetBugImage.y = 1366; // Start at center vertically (2732/2)
game.addChild(targetBugImage);
if (targetCountTxt.parent) {
game.removeChild(targetCountTxt);
}
targetCountTxt.setText(newTarget.required.toString());
targetCountTxt.scaleX = 1.0; // Reset scale
targetCountTxt.scaleY = 1.0; // Reset scale
targetCountTxt.x = 1124; // Start at center, next to bug image
targetCountTxt.y = 1366; // Start at center
game.addChild(targetCountTxt);
// Animate target bug icon and count to top-right corner
LK.setTimeout(function () {
tween(targetBugImage, {
x: 1968,
y: 60,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetBugImage);
LK.gui.topRight.addChild(targetBugImage);
targetBugImage.x = -80;
targetBugImage.y = 60;
}
});
tween(targetCountTxt, {
x: 2028,
y: 60,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetCountTxt);
LK.gui.topRight.addChild(targetCountTxt);
targetCountTxt.x = -20;
targetCountTxt.y = 60;
}
});
}, 1000); // Show at center for 1 second
// Reset level timer for new level
levelStartTime = LK.ticks;
levelTimeLimit = getLevelTarget(currentLevel).required * 30 * 60; // 30 seconds per bug × 60 fps
timeSpentOutsideSafeZone = 0; // Reset time counter for new level
}
} else if (currentTarget) {
targetCountTxt.setText((currentTarget.required - levelProgress[currentTarget.type]).toString());
}
// Give +10 web credits for catching bug
if (currentWebs > 0) {
currentWebs = Math.max(0, currentWebs - 10);
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
}
scoreTxt.setText(score.toString());
LK.setScore(score);
if (LK.getSound('bug_catch')) {
LK.getSound('bug_catch').play();
}
// Remove bug from array immediately
bugs.splice(b, 1);
// Create star animation
var bugSize = bug.bugType === 'fly' ? 50 : bug.bugType === 'butterfly' ? 70 : 60;
var starSize = bugSize * 3; // 3x bigger than bug
var star = new Text2('⭐', {
size: starSize,
fill: 0xFFD700
});
star.anchor.set(0.5, 0.5);
star.x = bug.x;
star.y = bug.y;
game.addChild(star);
// Play collect sound
if (LK.getSound('collect')) {
LK.getSound('collect').play();
}
// Display star for 500ms then animate to score
LK.setTimeout(function () {
// Get score icon world position
var scoreIconPos = starIcon.toGlobal({
x: 0,
y: 0
});
var scoreWorldPos = game.toLocal(scoreIconPos);
// Animate star to score position
tween(star, {
x: scoreWorldPos.x,
y: scoreWorldPos.y,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (star.parent) {
star.destroy();
}
}
});
}, 500);
// Hide original bug immediately
tween(bug, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (bug.parent) {
var caughtBugType = bug.bugType; // Store the bug type before destroying
bug.destroy();
// Spawn a new bug of the same type to replace the caught one
var newBug = new Bug(caughtBugType);
// Spawn in middle areas of screen, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Avoid safe zone area (bottom left) and spider's current position
var validPosition = false;
var attempts = 0;
do {
newBug.x = minX + Math.random() * (maxX - minX);
newBug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = newBug.x - spider.x;
var dy = newBug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(newBug.x <= 500 && newBug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
bugs.push(newBug);
game.addChild(newBug);
}
}
});
}
}
}
}
// Check web nodes for bug collision (kill bugs that touch web nodes)
for (var n = 0; n < spider.webNodes.length; n++) {
var webNode = spider.webNodes[n];
for (var b = 0; b < bugs.length; b++) {
var bug = bugs[b];
if (!bug.isCaught && !bug.isInvulnerable) {
var dx = bug.x - webNode.x;
var dy = bug.y - webNode.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 50) {
// Collision distance
// Bug touched web node - kill it
bug.isCaught = true;
score += bug.bugType === 'fly' ? 5 : bug.bugType === 'butterfly' ? 10 : 15;
// Track level progress
levelProgress[bug.bugType]++;
var currentTarget = getLevelTarget(currentLevel);
if (currentTarget && levelProgress[currentTarget.type] >= currentTarget.required) {
// Level completed!
// Play level completion sound
if (LK.getSound('level_complete')) {
LK.getSound('level_complete').play();
}
currentLevel++;
storage.currentLevel = currentLevel;
var nextTarget = getLevelTarget(currentLevel);
if (nextTarget) {
// Clear all web nodes first before anything else
for (var w = 0; w < spider.webNodes.length; w++) {
if (spider.webNodes[w] && spider.webNodes[w].destroy) {
spider.webNodes[w].destroy();
}
}
spider.webNodes = [];
webTriangles = [];
currentWebs = 0;
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
// Regenerate map for new level
createStones();
// Destroy all existing bugs first
for (var b = bugs.length - 1; b >= 0; b--) {
var bug = bugs[b];
if (bug && bug.destroy) {
bug.destroy();
}
}
bugs = [];
// Spawn sufficient bugs for new level - all will be newborn and invulnerable
for (var i = 0; i < 10; i++) {
spawnBug();
}
// Make spider invulnerable for level start
spider.isInvulnerable = true;
spider.invulnerabilityTimer = 180; // 3 seconds at 60fps
// Reset spider position to safe zone after clearing webs
spider.x = 100;
spider.y = 2632;
spider.isMoving = false;
spider.velocityX = 0;
spider.velocityY = 0;
// Update target bug display for new level
var newTarget = getLevelTarget(currentLevel);
if (targetBugImage.parent) {
targetBugImage.destroy();
}
targetBugImage = LK.getAsset(newTarget.type, {
anchorX: 0.5,
anchorY: 0.5
});
targetBugImage.scaleX = 2.0; // Start large
targetBugImage.scaleY = 2.0; // Start large
targetBugImage.x = 1024; // Start at center
targetBugImage.y = 1366; // Start at center vertically (2732/2)
game.addChild(targetBugImage);
if (targetCountTxt.parent) {
game.removeChild(targetCountTxt);
}
targetCountTxt.setText(newTarget.required.toString());
targetCountTxt.scaleX = 1.0; // Reset scale
targetCountTxt.scaleY = 1.0; // Reset scale
targetCountTxt.x = 1124; // Start at center, next to bug image
targetCountTxt.y = 1366; // Start at center
game.addChild(targetCountTxt);
// Animate target bug icon and count to top-right corner
LK.setTimeout(function () {
tween(targetBugImage, {
x: 1968,
y: 60,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetBugImage);
LK.gui.topRight.addChild(targetBugImage);
targetBugImage.x = -80;
targetBugImage.y = 60;
}
});
tween(targetCountTxt, {
x: 2028,
y: 60,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove from game and add to GUI
game.removeChild(targetCountTxt);
LK.gui.topRight.addChild(targetCountTxt);
targetCountTxt.x = -20;
targetCountTxt.y = 60;
}
});
}, 1000); // Show at center for 1 second
// Reset level timer for new level
levelStartTime = LK.ticks;
levelTimeLimit = getLevelTarget(currentLevel).required * 30 * 60; // 30 seconds per bug × 60 fps
timeSpentOutsideSafeZone = 0; // Reset time counter for new level
}
} else if (currentTarget) {
targetCountTxt.setText((currentTarget.required - levelProgress[currentTarget.type]).toString());
}
// Give +10 web credits for catching bug
if (currentWebs > 0) {
currentWebs = Math.max(0, currentWebs - 10);
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
webCountDisplayTxt.setText((maxWebs - currentWebs).toString());
}
scoreTxt.setText(score.toString());
LK.setScore(score);
if (LK.getSound('bug_catch')) {
LK.getSound('bug_catch').play();
}
// Remove bug from array immediately
bugs.splice(b, 1);
// Create star animation
var bugSize = bug.bugType === 'fly' ? 50 : bug.bugType === 'butterfly' ? 70 : 60;
var starSize = bugSize * 3; // 3x bigger than bug
var star = new Text2('⭐', {
size: starSize,
fill: 0xFFD700
});
star.anchor.set(0.5, 0.5);
star.x = bug.x;
star.y = bug.y;
game.addChild(star);
// Play collect sound
if (LK.getSound('collect')) {
LK.getSound('collect').play();
}
// Display star for 500ms then animate to score
LK.setTimeout(function () {
// Get score icon world position
var scoreIconPos = starIcon.toGlobal({
x: 0,
y: 0
});
var scoreWorldPos = game.toLocal(scoreIconPos);
// Animate star to score position
tween(star, {
x: scoreWorldPos.x,
y: scoreWorldPos.y,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (star.parent) {
star.destroy();
}
}
});
}, 500);
// Hide original bug immediately
tween(bug, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 150,
onFinish: function onFinish() {
if (bug.parent) {
var killedBugType = bug.bugType; // Store the bug type before destroying
bug.destroy();
// Spawn a new bug of the same type to replace the killed one
var newBug = new Bug(killedBugType);
// Spawn in middle areas of screen, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Avoid safe zone area (bottom left) and spider's current position
var validPosition = false;
var attempts = 0;
do {
newBug.x = minX + Math.random() * (maxX - minX);
newBug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = newBug.x - spider.x;
var dy = newBug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(newBug.x <= 500 && newBug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
bugs.push(newBug);
game.addChild(newBug);
}
}
});
break; // Exit web node loop for this bug
}
}
}
}
// Create web triangles from spider's web nodes
if (spider.webNodes.length >= 3) {
// Check if we can form triangles from the last 3 nodes
var nodeCount = spider.webNodes.length;
if (nodeCount % 3 === 0) {
var lastThreeNodes = spider.webNodes.slice(-3);
var newTriangle = new WebTriangle(lastThreeNodes[0], lastThreeNodes[1], lastThreeNodes[2]);
if (newTriangle.checkIfComplete()) {
webTriangles.push(newTriangle);
}
}
}
// Reset bugs that go off screen instead of removing them
for (var b = bugs.length - 1; b >= 0; b--) {
var bug = bugs[b];
if (bug.x < -50 || bug.x > 2098 || bug.y < -50 || bug.y > 2782) {
// Reset bug position to middle areas, avoiding corners and edges
// Define middle area bounds (avoiding 300px from each edge)
var minX = 300;
var maxX = 2048 - 300;
var minY = 300;
var maxY = 2732 - 300;
// Avoid safe zone area (bottom left) and spider's current position
var validPosition = false;
var attempts = 0;
do {
bug.x = minX + Math.random() * (maxX - minX);
bug.y = minY + Math.random() * (maxY - minY);
// Check if bug is too close to spider (within 200 pixels)
var dx = bug.x - spider.x;
var dy = bug.y - spider.y;
var distanceToSpider = Math.sqrt(dx * dx + dy * dy);
// Valid if away from safe zone and far enough from spider
validPosition = !(bug.x <= 500 && bug.y >= 2232) && distanceToSpider >= 200;
attempts++;
} while (!validPosition && attempts < 50); // Keep away from safe zone and spider
}
}
// Check if spider touches any bug (game over condition)
for (var b = 0; b < bugs.length; b++) {
var bug = bugs[b];
// More defensive collision check - ensure bug is fully vulnerable and spider is not invulnerable
if (!bug.isCaught && !bug.isInvulnerable && !bug.isNewborn && bug.newbornTimer <= 0 && !spider.isInvulnerable) {
var dx = bug.x - spider.x;
var dy = bug.y - spider.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
// Spider touched bug - game over immediately
LK.showGameOver();
return;
}
}
}
};
// Start ambient nature sounds
LK.playMusic('ambient_nature', {
loop: true
});
kuş bakışı gördüğümüz bir örümcek çiz. In-Game asset. 2d. High contrast. No shadows
Örümcek Ağı. In-Game asset. 2d. High contrast. No shadows
sağa doğru bakan zehirli karınca. In-Game asset. 2d. High contrast. No shadows
taş parçası. In-Game asset. 2d. High contrast. No shadows
toprak bir zemine ihtiyacım var kafa karıştırmayacak sadece zemin görebi görecek çok ufak detaylar olsun sadece üstünde ve açık renk olsun. In-Game asset. 2d. High contrast. No shadows
ağın orta noktası resmin sol alt köşesi olan bir örüncek ağı çiz. In-Game asset. 2d. High contrast. No shadows