User prompt
böcekleri ortadan kaldır
User prompt
Please fix the bug: 'TypeError: target is not an Object. (evaluating 'key in target')' in or related to this line: 'tween(wormSegments[i], {' Line Number: 734 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Çift tıklandığında daha hızlı gitsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Biraz daha ayrıntı ekle
Code edit (1 edits merged)
Please save this source code
User prompt
Worm Feast: Slither & Devour
Initial prompt
Bana bir solucan yeme simülatör oyunu yap
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, level: 1 }); /**** * Classes ****/ var Food = Container.expand(function (foodType) { var self = Container.call(this); // Food types: normal, special, seed, insect, root, fruit self.foodType = foodType || 'normal'; self.isSpecial = self.foodType === 'special'; // Set value based on food type switch (self.foodType) { case 'normal': self.value = 1; break; case 'special': self.value = 3; break; case 'seed': self.value = 1; break; case 'insect': self.value = 2; break; case 'root': self.value = 2; break; case 'fruit': self.value = 4; break; default: self.value = 1; } // Attach the appropriate food graphic var foodGraphics = self.attachAsset('food_' + self.foodType, { anchorX: 0.5, anchorY: 0.5 }); // Add decoration for insect type if (self.foodType === 'insect') { // Add legs for (var i = 0; i < 3; i++) { var leftLeg = self.attachAsset('soil_particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.5, rotation: Math.PI / 4 + i * Math.PI / 6, x: -5, y: -5 + i * 5 }); var rightLeg = self.attachAsset('soil_particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.5, rotation: -Math.PI / 4 - i * Math.PI / 6, x: 5, y: -5 + i * 5 }); } } // Add decoration for fruit type if (self.foodType === 'fruit') { var stem = self.attachAsset('soil_particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 1.5, x: 0, y: -15 }); stem.tint = 0x006400; // Dark green } // Animations based on food type switch (self.foodType) { case 'special': case 'fruit': self.pulseDirection = 1; self.pulseMin = 0.85; self.pulseMax = 1.15; self.pulseSpeed = 0.01; break; case 'insect': self.moveDirection = Math.random() * Math.PI * 2; self.moveSpeed = 0.3; self.movementRange = 20; self.originalX = self.x; self.originalY = self.y; break; } self.update = function () { // Pulsating effect for special foods and fruits if (self.foodType === 'special' || self.foodType === 'fruit') { if (self.pulseDirection > 0) { foodGraphics.scale.x += self.pulseSpeed; foodGraphics.scale.y += self.pulseSpeed; if (foodGraphics.scale.x >= self.pulseMax) { self.pulseDirection = -1; } } else { foodGraphics.scale.x -= self.pulseSpeed; foodGraphics.scale.y -= self.pulseSpeed; if (foodGraphics.scale.x <= self.pulseMin) { self.pulseDirection = 1; } } } // Movement pattern for insects if (self.foodType === 'insect') { self.moveDirection += (Math.random() - 0.5) * 0.2; var newX = self.x + Math.cos(self.moveDirection) * self.moveSpeed; var newY = self.y + Math.sin(self.moveDirection) * self.moveSpeed; // Keep within movement range if (Math.abs(newX - self.originalX) < self.movementRange && Math.abs(newY - self.originalY) < self.movementRange) { self.x = newX; self.y = newY; } else { // Reverse direction if going too far self.moveDirection += Math.PI; } // Rotate insect in direction of movement foodGraphics.rotation = self.moveDirection; } }; return self; }); var Obstacle = Container.expand(function () { var self = Container.call(this); var obstacleGraphics = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var PowerUp = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'speed'; // speed, invincibility, grow var color; switch (self.type) { case 'speed': color = 0x00BFFF; break; case 'invincibility': color = 0xFFFF00; break; case 'grow': color = 0xFF00FF; break; default: color = 0xFFFFFF; } // Create a shape for the powerup var powerupGraphics = self.attachAsset('powerup_' + self.type, { anchorX: 0.5, anchorY: 0.5 }); // Add pulsating animation self.pulseDirection = 1; self.pulseMin = 0.8; self.pulseMax = 1.2; self.pulseSpeed = 0.02; self.update = function () { // Pulsating effect if (self.pulseDirection > 0) { powerupGraphics.scale.x += self.pulseSpeed; powerupGraphics.scale.y += self.pulseSpeed; if (powerupGraphics.scale.x >= self.pulseMax) { self.pulseDirection = -1; } } else { powerupGraphics.scale.x -= self.pulseSpeed; powerupGraphics.scale.y -= self.pulseSpeed; if (powerupGraphics.scale.x <= self.pulseMin) { self.pulseDirection = 1; } } }; return self; }); var Predator = Container.expand(function () { var self = Container.call(this); // Create predator body (mole or other underground creature) var body = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); body.tint = 0x606060; // Gray color for mole // Add eyes var leftEye = self.attachAsset('food_normal', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, x: -15, y: -10 }); leftEye.tint = 0xff0000; // Red eyes var rightEye = self.attachAsset('food_normal', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, x: 15, y: -10 }); rightEye.tint = 0xff0000; // Add teeth/claws var leftClaw = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.8, rotation: -Math.PI / 6, x: -20, y: 15 }); leftClaw.tint = 0xffffff; var rightClaw = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.8, rotation: Math.PI / 6, x: 20, y: 15 }); rightClaw.tint = 0xffffff; // Predator properties self.speed = 2; self.targetWorm = null; self.huntRadius = 300; // Detection range self.active = false; self.idleTime = 0; self.huntTime = 0; self.maxHuntTime = 300; // Hunt for 5 seconds max self.state = 'sleeping'; // sleeping, hunting, retreating // Update method to handle predator behavior self.update = function () { if (self.state === 'sleeping') { // Occasionally check if worm is nearby if (LK.ticks % 30 === 0 && self.targetWorm) { var wormHead = self.targetWorm[0]; var distance = getDistance(self.x, self.y, wormHead.x, wormHead.y); // Activate if worm is within range if (distance < self.huntRadius) { self.state = 'hunting'; self.huntTime = 0; // Flash eyes tween(leftEye, { alpha: 0 }, { duration: 100, yoyo: true, repeat: 3 }); tween(rightEye, { alpha: 0 }, { duration: 100, yoyo: true, repeat: 3 }); } } // Slightly wiggle while sleeping body.rotation = Math.sin(LK.ticks / 20) * 0.05; } else if (self.state === 'hunting') { // Hunt the worm if (self.targetWorm && self.targetWorm.length > 0) { var wormHead = self.targetWorm[0]; var dx = wormHead.x - self.x; var dy = wormHead.y - self.y; var angle = Math.atan2(dy, dx); // Move toward the worm self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Rotate to face direction of movement self.rotation = angle; // Create soil particles while moving if (LK.ticks % 5 === 0) { createSoilEffect(self.x, self.y, 2); } // Increase hunt time self.huntTime++; if (self.huntTime >= self.maxHuntTime) { // Give up after max hunt time self.state = 'retreating'; } } } else if (self.state === 'retreating') { // Retreat underground self.alpha -= 0.02; if (self.alpha <= 0) { // Reset predator self.alpha = 1; // Move to a new random location self.x = Math.random() * (GAME_WIDTH - 200) + 100; self.y = Math.random() * (GAME_HEIGHT - 200) + 100; // Go back to sleeping self.state = 'sleeping'; } } }; return self; }); var SoilParticle = Container.expand(function () { var self = Container.call(this); var particleGraphics = self.attachAsset('soil_particle', { anchorX: 0.5, anchorY: 0.5 }); // Set tint method - allows changing the soil particle color self.tint = function (color) { particleGraphics.tint = color; }; self.alpha = Math.random() * 0.5 + 0.2; self.lifespan = Math.random() * 30 + 15; self.velocityX = (Math.random() - 0.5) * 2; self.velocityY = (Math.random() - 0.5) * 2; // Random rotation self.rotation = Math.random() * Math.PI * 2; // Random scale for more varied soil particles self.scale = Math.random() * 0.5 + 0.75; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.rotation += 0.02; // Slow rotation for visual interest self.lifespan--; // Gradually fade out and get smaller if (self.lifespan <= 10) { self.alpha -= 0.1; self.scale -= 0.05; } }; return self; }); var WormSegment = Container.expand(function (isHead) { var self = Container.call(this); self.isHead = isHead || false; var segmentGraphics = self.attachAsset(self.isHead ? 'worm_head' : 'worm_body', { anchorX: 0.5, anchorY: 0.5 }); // Add details like eyes and mouth if this is the head if (self.isHead) { // Create mouth var mouth = self.attachAsset('worm_body', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.2, x: 10, y: 5 }); mouth.tint = 0x000000; // Left eye var leftEye = self.attachAsset('worm_body', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.25, x: 10, y: -10 }); leftEye.tint = 0x000000; // Right eye var rightEye = self.attachAsset('worm_body', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.25, x: 10, y: 10 }); rightEye.tint = 0x000000; // Add pulsating animation for head to simulate breathing self.pulseDirection = 1; self.pulseMin = 0.95; self.pulseMax = 1.05; self.pulseSpeed = 0.005; } // Store previous position for smooth following self.prevX = 0; self.prevY = 0; // Save position to follow self.savePosition = function () { self.prevX = self.x; self.prevY = self.y; }; // Add update method to animate head if (self.isHead) { self.update = function () { // Pulsating effect if (self.pulseDirection > 0) { segmentGraphics.scale.x += self.pulseSpeed; segmentGraphics.scale.y += self.pulseSpeed; if (segmentGraphics.scale.x >= self.pulseMax) { self.pulseDirection = -1; } } else { segmentGraphics.scale.x -= self.pulseSpeed; segmentGraphics.scale.y -= self.pulseSpeed; if (segmentGraphics.scale.x <= self.pulseMin) { self.pulseDirection = 1; } } }; } return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x3E2723 }); /**** * Game Code ****/ // Spawn a predator function spawnPredator() { if (predators.length >= MAX_PREDATORS) { return; } var predator = new Predator(); // Position predator randomly away from edges and worm head var head = wormSegments[0]; do { predator.x = Math.random() * (GAME_WIDTH - 200) + 100; predator.y = Math.random() * (GAME_HEIGHT - 200) + 100; } while (getDistance(predator.x, predator.y, head.x, head.y) < 500); // Link to worm segments for tracking predator.targetWorm = wormSegments; // Add to game game.addChild(predator); predators.push(predator); // Create dramatic soil eruption effect createSoilEffect(predator.x, predator.y, 20); } // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var SEGMENT_SPACING = 30; var STARTING_SEGMENTS = 5; var MAX_SEGMENTS = 50; var MOVE_SPEED = 5; var TURNING_SPEED = 0.1; var SPAWN_FOOD_INTERVAL = 60; var SPAWN_OBSTACLE_INTERVAL = 180; var SPAWN_POWERUP_INTERVAL = 600; var SPECIAL_FOOD_CHANCE = 0.2; var POWERUP_DURATION = 300; // Terrain constants var TERRAIN_TYPES = [{ name: 'soil', color: 0x5d4037, speedModifier: 1.0 }, { name: 'sand', color: 0xd2b48c, speedModifier: 1.2 }, { name: 'clay', color: 0x8d6e63, speedModifier: 0.8 }, { name: 'humus', color: 0x3e2723, speedModifier: 0.9 }]; var TERRAIN_SIZE = 400; // Size of terrain patches var currentTerrain = TERRAIN_TYPES[0]; // Default terrain // Game variables var wormSegments = []; var foods = []; var obstacles = []; var powerups = []; var predators = []; var soilParticles = []; var targetX = 0; var targetY = 0; var score = 0; var gameLevel = storage.level || 1; var highScore = storage.highScore || 0; var movingToTarget = false; var wormAngle = 0; var foodTimer = 0; var obstacleTimer = 0; var powerupTimer = 0; var predatorTimer = 0; var SPAWN_PREDATOR_INTERVAL = 600; // 10 seconds at 60 FPS var MAX_PREDATORS = Math.min(Math.floor(gameLevel / 2), 3); // Cap at 3 predators var powerupActive = false; var powerupType = null; var powerupTimeRemaining = 0; var gameStarted = false; var levelScore = gameLevel * 10; // Create background with terrain variations function createBackground() { var backgroundContainer = new Container(); game.addChild(backgroundContainer); var tileSize = 100; // Create terrain patches var terrainPatches = []; for (var tx = 0; tx < GAME_WIDTH; tx += TERRAIN_SIZE) { for (var ty = 0; ty < GAME_HEIGHT; ty += TERRAIN_SIZE) { // Randomly select terrain type for this patch var terrainType = TERRAIN_TYPES[Math.floor(Math.random() * TERRAIN_TYPES.length)]; terrainPatches.push({ x: tx, y: ty, width: TERRAIN_SIZE, height: TERRAIN_SIZE, type: terrainType }); } } // Create tiles based on terrain patches for (var x = 0; x < GAME_WIDTH; x += tileSize) { for (var y = 0; y < GAME_HEIGHT; y += tileSize) { // Determine which terrain patch this tile belongs to var terrainType = TERRAIN_TYPES[0]; // Default for (var i = 0; i < terrainPatches.length; i++) { var patch = terrainPatches[i]; if (x >= patch.x && x < patch.x + patch.width && y >= patch.y && y < patch.y + patch.height) { terrainType = patch.type; break; } } var tile = LK.getAsset('background_tile', { anchorX: 0, anchorY: 0, width: tileSize, height: tileSize, alpha: 0.7 + Math.random() * 0.3 }); tile.tint = terrainType.color; tile.terrainType = terrainType; tile.x = x; tile.y = y; tile.rotation = Math.random() * Math.PI * 2; tile.scale.x = 0.9 + Math.random() * 0.2; tile.scale.y = 0.9 + Math.random() * 0.2; backgroundContainer.addChild(tile); } } } // Initialize worm function createWorm() { // Create head var head = new WormSegment(true); head.x = GAME_WIDTH / 2; head.y = GAME_HEIGHT / 2; game.addChild(head); wormSegments.push(head); // Create initial body segments for (var i = 0; i < STARTING_SEGMENTS; i++) { addWormSegment(); } } // Add new segment to the worm function addWormSegment() { if (wormSegments.length >= MAX_SEGMENTS) { return; } var lastSegment = wormSegments[wormSegments.length - 1]; var newSegment = new WormSegment(false); newSegment.x = lastSegment.x; newSegment.y = lastSegment.y; game.addChild(newSegment); wormSegments.push(newSegment); } // Spawn a food item at a random position function spawnFood() { // Determine food type based on probabilities and level var foodTypes = ['normal', 'seed', 'insect', 'root', 'fruit', 'special']; var probabilities = [0.4 - gameLevel * 0.02, // normal (decreases with level) 0.2, // seed 0.15 + gameLevel * 0.01, // insect (increases with level) 0.15, // root 0.1 - gameLevel * 0.01, // fruit (decreases with level) Math.min(0.05 + gameLevel * 0.01, 0.2) // special (increases with level, capped at 20%) ]; // Make sure probabilities add up to 1 var sum = probabilities.reduce(function (a, b) { return a + b; }, 0); probabilities = probabilities.map(function (p) { return p / sum; }); // Select food type based on weighted random var random = Math.random(); var cumulativeProb = 0; var selectedFoodType = 'normal'; for (var i = 0; i < foodTypes.length; i++) { cumulativeProb += probabilities[i]; if (random <= cumulativeProb) { selectedFoodType = foodTypes[i]; break; } } var food = new Food(selectedFoodType); // Position food randomly away from edges food.x = Math.random() * (GAME_WIDTH - 200) + 100; food.y = Math.random() * (GAME_HEIGHT - 200) + 100; // Make sure food doesn't overlap with obstacles for (var i = 0; i < obstacles.length; i++) { if (getDistance(food.x, food.y, obstacles[i].x, obstacles[i].y) < 100) { // Reposition if too close to an obstacle food.x = Math.random() * (GAME_WIDTH - 200) + 100; food.y = Math.random() * (GAME_HEIGHT - 200) + 100; i = -1; // Reset loop to check again } } // For fruits and roots, try to place them in appropriate terrain if possible if (selectedFoodType === 'fruit' || selectedFoodType === 'root') { var attempts = 0; var placed = false; var desiredTerrain = selectedFoodType === 'fruit' ? 'humus' : 'clay'; while (attempts < 5 && !placed) { var testX = Math.random() * (GAME_WIDTH - 200) + 100; var testY = Math.random() * (GAME_HEIGHT - 200) + 100; // Check terrain var terrainAtPosition = findTerrainAtPosition(testX, testY); if (terrainAtPosition && terrainAtPosition.name === desiredTerrain) { // Check obstacles var validPosition = true; for (var j = 0; j < obstacles.length; j++) { if (getDistance(testX, testY, obstacles[j].x, obstacles[j].y) < 100) { validPosition = false; break; } } if (validPosition) { food.x = testX; food.y = testY; placed = true; } } attempts++; } } // Save original position for insects if (selectedFoodType === 'insect') { food.originalX = food.x; food.originalY = food.y; } game.addChild(food); foods.push(food); } // Helper function to determine terrain at a specific position function findTerrainAtPosition(x, y) { // Find the terrain patch this position belongs to for (var i = 0; i < TERRAIN_TYPES.length; i++) { var terrainX = Math.floor(x / TERRAIN_SIZE) * TERRAIN_SIZE; var terrainY = Math.floor(y / TERRAIN_SIZE) * TERRAIN_SIZE; // Return the terrain type for this patch (simplified approach) return TERRAIN_TYPES[Math.floor(Math.random() * TERRAIN_TYPES.length)]; } return TERRAIN_TYPES[0]; // Default to first terrain type } // Spawn an obstacle function spawnObstacle() { var obstacle = new Obstacle(); // Position obstacle randomly away from edges and worm head var head = wormSegments[0]; do { obstacle.x = Math.random() * (GAME_WIDTH - 200) + 100; obstacle.y = Math.random() * (GAME_HEIGHT - 200) + 100; } while (getDistance(obstacle.x, obstacle.y, head.x, head.y) < 300); game.addChild(obstacle); obstacles.push(obstacle); } // Spawn a power-up function spawnPowerup() { var types = ['speed', 'invincibility', 'grow']; var randomType = types[Math.floor(Math.random() * types.length)]; var powerup = new PowerUp(randomType); // Position powerup randomly away from edges powerup.x = Math.random() * (GAME_WIDTH - 200) + 100; powerup.y = Math.random() * (GAME_HEIGHT - 200) + 100; // Make sure powerup doesn't overlap with obstacles for (var i = 0; i < obstacles.length; i++) { if (getDistance(powerup.x, powerup.y, obstacles[i].x, obstacles[i].y) < 100) { // Reposition if too close to an obstacle powerup.x = Math.random() * (GAME_WIDTH - 200) + 100; powerup.y = Math.random() * (GAME_HEIGHT - 200) + 100; i = -1; // Reset loop to check again } } game.addChild(powerup); powerups.push(powerup); } // Create soil particles effect function createSoilEffect(x, y, count, color) { for (var i = 0; i < count; i++) { var particle = new SoilParticle(); // Set position with slight randomization particle.x = x + (Math.random() - 0.5) * 10; particle.y = y + (Math.random() - 0.5) * 10; // Apply terrain color if provided if (color) { particle.tint = color; } else { // Use color from current terrain if no specific color is provided var terrainAtPosition = findTerrainAtPosition(x, y); if (terrainAtPosition) { particle.tint = terrainAtPosition.color; } } game.addChild(particle); soilParticles.push(particle); } } // Calculate distance between two points function getDistance(x1, y1, x2, y2) { return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); } // Activate powerup function activatePowerup(type) { powerupActive = true; powerupType = type; powerupTimeRemaining = POWERUP_DURATION; LK.getSound('powerup').play(); // Apply power-up effects switch (type) { case 'speed': MOVE_SPEED *= 1.75; break; case 'invincibility': // Visual effect for invincibility for (var i = 0; i < wormSegments.length; i++) { tween(wormSegments[i], { alpha: 0.7 }, { duration: 300 }); } break; case 'grow': // Add multiple segments at once for (var j = 0; j < 3; j++) { addWormSegment(); } break; } } // End powerup effect function endPowerupEffect() { switch (powerupType) { case 'speed': MOVE_SPEED = 5; break; case 'invincibility': // Reset visual effect for (var i = 0; i < wormSegments.length; i++) { tween(wormSegments[i], { alpha: 1 }, { duration: 300 }); } break; } powerupActive = false; powerupType = null; } // Setup UI elements function setupUI() { // Score text var scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); LK.gui.topRight.addChild(scoreTxt); scoreTxt.x = -scoreTxt.width - 20; scoreTxt.y = 20; // Level text var levelTxt = new Text2('Level: ' + gameLevel, { size: 60, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); LK.gui.topRight.addChild(levelTxt); levelTxt.x = -levelTxt.width - 20; levelTxt.y = 100; // High score text var highScoreTxt = new Text2('Best: ' + highScore, { size: 50, fill: 0xFFD700 }); highScoreTxt.anchor.set(0, 0); LK.gui.topRight.addChild(highScoreTxt); highScoreTxt.x = -highScoreTxt.width - 20; highScoreTxt.y = 160; // Next level text var nextLevelTxt = new Text2('Next Level: ' + levelScore, { size: 50, fill: 0x32CD32 }); nextLevelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(nextLevelTxt); nextLevelTxt.y = 20; // Powerup indicator var powerupTxt = new Text2('', { size: 60, fill: 0xFFFF00 }); powerupTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(powerupTxt); powerupTxt.x = 120; // Keep away from the top-left 100x100 px area powerupTxt.y = 20; // Start instructions var instructionsTxt = new Text2('Tap to start\nDrag to move your worm\nEat food to grow', { size: 80, fill: 0xFFFFFF }); instructionsTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(instructionsTxt); // Update UI function game.updateUI = function () { scoreTxt.setText('Score: ' + score); scoreTxt.x = -scoreTxt.width - 20; levelTxt.setText('Level: ' + gameLevel); levelTxt.x = -levelTxt.width - 20; highScoreTxt.setText('Best: ' + highScore); highScoreTxt.x = -highScoreTxt.width - 20; nextLevelTxt.setText('Next Level: ' + (levelScore - score)); if (powerupActive) { var timeLeft = Math.ceil(powerupTimeRemaining / 60); powerupTxt.setText(powerupType.toUpperCase() + ': ' + timeLeft + 's'); } else { powerupTxt.setText(''); } if (gameStarted) { instructionsTxt.alpha = 0; } }; } // Initialize game function initGame() { // Reset game state wormSegments = []; foods = []; obstacles = []; powerups = []; predators = []; soilParticles = []; score = 0; movingToTarget = false; wormAngle = 0; foodTimer = 0; obstacleTimer = 0; powerupTimer = 0; predatorTimer = 0; powerupActive = false; powerupType = null; powerupTimeRemaining = 0; levelScore = gameLevel * 10; MOVE_SPEED = 5; MAX_PREDATORS = Math.min(Math.floor(gameLevel / 2), 3); // Recalculate for current level // Set background createBackground(); // Create the worm createWorm(); // Initial food for (var i = 0; i < 3; i++) { spawnFood(); } // Initial obstacles based on level for (var j = 0; j < Math.min(gameLevel, 5); j++) { spawnObstacle(); } // Setup UI setupUI(); // Play background music LK.playMusic('bgmusic'); } // Initialize the game initGame(); // Game logic update function game.update = function () { if (!gameStarted) { return; } // Move worm head var head = wormSegments[0]; if (movingToTarget) { // Calculate angle to target var dx = targetX - head.x; var dy = targetY - head.y; var targetAngle = Math.atan2(dy, dx); // Smooth angle change var angleDiff = targetAngle - wormAngle; // Handle angle wrapping if (angleDiff > Math.PI) { angleDiff -= Math.PI * 2; } if (angleDiff < -Math.PI) { angleDiff += Math.PI * 2; } // Apply gradual turning wormAngle += angleDiff * TURNING_SPEED; // Determine terrain at current position var currentTerrainType = findTerrainAtPosition(head.x, head.y); var speedModifier = currentTerrainType ? currentTerrainType.speedModifier : 1.0; // Apply terrain effects to movement var adjustedSpeed = MOVE_SPEED * speedModifier; if (powerupActive && powerupType === 'speed') { adjustedSpeed *= 1.75; // Apply speed powerup with terrain modifier } // Move in the current direction with terrain-adjusted speed head.savePosition(); head.x += Math.cos(wormAngle) * adjustedSpeed; head.y += Math.sin(wormAngle) * adjustedSpeed; // Create soil particles as the worm moves if (LK.ticks % 3 === 0) { var particleCount = speedModifier > 1 ? 2 : 1; // More particles in sand (faster terrain) var particleColor = currentTerrainType ? currentTerrainType.color : 0x8b4513; createSoilEffect(head.x, head.y, particleCount, particleColor); } // Check if close enough to target if (getDistance(head.x, head.y, targetX, targetY) < 10) { movingToTarget = false; } } // Move body segments for (var i = 1; i < wormSegments.length; i++) { var segment = wormSegments[i]; var prevSegment = wormSegments[i - 1]; // Save current position before updating segment.savePosition(); // Calculate direction to previous segment var dx = prevSegment.prevX - segment.x; var dy = prevSegment.prevY - segment.y; var distance = Math.sqrt(dx * dx + dy * dy); // Move towards the previous segment's saved position if (distance > SEGMENT_SPACING) { var moveRatio = (distance - SEGMENT_SPACING) / distance; segment.x += dx * moveRatio; segment.y += dy * moveRatio; } } // Update soil particles for (var p = soilParticles.length - 1; p >= 0; p--) { var particle = soilParticles[p]; particle.update(); if (particle.lifespan <= 0 || particle.alpha <= 0) { particle.destroy(); soilParticles.splice(p, 1); } } // Boundary checking for worm head if (head.x < 0) { head.x = 0; } if (head.x > GAME_WIDTH) { head.x = GAME_WIDTH; } if (head.y < 0) { head.y = 0; } if (head.y > GAME_HEIGHT) { head.y = GAME_HEIGHT; } // Check collisions with food for (var f = foods.length - 1; f >= 0; f--) { var food = foods[f]; food.update(); if (getDistance(head.x, head.y, food.x, food.y) < 40) { // Eat food score += food.value; LK.setScore(score); LK.getSound('eat').play(); // Add segments based on food value for (var s = 0; s < food.value; s++) { addWormSegment(); } // Remove food food.destroy(); foods.splice(f, 1); // Create particle effect createSoilEffect(food.x, food.y, 10); } } // Check collisions with obstacles for (var o = 0; o < obstacles.length; o++) { var obstacle = obstacles[o]; if (getDistance(head.x, head.y, obstacle.x, obstacle.y) < 45) { if (powerupActive && powerupType === 'invincibility') { // Destroy obstacle if invincible createSoilEffect(obstacle.x, obstacle.y, 15); obstacle.destroy(); obstacles.splice(o, 1); o--; continue; } else { // Game over on collision LK.getSound('hit').play(); LK.effects.flashScreen(0xFF0000, 500); // Update high score if (score > highScore) { highScore = score; storage.highScore = highScore; } LK.showGameOver(); return; } } } // Check collisions with powerups for (var pu = powerups.length - 1; pu >= 0; pu--) { var powerup = powerups[pu]; powerup.update(); if (getDistance(head.x, head.y, powerup.x, powerup.y) < 40) { // Collect powerup activatePowerup(powerup.type); // Remove powerup powerup.destroy(); powerups.splice(pu, 1); // Create particle effect createSoilEffect(powerup.x, powerup.y, 15); } } // Spawn new food foodTimer++; if (foodTimer >= SPAWN_FOOD_INTERVAL) { spawnFood(); foodTimer = 0; } // Spawn new obstacles based on level obstacleTimer++; if (obstacleTimer >= SPAWN_OBSTACLE_INTERVAL && obstacles.length < gameLevel + 2) { spawnObstacle(); obstacleTimer = 0; } // Spawn powerups occasionally powerupTimer++; if (powerupTimer >= SPAWN_POWERUP_INTERVAL) { spawnPowerup(); powerupTimer = 0; } // Spawn predators if game level is high enough if (gameLevel >= 2) { predatorTimer++; if (predatorTimer >= SPAWN_PREDATOR_INTERVAL && predators.length < MAX_PREDATORS) { spawnPredator(); predatorTimer = 0; } // Update predators for (var pr = predators.length - 1; pr >= 0; pr--) { var predator = predators[pr]; predator.update(); // Check for collisions with worm head var head = wormSegments[0]; if (getDistance(head.x, head.y, predator.x, predator.y) < 50 && predator.state === 'hunting' && predator.alpha > 0.5) { if (powerupActive && powerupType === 'invincibility') { // Destroy predator if invincible createSoilEffect(predator.x, predator.y, 25); predator.destroy(); predators.splice(pr, 1); } else { // Game over on collision LK.getSound('hit').play(); LK.effects.flashScreen(0xFF0000, 500); // Update high score if (score > highScore) { highScore = score; storage.highScore = highScore; } LK.showGameOver(); return; } } } } // Update powerup duration if (powerupActive) { powerupTimeRemaining--; if (powerupTimeRemaining <= 0) { endPowerupEffect(); } } // Level up check if (score >= levelScore) { // Level up gameLevel++; storage.level = gameLevel; levelScore = gameLevel * 10; // Flash screen green LK.effects.flashScreen(0x00FF00, 500); // Speed increase with level MOVE_SPEED = 5 + gameLevel * 0.25; if (MOVE_SPEED > 10) { MOVE_SPEED = 10; } } // Update UI game.updateUI(); }; // Event handlers game.down = function (x, y, obj) { if (!gameStarted) { gameStarted = true; return; } targetX = x; targetY = y; movingToTarget = true; }; game.move = function (x, y, obj) { if (gameStarted) { targetX = x; targetY = y; movingToTarget = true; } }; game.up = function (x, y, obj) { // Keep moving to the last target point };
===================================================================
--- original.js
+++ change.js
@@ -9,26 +9,97 @@
/****
* Classes
****/
-var Food = Container.expand(function (isSpecial) {
+var Food = Container.expand(function (foodType) {
var self = Container.call(this);
- self.isSpecial = isSpecial || false;
- self.value = self.isSpecial ? 3 : 1;
- var foodGraphics = self.attachAsset(self.isSpecial ? 'food_special' : 'food_normal', {
+ // Food types: normal, special, seed, insect, root, fruit
+ self.foodType = foodType || 'normal';
+ self.isSpecial = self.foodType === 'special';
+ // Set value based on food type
+ switch (self.foodType) {
+ case 'normal':
+ self.value = 1;
+ break;
+ case 'special':
+ self.value = 3;
+ break;
+ case 'seed':
+ self.value = 1;
+ break;
+ case 'insect':
+ self.value = 2;
+ break;
+ case 'root':
+ self.value = 2;
+ break;
+ case 'fruit':
+ self.value = 4;
+ break;
+ default:
+ self.value = 1;
+ }
+ // Attach the appropriate food graphic
+ var foodGraphics = self.attachAsset('food_' + self.foodType, {
anchorX: 0.5,
anchorY: 0.5
});
- if (self.isSpecial) {
- // Add pulsating animation for special food
- self.pulseDirection = 1;
- self.pulseMin = 0.85;
- self.pulseMax = 1.15;
- self.pulseSpeed = 0.01;
+ // Add decoration for insect type
+ if (self.foodType === 'insect') {
+ // Add legs
+ for (var i = 0; i < 3; i++) {
+ var leftLeg = self.attachAsset('soil_particle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 2,
+ scaleY: 0.5,
+ rotation: Math.PI / 4 + i * Math.PI / 6,
+ x: -5,
+ y: -5 + i * 5
+ });
+ var rightLeg = self.attachAsset('soil_particle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 2,
+ scaleY: 0.5,
+ rotation: -Math.PI / 4 - i * Math.PI / 6,
+ x: 5,
+ y: -5 + i * 5
+ });
+ }
}
+ // Add decoration for fruit type
+ if (self.foodType === 'fruit') {
+ var stem = self.attachAsset('soil_particle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.8,
+ scaleY: 1.5,
+ x: 0,
+ y: -15
+ });
+ stem.tint = 0x006400; // Dark green
+ }
+ // Animations based on food type
+ switch (self.foodType) {
+ case 'special':
+ case 'fruit':
+ self.pulseDirection = 1;
+ self.pulseMin = 0.85;
+ self.pulseMax = 1.15;
+ self.pulseSpeed = 0.01;
+ break;
+ case 'insect':
+ self.moveDirection = Math.random() * Math.PI * 2;
+ self.moveSpeed = 0.3;
+ self.movementRange = 20;
+ self.originalX = self.x;
+ self.originalY = self.y;
+ break;
+ }
self.update = function () {
- if (self.isSpecial) {
- // Pulsating effect
+ // Pulsating effect for special foods and fruits
+ if (self.foodType === 'special' || self.foodType === 'fruit') {
if (self.pulseDirection > 0) {
foodGraphics.scale.x += self.pulseSpeed;
foodGraphics.scale.y += self.pulseSpeed;
if (foodGraphics.scale.x >= self.pulseMax) {
@@ -41,8 +112,24 @@
self.pulseDirection = 1;
}
}
}
+ // Movement pattern for insects
+ if (self.foodType === 'insect') {
+ self.moveDirection += (Math.random() - 0.5) * 0.2;
+ var newX = self.x + Math.cos(self.moveDirection) * self.moveSpeed;
+ var newY = self.y + Math.sin(self.moveDirection) * self.moveSpeed;
+ // Keep within movement range
+ if (Math.abs(newX - self.originalX) < self.movementRange && Math.abs(newY - self.originalY) < self.movementRange) {
+ self.x = newX;
+ self.y = newY;
+ } else {
+ // Reverse direction if going too far
+ self.moveDirection += Math.PI;
+ }
+ // Rotate insect in direction of movement
+ foodGraphics.rotation = self.moveDirection;
+ }
};
return self;
});
var Obstacle = Container.expand(function () {
@@ -97,24 +184,163 @@
}
};
return self;
});
+var Predator = Container.expand(function () {
+ var self = Container.call(this);
+ // Create predator body (mole or other underground creature)
+ var body = self.attachAsset('obstacle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 1.5,
+ scaleY: 1.5
+ });
+ body.tint = 0x606060; // Gray color for mole
+ // Add eyes
+ var leftEye = self.attachAsset('food_normal', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.4,
+ scaleY: 0.4,
+ x: -15,
+ y: -10
+ });
+ leftEye.tint = 0xff0000; // Red eyes
+ var rightEye = self.attachAsset('food_normal', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.4,
+ scaleY: 0.4,
+ x: 15,
+ y: -10
+ });
+ rightEye.tint = 0xff0000;
+ // Add teeth/claws
+ var leftClaw = self.attachAsset('obstacle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.3,
+ scaleY: 0.8,
+ rotation: -Math.PI / 6,
+ x: -20,
+ y: 15
+ });
+ leftClaw.tint = 0xffffff;
+ var rightClaw = self.attachAsset('obstacle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.3,
+ scaleY: 0.8,
+ rotation: Math.PI / 6,
+ x: 20,
+ y: 15
+ });
+ rightClaw.tint = 0xffffff;
+ // Predator properties
+ self.speed = 2;
+ self.targetWorm = null;
+ self.huntRadius = 300; // Detection range
+ self.active = false;
+ self.idleTime = 0;
+ self.huntTime = 0;
+ self.maxHuntTime = 300; // Hunt for 5 seconds max
+ self.state = 'sleeping'; // sleeping, hunting, retreating
+ // Update method to handle predator behavior
+ self.update = function () {
+ if (self.state === 'sleeping') {
+ // Occasionally check if worm is nearby
+ if (LK.ticks % 30 === 0 && self.targetWorm) {
+ var wormHead = self.targetWorm[0];
+ var distance = getDistance(self.x, self.y, wormHead.x, wormHead.y);
+ // Activate if worm is within range
+ if (distance < self.huntRadius) {
+ self.state = 'hunting';
+ self.huntTime = 0;
+ // Flash eyes
+ tween(leftEye, {
+ alpha: 0
+ }, {
+ duration: 100,
+ yoyo: true,
+ repeat: 3
+ });
+ tween(rightEye, {
+ alpha: 0
+ }, {
+ duration: 100,
+ yoyo: true,
+ repeat: 3
+ });
+ }
+ }
+ // Slightly wiggle while sleeping
+ body.rotation = Math.sin(LK.ticks / 20) * 0.05;
+ } else if (self.state === 'hunting') {
+ // Hunt the worm
+ if (self.targetWorm && self.targetWorm.length > 0) {
+ var wormHead = self.targetWorm[0];
+ var dx = wormHead.x - self.x;
+ var dy = wormHead.y - self.y;
+ var angle = Math.atan2(dy, dx);
+ // Move toward the worm
+ self.x += Math.cos(angle) * self.speed;
+ self.y += Math.sin(angle) * self.speed;
+ // Rotate to face direction of movement
+ self.rotation = angle;
+ // Create soil particles while moving
+ if (LK.ticks % 5 === 0) {
+ createSoilEffect(self.x, self.y, 2);
+ }
+ // Increase hunt time
+ self.huntTime++;
+ if (self.huntTime >= self.maxHuntTime) {
+ // Give up after max hunt time
+ self.state = 'retreating';
+ }
+ }
+ } else if (self.state === 'retreating') {
+ // Retreat underground
+ self.alpha -= 0.02;
+ if (self.alpha <= 0) {
+ // Reset predator
+ self.alpha = 1;
+ // Move to a new random location
+ self.x = Math.random() * (GAME_WIDTH - 200) + 100;
+ self.y = Math.random() * (GAME_HEIGHT - 200) + 100;
+ // Go back to sleeping
+ self.state = 'sleeping';
+ }
+ }
+ };
+ return self;
+});
var SoilParticle = Container.expand(function () {
var self = Container.call(this);
var particleGraphics = self.attachAsset('soil_particle', {
anchorX: 0.5,
anchorY: 0.5
});
+ // Set tint method - allows changing the soil particle color
+ self.tint = function (color) {
+ particleGraphics.tint = color;
+ };
self.alpha = Math.random() * 0.5 + 0.2;
self.lifespan = Math.random() * 30 + 15;
self.velocityX = (Math.random() - 0.5) * 2;
self.velocityY = (Math.random() - 0.5) * 2;
+ // Random rotation
+ self.rotation = Math.random() * Math.PI * 2;
+ // Random scale for more varied soil particles
+ self.scale = Math.random() * 0.5 + 0.75;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
+ self.rotation += 0.02; // Slow rotation for visual interest
self.lifespan--;
+ // Gradually fade out and get smaller
if (self.lifespan <= 10) {
self.alpha -= 0.1;
+ self.scale -= 0.05;
}
};
return self;
});
@@ -124,16 +350,73 @@
var segmentGraphics = self.attachAsset(self.isHead ? 'worm_head' : 'worm_body', {
anchorX: 0.5,
anchorY: 0.5
});
+ // Add details like eyes and mouth if this is the head
+ if (self.isHead) {
+ // Create mouth
+ var mouth = self.attachAsset('worm_body', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.5,
+ scaleY: 0.2,
+ x: 10,
+ y: 5
+ });
+ mouth.tint = 0x000000;
+ // Left eye
+ var leftEye = self.attachAsset('worm_body', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.25,
+ scaleY: 0.25,
+ x: 10,
+ y: -10
+ });
+ leftEye.tint = 0x000000;
+ // Right eye
+ var rightEye = self.attachAsset('worm_body', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.25,
+ scaleY: 0.25,
+ x: 10,
+ y: 10
+ });
+ rightEye.tint = 0x000000;
+ // Add pulsating animation for head to simulate breathing
+ self.pulseDirection = 1;
+ self.pulseMin = 0.95;
+ self.pulseMax = 1.05;
+ self.pulseSpeed = 0.005;
+ }
// Store previous position for smooth following
self.prevX = 0;
self.prevY = 0;
// Save position to follow
self.savePosition = function () {
self.prevX = self.x;
self.prevY = self.y;
};
+ // Add update method to animate head
+ if (self.isHead) {
+ self.update = function () {
+ // Pulsating effect
+ if (self.pulseDirection > 0) {
+ segmentGraphics.scale.x += self.pulseSpeed;
+ segmentGraphics.scale.y += self.pulseSpeed;
+ if (segmentGraphics.scale.x >= self.pulseMax) {
+ self.pulseDirection = -1;
+ }
+ } else {
+ segmentGraphics.scale.x -= self.pulseSpeed;
+ segmentGraphics.scale.y -= self.pulseSpeed;
+ if (segmentGraphics.scale.x <= self.pulseMin) {
+ self.pulseDirection = 1;
+ }
+ }
+ };
+ }
return self;
});
/****
@@ -145,8 +428,28 @@
/****
* Game Code
****/
+// Spawn a predator
+function spawnPredator() {
+ if (predators.length >= MAX_PREDATORS) {
+ return;
+ }
+ var predator = new Predator();
+ // Position predator randomly away from edges and worm head
+ var head = wormSegments[0];
+ do {
+ predator.x = Math.random() * (GAME_WIDTH - 200) + 100;
+ predator.y = Math.random() * (GAME_HEIGHT - 200) + 100;
+ } while (getDistance(predator.x, predator.y, head.x, head.y) < 500);
+ // Link to worm segments for tracking
+ predator.targetWorm = wormSegments;
+ // Add to game
+ game.addChild(predator);
+ predators.push(predator);
+ // Create dramatic soil eruption effect
+ createSoilEffect(predator.x, predator.y, 20);
+}
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var SEGMENT_SPACING = 30;
@@ -158,13 +461,34 @@
var SPAWN_OBSTACLE_INTERVAL = 180;
var SPAWN_POWERUP_INTERVAL = 600;
var SPECIAL_FOOD_CHANCE = 0.2;
var POWERUP_DURATION = 300;
+// Terrain constants
+var TERRAIN_TYPES = [{
+ name: 'soil',
+ color: 0x5d4037,
+ speedModifier: 1.0
+}, {
+ name: 'sand',
+ color: 0xd2b48c,
+ speedModifier: 1.2
+}, {
+ name: 'clay',
+ color: 0x8d6e63,
+ speedModifier: 0.8
+}, {
+ name: 'humus',
+ color: 0x3e2723,
+ speedModifier: 0.9
+}];
+var TERRAIN_SIZE = 400; // Size of terrain patches
+var currentTerrain = TERRAIN_TYPES[0]; // Default terrain
// Game variables
var wormSegments = [];
var foods = [];
var obstacles = [];
var powerups = [];
+var predators = [];
var soilParticles = [];
var targetX = 0;
var targetY = 0;
var score = 0;
@@ -174,27 +498,57 @@
var wormAngle = 0;
var foodTimer = 0;
var obstacleTimer = 0;
var powerupTimer = 0;
+var predatorTimer = 0;
+var SPAWN_PREDATOR_INTERVAL = 600; // 10 seconds at 60 FPS
+var MAX_PREDATORS = Math.min(Math.floor(gameLevel / 2), 3); // Cap at 3 predators
var powerupActive = false;
var powerupType = null;
var powerupTimeRemaining = 0;
var gameStarted = false;
var levelScore = gameLevel * 10;
-// Create background with soil texture
+// Create background with terrain variations
function createBackground() {
var backgroundContainer = new Container();
game.addChild(backgroundContainer);
var tileSize = 100;
+ // Create terrain patches
+ var terrainPatches = [];
+ for (var tx = 0; tx < GAME_WIDTH; tx += TERRAIN_SIZE) {
+ for (var ty = 0; ty < GAME_HEIGHT; ty += TERRAIN_SIZE) {
+ // Randomly select terrain type for this patch
+ var terrainType = TERRAIN_TYPES[Math.floor(Math.random() * TERRAIN_TYPES.length)];
+ terrainPatches.push({
+ x: tx,
+ y: ty,
+ width: TERRAIN_SIZE,
+ height: TERRAIN_SIZE,
+ type: terrainType
+ });
+ }
+ }
+ // Create tiles based on terrain patches
for (var x = 0; x < GAME_WIDTH; x += tileSize) {
for (var y = 0; y < GAME_HEIGHT; y += tileSize) {
+ // Determine which terrain patch this tile belongs to
+ var terrainType = TERRAIN_TYPES[0]; // Default
+ for (var i = 0; i < terrainPatches.length; i++) {
+ var patch = terrainPatches[i];
+ if (x >= patch.x && x < patch.x + patch.width && y >= patch.y && y < patch.y + patch.height) {
+ terrainType = patch.type;
+ break;
+ }
+ }
var tile = LK.getAsset('background_tile', {
anchorX: 0,
anchorY: 0,
width: tileSize,
height: tileSize,
alpha: 0.7 + Math.random() * 0.3
});
+ tile.tint = terrainType.color;
+ tile.terrainType = terrainType;
tile.x = x;
tile.y = y;
tile.rotation = Math.random() * Math.PI * 2;
tile.scale.x = 0.9 + Math.random() * 0.2;
@@ -229,10 +583,41 @@
wormSegments.push(newSegment);
}
// Spawn a food item at a random position
function spawnFood() {
- var isSpecial = Math.random() < SPECIAL_FOOD_CHANCE;
- var food = new Food(isSpecial);
+ // Determine food type based on probabilities and level
+ var foodTypes = ['normal', 'seed', 'insect', 'root', 'fruit', 'special'];
+ var probabilities = [0.4 - gameLevel * 0.02,
+ // normal (decreases with level)
+ 0.2,
+ // seed
+ 0.15 + gameLevel * 0.01,
+ // insect (increases with level)
+ 0.15,
+ // root
+ 0.1 - gameLevel * 0.01,
+ // fruit (decreases with level)
+ Math.min(0.05 + gameLevel * 0.01, 0.2) // special (increases with level, capped at 20%)
+ ];
+ // Make sure probabilities add up to 1
+ var sum = probabilities.reduce(function (a, b) {
+ return a + b;
+ }, 0);
+ probabilities = probabilities.map(function (p) {
+ return p / sum;
+ });
+ // Select food type based on weighted random
+ var random = Math.random();
+ var cumulativeProb = 0;
+ var selectedFoodType = 'normal';
+ for (var i = 0; i < foodTypes.length; i++) {
+ cumulativeProb += probabilities[i];
+ if (random <= cumulativeProb) {
+ selectedFoodType = foodTypes[i];
+ break;
+ }
+ }
+ var food = new Food(selectedFoodType);
// Position food randomly away from edges
food.x = Math.random() * (GAME_WIDTH - 200) + 100;
food.y = Math.random() * (GAME_HEIGHT - 200) + 100;
// Make sure food doesn't overlap with obstacles
@@ -243,11 +628,55 @@
food.y = Math.random() * (GAME_HEIGHT - 200) + 100;
i = -1; // Reset loop to check again
}
}
+ // For fruits and roots, try to place them in appropriate terrain if possible
+ if (selectedFoodType === 'fruit' || selectedFoodType === 'root') {
+ var attempts = 0;
+ var placed = false;
+ var desiredTerrain = selectedFoodType === 'fruit' ? 'humus' : 'clay';
+ while (attempts < 5 && !placed) {
+ var testX = Math.random() * (GAME_WIDTH - 200) + 100;
+ var testY = Math.random() * (GAME_HEIGHT - 200) + 100;
+ // Check terrain
+ var terrainAtPosition = findTerrainAtPosition(testX, testY);
+ if (terrainAtPosition && terrainAtPosition.name === desiredTerrain) {
+ // Check obstacles
+ var validPosition = true;
+ for (var j = 0; j < obstacles.length; j++) {
+ if (getDistance(testX, testY, obstacles[j].x, obstacles[j].y) < 100) {
+ validPosition = false;
+ break;
+ }
+ }
+ if (validPosition) {
+ food.x = testX;
+ food.y = testY;
+ placed = true;
+ }
+ }
+ attempts++;
+ }
+ }
+ // Save original position for insects
+ if (selectedFoodType === 'insect') {
+ food.originalX = food.x;
+ food.originalY = food.y;
+ }
game.addChild(food);
foods.push(food);
}
+// Helper function to determine terrain at a specific position
+function findTerrainAtPosition(x, y) {
+ // Find the terrain patch this position belongs to
+ for (var i = 0; i < TERRAIN_TYPES.length; i++) {
+ var terrainX = Math.floor(x / TERRAIN_SIZE) * TERRAIN_SIZE;
+ var terrainY = Math.floor(y / TERRAIN_SIZE) * TERRAIN_SIZE;
+ // Return the terrain type for this patch (simplified approach)
+ return TERRAIN_TYPES[Math.floor(Math.random() * TERRAIN_TYPES.length)];
+ }
+ return TERRAIN_TYPES[0]; // Default to first terrain type
+}
// Spawn an obstacle
function spawnObstacle() {
var obstacle = new Obstacle();
// Position obstacle randomly away from edges and worm head
@@ -279,13 +708,24 @@
game.addChild(powerup);
powerups.push(powerup);
}
// Create soil particles effect
-function createSoilEffect(x, y, count) {
+function createSoilEffect(x, y, count, color) {
for (var i = 0; i < count; i++) {
var particle = new SoilParticle();
- particle.x = x;
- particle.y = y;
+ // Set position with slight randomization
+ particle.x = x + (Math.random() - 0.5) * 10;
+ particle.y = y + (Math.random() - 0.5) * 10;
+ // Apply terrain color if provided
+ if (color) {
+ particle.tint = color;
+ } else {
+ // Use color from current terrain if no specific color is provided
+ var terrainAtPosition = findTerrainAtPosition(x, y);
+ if (terrainAtPosition) {
+ particle.tint = terrainAtPosition.color;
+ }
+ }
game.addChild(particle);
soilParticles.push(particle);
}
}
@@ -421,20 +861,23 @@
wormSegments = [];
foods = [];
obstacles = [];
powerups = [];
+ predators = [];
soilParticles = [];
score = 0;
movingToTarget = false;
wormAngle = 0;
foodTimer = 0;
obstacleTimer = 0;
powerupTimer = 0;
+ predatorTimer = 0;
powerupActive = false;
powerupType = null;
powerupTimeRemaining = 0;
levelScore = gameLevel * 10;
MOVE_SPEED = 5;
+ MAX_PREDATORS = Math.min(Math.floor(gameLevel / 2), 3); // Recalculate for current level
// Set background
createBackground();
// Create the worm
createWorm();
@@ -475,15 +918,25 @@
angleDiff += Math.PI * 2;
}
// Apply gradual turning
wormAngle += angleDiff * TURNING_SPEED;
- // Move in the current direction
+ // Determine terrain at current position
+ var currentTerrainType = findTerrainAtPosition(head.x, head.y);
+ var speedModifier = currentTerrainType ? currentTerrainType.speedModifier : 1.0;
+ // Apply terrain effects to movement
+ var adjustedSpeed = MOVE_SPEED * speedModifier;
+ if (powerupActive && powerupType === 'speed') {
+ adjustedSpeed *= 1.75; // Apply speed powerup with terrain modifier
+ }
+ // Move in the current direction with terrain-adjusted speed
head.savePosition();
- head.x += Math.cos(wormAngle) * MOVE_SPEED;
- head.y += Math.sin(wormAngle) * MOVE_SPEED;
+ head.x += Math.cos(wormAngle) * adjustedSpeed;
+ head.y += Math.sin(wormAngle) * adjustedSpeed;
// Create soil particles as the worm moves
if (LK.ticks % 3 === 0) {
- createSoilEffect(head.x, head.y, 1);
+ var particleCount = speedModifier > 1 ? 2 : 1; // More particles in sand (faster terrain)
+ var particleColor = currentTerrainType ? currentTerrainType.color : 0x8b4513;
+ createSoilEffect(head.x, head.y, particleCount, particleColor);
}
// Check if close enough to target
if (getDistance(head.x, head.y, targetX, targetY) < 10) {
movingToTarget = false;
@@ -604,8 +1057,42 @@
if (powerupTimer >= SPAWN_POWERUP_INTERVAL) {
spawnPowerup();
powerupTimer = 0;
}
+ // Spawn predators if game level is high enough
+ if (gameLevel >= 2) {
+ predatorTimer++;
+ if (predatorTimer >= SPAWN_PREDATOR_INTERVAL && predators.length < MAX_PREDATORS) {
+ spawnPredator();
+ predatorTimer = 0;
+ }
+ // Update predators
+ for (var pr = predators.length - 1; pr >= 0; pr--) {
+ var predator = predators[pr];
+ predator.update();
+ // Check for collisions with worm head
+ var head = wormSegments[0];
+ if (getDistance(head.x, head.y, predator.x, predator.y) < 50 && predator.state === 'hunting' && predator.alpha > 0.5) {
+ if (powerupActive && powerupType === 'invincibility') {
+ // Destroy predator if invincible
+ createSoilEffect(predator.x, predator.y, 25);
+ predator.destroy();
+ predators.splice(pr, 1);
+ } else {
+ // Game over on collision
+ LK.getSound('hit').play();
+ LK.effects.flashScreen(0xFF0000, 500);
+ // Update high score
+ if (score > highScore) {
+ highScore = score;
+ storage.highScore = highScore;
+ }
+ LK.showGameOver();
+ return;
+ }
+ }
+ }
+ }
// Update powerup duration
if (powerupActive) {
powerupTimeRemaining--;
if (powerupTimeRemaining <= 0) {