User prompt
add longer enemys. They will come after get 100 score. Every 100 score pass enemies come longer and longer
User prompt
snake can be move only mouse button is holding.
User prompt
when snake reach the last mouse position dont change the face rotation
User prompt
when snake is not moving its head are shaking fix this
User prompt
Scale ratio isint working properly Fix this. It must be like triangle lookalike
User prompt
When player eating First segments are going to be bigger. For example if head scale is end tail scale is 0.2 and middle segments must be ratio scale in these two numbers. (The numbers is example use active scalse)
User prompt
Make a player snake tail more smaller than head. The segment scales are rated whit its positions
User prompt
add rotation to enemy head and segments like a players.
User prompt
Oh c onn. Use snake head asset for snake head. Use snake body asset for snake body. Fix immediately.
User prompt
I change the snake parts whit square. So you need to fix like its need to rotate when following the mouse.
User prompt
Add mega fart to player. This is like a nuke particle. It will be shown when player eat something
User prompt
- Player can fart every eating lure
User prompt
- Fart is not working properly. Fix this - Remove win condition
User prompt
-Add a fart to player. It trigger when player kill the enemy. - Increase enemy speed - ıNCREASE ENEMY SPAWN RATE -
User prompt
Remove enemy fart mechanic
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'x')' in or related to this line: 'var dx = playerPos.x - self.head.x + (Math.random() - 0.5) * 200;' Line Number: 79
Code edit (1 edits merged)
Please save this source code
User prompt
Snake Minigun Mayhem
Initial prompt
Make me a classic snake game. But add a minigun to this snake. It will be shoot when enemy is nearby. Every time shoot the enemy snake going to fart as flame.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bullet var Bullet = Container.expand(function () { var self = Container.call(this); var bullet = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.vx = 0; self.vy = 0; self.owner = null; // 'player' or 'enemy' self.update = function () { self.x += self.vx; self.y += self.vy; }; return self; }); // No music or sound for MVP // Countdown Timer Display var CountdownTimer = Container.expand(function () { var self = Container.call(this); self.timerText = new Text2('5', { size: 80, fill: 0x00AAFF }); self.timerText.anchor.set(0.5, 0.5); self.addChild(self.timerText); self.visible = false; self.startCountdown = function (seconds) { self.visible = true; self.remainingTime = seconds; self.timerText.setText(Math.ceil(self.remainingTime / 60).toString()); // Convert ticks to seconds }; self.update = function () { if (self.visible && self.remainingTime > 0) { self.remainingTime--; var secondsLeft = Math.ceil(self.remainingTime / 60); self.timerText.setText(secondsLeft.toString()); if (self.remainingTime <= 0) { self.visible = false; } } }; return self; }); // Enemy Snake Head var EnemyHead = Container.expand(function () { var self = Container.call(this); var head = self.attachAsset('enemyHead', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // EnemyMegaFart effect (distinct visuals for enemy snake nuke fart, now asset-based) var EnemyMegaFart = Container.expand(function () { var self = Container.call(this); var fart = self.attachAsset('enemyNukeFart', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1, alpha: 0.85 }); // Animate: expand and fade out, then destroy self.play = function () { tween(fart, { scaleX: 2.2, scaleY: 2.2, alpha: 0 }, { duration: 400, easing: tween.easeOutCubic }); tween(self, {}, { duration: 400, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); // Enemy Snake Segment var EnemySegment = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('enemyBody', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Enemy Snake var EnemySnake = Container.expand(function () { var self = Container.call(this); self.segments = []; self.head = new EnemyHead(); self.addChild(self.head); self.segments.push(self.head); self.length = 4 + Math.floor(Math.random() * 3); self.speed = 13 + Math.random() * 4; self.targetX = 1024 + (Math.random() - 0.5) * 800; self.targetY = 1366 + (Math.random() - 0.5) * 1200; self.lastMoveX = 0; self.lastMoveY = 1; self.alive = true; // Initialize body for (var i = 1; i < self.length; i++) { var seg = new EnemySegment(); seg.x = self.head.x; seg.y = self.head.y + i * 80; self.addChild(seg); self.segments.push(seg); } // Move snake self.moveSnake = function (playerPos) { // Check if frozen if (freezeActive) { // Change color to blue when frozen for (var i = 0; i < self.segments.length; i++) { self.segments[i].tint = 0x4444FF; } return; // Don't move when frozen } else { // Reset color when not frozen for (var i = 0; i < self.segments.length; i++) { self.segments[i].tint = 0xFFFFFF; } } // Move toward player, but with some randomness var dx = 0, dy = 0; if (playerPos && typeof playerPos.x === "number" && typeof playerPos.y === "number" && self.head && typeof self.head.x === "number" && typeof self.head.y === "number") { dx = playerPos.x - self.head.x + (Math.random() - 0.5) * 200; dy = playerPos.y - self.head.y + (Math.random() - 0.5) * 200; } else { // fallback: move randomly if playerPos or self.head is not valid dx = (Math.random() - 0.5) * 200; dy = (Math.random() - 0.5) * 200; } var dist = Math.sqrt(dx * dx + dy * dy); var moveX = 0, moveY = 0; if (dist > 10) { moveX = dx / dist; moveY = dy / dist; self.lastMoveX = moveX; self.lastMoveY = moveY; } else { moveX = self.lastMoveX; moveY = self.lastMoveY; } self.head.x += moveX * self.speed; self.head.y += moveY * self.speed; // Rotate head to face movement direction if (Math.abs(moveX) > 0.01 || Math.abs(moveY) > 0.01) { self.head.rotation = Math.atan2(moveY, moveX); } // Move body for (var i = self.segments.length - 1; i > 0; i--) { var prev = self.segments[i - 1]; var seg = self.segments[i]; var pdx = prev.x - seg.x; var pdy = prev.y - seg.y; var pdist = Math.sqrt(pdx * pdx + pdy * pdy); if (pdist > 80) { seg.x += pdx / pdist * Math.min(self.speed, pdist - 80); seg.y += pdy / pdist * Math.min(self.speed, pdist - 80); } // Rotate segment to face previous segment if (i > 0 && (Math.abs(pdx) > 0.01 || Math.abs(pdy) > 0.01)) { seg.rotation = Math.atan2(pdy, pdx); } } }; // Update self.update = function (playerPos) { if (!self.alive) { return; } self.moveSnake(playerPos); }; // Get head position self.getHeadPos = function () { return { x: self.head.x, y: self.head.y }; }; // Get direction vector self.getDirection = function () { return { x: self.lastMoveX, y: self.lastMoveY }; }; // Die self.die = function () { self.alive = false; // Fade out all segments, then return to pool var faded = 0; var total = self.segments.length; for (var i = 0; i < self.segments.length; i++) { tween(self.segments[i], { alpha: 0 }, { duration: 400, easing: tween.linear, onFinish: function onFinish() { faded++; // When all segments faded, return to pool if (faded === total) { // Remove from parent if still attached if (self.parent) { self.parent.removeChild(self); } // Reset state for reuse self.resetForPool(); // Add to pool if (enemySnakePool.indexOf(self) === -1) { enemySnakePool.push(self); } } } }); } }; // Add a resetForPool method to clear state for reuse self.resetForPool = function () { // Reset alpha and visibility for all segments for (var i = 0; i < self.segments.length; i++) { self.segments[i].alpha = 1; } self.visible = false; self.alive = false; // Optionally reset other state if needed }; return self; }); // (Flame Fart Burst removed) // Food var Food = Container.expand(function () { var self = Container.call(this); var food = self.attachAsset('food', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // MegaFart effect (nuke particle, now asset-based) var MegaFart = Container.expand(function () { var self = Container.call(this); var fart = self.attachAsset('playerNukeFart', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1, alpha: 0.85 }); // Animate: expand and fade out, then destroy self.play = function () { tween(fart, { scaleX: 2.2, scaleY: 2.2, alpha: 0 }, { duration: 350, easing: tween.easeOutCubic }); tween(self, {}, { duration: 350, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); // Musical Note Progress Slider var NoteSlider = Container.expand(function () { var self = Container.call(this); // Background bar - scaled up and using new tileable image var bgBar = LK.getAsset('sliderBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 1.5 }); self.addChild(bgBar); // Progress bar - scaled up and using new tileable image self.progressBar = LK.getAsset('sliderFill', { anchorX: 0, anchorY: 0.5, scaleX: 0, scaleY: 1.5 }); self.progressBar.x = -300; // Align with left edge of scaled background bar (600px wide / 2 = 300px offset) self.addChild(self.progressBar); // Snowflake icon (hidden initially) - scaled up self.snowflakeIcon = new Text2('❄', { size: 80, fill: 0x00AAFF }); self.snowflakeIcon.anchor.set(0.5, 0.5); self.snowflakeIcon.x = 420; self.snowflakeIcon.y = 0; self.snowflakeIcon.visible = false; self.addChild(self.snowflakeIcon); // Update progress (0-14, representing notes across two cycles) self.updateProgress = function (noteIndex) { var progress = noteIndex / 14; // Now requires 14 notes (two full cycles) tween(self.progressBar, { scaleX: progress * 2.0 }, { duration: 200, easing: tween.easeOut }); if (noteIndex >= 14) { self.snowflakeIcon.visible = true; // Animate snowflake tween(self.snowflakeIcon, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.bounceOut }); } }; // Reset slider self.reset = function () { tween(self.progressBar, { scaleX: 0 }, { duration: 300, easing: tween.easeIn }); self.snowflakeIcon.visible = false; self.snowflakeIcon.scaleX = 1; self.snowflakeIcon.scaleY = 1; }; return self; }); // Player Snake var PlayerSnake = Container.expand(function () { var self = Container.call(this); self.segments = []; self.directions = []; self.head = new SnakeHead(); self.addChild(self.head); self.segments.push(self.head); self.length = 5; self.speed = 12; self.targetX = 1024; self.targetY = 1366; self.lastMoveX = 0; self.lastMoveY = -1; self.growPending = 0; self.lastFireTick = 0; // Initialize body for (var i = 1; i < self.length; i++) { var seg = new SnakeSegment(); seg.x = self.head.x; seg.y = self.head.y + i * 80; self.addChild(seg); self.segments.push(seg); } // Move snake self.moveSnake = function () { // Only move if dragNode is playerSnake (mouse/touch is held) if (_typeof(dragNode) === "object" && dragNode === self) { // Calculate direction to target var dx = self.targetX - self.head.x; var dy = self.targetY - self.head.y; var dist = Math.sqrt(dx * dx + dy * dy); var moveX = 0, moveY = 0; if (dist > 10) { moveX = dx / dist; moveY = dy / dist; self.lastMoveX = moveX; self.lastMoveY = moveY; } else { moveX = self.lastMoveX; moveY = self.lastMoveY; } // Move head self.head.x += moveX * self.speed; self.head.y += moveY * self.speed; // Rotate head to face movement direction if (dist > 10) { self.head.updateRotation(moveX, moveY); } // Move body for (var i = self.segments.length - 1; i > 0; i--) { var prev = self.segments[i - 1]; var seg = self.segments[i]; var pdx = prev.x - seg.x; var pdy = prev.y - seg.y; var pdist = Math.sqrt(pdx * pdx + pdy * pdy); if (pdist > 80) { seg.x += pdx / pdist * Math.min(self.speed, pdist - 80); seg.y += pdy / pdist * Math.min(self.speed, pdist - 80); } // Rotate segment to face previous segment seg.updateRotation(pdx, pdy); } } }; // Grow snake self.grow = function (n) { self.growPending += n; }; // Update self.update = function () { self.moveSnake(); // Handle growth if (self.growPending > 0) { var last = self.segments[self.segments.length - 1]; var seg = new SnakeSegment(); seg.x = last.x; seg.y = last.y; self.addChild(seg); self.segments.push(seg); self.growPending--; } }; // Get head position self.getHeadPos = function () { return { x: self.head.x, y: self.head.y }; }; // Get direction vector self.getDirection = function () { return { x: self.lastMoveX, y: self.lastMoveY }; }; return self; }); // Player Snake Head (uses snakeHead asset, rotates to face movement) var SnakeHead = Container.expand(function () { var self = Container.call(this); var head = self.attachAsset('snakeHead', { anchorX: 0.5, anchorY: 0.5 }); // Add a semi-transparent shooting range circle var rangeRadius = 600; // matches minigun range in game.update var rangeCircle = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: rangeRadius * 2 / 100, // centerCircle asset is 100x100 scaleY: rangeRadius * 2 / 100, alpha: 0.05, // subtle visibility x: 0, y: 0 }); self.addChild(rangeCircle); self._lastX = 0; self._lastY = 0; self._lastRotation = 0; self.updateRotation = function (dx, dy) { // Only update if movement is significant if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) { self._lastRotation = Math.atan2(dy, dx); self.rotation = self._lastRotation; } else { // If not moving, keep last rotation to prevent shaking self.rotation = self._lastRotation; } }; return self; }); // Player Snake Segment (uses snakeBody asset, rotates to face previous segment) var SnakeSegment = Container.expand(function () { var self = Container.call(this); var body = self.attachAsset('snakeBody', { anchorX: 0.5, anchorY: 0.5 }); self._lastRotation = 0; self.updateRotation = function (dx, dy) { if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) { self._lastRotation = Math.atan2(dy, dx); self.rotation = self._lastRotation; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Start background music LK.playMusic('bgMusic'); // Game variables // Snake body segment (player) // Snake head (player) // Enemy snake body // Enemy snake head // Food // Bullet // Flame fart burst // (flame asset removed) // Nuke fart assets (player and enemy, can be replaced with custom art) function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } var playerSnake; var enemySnakes = []; var enemySnakePool = []; // Pool for dead/reusable enemy snakes var foods = []; var bullets = []; // (flames array removed) var scoreTxt; var dragNode = null; var lastFireTick = 0; var spawnFoodTick = 0; var spawnEnemyTick = 0; var musicSpeed = 1.0; // Current music playback speed var enemyKillCount = 0; // Track how many enemies killed // Musical note progression system var noteNames = ['noteDo', 'noteRe', 'noteMi', 'noteFa', 'noteSol', 'noteLa', 'noteSi']; var currentNoteIndex = 0; var notesCycleCount = 0; // Track how many complete cycles we've done var freezeActive = false; var freezeEndTime = 0; var fireRateBoostActive = false; var fireRateBoostEndTime = 0; var fireRateBoostIndicator; var noteSlider; var countdownTimer; var gameArea = { x: 120, y: 120, w: 2048 - 240, h: 2732 - 240 }; // leave margin for UI // Track current enemy snake length scaling with score var currentEnemySnakeLength = 0; // 0 means default (random 4-6), will be set after 100 score // Add hexagonal tilemap ground image, tile to fill game area var groundTiles = []; var tileW = 1020; var tileH = 1065.8; // Start tiling from (0,0) to ensure no gap in top/left corner for (var y = 0; y < 2732; y += tileH) { for (var x = 0; x < 2048; x += tileW) { var tile = LK.getAsset('hexGround', { anchorX: 0, anchorY: 0, x: x, y: y }); groundTiles.push(tile); game.addChild(tile); } } // Score display scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Initialize note slider noteSlider = new NoteSlider(); noteSlider.x = 0; // Center horizontally using GUI coordinate system noteSlider.y = 180; // Position below score text LK.gui.top.addChild(noteSlider); // Initialize countdown timer countdownTimer = new CountdownTimer(); countdownTimer.x = 0; countdownTimer.y = 0; LK.gui.center.addChild(countdownTimer); // Initialize fire rate boost indicator fireRateBoostIndicator = new Text2('⚡ 3X FIRE RATE', { size: 60, fill: 0xFFFF00 }); fireRateBoostIndicator.anchor.set(0.5, 0.5); fireRateBoostIndicator.x = 0; fireRateBoostIndicator.y = -200; fireRateBoostIndicator.visible = false; LK.gui.center.addChild(fireRateBoostIndicator); // Initialize player snake playerSnake = new PlayerSnake(); playerSnake.head.x = 1024; playerSnake.head.y = 1800; for (var i = 0; i < playerSnake.segments.length; i++) { playerSnake.segments[i].x = 1024; playerSnake.segments[i].y = 1800 + i * 80; } game.addChild(playerSnake); // Spawn initial food function spawnFood() { var f = new Food(); f.x = gameArea.x + 100 + Math.random() * (gameArea.w - 200); f.y = gameArea.y + 100 + Math.random() * (gameArea.h - 200); foods.push(f); game.addChild(f); } for (var i = 0; i < 8; i++) { spawnFood(); } // Spawn enemy snake function spawnEnemy() { // Determine enemy length based on score var enemyLength = 0; if (currentEnemySnakeLength > 0) { enemyLength = currentEnemySnakeLength; } else { enemyLength = 4 + Math.floor(Math.random() * 3); // default: 4-6 } // Fetch from pool or create new var e; if (enemySnakePool.length > 0) { e = enemySnakePool.pop(); // Remove from previous parent if needed if (e.parent) { e.parent.removeChild(e); } // Reset segments while (e.segments.length > 1) { var seg = e.segments.pop(); seg.destroy(); } // Add segments to match desired length for (var i = 1; i < enemyLength; i++) { var seg = new EnemySegment(); seg.x = e.head.x; seg.y = e.head.y + i * 80; e.addChild(seg); e.segments.push(seg); } e.length = enemyLength; // Reset alpha for all segments for (var i = 0; i < e.segments.length; i++) { e.segments[i].alpha = 1; } e.visible = true; e.alive = true; } else { // Create enemy snake with custom length if needed e = new EnemySnake(); if (currentEnemySnakeLength > 0) { // Remove all but head while (e.segments.length > 1) { var seg = e.segments.pop(); seg.destroy(); } // Add segments to match desired length for (var i = 1; i < enemyLength; i++) { var seg = new EnemySegment(); seg.x = e.head.x; seg.y = e.head.y + i * 80; e.addChild(seg); e.segments.push(seg); } e.length = enemyLength; } } // Spawn at random edge OUTSIDE the visible screen var edge = Math.floor(Math.random() * 4); if (edge === 0) { // top (above screen) e.head.x = gameArea.x + Math.random() * gameArea.w; e.head.y = gameArea.y - 200; } else if (edge === 1) { // bottom (below screen) e.head.x = gameArea.x + Math.random() * gameArea.w; e.head.y = gameArea.y + gameArea.h + 200; } else if (edge === 2) { // left (left of screen) e.head.x = gameArea.x - 200; e.head.y = gameArea.y + Math.random() * gameArea.h; } else { // right (right of screen) e.head.x = gameArea.x + gameArea.w + 200; e.head.y = gameArea.y + Math.random() * gameArea.h; } for (var i = 1; i < e.segments.length; i++) { e.segments[i].x = e.head.x; e.segments[i].y = e.head.y + i * 80; } e.visible = true; e.alive = true; // Apply freeze effect if currently active if (freezeActive) { for (var i = 0; i < e.segments.length; i++) { e.segments[i].tint = 0x4444FF; } } enemySnakes.push(e); game.addChild(e); } for (var i = 0; i < 2; i++) { spawnEnemy(); } // Handle dragging game.down = function (x, y, obj) { dragNode = playerSnake; playerSnake.targetX = x; playerSnake.targetY = y; }; game.move = function (x, y, obj) { if (dragNode) { playerSnake.targetX = x; playerSnake.targetY = y; } }; game.up = function (x, y, obj) { dragNode = null; }; // Helper: check collision between two containers (circle-based) function circlesIntersect(a, b, rA, rB) { var dx = a.x - b.x; var dy = a.y - b.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < (rA + rB) * 0.5; } // Helper: clamp to game area function clampToArea(x, y) { x = Math.max(gameArea.x + 50, Math.min(gameArea.x + gameArea.w - 50, x)); y = Math.max(gameArea.y + 50, Math.min(gameArea.y + gameArea.h - 50, y)); return { x: x, y: y }; } // Main game update game.update = function () { // Handle freeze effect timing if (freezeActive && LK.ticks >= freezeEndTime) { freezeActive = false; noteSlider.reset(); // Reset enemy colors for (var i = 0; i < enemySnakes.length; i++) { var e = enemySnakes[i]; for (var j = 0; j < e.segments.length; j++) { e.segments[j].tint = 0xFFFFFF; } } // Activate fire rate boost fireRateBoostActive = true; fireRateBoostEndTime = LK.ticks + 300; // 5 seconds at 60fps fireRateBoostIndicator.visible = true; // Animate indicator appearance tween(fireRateBoostIndicator, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.bounceOut }); } // Handle fire rate boost timing if (fireRateBoostActive && LK.ticks >= fireRateBoostEndTime) { fireRateBoostActive = false; // Animate indicator disappearance tween(fireRateBoostIndicator, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { fireRateBoostIndicator.visible = false; fireRateBoostIndicator.alpha = 1; fireRateBoostIndicator.scaleX = 1; fireRateBoostIndicator.scaleY = 1; } }); } // Update countdown timer countdownTimer.update(); // Update player snake playerSnake.update(); // Clamp player head to area var clamped = clampToArea(playerSnake.head.x, playerSnake.head.y); playerSnake.head.x = clamped.x; playerSnake.head.y = clamped.y; // Update enemy snakes for (var i = enemySnakes.length - 1; i >= 0; i--) { var e = enemySnakes[i]; if (!e.alive) { // Remove from active array if dead and pooled enemySnakes.splice(i, 1); continue; } e.update(playerSnake.getHeadPos()); // Clamp enemy head to area var ce = clampToArea(e.head.x, e.head.y); e.head.x = ce.x; e.head.y = ce.y; } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var b = bullets[i]; b.update(); // Remove if out of bounds if (b.x < gameArea.x - 60 || b.x > gameArea.x + gameArea.w + 60 || b.y < gameArea.y - 60 || b.y > gameArea.y + gameArea.h + 60) { b.destroy(); bullets.splice(i, 1); continue; } // Bullet hits enemy snake if (b.owner === 'player') { for (var j = 0; j < enemySnakes.length; j++) { var e = enemySnakes[j]; if (!e.alive) { continue; } if (circlesIntersect(b, e.head, 60, 60)) { // Hit! b.destroy(); bullets.splice(i, 1); // Play hit sound effect LK.getSound('hitSnake').play(); // Shorten enemy snake or kill var killed = false; // Nuke fart effect at enemy head when hit (use EnemyMegaFart for enemy snake) var nukeFart = new EnemyMegaFart(); nukeFart.x = e.head.x; nukeFart.y = e.head.y; game.addChild(nukeFart); nukeFart.play(); if (e.segments.length > 2) { var seg = e.segments.pop(); seg.destroy(); } else { e.die(); killed = true; } LK.setScore(LK.getScore() + 5); scoreTxt.setText(LK.getScore()); // Player fart effect when enemy is killed if (killed) { playerSnake.grow(1); // Play snake death sound effect LK.getSound('snakeDeath').play(); // Flash the head and all body segments for a more visible fart effect for (var f = 0; f < playerSnake.segments.length; f++) { LK.effects.flashObject(playerSnake.segments[f], 0xcccc00, 600); } // Increase music speed when enemy is killed enemyKillCount++; var newMusicSpeed = Math.min(2.0, 1.0 + enemyKillCount * 0.25); // Cap at 2x speed if (newMusicSpeed > musicSpeed) { // Use tween to smoothly transition music speed without breaking loop tween({ speed: musicSpeed }, { speed: newMusicSpeed }, { duration: 500, easing: tween.easeOutCubic, onFinish: function onFinish() { musicSpeed = newMusicSpeed; } }); } } break; } } } // Bullet hits player (if we add enemy bullets in future) } // Player eats food for (var i = foods.length - 1; i >= 0; i--) { var food = foods[i]; if (circlesIntersect(playerSnake.head, food, 60, 35)) { food.destroy(); foods.splice(i, 1); playerSnake.grow(1); LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); // Play musical note LK.getSound(noteNames[currentNoteIndex]).play(); // Update slider currentNoteIndex++; noteSlider.updateProgress(currentNoteIndex + notesCycleCount * 7); // Check if we've completed a full cycle if (currentNoteIndex >= 7) { notesCycleCount++; currentNoteIndex = 0; // Check if we've completed two full cycles (14 notes total) if (notesCycleCount >= 2) { // Activate freeze effect freezeActive = true; freezeEndTime = LK.ticks + 300; // 5 seconds at 60fps // Flash screen blue to indicate freeze LK.effects.flashScreen(0x4444FF, 500); // Start countdown timer countdownTimer.startCountdown(300); // 5 seconds in ticks // Reset note progression currentNoteIndex = 0; notesCycleCount = 0; } } // MegaFart effect: nuke particle at player head var megaFart = new MegaFart(); megaFart.x = playerSnake.head.x; megaFart.y = playerSnake.head.y; game.addChild(megaFart); megaFart.play(); // Win condition removed } } // Player collides with enemy snake head or body for (var i = 0; i < enemySnakes.length; i++) { var e = enemySnakes[i]; if (!e.alive) { continue; } // Head to head if (circlesIntersect(playerSnake.head, e.head, 60, 60)) { LK.effects.flashScreen(0xff3300, 800); LK.showGameOver(); return; } // Head to body for (var j = 1; j < e.segments.length; j++) { if (circlesIntersect(playerSnake.head, e.segments[j], 60, 40)) { LK.effects.flashScreen(0xff3300, 800); LK.showGameOver(); return; } } } // Player collides with self (not head) for (var i = 2; i < playerSnake.segments.length; i++) { if (circlesIntersect(playerSnake.head, playerSnake.segments[i], 60, 40)) { LK.effects.flashScreen(0xff3300, 800); LK.showGameOver(); return; } } // Spawn food spawnFoodTick++; if (spawnFoodTick > 60 && foods.length < 12) { // Increased spawn rate spawnFood(); spawnFoodTick = 0; } // Spawn enemy spawnEnemyTick++; if (spawnEnemyTick > 120 && enemySnakes.length < 8) { // 120 ticks ≈ 2 seconds at 60fps spawnEnemy(); spawnEnemyTick = 0; } // Check and update enemy snake length scaling with score var score = LK.getScore(); if (score >= 1000) { LK.showYouWin(); return; } if (score >= 100) { // Every 100 points, increase enemy length by 2 (starting at 6) var newLen = 6 + Math.floor((score - 100) / 100) * 2; if (newLen !== currentEnemySnakeLength) { currentEnemySnakeLength = newLen; } } // Minigun: auto-fire at nearest enemy in range var fireInterval = fireRateBoostActive ? 7 : 20; // 3x fire rate when boosted (7 ticks vs 20 ticks) if (LK.ticks - lastFireTick > fireInterval) { // Normal: 20 ticks ≈ 0.33 seconds at 60fps (~3 shots/sec) // Boosted: 7 ticks ≈ 0.12 seconds at 60fps (~9 shots/sec, 3x faster) var nearest = null; var minDist = 99999; for (var i = 0; i < enemySnakes.length; i++) { var e = enemySnakes[i]; if (!e.alive) { continue; } var dx = e.head.x - playerSnake.head.x; var dy = e.head.y - playerSnake.head.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 600 && dist < minDist) { minDist = dist; nearest = e; } } if (nearest) { fireBulletAt(playerSnake.head.x, playerSnake.head.y, nearest.head.x, nearest.head.y, 'player'); lastFireTick = LK.ticks; } } }; // Fire bullet from (x0,y0) to (x1,y1) function fireBulletAt(x0, y0, x1, y1, owner) { var b = new Bullet(); b.x = x0; b.y = y0; var dx = x1 - x0; var dy = y1 - y0; var dist = Math.sqrt(dx * dx + dy * dy); var speed = 38; b.vx = dx / dist * speed; b.vy = dy / dist * speed; b.owner = owner; // Rotate bullet to face direction of travel if (dist > 0.01) { b.rotation = Math.atan2(dy, dx); } bullets.push(b); game.addChild(b); } // (spawnFlameBurst removed) // Center score text scoreTxt.setText(LK.getScore()); scoreTxt.anchor.set(0.5, 0);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bullet
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bullet = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.owner = null; // 'player' or 'enemy'
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// No music or sound for MVP
// Countdown Timer Display
var CountdownTimer = Container.expand(function () {
var self = Container.call(this);
self.timerText = new Text2('5', {
size: 80,
fill: 0x00AAFF
});
self.timerText.anchor.set(0.5, 0.5);
self.addChild(self.timerText);
self.visible = false;
self.startCountdown = function (seconds) {
self.visible = true;
self.remainingTime = seconds;
self.timerText.setText(Math.ceil(self.remainingTime / 60).toString()); // Convert ticks to seconds
};
self.update = function () {
if (self.visible && self.remainingTime > 0) {
self.remainingTime--;
var secondsLeft = Math.ceil(self.remainingTime / 60);
self.timerText.setText(secondsLeft.toString());
if (self.remainingTime <= 0) {
self.visible = false;
}
}
};
return self;
});
// Enemy Snake Head
var EnemyHead = Container.expand(function () {
var self = Container.call(this);
var head = self.attachAsset('enemyHead', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// EnemyMegaFart effect (distinct visuals for enemy snake nuke fart, now asset-based)
var EnemyMegaFart = Container.expand(function () {
var self = Container.call(this);
var fart = self.attachAsset('enemyNukeFart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1,
alpha: 0.85
});
// Animate: expand and fade out, then destroy
self.play = function () {
tween(fart, {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
}, {
duration: 400,
easing: tween.easeOutCubic
});
tween(self, {}, {
duration: 400,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
// Enemy Snake Segment
var EnemySegment = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('enemyBody', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Enemy Snake
var EnemySnake = Container.expand(function () {
var self = Container.call(this);
self.segments = [];
self.head = new EnemyHead();
self.addChild(self.head);
self.segments.push(self.head);
self.length = 4 + Math.floor(Math.random() * 3);
self.speed = 13 + Math.random() * 4;
self.targetX = 1024 + (Math.random() - 0.5) * 800;
self.targetY = 1366 + (Math.random() - 0.5) * 1200;
self.lastMoveX = 0;
self.lastMoveY = 1;
self.alive = true;
// Initialize body
for (var i = 1; i < self.length; i++) {
var seg = new EnemySegment();
seg.x = self.head.x;
seg.y = self.head.y + i * 80;
self.addChild(seg);
self.segments.push(seg);
}
// Move snake
self.moveSnake = function (playerPos) {
// Check if frozen
if (freezeActive) {
// Change color to blue when frozen
for (var i = 0; i < self.segments.length; i++) {
self.segments[i].tint = 0x4444FF;
}
return; // Don't move when frozen
} else {
// Reset color when not frozen
for (var i = 0; i < self.segments.length; i++) {
self.segments[i].tint = 0xFFFFFF;
}
}
// Move toward player, but with some randomness
var dx = 0,
dy = 0;
if (playerPos && typeof playerPos.x === "number" && typeof playerPos.y === "number" && self.head && typeof self.head.x === "number" && typeof self.head.y === "number") {
dx = playerPos.x - self.head.x + (Math.random() - 0.5) * 200;
dy = playerPos.y - self.head.y + (Math.random() - 0.5) * 200;
} else {
// fallback: move randomly if playerPos or self.head is not valid
dx = (Math.random() - 0.5) * 200;
dy = (Math.random() - 0.5) * 200;
}
var dist = Math.sqrt(dx * dx + dy * dy);
var moveX = 0,
moveY = 0;
if (dist > 10) {
moveX = dx / dist;
moveY = dy / dist;
self.lastMoveX = moveX;
self.lastMoveY = moveY;
} else {
moveX = self.lastMoveX;
moveY = self.lastMoveY;
}
self.head.x += moveX * self.speed;
self.head.y += moveY * self.speed;
// Rotate head to face movement direction
if (Math.abs(moveX) > 0.01 || Math.abs(moveY) > 0.01) {
self.head.rotation = Math.atan2(moveY, moveX);
}
// Move body
for (var i = self.segments.length - 1; i > 0; i--) {
var prev = self.segments[i - 1];
var seg = self.segments[i];
var pdx = prev.x - seg.x;
var pdy = prev.y - seg.y;
var pdist = Math.sqrt(pdx * pdx + pdy * pdy);
if (pdist > 80) {
seg.x += pdx / pdist * Math.min(self.speed, pdist - 80);
seg.y += pdy / pdist * Math.min(self.speed, pdist - 80);
}
// Rotate segment to face previous segment
if (i > 0 && (Math.abs(pdx) > 0.01 || Math.abs(pdy) > 0.01)) {
seg.rotation = Math.atan2(pdy, pdx);
}
}
};
// Update
self.update = function (playerPos) {
if (!self.alive) {
return;
}
self.moveSnake(playerPos);
};
// Get head position
self.getHeadPos = function () {
return {
x: self.head.x,
y: self.head.y
};
};
// Get direction vector
self.getDirection = function () {
return {
x: self.lastMoveX,
y: self.lastMoveY
};
};
// Die
self.die = function () {
self.alive = false;
// Fade out all segments, then return to pool
var faded = 0;
var total = self.segments.length;
for (var i = 0; i < self.segments.length; i++) {
tween(self.segments[i], {
alpha: 0
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
faded++;
// When all segments faded, return to pool
if (faded === total) {
// Remove from parent if still attached
if (self.parent) {
self.parent.removeChild(self);
}
// Reset state for reuse
self.resetForPool();
// Add to pool
if (enemySnakePool.indexOf(self) === -1) {
enemySnakePool.push(self);
}
}
}
});
}
};
// Add a resetForPool method to clear state for reuse
self.resetForPool = function () {
// Reset alpha and visibility for all segments
for (var i = 0; i < self.segments.length; i++) {
self.segments[i].alpha = 1;
}
self.visible = false;
self.alive = false;
// Optionally reset other state if needed
};
return self;
});
// (Flame Fart Burst removed)
// Food
var Food = Container.expand(function () {
var self = Container.call(this);
var food = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// MegaFart effect (nuke particle, now asset-based)
var MegaFart = Container.expand(function () {
var self = Container.call(this);
var fart = self.attachAsset('playerNukeFart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1,
alpha: 0.85
});
// Animate: expand and fade out, then destroy
self.play = function () {
tween(fart, {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
}, {
duration: 350,
easing: tween.easeOutCubic
});
tween(self, {}, {
duration: 350,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
// Musical Note Progress Slider
var NoteSlider = Container.expand(function () {
var self = Container.call(this);
// Background bar - scaled up and using new tileable image
var bgBar = LK.getAsset('sliderBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 1.5
});
self.addChild(bgBar);
// Progress bar - scaled up and using new tileable image
self.progressBar = LK.getAsset('sliderFill', {
anchorX: 0,
anchorY: 0.5,
scaleX: 0,
scaleY: 1.5
});
self.progressBar.x = -300; // Align with left edge of scaled background bar (600px wide / 2 = 300px offset)
self.addChild(self.progressBar);
// Snowflake icon (hidden initially) - scaled up
self.snowflakeIcon = new Text2('❄', {
size: 80,
fill: 0x00AAFF
});
self.snowflakeIcon.anchor.set(0.5, 0.5);
self.snowflakeIcon.x = 420;
self.snowflakeIcon.y = 0;
self.snowflakeIcon.visible = false;
self.addChild(self.snowflakeIcon);
// Update progress (0-14, representing notes across two cycles)
self.updateProgress = function (noteIndex) {
var progress = noteIndex / 14; // Now requires 14 notes (two full cycles)
tween(self.progressBar, {
scaleX: progress * 2.0
}, {
duration: 200,
easing: tween.easeOut
});
if (noteIndex >= 14) {
self.snowflakeIcon.visible = true;
// Animate snowflake
tween(self.snowflakeIcon, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut
});
}
};
// Reset slider
self.reset = function () {
tween(self.progressBar, {
scaleX: 0
}, {
duration: 300,
easing: tween.easeIn
});
self.snowflakeIcon.visible = false;
self.snowflakeIcon.scaleX = 1;
self.snowflakeIcon.scaleY = 1;
};
return self;
});
// Player Snake
var PlayerSnake = Container.expand(function () {
var self = Container.call(this);
self.segments = [];
self.directions = [];
self.head = new SnakeHead();
self.addChild(self.head);
self.segments.push(self.head);
self.length = 5;
self.speed = 12;
self.targetX = 1024;
self.targetY = 1366;
self.lastMoveX = 0;
self.lastMoveY = -1;
self.growPending = 0;
self.lastFireTick = 0;
// Initialize body
for (var i = 1; i < self.length; i++) {
var seg = new SnakeSegment();
seg.x = self.head.x;
seg.y = self.head.y + i * 80;
self.addChild(seg);
self.segments.push(seg);
}
// Move snake
self.moveSnake = function () {
// Only move if dragNode is playerSnake (mouse/touch is held)
if (_typeof(dragNode) === "object" && dragNode === self) {
// Calculate direction to target
var dx = self.targetX - self.head.x;
var dy = self.targetY - self.head.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var moveX = 0,
moveY = 0;
if (dist > 10) {
moveX = dx / dist;
moveY = dy / dist;
self.lastMoveX = moveX;
self.lastMoveY = moveY;
} else {
moveX = self.lastMoveX;
moveY = self.lastMoveY;
}
// Move head
self.head.x += moveX * self.speed;
self.head.y += moveY * self.speed;
// Rotate head to face movement direction
if (dist > 10) {
self.head.updateRotation(moveX, moveY);
}
// Move body
for (var i = self.segments.length - 1; i > 0; i--) {
var prev = self.segments[i - 1];
var seg = self.segments[i];
var pdx = prev.x - seg.x;
var pdy = prev.y - seg.y;
var pdist = Math.sqrt(pdx * pdx + pdy * pdy);
if (pdist > 80) {
seg.x += pdx / pdist * Math.min(self.speed, pdist - 80);
seg.y += pdy / pdist * Math.min(self.speed, pdist - 80);
}
// Rotate segment to face previous segment
seg.updateRotation(pdx, pdy);
}
}
};
// Grow snake
self.grow = function (n) {
self.growPending += n;
};
// Update
self.update = function () {
self.moveSnake();
// Handle growth
if (self.growPending > 0) {
var last = self.segments[self.segments.length - 1];
var seg = new SnakeSegment();
seg.x = last.x;
seg.y = last.y;
self.addChild(seg);
self.segments.push(seg);
self.growPending--;
}
};
// Get head position
self.getHeadPos = function () {
return {
x: self.head.x,
y: self.head.y
};
};
// Get direction vector
self.getDirection = function () {
return {
x: self.lastMoveX,
y: self.lastMoveY
};
};
return self;
});
// Player Snake Head (uses snakeHead asset, rotates to face movement)
var SnakeHead = Container.expand(function () {
var self = Container.call(this);
var head = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
// Add a semi-transparent shooting range circle
var rangeRadius = 600; // matches minigun range in game.update
var rangeCircle = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: rangeRadius * 2 / 100,
// centerCircle asset is 100x100
scaleY: rangeRadius * 2 / 100,
alpha: 0.05,
// subtle visibility
x: 0,
y: 0
});
self.addChild(rangeCircle);
self._lastX = 0;
self._lastY = 0;
self._lastRotation = 0;
self.updateRotation = function (dx, dy) {
// Only update if movement is significant
if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) {
self._lastRotation = Math.atan2(dy, dx);
self.rotation = self._lastRotation;
} else {
// If not moving, keep last rotation to prevent shaking
self.rotation = self._lastRotation;
}
};
return self;
});
// Player Snake Segment (uses snakeBody asset, rotates to face previous segment)
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
var body = self.attachAsset('snakeBody', {
anchorX: 0.5,
anchorY: 0.5
});
self._lastRotation = 0;
self.updateRotation = function (dx, dy) {
if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) {
self._lastRotation = Math.atan2(dy, dx);
self.rotation = self._lastRotation;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Start background music
LK.playMusic('bgMusic');
// Game variables
// Snake body segment (player)
// Snake head (player)
// Enemy snake body
// Enemy snake head
// Food
// Bullet
// Flame fart burst
// (flame asset removed)
// Nuke fart assets (player and enemy, can be replaced with custom art)
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var playerSnake;
var enemySnakes = [];
var enemySnakePool = []; // Pool for dead/reusable enemy snakes
var foods = [];
var bullets = [];
// (flames array removed)
var scoreTxt;
var dragNode = null;
var lastFireTick = 0;
var spawnFoodTick = 0;
var spawnEnemyTick = 0;
var musicSpeed = 1.0; // Current music playback speed
var enemyKillCount = 0; // Track how many enemies killed
// Musical note progression system
var noteNames = ['noteDo', 'noteRe', 'noteMi', 'noteFa', 'noteSol', 'noteLa', 'noteSi'];
var currentNoteIndex = 0;
var notesCycleCount = 0; // Track how many complete cycles we've done
var freezeActive = false;
var freezeEndTime = 0;
var fireRateBoostActive = false;
var fireRateBoostEndTime = 0;
var fireRateBoostIndicator;
var noteSlider;
var countdownTimer;
var gameArea = {
x: 120,
y: 120,
w: 2048 - 240,
h: 2732 - 240
}; // leave margin for UI
// Track current enemy snake length scaling with score
var currentEnemySnakeLength = 0; // 0 means default (random 4-6), will be set after 100 score
// Add hexagonal tilemap ground image, tile to fill game area
var groundTiles = [];
var tileW = 1020;
var tileH = 1065.8;
// Start tiling from (0,0) to ensure no gap in top/left corner
for (var y = 0; y < 2732; y += tileH) {
for (var x = 0; x < 2048; x += tileW) {
var tile = LK.getAsset('hexGround', {
anchorX: 0,
anchorY: 0,
x: x,
y: y
});
groundTiles.push(tile);
game.addChild(tile);
}
}
// Score display
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Initialize note slider
noteSlider = new NoteSlider();
noteSlider.x = 0; // Center horizontally using GUI coordinate system
noteSlider.y = 180; // Position below score text
LK.gui.top.addChild(noteSlider);
// Initialize countdown timer
countdownTimer = new CountdownTimer();
countdownTimer.x = 0;
countdownTimer.y = 0;
LK.gui.center.addChild(countdownTimer);
// Initialize fire rate boost indicator
fireRateBoostIndicator = new Text2('⚡ 3X FIRE RATE', {
size: 60,
fill: 0xFFFF00
});
fireRateBoostIndicator.anchor.set(0.5, 0.5);
fireRateBoostIndicator.x = 0;
fireRateBoostIndicator.y = -200;
fireRateBoostIndicator.visible = false;
LK.gui.center.addChild(fireRateBoostIndicator);
// Initialize player snake
playerSnake = new PlayerSnake();
playerSnake.head.x = 1024;
playerSnake.head.y = 1800;
for (var i = 0; i < playerSnake.segments.length; i++) {
playerSnake.segments[i].x = 1024;
playerSnake.segments[i].y = 1800 + i * 80;
}
game.addChild(playerSnake);
// Spawn initial food
function spawnFood() {
var f = new Food();
f.x = gameArea.x + 100 + Math.random() * (gameArea.w - 200);
f.y = gameArea.y + 100 + Math.random() * (gameArea.h - 200);
foods.push(f);
game.addChild(f);
}
for (var i = 0; i < 8; i++) {
spawnFood();
}
// Spawn enemy snake
function spawnEnemy() {
// Determine enemy length based on score
var enemyLength = 0;
if (currentEnemySnakeLength > 0) {
enemyLength = currentEnemySnakeLength;
} else {
enemyLength = 4 + Math.floor(Math.random() * 3); // default: 4-6
}
// Fetch from pool or create new
var e;
if (enemySnakePool.length > 0) {
e = enemySnakePool.pop();
// Remove from previous parent if needed
if (e.parent) {
e.parent.removeChild(e);
}
// Reset segments
while (e.segments.length > 1) {
var seg = e.segments.pop();
seg.destroy();
}
// Add segments to match desired length
for (var i = 1; i < enemyLength; i++) {
var seg = new EnemySegment();
seg.x = e.head.x;
seg.y = e.head.y + i * 80;
e.addChild(seg);
e.segments.push(seg);
}
e.length = enemyLength;
// Reset alpha for all segments
for (var i = 0; i < e.segments.length; i++) {
e.segments[i].alpha = 1;
}
e.visible = true;
e.alive = true;
} else {
// Create enemy snake with custom length if needed
e = new EnemySnake();
if (currentEnemySnakeLength > 0) {
// Remove all but head
while (e.segments.length > 1) {
var seg = e.segments.pop();
seg.destroy();
}
// Add segments to match desired length
for (var i = 1; i < enemyLength; i++) {
var seg = new EnemySegment();
seg.x = e.head.x;
seg.y = e.head.y + i * 80;
e.addChild(seg);
e.segments.push(seg);
}
e.length = enemyLength;
}
}
// Spawn at random edge OUTSIDE the visible screen
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top (above screen)
e.head.x = gameArea.x + Math.random() * gameArea.w;
e.head.y = gameArea.y - 200;
} else if (edge === 1) {
// bottom (below screen)
e.head.x = gameArea.x + Math.random() * gameArea.w;
e.head.y = gameArea.y + gameArea.h + 200;
} else if (edge === 2) {
// left (left of screen)
e.head.x = gameArea.x - 200;
e.head.y = gameArea.y + Math.random() * gameArea.h;
} else {
// right (right of screen)
e.head.x = gameArea.x + gameArea.w + 200;
e.head.y = gameArea.y + Math.random() * gameArea.h;
}
for (var i = 1; i < e.segments.length; i++) {
e.segments[i].x = e.head.x;
e.segments[i].y = e.head.y + i * 80;
}
e.visible = true;
e.alive = true;
// Apply freeze effect if currently active
if (freezeActive) {
for (var i = 0; i < e.segments.length; i++) {
e.segments[i].tint = 0x4444FF;
}
}
enemySnakes.push(e);
game.addChild(e);
}
for (var i = 0; i < 2; i++) {
spawnEnemy();
}
// Handle dragging
game.down = function (x, y, obj) {
dragNode = playerSnake;
playerSnake.targetX = x;
playerSnake.targetY = y;
};
game.move = function (x, y, obj) {
if (dragNode) {
playerSnake.targetX = x;
playerSnake.targetY = y;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Helper: check collision between two containers (circle-based)
function circlesIntersect(a, b, rA, rB) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < (rA + rB) * 0.5;
}
// Helper: clamp to game area
function clampToArea(x, y) {
x = Math.max(gameArea.x + 50, Math.min(gameArea.x + gameArea.w - 50, x));
y = Math.max(gameArea.y + 50, Math.min(gameArea.y + gameArea.h - 50, y));
return {
x: x,
y: y
};
}
// Main game update
game.update = function () {
// Handle freeze effect timing
if (freezeActive && LK.ticks >= freezeEndTime) {
freezeActive = false;
noteSlider.reset();
// Reset enemy colors
for (var i = 0; i < enemySnakes.length; i++) {
var e = enemySnakes[i];
for (var j = 0; j < e.segments.length; j++) {
e.segments[j].tint = 0xFFFFFF;
}
}
// Activate fire rate boost
fireRateBoostActive = true;
fireRateBoostEndTime = LK.ticks + 300; // 5 seconds at 60fps
fireRateBoostIndicator.visible = true;
// Animate indicator appearance
tween(fireRateBoostIndicator, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut
});
}
// Handle fire rate boost timing
if (fireRateBoostActive && LK.ticks >= fireRateBoostEndTime) {
fireRateBoostActive = false;
// Animate indicator disappearance
tween(fireRateBoostIndicator, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
fireRateBoostIndicator.visible = false;
fireRateBoostIndicator.alpha = 1;
fireRateBoostIndicator.scaleX = 1;
fireRateBoostIndicator.scaleY = 1;
}
});
}
// Update countdown timer
countdownTimer.update();
// Update player snake
playerSnake.update();
// Clamp player head to area
var clamped = clampToArea(playerSnake.head.x, playerSnake.head.y);
playerSnake.head.x = clamped.x;
playerSnake.head.y = clamped.y;
// Update enemy snakes
for (var i = enemySnakes.length - 1; i >= 0; i--) {
var e = enemySnakes[i];
if (!e.alive) {
// Remove from active array if dead and pooled
enemySnakes.splice(i, 1);
continue;
}
e.update(playerSnake.getHeadPos());
// Clamp enemy head to area
var ce = clampToArea(e.head.x, e.head.y);
e.head.x = ce.x;
e.head.y = ce.y;
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
// Remove if out of bounds
if (b.x < gameArea.x - 60 || b.x > gameArea.x + gameArea.w + 60 || b.y < gameArea.y - 60 || b.y > gameArea.y + gameArea.h + 60) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Bullet hits enemy snake
if (b.owner === 'player') {
for (var j = 0; j < enemySnakes.length; j++) {
var e = enemySnakes[j];
if (!e.alive) {
continue;
}
if (circlesIntersect(b, e.head, 60, 60)) {
// Hit!
b.destroy();
bullets.splice(i, 1);
// Play hit sound effect
LK.getSound('hitSnake').play();
// Shorten enemy snake or kill
var killed = false;
// Nuke fart effect at enemy head when hit (use EnemyMegaFart for enemy snake)
var nukeFart = new EnemyMegaFart();
nukeFart.x = e.head.x;
nukeFart.y = e.head.y;
game.addChild(nukeFart);
nukeFart.play();
if (e.segments.length > 2) {
var seg = e.segments.pop();
seg.destroy();
} else {
e.die();
killed = true;
}
LK.setScore(LK.getScore() + 5);
scoreTxt.setText(LK.getScore());
// Player fart effect when enemy is killed
if (killed) {
playerSnake.grow(1);
// Play snake death sound effect
LK.getSound('snakeDeath').play();
// Flash the head and all body segments for a more visible fart effect
for (var f = 0; f < playerSnake.segments.length; f++) {
LK.effects.flashObject(playerSnake.segments[f], 0xcccc00, 600);
}
// Increase music speed when enemy is killed
enemyKillCount++;
var newMusicSpeed = Math.min(2.0, 1.0 + enemyKillCount * 0.25); // Cap at 2x speed
if (newMusicSpeed > musicSpeed) {
// Use tween to smoothly transition music speed without breaking loop
tween({
speed: musicSpeed
}, {
speed: newMusicSpeed
}, {
duration: 500,
easing: tween.easeOutCubic,
onFinish: function onFinish() {
musicSpeed = newMusicSpeed;
}
});
}
}
break;
}
}
}
// Bullet hits player (if we add enemy bullets in future)
}
// Player eats food
for (var i = foods.length - 1; i >= 0; i--) {
var food = foods[i];
if (circlesIntersect(playerSnake.head, food, 60, 35)) {
food.destroy();
foods.splice(i, 1);
playerSnake.grow(1);
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
// Play musical note
LK.getSound(noteNames[currentNoteIndex]).play();
// Update slider
currentNoteIndex++;
noteSlider.updateProgress(currentNoteIndex + notesCycleCount * 7);
// Check if we've completed a full cycle
if (currentNoteIndex >= 7) {
notesCycleCount++;
currentNoteIndex = 0;
// Check if we've completed two full cycles (14 notes total)
if (notesCycleCount >= 2) {
// Activate freeze effect
freezeActive = true;
freezeEndTime = LK.ticks + 300; // 5 seconds at 60fps
// Flash screen blue to indicate freeze
LK.effects.flashScreen(0x4444FF, 500);
// Start countdown timer
countdownTimer.startCountdown(300); // 5 seconds in ticks
// Reset note progression
currentNoteIndex = 0;
notesCycleCount = 0;
}
}
// MegaFart effect: nuke particle at player head
var megaFart = new MegaFart();
megaFart.x = playerSnake.head.x;
megaFart.y = playerSnake.head.y;
game.addChild(megaFart);
megaFart.play();
// Win condition removed
}
}
// Player collides with enemy snake head or body
for (var i = 0; i < enemySnakes.length; i++) {
var e = enemySnakes[i];
if (!e.alive) {
continue;
}
// Head to head
if (circlesIntersect(playerSnake.head, e.head, 60, 60)) {
LK.effects.flashScreen(0xff3300, 800);
LK.showGameOver();
return;
}
// Head to body
for (var j = 1; j < e.segments.length; j++) {
if (circlesIntersect(playerSnake.head, e.segments[j], 60, 40)) {
LK.effects.flashScreen(0xff3300, 800);
LK.showGameOver();
return;
}
}
}
// Player collides with self (not head)
for (var i = 2; i < playerSnake.segments.length; i++) {
if (circlesIntersect(playerSnake.head, playerSnake.segments[i], 60, 40)) {
LK.effects.flashScreen(0xff3300, 800);
LK.showGameOver();
return;
}
}
// Spawn food
spawnFoodTick++;
if (spawnFoodTick > 60 && foods.length < 12) {
// Increased spawn rate
spawnFood();
spawnFoodTick = 0;
}
// Spawn enemy
spawnEnemyTick++;
if (spawnEnemyTick > 120 && enemySnakes.length < 8) {
// 120 ticks ≈ 2 seconds at 60fps
spawnEnemy();
spawnEnemyTick = 0;
}
// Check and update enemy snake length scaling with score
var score = LK.getScore();
if (score >= 1000) {
LK.showYouWin();
return;
}
if (score >= 100) {
// Every 100 points, increase enemy length by 2 (starting at 6)
var newLen = 6 + Math.floor((score - 100) / 100) * 2;
if (newLen !== currentEnemySnakeLength) {
currentEnemySnakeLength = newLen;
}
}
// Minigun: auto-fire at nearest enemy in range
var fireInterval = fireRateBoostActive ? 7 : 20; // 3x fire rate when boosted (7 ticks vs 20 ticks)
if (LK.ticks - lastFireTick > fireInterval) {
// Normal: 20 ticks ≈ 0.33 seconds at 60fps (~3 shots/sec)
// Boosted: 7 ticks ≈ 0.12 seconds at 60fps (~9 shots/sec, 3x faster)
var nearest = null;
var minDist = 99999;
for (var i = 0; i < enemySnakes.length; i++) {
var e = enemySnakes[i];
if (!e.alive) {
continue;
}
var dx = e.head.x - playerSnake.head.x;
var dy = e.head.y - playerSnake.head.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 600 && dist < minDist) {
minDist = dist;
nearest = e;
}
}
if (nearest) {
fireBulletAt(playerSnake.head.x, playerSnake.head.y, nearest.head.x, nearest.head.y, 'player');
lastFireTick = LK.ticks;
}
}
};
// Fire bullet from (x0,y0) to (x1,y1)
function fireBulletAt(x0, y0, x1, y1, owner) {
var b = new Bullet();
b.x = x0;
b.y = y0;
var dx = x1 - x0;
var dy = y1 - y0;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 38;
b.vx = dx / dist * speed;
b.vy = dy / dist * speed;
b.owner = owner;
// Rotate bullet to face direction of travel
if (dist > 0.01) {
b.rotation = Math.atan2(dy, dx);
}
bullets.push(b);
game.addChild(b);
}
// (spawnFlameBurst removed)
// Center score text
scoreTxt.setText(LK.getScore());
scoreTxt.anchor.set(0.5, 0);
cartoon snake texture. In-Game asset. 2d. High contrast. No shadows
cartoon snake head. In-Game asset. 2d. High contrast. No shadows
red
make it red
Remove this area. Make it image looks more circle
make it red and cursed
2d white empty thin circle png.. In-Game asset. 2d. High contrast. No shadows
make it rounded edge. And color is whtie