User prompt
ateş 10 kademe olarak büyüsün. Söndürülmedikçe ateş büyüsün ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
yılanın başı yöne göre dönsün
Code edit (1 edits merged)
Please save this source code
User prompt
Let there be a fire in several places at advanced levels. If the snake does not have enough water, it will burn when it touches the fire. Let's put a forest image in the background and let it pass through the roads between. Let animals occasionally escape from the place where the fire is
Code edit (1 edits merged)
Please save this source code
User prompt
Snake Fire Brigade
Initial prompt
snake style game. In the game, the snake collects water to extinguish the fire. There will be fires in some places. If the fire is small, the number of waters it collects will change according to the size of 1 water. When the fire is extinguished, a fire will start in a different place.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Animal = Container.expand(function () { var self = Container.call(this); var animalGraphics = self.attachAsset('animal', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = (Math.random() - 0.5) * 4; self.speedY = (Math.random() - 0.5) * 4; self.lifeTime = 300; // 5 seconds at 60fps self.update = function () { self.x += self.speedX; self.y += self.speedY; self.lifeTime--; if (self.lifeTime <= 0) { self.destroy(); } }; return self; }); var Bird = Container.expand(function () { var self = Container.call(this); var birdGraphics = self.attachAsset('bird', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = (Math.random() - 0.5) * 5; self.speedY = (Math.random() - 0.5) * 5; self.lifeTime = 350; self.flapTimer = 0; self.baseY = 0; self.animOffset = Math.random() * Math.PI * 2; self.update = function () { // Flying movement with up/down oscillation if (self.baseY === 0) { self.baseY = self.y; } self.x += self.speedX; self.y = self.baseY + Math.sin(LK.ticks * 0.1 + self.animOffset) * 15; // Wing flapping animation self.flapTimer++; if (self.flapTimer >= 5) { self.flapTimer = 0; var flapScale = 0.8 + Math.sin(LK.ticks * 0.3 + self.animOffset) * 0.2; birdGraphics.scaleX = flapScale; } self.lifeTime--; if (self.lifeTime <= 0) { self.destroy(); } }; return self; }); var Deer = Container.expand(function () { var self = Container.call(this); var deerGraphics = self.attachAsset('deer', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = (Math.random() - 0.5) * 2; // Slower movement self.speedY = (Math.random() - 0.5) * 2; self.lifeTime = 500; // Lives longest self.panicTimer = 0; self.isPanicking = false; self.update = function () { // Deer occasionally panic and run faster if (!self.isPanicking && Math.random() < 0.005) { self.isPanicking = true; self.panicTimer = 60; // Panic for 1 second self.speedX *= 3; self.speedY *= 3; // Flash effect when panicking tween(deerGraphics, { tint: 0xFFAAAA }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(deerGraphics, { tint: 0xFFFFFF }, { duration: 200, easing: tween.easeInOut }); } }); } if (self.isPanicking) { self.panicTimer--; if (self.panicTimer <= 0) { self.isPanicking = false; self.speedX /= 3; self.speedY /= 3; } } self.x += self.speedX; self.y += self.speedY; self.lifeTime--; if (self.lifeTime <= 0) { self.destroy(); } }; return self; }); var Fire = Container.expand(function (size) { var self = Container.call(this); self.size = size || 1; self.maxSize = 10; // Maximum fire size self.waterNeeded = self.size; self.growthTimer = 0; // Fire grows every 20 seconds (1200 ticks at 60fps) self.growthInterval = 1200; // 20 seconds fixed interval var fireGraphics = self.attachAsset(self.size === 1 ? 'smallFire' : 'largeFire', { anchorX: 0.5, anchorY: 0.5 }); // Set initial scale based on fire size var initialScale = 0.5 + (self.size - 1) * 0.2; // Start smaller, scale up with size fireGraphics.scaleX = initialScale; fireGraphics.scaleY = initialScale; // Add flickering animation self.animOffset = Math.random() * Math.PI * 2; self.update = function () { // Growth logic - fire grows every 7 seconds self.growthTimer++; if (self.growthTimer >= self.growthInterval && self.size < self.maxSize) { self.size++; self.waterNeeded = self.size; self.growthTimer = 0; // Update size text if (self.sizeText) { self.sizeText.setText(self.size.toString()); } // Update visual scale based on size - smaller start, bigger growth var scale = 0.5 + (self.size - 1) * 0.25; // Start at 0.5, increase by 25% per size level tween(fireGraphics, { scaleX: scale, scaleY: scale }, { duration: 500, easing: tween.easeOut }); // Change color intensity based on size var intensity = Math.min(1, 0.5 + (self.size - 1) * 0.1); var redComponent = 0xFF; var greenBlueComponent = Math.floor((1 - intensity) * 0x44); var redTint = redComponent << 16 | greenBlueComponent << 8 | greenBlueComponent; tween(fireGraphics, { tint: redTint }, { duration: 500, easing: tween.easeOut }); } // Flickering animation var flicker = 0.8 + Math.sin(LK.ticks * 0.1 + self.animOffset) * 0.2; fireGraphics.alpha = flicker; var baseScaleX = fireGraphics.scaleX || 1; var baseScaleY = fireGraphics.scaleY || 1; fireGraphics.scaleX = baseScaleX + Math.sin(LK.ticks * 0.08 + self.animOffset) * 0.1; fireGraphics.scaleY = baseScaleY + Math.sin(LK.ticks * 0.08 + self.animOffset) * 0.1; }; return self; }); var HeartPowerUp = Container.expand(function () { var self = Container.call(this); var heartGraphics = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); self.lifeTime = 600; // 10 seconds at 60fps self.animOffset = Math.random() * Math.PI * 2; self.update = function () { // Floating animation heartGraphics.y = Math.sin(LK.ticks * 0.1 + self.animOffset) * 10; // Pulsing animation var pulse = 1.0 + Math.sin(LK.ticks * 0.15 + self.animOffset) * 0.3; heartGraphics.scaleX = 1.5 * pulse; heartGraphics.scaleY = 1.5 * pulse; // Fade out in last 2 seconds if (self.lifeTime <= 120) { heartGraphics.alpha = self.lifeTime / 120; } self.lifeTime--; if (self.lifeTime <= 0) { self.destroy(); } }; return self; }); var Rabbit = Container.expand(function () { var self = Container.call(this); var rabbitGraphics = self.attachAsset('rabbit', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = (Math.random() - 0.5) * 6; // Faster than regular animals self.speedY = (Math.random() - 0.5) * 6; self.lifeTime = 400; // Lives longer self.hopTimer = 0; self.hopInterval = 20; // Hop every 20 ticks self.update = function () { self.hopTimer++; // Hopping movement - move in bursts if (self.hopTimer >= self.hopInterval) { self.x += self.speedX * 2; // Burst movement self.y += self.speedY * 2; self.hopTimer = 0; // Add hop animation tween(rabbitGraphics, { scaleY: 1.3 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(rabbitGraphics, { scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); } self.lifeTime--; if (self.lifeTime <= 0) { self.destroy(); } }; return self; }); var Rock = Container.expand(function () { var self = Container.call(this); var rockGraphics = self.attachAsset('rock', { anchorX: 0.5, anchorY: 0.5 }); // Add some visual variation to rocks self.animOffset = Math.random() * Math.PI * 2; self.update = function () { // Subtle rotation animation rockGraphics.rotation = Math.sin(LK.ticks * 0.01 + self.animOffset) * 0.1; }; return self; }); var SnakeSegment = Container.expand(function (isHead, isTail) { var self = Container.call(this); var assetType = isHead ? 'snakeHead' : isTail ? 'snakeTail' : 'snakeBody'; var segmentGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); return self; }); var WaterDroplet = Container.expand(function () { var self = Container.call(this); var waterGraphics = self.attachAsset('water', { anchorX: 0.5, anchorY: 0.5 }); // Add floating animation self.animOffset = Math.random() * Math.PI * 2; self.update = function () { waterGraphics.y = Math.sin(LK.ticks * 0.05 + self.animOffset) * 5; }; return self; }); var WaterSpray = Container.expand(function (startX, startY, targetX, targetY) { var self = Container.call(this); // Create multiple water droplets for spray effect self.droplets = []; for (var i = 0; i < 8; i++) { var droplet = self.attachAsset('water', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); // Position droplet at start position droplet.x = 0; droplet.y = 0; // Calculate spray direction with some randomness var angle = Math.atan2(targetY - startY, targetX - startX); var spreadAngle = (Math.random() - 0.5) * 0.8; // Random spread var finalAngle = angle + spreadAngle; // Set movement properties droplet.velocityX = Math.cos(finalAngle) * (3 + Math.random() * 2); droplet.velocityY = Math.sin(finalAngle) * (3 + Math.random() * 2); droplet.life = 30 + Math.random() * 20; // Random lifetime droplet.maxLife = droplet.life; self.droplets.push(droplet); } self.x = startX; self.y = startY; self.totalLife = 60; self.update = function () { self.totalLife--; // Update each droplet for (var i = self.droplets.length - 1; i >= 0; i--) { var droplet = self.droplets[i]; // Move droplet droplet.x += droplet.velocityX; droplet.y += droplet.velocityY; // Fade out droplet droplet.life--; droplet.alpha = droplet.life / droplet.maxLife; // Remove dead droplets if (droplet.life <= 0) { self.removeChild(droplet); self.droplets.splice(i, 1); } } // Remove spray when all droplets are gone or time is up if (self.droplets.length === 0 || self.totalLife <= 0) { self.destroy(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2F4F2F }); /**** * Game Code ****/ // Add forest background var forestBackground = game.attachAsset('forest', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Background tracking variables var currentBackgroundLevel = 0; var backgrounds = ['forest', 'desert', 'snow', 'jungle', 'volcano']; var backgroundAssets = [forestBackground]; // Game variables var snake = []; var snakeDirection = { x: 1, y: 0 }; var nextDirection = { x: 1, y: 0 }; var gridSize = 60; var gameWidth = 2048; var gameHeight = 2732; var cols = Math.floor(gameWidth / gridSize); var rows = Math.floor(gameHeight / gridSize); var waterDroplets = []; var fires = []; var waterCollected = 0; var moveTimer = 0; var moveInterval = 15; // Snake moves every 15 ticks var gameRunning = true; var animals = []; var rocks = []; var heartPowerUps = []; var maxFires = 1; // Start with 1 fire, increase with level var lives = 3; // Player starts with 3 lives var heartIcons = []; // UI Elements var scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var waterTxt = new Text2('Water: 0', { size: 70, fill: 0x1E90FF }); waterTxt.anchor.set(1, 0); waterTxt.x = -20; waterTxt.y = 80; LK.gui.topRight.addChild(waterTxt); // Create UI bar container positioned at the top var uiBar = new Container(); uiBar.y = 20; uiBar.x = 150; LK.gui.topLeft.addChild(uiBar); // Create heart icons for lives for (var i = 0; i < 3; i++) { var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.4, scaleY: 2.4 }); heart.x = i * 50; heart.y = 0; heartIcons.push(heart); uiBar.addChild(heart); } // Sound control button var isMuted = false; var soundBtn = LK.getAsset('soundOn', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.4, scaleY: 2.4 }); soundBtn.x = 200; soundBtn.y = 0; uiBar.addChild(soundBtn); // Sound button click handler soundBtn.down = function (x, y, obj) { isMuted = !isMuted; if (isMuted) { LK.stopMusic(); soundBtn.removeChild(soundBtn.children[0]); soundBtn.attachAsset('soundOff', { anchorX: 0.5, anchorY: 0.5 }); } else { LK.playMusic('bgmusic'); soundBtn.removeChild(soundBtn.children[0]); soundBtn.attachAsset('soundOn', { anchorX: 0.5, anchorY: 0.5 }); } }; // Initialize snake function initSnake() { // Create initial snake with 3 segments for (var i = 0; i < 3; i++) { var isHead = i === 0; var isTail = i === 2; // Last segment is tail var segment = new SnakeSegment(isHead, isTail); segment.x = (5 - i) * gridSize + gridSize / 2; segment.y = 5 * gridSize + gridSize / 2; snake.push(segment); game.addChild(segment); } } // Convert grid coordinates to world coordinates function gridToWorld(gridX, gridY) { return { x: gridX * gridSize + gridSize / 2, y: gridY * gridSize + gridSize / 2 }; } // Convert world coordinates to grid coordinates function worldToGrid(worldX, worldY) { return { x: Math.floor(worldX / gridSize), y: Math.floor(worldY / gridSize) }; } // Get random empty grid position function getRandomEmptyPosition() { var attempts = 0; while (attempts < 100) { var gridX = Math.floor(Math.random() * cols); var gridY = Math.floor(Math.random() * rows); var worldPos = gridToWorld(gridX, gridY); var occupied = false; // Check if position is occupied by snake for (var i = 0; i < snake.length; i++) { var snakeGrid = worldToGrid(snake[i].x, snake[i].y); if (snakeGrid.x === gridX && snakeGrid.y === gridY) { occupied = true; break; } } // Check if position is occupied by water if (!occupied) { for (var i = 0; i < waterDroplets.length; i++) { var waterGrid = worldToGrid(waterDroplets[i].x, waterDroplets[i].y); if (waterGrid.x === gridX && waterGrid.y === gridY) { occupied = true; break; } } } // Check if position is occupied by fire if (!occupied) { for (var i = 0; i < fires.length; i++) { var fireGrid = worldToGrid(fires[i].x, fires[i].y); if (fireGrid.x === gridX && fireGrid.y === gridY) { occupied = true; break; } } } // Check if position is occupied by rock if (!occupied) { for (var i = 0; i < rocks.length; i++) { var rockGrid = worldToGrid(rocks[i].x, rocks[i].y); if (rockGrid.x === gridX && rockGrid.y === gridY) { occupied = true; break; } } } if (!occupied) { return worldPos; } attempts++; } // Fallback to center if no empty position found return gridToWorld(Math.floor(cols / 2), Math.floor(rows / 2)); } // Spawn water droplet function spawnWater() { var pos = getRandomEmptyPosition(); var water = new WaterDroplet(); water.x = pos.x; water.y = pos.y; waterDroplets.push(water); game.addChild(water); } // Spawn fire function spawnFire() { var pos = getRandomEmptyPosition(); var fireSize = Math.random() < 0.7 ? 1 : 2; // 70% chance for small fire var fire = new Fire(fireSize); fire.x = pos.x; fire.y = pos.y; // Add size display text fire.sizeText = new Text2(fire.size.toString(), { size: 50, fill: 0xFFFFFF }); fire.sizeText.anchor.set(0.5, 0.5); fire.sizeText.x = 0; fire.sizeText.y = -50; fire.addChild(fire.sizeText); fires.push(fire); game.addChild(fire); // Spawn multiple escaping animals near fire var animalCount = Math.floor(Math.random() * 3) + 2; // 2-4 animals for (var j = 0; j < animalCount; j++) { var animal; var animalType = Math.random(); if (animalType < 0.4) { // 40% chance for rabbit animal = new Rabbit(); } else if (animalType < 0.7) { // 30% chance for deer animal = new Deer(); } else if (animalType < 0.9) { // 20% chance for bird animal = new Bird(); } else { // 10% chance for regular animal animal = new Animal(); } animal.x = pos.x + (Math.random() - 0.5) * 120; animal.y = pos.y + (Math.random() - 0.5) * 120; animals.push(animal); game.addChild(animal); } } // Spawn rock obstacle function spawnRock() { var pos = getRandomEmptyPosition(); var rock = new Rock(); rock.x = pos.x; rock.y = pos.y; // Scale rock based on score - start at 2x, increase with score var baseScale = 2.0; // Start at 2x size var scoreMultiplier = Math.floor(LK.getScore() / 1000) * 0.5; // +0.5x every 1000 points var finalScale = baseScale + scoreMultiplier; rock.children[0].scaleX = finalScale; rock.children[0].scaleY = finalScale; rocks.push(rock); game.addChild(rock); } // Spawn heart power-up function spawnHeartPowerUp() { var pos = getRandomEmptyPosition(); var heartPowerUp = new HeartPowerUp(); heartPowerUp.x = pos.x; heartPowerUp.y = pos.y; heartPowerUps.push(heartPowerUp); game.addChild(heartPowerUp); // Auto-remove after 10 seconds using tween tween(heartPowerUp, {}, { duration: 10000, onFinish: function onFinish() { for (var i = heartPowerUps.length - 1; i >= 0; i--) { if (heartPowerUps[i] === heartPowerUp) { heartPowerUp.destroy(); heartPowerUps.splice(i, 1); break; } } } }); } // Move snake function moveSnake() { if (!gameRunning) { return; } // Update direction snakeDirection.x = nextDirection.x; snakeDirection.y = nextDirection.y; // Calculate new head position var head = snake[0]; var headGrid = worldToGrid(head.x, head.y); var newHeadGrid = { x: headGrid.x + snakeDirection.x, y: headGrid.y + snakeDirection.y }; // Check boundaries if (newHeadGrid.x < 0 || newHeadGrid.x >= cols || newHeadGrid.y < 0 || newHeadGrid.y >= rows) { gameOver(); return; } // Check self collision for (var i = 0; i < snake.length; i++) { var segmentGrid = worldToGrid(snake[i].x, snake[i].y); if (segmentGrid.x === newHeadGrid.x && segmentGrid.y === newHeadGrid.y) { gameOver(); return; } } // Check rock collision for (var i = 0; i < rocks.length; i++) { var rock = rocks[i]; var rockGrid = worldToGrid(rock.x, rock.y); if (rockGrid.x === newHeadGrid.x && rockGrid.y === newHeadGrid.y) { gameOver(); return; } } // Move snake body and rotate segments for (var i = snake.length - 1; i > 0; i--) { // Store previous position for rotation calculation var prevX = snake[i].x; var prevY = snake[i].y; // Move to new position snake[i].x = snake[i - 1].x; snake[i].y = snake[i - 1].y; // Calculate direction for body segment rotation var dirX = snake[i].x - prevX; var dirY = snake[i].y - prevY; // Rotate body segment based on movement direction if (dirX > 0 && dirY === 0) { // Moving right snake[i].children[0].rotation = 0; } else if (dirX < 0 && dirY === 0) { // Moving left snake[i].children[0].rotation = Math.PI; } else if (dirX === 0 && dirY > 0) { // Moving down snake[i].children[0].rotation = Math.PI / 2; } else if (dirX === 0 && dirY < 0) { // Moving up snake[i].children[0].rotation = -Math.PI / 2; } } // Move head var newHeadPos = gridToWorld(newHeadGrid.x, newHeadGrid.y); head.x = newHeadPos.x; head.y = newHeadPos.y; // Rotate head based on direction if (snakeDirection.x === 1 && snakeDirection.y === 0) { // Moving right head.children[0].rotation = 0; } else if (snakeDirection.x === -1 && snakeDirection.y === 0) { // Moving left head.children[0].rotation = Math.PI; } else if (snakeDirection.x === 0 && snakeDirection.y === 1) { // Moving down head.children[0].rotation = Math.PI / 2; } else if (snakeDirection.x === 0 && snakeDirection.y === -1) { // Moving up head.children[0].rotation = -Math.PI / 2; } // Check water collection for (var i = waterDroplets.length - 1; i >= 0; i--) { var water = waterDroplets[i]; var waterGrid = worldToGrid(water.x, water.y); if (waterGrid.x === newHeadGrid.x && waterGrid.y === newHeadGrid.y) { // Collect water waterCollected++; waterTxt.setText('Water: ' + waterCollected); LK.setScore(LK.getScore() + 10); scoreTxt.setText('Score: ' + LK.getScore()); // Grow snake - insert new body segment before tail var lastSegment = snake[snake.length - 1]; // Convert current tail to body segment if (snake.length > 1) { var currentTail = snake[snake.length - 1]; currentTail.removeChild(currentTail.children[0]); var bodyGraphics = currentTail.attachAsset('snakeBody', { anchorX: 0.5, anchorY: 0.5 }); } // Create new tail segment var newTail = new SnakeSegment(false, true); newTail.x = lastSegment.x; newTail.y = lastSegment.y; snake.push(newTail); game.addChild(newTail); // Remove water water.destroy(); waterDroplets.splice(i, 1); // Play sound if (!isMuted) { LK.getSound('collect').play(); } // Spawn new water spawnWater(); break; } } // Check heart power-up collection for (var i = heartPowerUps.length - 1; i >= 0; i--) { var heartPowerUp = heartPowerUps[i]; var heartGrid = worldToGrid(heartPowerUp.x, heartPowerUp.y); if (heartGrid.x === newHeadGrid.x && heartGrid.y === newHeadGrid.y) { // Collect heart power-up if (lives < 3) { lives++; updateHearts(); // Flash effect LK.effects.flashObject(head, 0xFF69B4, 300); // Play sound if (!isMuted) { LK.getSound('heartPickup').play(); } } // Remove heart power-up heartPowerUp.destroy(); heartPowerUps.splice(i, 1); break; } } // Check fire extinguishing - use area-based collision instead of grid-based for (var i = fires.length - 1; i >= 0; i--) { var fire = fires[i]; // Calculate fire's collision radius based on size (max 4 grid cells) var extinguishCells = Math.min(fire.size, 4); var fireRadius = extinguishCells * gridSize / 2; // Calculate distance between snake head and fire center var deltaX = head.x - fire.x; var deltaY = head.y - fire.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Check if snake head is within fire's area if (distance <= fireRadius) { if (waterCollected >= fire.waterNeeded) { // Create water spray effect var spray = new WaterSpray(head.x, head.y, fire.x, fire.y); game.addChild(spray); // Extinguish fire waterCollected -= fire.waterNeeded; waterTxt.setText('Water: ' + waterCollected); LK.setScore(LK.getScore() + (fire.size === 1 ? 50 : 100)); scoreTxt.setText('Score: ' + LK.getScore()); // Remove fire fire.destroy(); fires.splice(i, 1); // Play sound if (!isMuted) { LK.getSound('extinguish').play(); } // Spawn extra water after extinguishing fire spawnWater(); if (waterDroplets.length < 5) { spawnWater(); } // Spawn new fire if needed if (fires.length < maxFires) { spawnFire(); } // Flash effect LK.effects.flashObject(head, 0x00FF00, 300); } else { // Snake burns when touching fire without enough water LK.effects.flashObject(head, 0xFF0000, 500); // Add burning effect - make snake segments flash and shake for (var j = 0; j < snake.length; j++) { var segment = snake[j]; // Flash red with varying intensity tween(segment.children[0], { tint: 0xFF0000 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(segment.children[0], { tint: 0xFFFFFF }, { duration: 200, easing: tween.easeInOut }); } }); // Shake effect var originalX = segment.x; var originalY = segment.y; tween(segment, { x: originalX + (Math.random() - 0.5) * 20, y: originalY + (Math.random() - 0.5) * 20 }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { tween(segment, { x: originalX, y: originalY }, { duration: 100, easing: tween.easeInOut }); } }); } gameOver(); return; } break; } } } // Change background based on score function changeBackground() { var newLevel = Math.floor(LK.getScore() / 1000); if (newLevel !== currentBackgroundLevel && newLevel < backgrounds.length) { // Remove old background if (backgroundAssets[currentBackgroundLevel]) { backgroundAssets[currentBackgroundLevel].destroy(); } // Add new background var newBackground = game.attachAsset(backgrounds[newLevel], { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Move to back game.addChildAt(newBackground, 0); backgroundAssets[newLevel] = newBackground; currentBackgroundLevel = newLevel; } } // Update heart display function updateHearts() { for (var i = 0; i < heartIcons.length; i++) { if (i < lives) { heartIcons[i].alpha = 1.0; } else { heartIcons[i].alpha = 0.3; } } } // Game over function gameOver() { lives--; updateHearts(); if (lives <= 0) { gameRunning = false; LK.showGameOver(); } else { // Reset snake position and continue LK.effects.flashScreen(0xFF0000, 500); // Reset snake to starting position var head = snake[0]; head.x = 5 * gridSize + gridSize / 2; head.y = 5 * gridSize + gridSize / 2; // Reset direction snakeDirection = { x: 1, y: 0 }; nextDirection = { x: 1, y: 0 }; } } // Touch controls game.down = function (x, y, obj) { // No need to store touch start position for direct touch controls }; game.up = function (x, y, obj) { if (!gameRunning) { return; } // Get snake head position var head = snake[0]; var snakeX = head.x; var snakeY = head.y; // Calculate direction from snake head to touch position var deltaX = x - snakeX; var deltaY = y - snakeY; // Turn based on snake's current direction and touch relative position if (snakeDirection.x === 1) { // Snake moving right if (deltaY < 0) { // Touch above snake - turn up nextDirection = { x: 0, y: -1 }; } else if (deltaY > 0) { // Touch below snake - turn down nextDirection = { x: 0, y: 1 }; } } else if (snakeDirection.x === -1) { // Snake moving left if (deltaY < 0) { // Touch above snake - turn up nextDirection = { x: 0, y: -1 }; } else if (deltaY > 0) { // Touch below snake - turn down nextDirection = { x: 0, y: 1 }; } } else if (snakeDirection.y === 1) { // Snake moving down if (deltaX < 0) { // Touch left of snake - turn left nextDirection = { x: -1, y: 0 }; } else if (deltaX > 0) { // Touch right of snake - turn right nextDirection = { x: 1, y: 0 }; } } else if (snakeDirection.y === -1) { // Snake moving up if (deltaX < 0) { // Touch left of snake - turn left nextDirection = { x: -1, y: 0 }; } else if (deltaX > 0) { // Touch right of snake - turn right nextDirection = { x: 1, y: 0 }; } } }; // Initialize game initSnake(); updateHearts(); spawnWater(); spawnWater(); spawnWater(); spawnWater(); spawnFire(); // Start background music LK.playMusic('bgmusic'); // Game update loop game.update = function () { if (!gameRunning) { return; } moveTimer++; if (moveTimer >= moveInterval) { moveTimer = 0; moveSnake(); } // Spawn additional water periodically if (LK.ticks % 80 === 0 && waterDroplets.length < 6) { spawnWater(); } // Increase difficulty - add more fires at higher scores var currentLevel = Math.floor(LK.getScore() / 300) + 1; maxFires = Math.min(currentLevel, 5); // Maximum 5 fires if (fires.length < maxFires && LK.ticks % 900 === 0) { spawnFire(); } // Spawn heart power-up every 300 points if (LK.getScore() > 0 && LK.getScore() % 300 === 0 && LK.ticks % 5 === 0) { // Check if we should spawn a heart (only once per 300 point milestone) var currentScoreMilestone = Math.floor(LK.getScore() / 300); if (!game.lastHeartMilestone || game.lastHeartMilestone < currentScoreMilestone) { spawnHeartPowerUp(); game.lastHeartMilestone = currentScoreMilestone; } } // Clean up dead animals for (var i = animals.length - 1; i >= 0; i--) { if (animals[i].lifeTime <= 0) { animals.splice(i, 1); } } // Clean up expired heart power-ups for (var i = heartPowerUps.length - 1; i >= 0; i--) { if (heartPowerUps[i].lifeTime <= 0) { heartPowerUps.splice(i, 1); } } // Make game slightly faster as score increases - slower acceleration if (LK.getScore() > 0 && LK.getScore() % 500 === 0) { moveInterval = Math.max(10, moveInterval - 1); } // Spawn rock obstacles periodically if (LK.ticks % 1800 === 0 && rocks.length < 3) { spawnRock(); } // Check for background changes every 1000 points changeBackground(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Animal = Container.expand(function () {
var self = Container.call(this);
var animalGraphics = self.attachAsset('animal', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = (Math.random() - 0.5) * 4;
self.speedY = (Math.random() - 0.5) * 4;
self.lifeTime = 300; // 5 seconds at 60fps
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
self.lifeTime--;
if (self.lifeTime <= 0) {
self.destroy();
}
};
return self;
});
var Bird = Container.expand(function () {
var self = Container.call(this);
var birdGraphics = self.attachAsset('bird', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = (Math.random() - 0.5) * 5;
self.speedY = (Math.random() - 0.5) * 5;
self.lifeTime = 350;
self.flapTimer = 0;
self.baseY = 0;
self.animOffset = Math.random() * Math.PI * 2;
self.update = function () {
// Flying movement with up/down oscillation
if (self.baseY === 0) {
self.baseY = self.y;
}
self.x += self.speedX;
self.y = self.baseY + Math.sin(LK.ticks * 0.1 + self.animOffset) * 15;
// Wing flapping animation
self.flapTimer++;
if (self.flapTimer >= 5) {
self.flapTimer = 0;
var flapScale = 0.8 + Math.sin(LK.ticks * 0.3 + self.animOffset) * 0.2;
birdGraphics.scaleX = flapScale;
}
self.lifeTime--;
if (self.lifeTime <= 0) {
self.destroy();
}
};
return self;
});
var Deer = Container.expand(function () {
var self = Container.call(this);
var deerGraphics = self.attachAsset('deer', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = (Math.random() - 0.5) * 2; // Slower movement
self.speedY = (Math.random() - 0.5) * 2;
self.lifeTime = 500; // Lives longest
self.panicTimer = 0;
self.isPanicking = false;
self.update = function () {
// Deer occasionally panic and run faster
if (!self.isPanicking && Math.random() < 0.005) {
self.isPanicking = true;
self.panicTimer = 60; // Panic for 1 second
self.speedX *= 3;
self.speedY *= 3;
// Flash effect when panicking
tween(deerGraphics, {
tint: 0xFFAAAA
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(deerGraphics, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
if (self.isPanicking) {
self.panicTimer--;
if (self.panicTimer <= 0) {
self.isPanicking = false;
self.speedX /= 3;
self.speedY /= 3;
}
}
self.x += self.speedX;
self.y += self.speedY;
self.lifeTime--;
if (self.lifeTime <= 0) {
self.destroy();
}
};
return self;
});
var Fire = Container.expand(function (size) {
var self = Container.call(this);
self.size = size || 1;
self.maxSize = 10; // Maximum fire size
self.waterNeeded = self.size;
self.growthTimer = 0;
// Fire grows every 20 seconds (1200 ticks at 60fps)
self.growthInterval = 1200; // 20 seconds fixed interval
var fireGraphics = self.attachAsset(self.size === 1 ? 'smallFire' : 'largeFire', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial scale based on fire size
var initialScale = 0.5 + (self.size - 1) * 0.2; // Start smaller, scale up with size
fireGraphics.scaleX = initialScale;
fireGraphics.scaleY = initialScale;
// Add flickering animation
self.animOffset = Math.random() * Math.PI * 2;
self.update = function () {
// Growth logic - fire grows every 7 seconds
self.growthTimer++;
if (self.growthTimer >= self.growthInterval && self.size < self.maxSize) {
self.size++;
self.waterNeeded = self.size;
self.growthTimer = 0;
// Update size text
if (self.sizeText) {
self.sizeText.setText(self.size.toString());
}
// Update visual scale based on size - smaller start, bigger growth
var scale = 0.5 + (self.size - 1) * 0.25; // Start at 0.5, increase by 25% per size level
tween(fireGraphics, {
scaleX: scale,
scaleY: scale
}, {
duration: 500,
easing: tween.easeOut
});
// Change color intensity based on size
var intensity = Math.min(1, 0.5 + (self.size - 1) * 0.1);
var redComponent = 0xFF;
var greenBlueComponent = Math.floor((1 - intensity) * 0x44);
var redTint = redComponent << 16 | greenBlueComponent << 8 | greenBlueComponent;
tween(fireGraphics, {
tint: redTint
}, {
duration: 500,
easing: tween.easeOut
});
}
// Flickering animation
var flicker = 0.8 + Math.sin(LK.ticks * 0.1 + self.animOffset) * 0.2;
fireGraphics.alpha = flicker;
var baseScaleX = fireGraphics.scaleX || 1;
var baseScaleY = fireGraphics.scaleY || 1;
fireGraphics.scaleX = baseScaleX + Math.sin(LK.ticks * 0.08 + self.animOffset) * 0.1;
fireGraphics.scaleY = baseScaleY + Math.sin(LK.ticks * 0.08 + self.animOffset) * 0.1;
};
return self;
});
var HeartPowerUp = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.lifeTime = 600; // 10 seconds at 60fps
self.animOffset = Math.random() * Math.PI * 2;
self.update = function () {
// Floating animation
heartGraphics.y = Math.sin(LK.ticks * 0.1 + self.animOffset) * 10;
// Pulsing animation
var pulse = 1.0 + Math.sin(LK.ticks * 0.15 + self.animOffset) * 0.3;
heartGraphics.scaleX = 1.5 * pulse;
heartGraphics.scaleY = 1.5 * pulse;
// Fade out in last 2 seconds
if (self.lifeTime <= 120) {
heartGraphics.alpha = self.lifeTime / 120;
}
self.lifeTime--;
if (self.lifeTime <= 0) {
self.destroy();
}
};
return self;
});
var Rabbit = Container.expand(function () {
var self = Container.call(this);
var rabbitGraphics = self.attachAsset('rabbit', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = (Math.random() - 0.5) * 6; // Faster than regular animals
self.speedY = (Math.random() - 0.5) * 6;
self.lifeTime = 400; // Lives longer
self.hopTimer = 0;
self.hopInterval = 20; // Hop every 20 ticks
self.update = function () {
self.hopTimer++;
// Hopping movement - move in bursts
if (self.hopTimer >= self.hopInterval) {
self.x += self.speedX * 2; // Burst movement
self.y += self.speedY * 2;
self.hopTimer = 0;
// Add hop animation
tween(rabbitGraphics, {
scaleY: 1.3
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(rabbitGraphics, {
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
self.lifeTime--;
if (self.lifeTime <= 0) {
self.destroy();
}
};
return self;
});
var Rock = Container.expand(function () {
var self = Container.call(this);
var rockGraphics = self.attachAsset('rock', {
anchorX: 0.5,
anchorY: 0.5
});
// Add some visual variation to rocks
self.animOffset = Math.random() * Math.PI * 2;
self.update = function () {
// Subtle rotation animation
rockGraphics.rotation = Math.sin(LK.ticks * 0.01 + self.animOffset) * 0.1;
};
return self;
});
var SnakeSegment = Container.expand(function (isHead, isTail) {
var self = Container.call(this);
var assetType = isHead ? 'snakeHead' : isTail ? 'snakeTail' : 'snakeBody';
var segmentGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var WaterDroplet = Container.expand(function () {
var self = Container.call(this);
var waterGraphics = self.attachAsset('water', {
anchorX: 0.5,
anchorY: 0.5
});
// Add floating animation
self.animOffset = Math.random() * Math.PI * 2;
self.update = function () {
waterGraphics.y = Math.sin(LK.ticks * 0.05 + self.animOffset) * 5;
};
return self;
});
var WaterSpray = Container.expand(function (startX, startY, targetX, targetY) {
var self = Container.call(this);
// Create multiple water droplets for spray effect
self.droplets = [];
for (var i = 0; i < 8; i++) {
var droplet = self.attachAsset('water', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
// Position droplet at start position
droplet.x = 0;
droplet.y = 0;
// Calculate spray direction with some randomness
var angle = Math.atan2(targetY - startY, targetX - startX);
var spreadAngle = (Math.random() - 0.5) * 0.8; // Random spread
var finalAngle = angle + spreadAngle;
// Set movement properties
droplet.velocityX = Math.cos(finalAngle) * (3 + Math.random() * 2);
droplet.velocityY = Math.sin(finalAngle) * (3 + Math.random() * 2);
droplet.life = 30 + Math.random() * 20; // Random lifetime
droplet.maxLife = droplet.life;
self.droplets.push(droplet);
}
self.x = startX;
self.y = startY;
self.totalLife = 60;
self.update = function () {
self.totalLife--;
// Update each droplet
for (var i = self.droplets.length - 1; i >= 0; i--) {
var droplet = self.droplets[i];
// Move droplet
droplet.x += droplet.velocityX;
droplet.y += droplet.velocityY;
// Fade out droplet
droplet.life--;
droplet.alpha = droplet.life / droplet.maxLife;
// Remove dead droplets
if (droplet.life <= 0) {
self.removeChild(droplet);
self.droplets.splice(i, 1);
}
}
// Remove spray when all droplets are gone or time is up
if (self.droplets.length === 0 || self.totalLife <= 0) {
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F2F
});
/****
* Game Code
****/
// Add forest background
var forestBackground = game.attachAsset('forest', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Background tracking variables
var currentBackgroundLevel = 0;
var backgrounds = ['forest', 'desert', 'snow', 'jungle', 'volcano'];
var backgroundAssets = [forestBackground];
// Game variables
var snake = [];
var snakeDirection = {
x: 1,
y: 0
};
var nextDirection = {
x: 1,
y: 0
};
var gridSize = 60;
var gameWidth = 2048;
var gameHeight = 2732;
var cols = Math.floor(gameWidth / gridSize);
var rows = Math.floor(gameHeight / gridSize);
var waterDroplets = [];
var fires = [];
var waterCollected = 0;
var moveTimer = 0;
var moveInterval = 15; // Snake moves every 15 ticks
var gameRunning = true;
var animals = [];
var rocks = [];
var heartPowerUps = [];
var maxFires = 1; // Start with 1 fire, increase with level
var lives = 3; // Player starts with 3 lives
var heartIcons = [];
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var waterTxt = new Text2('Water: 0', {
size: 70,
fill: 0x1E90FF
});
waterTxt.anchor.set(1, 0);
waterTxt.x = -20;
waterTxt.y = 80;
LK.gui.topRight.addChild(waterTxt);
// Create UI bar container positioned at the top
var uiBar = new Container();
uiBar.y = 20;
uiBar.x = 150;
LK.gui.topLeft.addChild(uiBar);
// Create heart icons for lives
for (var i = 0; i < 3; i++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.4,
scaleY: 2.4
});
heart.x = i * 50;
heart.y = 0;
heartIcons.push(heart);
uiBar.addChild(heart);
}
// Sound control button
var isMuted = false;
var soundBtn = LK.getAsset('soundOn', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.4,
scaleY: 2.4
});
soundBtn.x = 200;
soundBtn.y = 0;
uiBar.addChild(soundBtn);
// Sound button click handler
soundBtn.down = function (x, y, obj) {
isMuted = !isMuted;
if (isMuted) {
LK.stopMusic();
soundBtn.removeChild(soundBtn.children[0]);
soundBtn.attachAsset('soundOff', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
LK.playMusic('bgmusic');
soundBtn.removeChild(soundBtn.children[0]);
soundBtn.attachAsset('soundOn', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
// Initialize snake
function initSnake() {
// Create initial snake with 3 segments
for (var i = 0; i < 3; i++) {
var isHead = i === 0;
var isTail = i === 2; // Last segment is tail
var segment = new SnakeSegment(isHead, isTail);
segment.x = (5 - i) * gridSize + gridSize / 2;
segment.y = 5 * gridSize + gridSize / 2;
snake.push(segment);
game.addChild(segment);
}
}
// Convert grid coordinates to world coordinates
function gridToWorld(gridX, gridY) {
return {
x: gridX * gridSize + gridSize / 2,
y: gridY * gridSize + gridSize / 2
};
}
// Convert world coordinates to grid coordinates
function worldToGrid(worldX, worldY) {
return {
x: Math.floor(worldX / gridSize),
y: Math.floor(worldY / gridSize)
};
}
// Get random empty grid position
function getRandomEmptyPosition() {
var attempts = 0;
while (attempts < 100) {
var gridX = Math.floor(Math.random() * cols);
var gridY = Math.floor(Math.random() * rows);
var worldPos = gridToWorld(gridX, gridY);
var occupied = false;
// Check if position is occupied by snake
for (var i = 0; i < snake.length; i++) {
var snakeGrid = worldToGrid(snake[i].x, snake[i].y);
if (snakeGrid.x === gridX && snakeGrid.y === gridY) {
occupied = true;
break;
}
}
// Check if position is occupied by water
if (!occupied) {
for (var i = 0; i < waterDroplets.length; i++) {
var waterGrid = worldToGrid(waterDroplets[i].x, waterDroplets[i].y);
if (waterGrid.x === gridX && waterGrid.y === gridY) {
occupied = true;
break;
}
}
}
// Check if position is occupied by fire
if (!occupied) {
for (var i = 0; i < fires.length; i++) {
var fireGrid = worldToGrid(fires[i].x, fires[i].y);
if (fireGrid.x === gridX && fireGrid.y === gridY) {
occupied = true;
break;
}
}
}
// Check if position is occupied by rock
if (!occupied) {
for (var i = 0; i < rocks.length; i++) {
var rockGrid = worldToGrid(rocks[i].x, rocks[i].y);
if (rockGrid.x === gridX && rockGrid.y === gridY) {
occupied = true;
break;
}
}
}
if (!occupied) {
return worldPos;
}
attempts++;
}
// Fallback to center if no empty position found
return gridToWorld(Math.floor(cols / 2), Math.floor(rows / 2));
}
// Spawn water droplet
function spawnWater() {
var pos = getRandomEmptyPosition();
var water = new WaterDroplet();
water.x = pos.x;
water.y = pos.y;
waterDroplets.push(water);
game.addChild(water);
}
// Spawn fire
function spawnFire() {
var pos = getRandomEmptyPosition();
var fireSize = Math.random() < 0.7 ? 1 : 2; // 70% chance for small fire
var fire = new Fire(fireSize);
fire.x = pos.x;
fire.y = pos.y;
// Add size display text
fire.sizeText = new Text2(fire.size.toString(), {
size: 50,
fill: 0xFFFFFF
});
fire.sizeText.anchor.set(0.5, 0.5);
fire.sizeText.x = 0;
fire.sizeText.y = -50;
fire.addChild(fire.sizeText);
fires.push(fire);
game.addChild(fire);
// Spawn multiple escaping animals near fire
var animalCount = Math.floor(Math.random() * 3) + 2; // 2-4 animals
for (var j = 0; j < animalCount; j++) {
var animal;
var animalType = Math.random();
if (animalType < 0.4) {
// 40% chance for rabbit
animal = new Rabbit();
} else if (animalType < 0.7) {
// 30% chance for deer
animal = new Deer();
} else if (animalType < 0.9) {
// 20% chance for bird
animal = new Bird();
} else {
// 10% chance for regular animal
animal = new Animal();
}
animal.x = pos.x + (Math.random() - 0.5) * 120;
animal.y = pos.y + (Math.random() - 0.5) * 120;
animals.push(animal);
game.addChild(animal);
}
}
// Spawn rock obstacle
function spawnRock() {
var pos = getRandomEmptyPosition();
var rock = new Rock();
rock.x = pos.x;
rock.y = pos.y;
// Scale rock based on score - start at 2x, increase with score
var baseScale = 2.0; // Start at 2x size
var scoreMultiplier = Math.floor(LK.getScore() / 1000) * 0.5; // +0.5x every 1000 points
var finalScale = baseScale + scoreMultiplier;
rock.children[0].scaleX = finalScale;
rock.children[0].scaleY = finalScale;
rocks.push(rock);
game.addChild(rock);
}
// Spawn heart power-up
function spawnHeartPowerUp() {
var pos = getRandomEmptyPosition();
var heartPowerUp = new HeartPowerUp();
heartPowerUp.x = pos.x;
heartPowerUp.y = pos.y;
heartPowerUps.push(heartPowerUp);
game.addChild(heartPowerUp);
// Auto-remove after 10 seconds using tween
tween(heartPowerUp, {}, {
duration: 10000,
onFinish: function onFinish() {
for (var i = heartPowerUps.length - 1; i >= 0; i--) {
if (heartPowerUps[i] === heartPowerUp) {
heartPowerUp.destroy();
heartPowerUps.splice(i, 1);
break;
}
}
}
});
}
// Move snake
function moveSnake() {
if (!gameRunning) {
return;
}
// Update direction
snakeDirection.x = nextDirection.x;
snakeDirection.y = nextDirection.y;
// Calculate new head position
var head = snake[0];
var headGrid = worldToGrid(head.x, head.y);
var newHeadGrid = {
x: headGrid.x + snakeDirection.x,
y: headGrid.y + snakeDirection.y
};
// Check boundaries
if (newHeadGrid.x < 0 || newHeadGrid.x >= cols || newHeadGrid.y < 0 || newHeadGrid.y >= rows) {
gameOver();
return;
}
// Check self collision
for (var i = 0; i < snake.length; i++) {
var segmentGrid = worldToGrid(snake[i].x, snake[i].y);
if (segmentGrid.x === newHeadGrid.x && segmentGrid.y === newHeadGrid.y) {
gameOver();
return;
}
}
// Check rock collision
for (var i = 0; i < rocks.length; i++) {
var rock = rocks[i];
var rockGrid = worldToGrid(rock.x, rock.y);
if (rockGrid.x === newHeadGrid.x && rockGrid.y === newHeadGrid.y) {
gameOver();
return;
}
}
// Move snake body and rotate segments
for (var i = snake.length - 1; i > 0; i--) {
// Store previous position for rotation calculation
var prevX = snake[i].x;
var prevY = snake[i].y;
// Move to new position
snake[i].x = snake[i - 1].x;
snake[i].y = snake[i - 1].y;
// Calculate direction for body segment rotation
var dirX = snake[i].x - prevX;
var dirY = snake[i].y - prevY;
// Rotate body segment based on movement direction
if (dirX > 0 && dirY === 0) {
// Moving right
snake[i].children[0].rotation = 0;
} else if (dirX < 0 && dirY === 0) {
// Moving left
snake[i].children[0].rotation = Math.PI;
} else if (dirX === 0 && dirY > 0) {
// Moving down
snake[i].children[0].rotation = Math.PI / 2;
} else if (dirX === 0 && dirY < 0) {
// Moving up
snake[i].children[0].rotation = -Math.PI / 2;
}
}
// Move head
var newHeadPos = gridToWorld(newHeadGrid.x, newHeadGrid.y);
head.x = newHeadPos.x;
head.y = newHeadPos.y;
// Rotate head based on direction
if (snakeDirection.x === 1 && snakeDirection.y === 0) {
// Moving right
head.children[0].rotation = 0;
} else if (snakeDirection.x === -1 && snakeDirection.y === 0) {
// Moving left
head.children[0].rotation = Math.PI;
} else if (snakeDirection.x === 0 && snakeDirection.y === 1) {
// Moving down
head.children[0].rotation = Math.PI / 2;
} else if (snakeDirection.x === 0 && snakeDirection.y === -1) {
// Moving up
head.children[0].rotation = -Math.PI / 2;
}
// Check water collection
for (var i = waterDroplets.length - 1; i >= 0; i--) {
var water = waterDroplets[i];
var waterGrid = worldToGrid(water.x, water.y);
if (waterGrid.x === newHeadGrid.x && waterGrid.y === newHeadGrid.y) {
// Collect water
waterCollected++;
waterTxt.setText('Water: ' + waterCollected);
LK.setScore(LK.getScore() + 10);
scoreTxt.setText('Score: ' + LK.getScore());
// Grow snake - insert new body segment before tail
var lastSegment = snake[snake.length - 1];
// Convert current tail to body segment
if (snake.length > 1) {
var currentTail = snake[snake.length - 1];
currentTail.removeChild(currentTail.children[0]);
var bodyGraphics = currentTail.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Create new tail segment
var newTail = new SnakeSegment(false, true);
newTail.x = lastSegment.x;
newTail.y = lastSegment.y;
snake.push(newTail);
game.addChild(newTail);
// Remove water
water.destroy();
waterDroplets.splice(i, 1);
// Play sound
if (!isMuted) {
LK.getSound('collect').play();
}
// Spawn new water
spawnWater();
break;
}
}
// Check heart power-up collection
for (var i = heartPowerUps.length - 1; i >= 0; i--) {
var heartPowerUp = heartPowerUps[i];
var heartGrid = worldToGrid(heartPowerUp.x, heartPowerUp.y);
if (heartGrid.x === newHeadGrid.x && heartGrid.y === newHeadGrid.y) {
// Collect heart power-up
if (lives < 3) {
lives++;
updateHearts();
// Flash effect
LK.effects.flashObject(head, 0xFF69B4, 300);
// Play sound
if (!isMuted) {
LK.getSound('heartPickup').play();
}
}
// Remove heart power-up
heartPowerUp.destroy();
heartPowerUps.splice(i, 1);
break;
}
}
// Check fire extinguishing - use area-based collision instead of grid-based
for (var i = fires.length - 1; i >= 0; i--) {
var fire = fires[i];
// Calculate fire's collision radius based on size (max 4 grid cells)
var extinguishCells = Math.min(fire.size, 4);
var fireRadius = extinguishCells * gridSize / 2;
// Calculate distance between snake head and fire center
var deltaX = head.x - fire.x;
var deltaY = head.y - fire.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Check if snake head is within fire's area
if (distance <= fireRadius) {
if (waterCollected >= fire.waterNeeded) {
// Create water spray effect
var spray = new WaterSpray(head.x, head.y, fire.x, fire.y);
game.addChild(spray);
// Extinguish fire
waterCollected -= fire.waterNeeded;
waterTxt.setText('Water: ' + waterCollected);
LK.setScore(LK.getScore() + (fire.size === 1 ? 50 : 100));
scoreTxt.setText('Score: ' + LK.getScore());
// Remove fire
fire.destroy();
fires.splice(i, 1);
// Play sound
if (!isMuted) {
LK.getSound('extinguish').play();
}
// Spawn extra water after extinguishing fire
spawnWater();
if (waterDroplets.length < 5) {
spawnWater();
}
// Spawn new fire if needed
if (fires.length < maxFires) {
spawnFire();
}
// Flash effect
LK.effects.flashObject(head, 0x00FF00, 300);
} else {
// Snake burns when touching fire without enough water
LK.effects.flashObject(head, 0xFF0000, 500);
// Add burning effect - make snake segments flash and shake
for (var j = 0; j < snake.length; j++) {
var segment = snake[j];
// Flash red with varying intensity
tween(segment.children[0], {
tint: 0xFF0000
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(segment.children[0], {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
// Shake effect
var originalX = segment.x;
var originalY = segment.y;
tween(segment, {
x: originalX + (Math.random() - 0.5) * 20,
y: originalY + (Math.random() - 0.5) * 20
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(segment, {
x: originalX,
y: originalY
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
}
gameOver();
return;
}
break;
}
}
}
// Change background based on score
function changeBackground() {
var newLevel = Math.floor(LK.getScore() / 1000);
if (newLevel !== currentBackgroundLevel && newLevel < backgrounds.length) {
// Remove old background
if (backgroundAssets[currentBackgroundLevel]) {
backgroundAssets[currentBackgroundLevel].destroy();
}
// Add new background
var newBackground = game.attachAsset(backgrounds[newLevel], {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Move to back
game.addChildAt(newBackground, 0);
backgroundAssets[newLevel] = newBackground;
currentBackgroundLevel = newLevel;
}
}
// Update heart display
function updateHearts() {
for (var i = 0; i < heartIcons.length; i++) {
if (i < lives) {
heartIcons[i].alpha = 1.0;
} else {
heartIcons[i].alpha = 0.3;
}
}
}
// Game over
function gameOver() {
lives--;
updateHearts();
if (lives <= 0) {
gameRunning = false;
LK.showGameOver();
} else {
// Reset snake position and continue
LK.effects.flashScreen(0xFF0000, 500);
// Reset snake to starting position
var head = snake[0];
head.x = 5 * gridSize + gridSize / 2;
head.y = 5 * gridSize + gridSize / 2;
// Reset direction
snakeDirection = {
x: 1,
y: 0
};
nextDirection = {
x: 1,
y: 0
};
}
}
// Touch controls
game.down = function (x, y, obj) {
// No need to store touch start position for direct touch controls
};
game.up = function (x, y, obj) {
if (!gameRunning) {
return;
}
// Get snake head position
var head = snake[0];
var snakeX = head.x;
var snakeY = head.y;
// Calculate direction from snake head to touch position
var deltaX = x - snakeX;
var deltaY = y - snakeY;
// Turn based on snake's current direction and touch relative position
if (snakeDirection.x === 1) {
// Snake moving right
if (deltaY < 0) {
// Touch above snake - turn up
nextDirection = {
x: 0,
y: -1
};
} else if (deltaY > 0) {
// Touch below snake - turn down
nextDirection = {
x: 0,
y: 1
};
}
} else if (snakeDirection.x === -1) {
// Snake moving left
if (deltaY < 0) {
// Touch above snake - turn up
nextDirection = {
x: 0,
y: -1
};
} else if (deltaY > 0) {
// Touch below snake - turn down
nextDirection = {
x: 0,
y: 1
};
}
} else if (snakeDirection.y === 1) {
// Snake moving down
if (deltaX < 0) {
// Touch left of snake - turn left
nextDirection = {
x: -1,
y: 0
};
} else if (deltaX > 0) {
// Touch right of snake - turn right
nextDirection = {
x: 1,
y: 0
};
}
} else if (snakeDirection.y === -1) {
// Snake moving up
if (deltaX < 0) {
// Touch left of snake - turn left
nextDirection = {
x: -1,
y: 0
};
} else if (deltaX > 0) {
// Touch right of snake - turn right
nextDirection = {
x: 1,
y: 0
};
}
}
};
// Initialize game
initSnake();
updateHearts();
spawnWater();
spawnWater();
spawnWater();
spawnWater();
spawnFire();
// Start background music
LK.playMusic('bgmusic');
// Game update loop
game.update = function () {
if (!gameRunning) {
return;
}
moveTimer++;
if (moveTimer >= moveInterval) {
moveTimer = 0;
moveSnake();
}
// Spawn additional water periodically
if (LK.ticks % 80 === 0 && waterDroplets.length < 6) {
spawnWater();
}
// Increase difficulty - add more fires at higher scores
var currentLevel = Math.floor(LK.getScore() / 300) + 1;
maxFires = Math.min(currentLevel, 5); // Maximum 5 fires
if (fires.length < maxFires && LK.ticks % 900 === 0) {
spawnFire();
}
// Spawn heart power-up every 300 points
if (LK.getScore() > 0 && LK.getScore() % 300 === 0 && LK.ticks % 5 === 0) {
// Check if we should spawn a heart (only once per 300 point milestone)
var currentScoreMilestone = Math.floor(LK.getScore() / 300);
if (!game.lastHeartMilestone || game.lastHeartMilestone < currentScoreMilestone) {
spawnHeartPowerUp();
game.lastHeartMilestone = currentScoreMilestone;
}
}
// Clean up dead animals
for (var i = animals.length - 1; i >= 0; i--) {
if (animals[i].lifeTime <= 0) {
animals.splice(i, 1);
}
}
// Clean up expired heart power-ups
for (var i = heartPowerUps.length - 1; i >= 0; i--) {
if (heartPowerUps[i].lifeTime <= 0) {
heartPowerUps.splice(i, 1);
}
}
// Make game slightly faster as score increases - slower acceleration
if (LK.getScore() > 0 && LK.getScore() % 500 === 0) {
moveInterval = Math.max(10, moveInterval - 1);
}
// Spawn rock obstacles periodically
if (LK.ticks % 1800 === 0 && rocks.length < 3) {
spawnRock();
}
// Check for background changes every 1000 points
changeBackground();
};