/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Food Class var Food = Container.expand(function () { var self = Container.call(this); // Food is a yellow ellipse var foodAsset = self.attachAsset('food', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Food2 Class (rare food) var Food2 = Container.expand(function () { var self = Container.call(this); // Use food2 asset var foodAsset = self.attachAsset('food2', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Food3 Class (health food) var Food3 = Container.expand(function () { var self = Container.call(this); // Use food3 asset var foodAsset = self.attachAsset('food3', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Food4 Class (special food every 4th) var Food4 = Container.expand(function () { var self = Container.call(this); // Use food4 asset var foodAsset = self.attachAsset('food4', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Snake Head Class var SnakeHead = Container.expand(function () { var self = Container.call(this); // Head is a unique asset var headAsset = self.attachAsset('snakeHead', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Snake Segment Class var SnakeSegment = Container.expand(function () { var self = Container.call(this); // Each segment is a green box var segmentAsset = self.attachAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Wall Class var Wall = Container.expand(function () { var self = Container.call(this); // Wall is a red box var wallAsset = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // --- Game Constants --- // --- Asset Initialization --- // Snake Head Class var GRID_SIZE = 180; // Each cell is 180x180 px (increased) var GRID_COLS = Math.floor(2048 / GRID_SIZE); // 11 var GRID_ROWS = Math.floor(2732 / GRID_SIZE); // 15 var BOARD_WIDTH = GRID_COLS * GRID_SIZE; var BOARD_HEIGHT = GRID_ROWS * GRID_SIZE; var BOARD_OFFSET_X = Math.floor((2048 - BOARD_WIDTH) / 2); var BOARD_OFFSET_Y = Math.floor((2732 - BOARD_HEIGHT) / 2); var SNAKE_START_LENGTH = 2; var SNAKE_MAX_LENGTH = (GRID_COLS - 2) * (GRID_ROWS - 2); // Win when snake fills entire playable area // --- Game State --- var snake = []; var snakeDir = { x: 1, y: 0 }; // Start moving right var nextDir = { x: 1, y: 0 }; var moveTimer = 0; var MOVE_INTERVAL = 210; // ms (biraz yavaşlatıldı) var foods = []; var walls = []; var isDead = false; var isWin = false; var pendingGrowth = 0; var lastTouch = null; var darknessOverlay = null; var isDarkened = false; var isSlowed = false; var originalMoveInterval = MOVE_INTERVAL; var darknessTimer = null; var speedTimer = null; var isMultiplierActive = false; var multiplierTimer = null; var scoreMultiplier = 1; var isYellowBackgroundActive = false; var yellowBackgroundTimer = null; var originalBackgroundColor = 0x181818; var playerLives = 1; var lastTapTime = 0; var isDoubleTouch = false; var speedMultiplier = 1; var touchStartTime = 0; var DOUBLE_TAP_THRESHOLD = 300; // ms // --- Score UI --- var scoreTxt = new Text2('1', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Health UI --- var healthTxt = new Text2('♥ 1', { size: 80, fill: 0xFF0000 }); healthTxt.anchor.set(1, 0); LK.gui.topRight.addChild(healthTxt); // --- Helper Functions --- function gridToPos(col, row) { return { x: BOARD_OFFSET_X + col * GRID_SIZE + GRID_SIZE / 2, y: BOARD_OFFSET_Y + row * GRID_SIZE + GRID_SIZE / 2 }; } function posToGrid(x, y) { var col = Math.floor((x - BOARD_OFFSET_X) / GRID_SIZE); var row = Math.floor((y - BOARD_OFFSET_Y) / GRID_SIZE); return { col: col, row: row }; } function randomFreeCell() { // Returns a random cell not occupied by snake or wall var free = []; for (var r = 1; r < GRID_ROWS - 1; r++) { for (var c = 1; c < GRID_COLS - 1; c++) { var occupied = false; for (var i = 0; i < snake.length; i++) { if (snake[i].col === c && snake[i].row === r) { occupied = true; break; } } for (var f = 0; f < foods.length; f++) { if (foods[f].col === c && foods[f].row === r) { occupied = true; break; } } if (!occupied) free.push({ col: c, row: r }); } } if (free.length === 0) return null; return free[Math.floor(Math.random() * free.length)]; } function setScore(val) { LK.setScore(val); scoreTxt.setText(val); } // --- Wall Setup --- function createWalls() { // Top and bottom for (var c = 0; c < GRID_COLS; c++) { var wallTop = new Wall(); var wallBottom = new Wall(); var posTop = gridToPos(c, 0); var posBottom = gridToPos(c, GRID_ROWS - 1); wallTop.x = posTop.x; wallTop.y = posTop.y; wallTop.col = c; wallTop.row = 0; wallBottom.x = posBottom.x; wallBottom.y = posBottom.y; wallBottom.col = c; wallBottom.row = GRID_ROWS - 1; game.addChild(wallTop); game.addChild(wallBottom); walls.push(wallTop, wallBottom); } // Left and right for (var r = 1; r < GRID_ROWS - 1; r++) { var wallLeft = new Wall(); var wallRight = new Wall(); var posLeft = gridToPos(0, r); var posRight = gridToPos(GRID_COLS - 1, r); wallLeft.x = posLeft.x; wallLeft.y = posLeft.y; wallLeft.col = 0; wallLeft.row = r; wallRight.x = posRight.x; wallRight.y = posRight.y; wallRight.col = GRID_COLS - 1; wallRight.row = r; game.addChild(wallLeft); game.addChild(wallRight); walls.push(wallLeft, wallRight); } } // --- Snake Setup --- function createSnake() { var startCol = Math.floor(GRID_COLS / 2); var startRow = Math.floor(GRID_ROWS / 2); // First segment is the head var head = new SnakeHead(); head.col = startCol; head.row = startRow; var pos = gridToPos(head.col, head.row); head.x = pos.x; head.y = pos.y; game.addChild(head); snake.push(head); // The rest are segments for (var i = 1; i < SNAKE_START_LENGTH; i++) { var seg = new SnakeSegment(); seg.col = startCol - i; seg.row = startRow; var pos2 = gridToPos(seg.col, seg.row); seg.x = pos2.x; seg.y = pos2.y; game.addChild(seg); snake.push(seg); } } // --- Food Setup --- // Track how many foods have been spawned var foodSpawnCount = 0; var food4Timers = []; // Track timers for Food4 items function spawnFood() { // Determine target food count based on score var targetFoodCount = LK.getScore() > 10 ? 2 : 1; // If we already have the target number of foods or more, don't spawn any new ones if (foods.length >= targetFoodCount) { return; } // Calculate how many foods we need to spawn to reach target var foodsToSpawn = targetFoodCount - foods.length; // Spawn the required number of foods for (var f = 0; f < foodsToSpawn; f++) { var cell = randomFreeCell(); if (!cell) continue; foodSpawnCount = typeof foodSpawnCount === "number" ? foodSpawnCount + 1 : 1; var newFood; var randomChance = Math.random(); if (foodSpawnCount % 4 === 0) { newFood = new Food4(); // Start timer to convert Food4 to Food after 10 seconds var food4Timer = LK.setTimeout(function () { // Check if this Food4 still exists and hasn't been eaten var foodIndex = foods.indexOf(newFood); if (foodIndex !== -1 && newFood instanceof Food4) { // Convert Food4 to regular Food var oldCol = newFood.col; var oldRow = newFood.row; var oldPos = gridToPos(oldCol, oldRow); // Remove old Food4 newFood.destroy(); foods.splice(foodIndex, 1); // Create new Food in same position var regularFood = new Food(); regularFood.col = oldCol; regularFood.row = oldRow; regularFood.x = oldPos.x; regularFood.y = oldPos.y; game.addChild(regularFood); foods.push(regularFood); } // Remove timer from tracking array var timerIndex = food4Timers.indexOf(food4Timer); if (timerIndex !== -1) { food4Timers.splice(timerIndex, 1); } }, 10000); // Track the timer food4Timers.push(food4Timer); } else if (randomChance < 0.10) { // 10% chance for Food3 newFood = new Food3(); } else if (randomChance < 0.20) { // 20% chance for Food2 (but only if not Food3) newFood = new Food2(); } else { newFood = new Food(); } newFood.col = cell.col; newFood.row = cell.row; var pos = gridToPos(cell.col, cell.row); newFood.x = pos.x; newFood.y = pos.y; game.addChild(newFood); foods.push(newFood); } } // --- Collision Checks --- function isWall(col, row) { return col === 0 || row === 0 || col === GRID_COLS - 1 || row === GRID_ROWS - 1; } function isSnake(col, row, skipHead) { for (var i = skipHead ? 1 : 0; i < snake.length; i++) { if (snake[i].col === col && snake[i].row === row) return true; } return false; } // --- Game Over / Win --- function triggerGameOver() { if (isDead) return; playerLives--; healthTxt.setText('♥ ' + playerLives); if (playerLives <= 0) { isDead = true; LK.effects.flashScreen(0xff0000, 1000); // Create a container for the game over display var goCont = new Container(); // Create large snake head asset (350x350) var snakeHeadAsset = LK.getAsset('snakeHead', { anchorX: 0.5, anchorY: 0.5, width: 350, height: 350 }); snakeHeadAsset.x = 350 / 2; snakeHeadAsset.y = 350 / 2; // Create large food asset (350x350) var foodAsset = LK.getAsset('food', { anchorX: 0.5, anchorY: 0.5, width: 350, height: 350 }); foodAsset.x = 350 + 350 / 2 + 40; // 40px gap between foodAsset.y = 350 / 2; // Set container size and center on screen var totalWidth = 350 * 2 + 40; var totalHeight = 350; goCont.x = 0; goCont.y = 0; goCont.width = totalWidth; goCont.height = totalHeight; goCont.addChild(snakeHeadAsset); goCont.addChild(foodAsset); // Show the container as a popup overlay (LK will remove it on reset) LK.showGameOver({ customContent: goCont, customContentWidth: totalWidth, customContentHeight: totalHeight, customContentY: 120 // Place below the menu (menu is top 100px, so 120px for margin) }); } else { // Player still has lives, flash red and continue LK.effects.flashScreen(0xff0000, 500); // Reset snake tail length to 1 (only keep the head) for (var i = snake.length - 1; i > 0; i--) { snake[i].destroy(); snake.splice(i, 1); } // Move snake head to a safe position automatically var head = snake[0]; var safeCell = randomFreeCell(); if (safeCell) { // Update head position data head.col = safeCell.col; head.row = safeCell.row; var safePos = gridToPos(safeCell.col, safeCell.row); // Smoothly animate to safe position tween(head, { x: safePos.x, y: safePos.y }, { duration: 300, easing: tween.easeOut }); } } } function triggerWin() { if (isWin) return; isWin = true; LK.effects.flashScreen(0x00ff00, 1000); LK.showYouWin(); } // --- Input Handling (Touch/Drag) --- var dragStart = null; var dragDir = null; function getDirectionFromDrag(dx, dy) { if (Math.abs(dx) > Math.abs(dy)) { return dx > 0 ? { x: 1, y: 0 } : { x: -1, y: 0 }; } else { return dy > 0 ? { x: 0, y: 1 } : { x: 0, y: -1 }; } } game.down = function (x, y, obj) { dragStart = { x: x, y: y }; dragDir = null; // Double-tap detection var currentTime = Date.now(); if (currentTime - lastTapTime < DOUBLE_TAP_THRESHOLD) { // Double tap detected isDoubleTouch = true; speedMultiplier = 2; touchStartTime = currentTime; } else { // Single tap isDoubleTouch = false; speedMultiplier = 1; } lastTapTime = currentTime; }; game.move = function (x, y, obj) { if (!dragStart) return; var dx = x - dragStart.x; var dy = y - dragStart.y; if (Math.abs(dx) > 40 || Math.abs(dy) > 40) { var dir = getDirectionFromDrag(dx, dy); // Prevent reversing if ((dir.x !== -snakeDir.x || dir.y !== -snakeDir.y) && (dir.x !== snakeDir.x || dir.y !== snakeDir.y)) { nextDir = dir; dragDir = dir; dragStart = null; } } }; game.up = function (x, y, obj) { dragStart = null; dragDir = null; // Reset speed when touch is released isDoubleTouch = false; speedMultiplier = 1; }; // --- Main Game Loop --- game.update = function () { if (isDead || isWin) return; moveTimer += 1000 / 60; var currentMoveInterval = MOVE_INTERVAL / speedMultiplier; if (moveTimer < currentMoveInterval) return; moveTimer = 0; // Update direction if (nextDir.x !== -snakeDir.x || nextDir.y !== -snakeDir.y) { snakeDir = { x: nextDir.x, y: nextDir.y }; } // Calculate new head position var head = snake[0]; var newCol = head.col + snakeDir.x; var newRow = head.row + snakeDir.y; // Check collisions if (isWall(newCol, newRow) || isSnake(newCol, newRow, false)) { triggerGameOver(); return; } // Move snake // Remove old head and replace with segment var oldHead = snake[0]; var newSegment = new SnakeSegment(); newSegment.col = oldHead.col; newSegment.row = oldHead.row; newSegment.x = oldHead.x; newSegment.y = oldHead.y; game.addChild(newSegment); snake[0].destroy(); snake[0] = newSegment; // Add new head at the front var newHead = new SnakeHead(); newHead.col = newCol; newHead.row = newRow; var pos = gridToPos(newCol, newRow); newHead.x = pos.x; newHead.y = pos.y; // Set rotation based on direction (asset is upright, so rotate by +90deg for right) if (snakeDir.x === 1 && snakeDir.y === 0) { newHead.rotation = Math.PI / 2; // Right } else if (snakeDir.x === -1 && snakeDir.y === 0) { newHead.rotation = -Math.PI / 2; // Left } else if (snakeDir.x === 0 && snakeDir.y === -1) { newHead.rotation = 0; // Up } else if (snakeDir.x === 0 && snakeDir.y === 1) { newHead.rotation = Math.PI; // Down } game.addChild(newHead); snake.unshift(newHead); // Check food collision var ateFood = false; var eatenFood = null; for (var f = 0; f < foods.length; f++) { if (foods[f].col === newCol && foods[f].row === newRow) { ateFood = true; eatenFood = foods[f]; break; } } if (ateFood) { // Remove the eaten food from the foods array and destroy it for (var i = 0; i < foods.length; i++) { if (foods[i] === eatenFood) { foods[i].destroy(); foods.splice(i, 1); break; } } // Calculate score based on food type and multiplier var pointsToAdd; if (eatenFood instanceof Food4) { pointsToAdd = 1 * scoreMultiplier; // Food4 gives 1 point } else { pointsToAdd = 2 * scoreMultiplier; // Food and Food2 give 2 points } setScore(LK.getScore() + pointsToAdd); pendingGrowth += 1; // Play feeding sound LK.getSound('feeding').play(); // Handle Food3 special effect - give health (max 3 lives) or bonus points if at max health if (eatenFood instanceof Food3) { if (playerLives < 3) { playerLives++; healthTxt.setText('♥ ' + playerLives); } else { // Player is at max health (3 lives), give 50 bonus points setScore(LK.getScore() + 50); } } // Handle Food2 special effect - 10x multiplier for 5 seconds if (eatenFood instanceof Food2) { if (!isMultiplierActive) { isMultiplierActive = true; scoreMultiplier = 10; // Clear any existing multiplier timer if (multiplierTimer) { LK.clearTimeout(multiplierTimer); } // Remove multiplier after exactly 5 seconds multiplierTimer = LK.setTimeout(function () { scoreMultiplier = 1; isMultiplierActive = false; multiplierTimer = null; }, 5000); } // Yellow background effect for 5 seconds if (!isYellowBackgroundActive) { isYellowBackgroundActive = true; game.setBackgroundColor(0xFFFF00); // Yellow color // Clear any existing yellow background timer if (yellowBackgroundTimer) { LK.clearTimeout(yellowBackgroundTimer); } // Restore original background after exactly 5 seconds yellowBackgroundTimer = LK.setTimeout(function () { game.setBackgroundColor(originalBackgroundColor); isYellowBackgroundActive = false; yellowBackgroundTimer = null; }, 5000); } } // Play feeding2 sound only when Food4 is eaten if (eatenFood instanceof Food4) { // Clear any associated timer since Food4 was eaten for (var t = food4Timers.length - 1; t >= 0; t--) { LK.clearTimeout(food4Timers[t]); food4Timers.splice(t, 1); } LK.getSound('feeding2').play(); // Create darkness effect for 10 seconds if (!isDarkened) { isDarkened = true; darknessOverlay = LK.getAsset('snakeSegment', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732, tint: 0x000000 }); darknessOverlay.alpha = 0; game.addChild(darknessOverlay); // Fade in darkness tween(darknessOverlay, { alpha: 0.7 }, { duration: 500 }); // Clear any existing darkness timer if (darknessTimer) { LK.clearTimeout(darknessTimer); } // Remove darkness after exactly 5 seconds darknessTimer = LK.setTimeout(function () { if (darknessOverlay) { tween(darknessOverlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { if (darknessOverlay) { darknessOverlay.destroy(); darknessOverlay = null; } isDarkened = false; darknessTimer = null; } }); } }, 5000); } // Reduce snake speed by 50% for 10 seconds if (!isSlowed) { isSlowed = true; MOVE_INTERVAL = originalMoveInterval * 1.5; // 50% slower // Clear any existing speed timer if (speedTimer) { LK.clearTimeout(speedTimer); } // Restore original speed after exactly 5 seconds speedTimer = LK.setTimeout(function () { MOVE_INTERVAL = originalMoveInterval; isSlowed = false; speedTimer = null; }, 5000); } } spawnFood(); // Win condition if (snake.length >= SNAKE_MAX_LENGTH) { triggerWin(); return; } } // Remove tail if not growing if (pendingGrowth > 0) { pendingGrowth--; } else { var tail = snake.pop(); tail.destroy(); } }; // --- Draw Board Grid Background --- var gridBg = new Container(); for (var r = 0; r < GRID_ROWS; r++) { for (var c = 0; c < GRID_COLS; c++) { // Use a light color for grid lines, and a slightly darker fill for the cell var cell = LK.getAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5, x: BOARD_OFFSET_X + c * GRID_SIZE + GRID_SIZE / 2, y: BOARD_OFFSET_Y + r * GRID_SIZE + GRID_SIZE / 2, width: GRID_SIZE, height: GRID_SIZE, tint: (r + c) % 2 === 0 ? 0x232323 : 0x1a1a1a // subtle checkerboard for clarity }); // Add a border by overlaying a slightly smaller cell of the same color as background var border = LK.getAsset('snakeSegment', { anchorX: 0.5, anchorY: 0.5, x: BOARD_OFFSET_X + c * GRID_SIZE + GRID_SIZE / 2, y: BOARD_OFFSET_Y + r * GRID_SIZE + GRID_SIZE / 2, width: GRID_SIZE - 8, height: GRID_SIZE - 8, tint: (r + c) % 2 === 0 ? 0x181818 : 0x181818 // match background }); gridBg.addChild(cell); gridBg.addChild(border); } } game.addChild(gridBg); // --- Game Initialization --- createWalls(); createSnake(); spawnFood(); setScore(1); ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Food Class
var Food = Container.expand(function () {
var self = Container.call(this);
// Food is a yellow ellipse
var foodAsset = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Food2 Class (rare food)
var Food2 = Container.expand(function () {
var self = Container.call(this);
// Use food2 asset
var foodAsset = self.attachAsset('food2', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Food3 Class (health food)
var Food3 = Container.expand(function () {
var self = Container.call(this);
// Use food3 asset
var foodAsset = self.attachAsset('food3', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Food4 Class (special food every 4th)
var Food4 = Container.expand(function () {
var self = Container.call(this);
// Use food4 asset
var foodAsset = self.attachAsset('food4', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Snake Head Class
var SnakeHead = Container.expand(function () {
var self = Container.call(this);
// Head is a unique asset
var headAsset = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Snake Segment Class
var SnakeSegment = Container.expand(function () {
var self = Container.call(this);
// Each segment is a green box
var segmentAsset = self.attachAsset('snakeSegment', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Wall Class
var Wall = Container.expand(function () {
var self = Container.call(this);
// Wall is a red box
var wallAsset = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// --- Game Constants ---
// --- Asset Initialization ---
// Snake Head Class
var GRID_SIZE = 180; // Each cell is 180x180 px (increased)
var GRID_COLS = Math.floor(2048 / GRID_SIZE); // 11
var GRID_ROWS = Math.floor(2732 / GRID_SIZE); // 15
var BOARD_WIDTH = GRID_COLS * GRID_SIZE;
var BOARD_HEIGHT = GRID_ROWS * GRID_SIZE;
var BOARD_OFFSET_X = Math.floor((2048 - BOARD_WIDTH) / 2);
var BOARD_OFFSET_Y = Math.floor((2732 - BOARD_HEIGHT) / 2);
var SNAKE_START_LENGTH = 2;
var SNAKE_MAX_LENGTH = (GRID_COLS - 2) * (GRID_ROWS - 2); // Win when snake fills entire playable area
// --- Game State ---
var snake = [];
var snakeDir = {
x: 1,
y: 0
}; // Start moving right
var nextDir = {
x: 1,
y: 0
};
var moveTimer = 0;
var MOVE_INTERVAL = 210; // ms (biraz yavaşlatıldı)
var foods = [];
var walls = [];
var isDead = false;
var isWin = false;
var pendingGrowth = 0;
var lastTouch = null;
var darknessOverlay = null;
var isDarkened = false;
var isSlowed = false;
var originalMoveInterval = MOVE_INTERVAL;
var darknessTimer = null;
var speedTimer = null;
var isMultiplierActive = false;
var multiplierTimer = null;
var scoreMultiplier = 1;
var isYellowBackgroundActive = false;
var yellowBackgroundTimer = null;
var originalBackgroundColor = 0x181818;
var playerLives = 1;
var lastTapTime = 0;
var isDoubleTouch = false;
var speedMultiplier = 1;
var touchStartTime = 0;
var DOUBLE_TAP_THRESHOLD = 300; // ms
// --- Score UI ---
var scoreTxt = new Text2('1', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Health UI ---
var healthTxt = new Text2('♥ 1', {
size: 80,
fill: 0xFF0000
});
healthTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(healthTxt);
// --- Helper Functions ---
function gridToPos(col, row) {
return {
x: BOARD_OFFSET_X + col * GRID_SIZE + GRID_SIZE / 2,
y: BOARD_OFFSET_Y + row * GRID_SIZE + GRID_SIZE / 2
};
}
function posToGrid(x, y) {
var col = Math.floor((x - BOARD_OFFSET_X) / GRID_SIZE);
var row = Math.floor((y - BOARD_OFFSET_Y) / GRID_SIZE);
return {
col: col,
row: row
};
}
function randomFreeCell() {
// Returns a random cell not occupied by snake or wall
var free = [];
for (var r = 1; r < GRID_ROWS - 1; r++) {
for (var c = 1; c < GRID_COLS - 1; c++) {
var occupied = false;
for (var i = 0; i < snake.length; i++) {
if (snake[i].col === c && snake[i].row === r) {
occupied = true;
break;
}
}
for (var f = 0; f < foods.length; f++) {
if (foods[f].col === c && foods[f].row === r) {
occupied = true;
break;
}
}
if (!occupied) free.push({
col: c,
row: r
});
}
}
if (free.length === 0) return null;
return free[Math.floor(Math.random() * free.length)];
}
function setScore(val) {
LK.setScore(val);
scoreTxt.setText(val);
}
// --- Wall Setup ---
function createWalls() {
// Top and bottom
for (var c = 0; c < GRID_COLS; c++) {
var wallTop = new Wall();
var wallBottom = new Wall();
var posTop = gridToPos(c, 0);
var posBottom = gridToPos(c, GRID_ROWS - 1);
wallTop.x = posTop.x;
wallTop.y = posTop.y;
wallTop.col = c;
wallTop.row = 0;
wallBottom.x = posBottom.x;
wallBottom.y = posBottom.y;
wallBottom.col = c;
wallBottom.row = GRID_ROWS - 1;
game.addChild(wallTop);
game.addChild(wallBottom);
walls.push(wallTop, wallBottom);
}
// Left and right
for (var r = 1; r < GRID_ROWS - 1; r++) {
var wallLeft = new Wall();
var wallRight = new Wall();
var posLeft = gridToPos(0, r);
var posRight = gridToPos(GRID_COLS - 1, r);
wallLeft.x = posLeft.x;
wallLeft.y = posLeft.y;
wallLeft.col = 0;
wallLeft.row = r;
wallRight.x = posRight.x;
wallRight.y = posRight.y;
wallRight.col = GRID_COLS - 1;
wallRight.row = r;
game.addChild(wallLeft);
game.addChild(wallRight);
walls.push(wallLeft, wallRight);
}
}
// --- Snake Setup ---
function createSnake() {
var startCol = Math.floor(GRID_COLS / 2);
var startRow = Math.floor(GRID_ROWS / 2);
// First segment is the head
var head = new SnakeHead();
head.col = startCol;
head.row = startRow;
var pos = gridToPos(head.col, head.row);
head.x = pos.x;
head.y = pos.y;
game.addChild(head);
snake.push(head);
// The rest are segments
for (var i = 1; i < SNAKE_START_LENGTH; i++) {
var seg = new SnakeSegment();
seg.col = startCol - i;
seg.row = startRow;
var pos2 = gridToPos(seg.col, seg.row);
seg.x = pos2.x;
seg.y = pos2.y;
game.addChild(seg);
snake.push(seg);
}
}
// --- Food Setup ---
// Track how many foods have been spawned
var foodSpawnCount = 0;
var food4Timers = []; // Track timers for Food4 items
function spawnFood() {
// Determine target food count based on score
var targetFoodCount = LK.getScore() > 10 ? 2 : 1;
// If we already have the target number of foods or more, don't spawn any new ones
if (foods.length >= targetFoodCount) {
return;
}
// Calculate how many foods we need to spawn to reach target
var foodsToSpawn = targetFoodCount - foods.length;
// Spawn the required number of foods
for (var f = 0; f < foodsToSpawn; f++) {
var cell = randomFreeCell();
if (!cell) continue;
foodSpawnCount = typeof foodSpawnCount === "number" ? foodSpawnCount + 1 : 1;
var newFood;
var randomChance = Math.random();
if (foodSpawnCount % 4 === 0) {
newFood = new Food4();
// Start timer to convert Food4 to Food after 10 seconds
var food4Timer = LK.setTimeout(function () {
// Check if this Food4 still exists and hasn't been eaten
var foodIndex = foods.indexOf(newFood);
if (foodIndex !== -1 && newFood instanceof Food4) {
// Convert Food4 to regular Food
var oldCol = newFood.col;
var oldRow = newFood.row;
var oldPos = gridToPos(oldCol, oldRow);
// Remove old Food4
newFood.destroy();
foods.splice(foodIndex, 1);
// Create new Food in same position
var regularFood = new Food();
regularFood.col = oldCol;
regularFood.row = oldRow;
regularFood.x = oldPos.x;
regularFood.y = oldPos.y;
game.addChild(regularFood);
foods.push(regularFood);
}
// Remove timer from tracking array
var timerIndex = food4Timers.indexOf(food4Timer);
if (timerIndex !== -1) {
food4Timers.splice(timerIndex, 1);
}
}, 10000);
// Track the timer
food4Timers.push(food4Timer);
} else if (randomChance < 0.10) {
// 10% chance for Food3
newFood = new Food3();
} else if (randomChance < 0.20) {
// 20% chance for Food2 (but only if not Food3)
newFood = new Food2();
} else {
newFood = new Food();
}
newFood.col = cell.col;
newFood.row = cell.row;
var pos = gridToPos(cell.col, cell.row);
newFood.x = pos.x;
newFood.y = pos.y;
game.addChild(newFood);
foods.push(newFood);
}
}
// --- Collision Checks ---
function isWall(col, row) {
return col === 0 || row === 0 || col === GRID_COLS - 1 || row === GRID_ROWS - 1;
}
function isSnake(col, row, skipHead) {
for (var i = skipHead ? 1 : 0; i < snake.length; i++) {
if (snake[i].col === col && snake[i].row === row) return true;
}
return false;
}
// --- Game Over / Win ---
function triggerGameOver() {
if (isDead) return;
playerLives--;
healthTxt.setText('♥ ' + playerLives);
if (playerLives <= 0) {
isDead = true;
LK.effects.flashScreen(0xff0000, 1000);
// Create a container for the game over display
var goCont = new Container();
// Create large snake head asset (350x350)
var snakeHeadAsset = LK.getAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5,
width: 350,
height: 350
});
snakeHeadAsset.x = 350 / 2;
snakeHeadAsset.y = 350 / 2;
// Create large food asset (350x350)
var foodAsset = LK.getAsset('food', {
anchorX: 0.5,
anchorY: 0.5,
width: 350,
height: 350
});
foodAsset.x = 350 + 350 / 2 + 40; // 40px gap between
foodAsset.y = 350 / 2;
// Set container size and center on screen
var totalWidth = 350 * 2 + 40;
var totalHeight = 350;
goCont.x = 0;
goCont.y = 0;
goCont.width = totalWidth;
goCont.height = totalHeight;
goCont.addChild(snakeHeadAsset);
goCont.addChild(foodAsset);
// Show the container as a popup overlay (LK will remove it on reset)
LK.showGameOver({
customContent: goCont,
customContentWidth: totalWidth,
customContentHeight: totalHeight,
customContentY: 120 // Place below the menu (menu is top 100px, so 120px for margin)
});
} else {
// Player still has lives, flash red and continue
LK.effects.flashScreen(0xff0000, 500);
// Reset snake tail length to 1 (only keep the head)
for (var i = snake.length - 1; i > 0; i--) {
snake[i].destroy();
snake.splice(i, 1);
}
// Move snake head to a safe position automatically
var head = snake[0];
var safeCell = randomFreeCell();
if (safeCell) {
// Update head position data
head.col = safeCell.col;
head.row = safeCell.row;
var safePos = gridToPos(safeCell.col, safeCell.row);
// Smoothly animate to safe position
tween(head, {
x: safePos.x,
y: safePos.y
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
function triggerWin() {
if (isWin) return;
isWin = true;
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
}
// --- Input Handling (Touch/Drag) ---
var dragStart = null;
var dragDir = null;
function getDirectionFromDrag(dx, dy) {
if (Math.abs(dx) > Math.abs(dy)) {
return dx > 0 ? {
x: 1,
y: 0
} : {
x: -1,
y: 0
};
} else {
return dy > 0 ? {
x: 0,
y: 1
} : {
x: 0,
y: -1
};
}
}
game.down = function (x, y, obj) {
dragStart = {
x: x,
y: y
};
dragDir = null;
// Double-tap detection
var currentTime = Date.now();
if (currentTime - lastTapTime < DOUBLE_TAP_THRESHOLD) {
// Double tap detected
isDoubleTouch = true;
speedMultiplier = 2;
touchStartTime = currentTime;
} else {
// Single tap
isDoubleTouch = false;
speedMultiplier = 1;
}
lastTapTime = currentTime;
};
game.move = function (x, y, obj) {
if (!dragStart) return;
var dx = x - dragStart.x;
var dy = y - dragStart.y;
if (Math.abs(dx) > 40 || Math.abs(dy) > 40) {
var dir = getDirectionFromDrag(dx, dy);
// Prevent reversing
if ((dir.x !== -snakeDir.x || dir.y !== -snakeDir.y) && (dir.x !== snakeDir.x || dir.y !== snakeDir.y)) {
nextDir = dir;
dragDir = dir;
dragStart = null;
}
}
};
game.up = function (x, y, obj) {
dragStart = null;
dragDir = null;
// Reset speed when touch is released
isDoubleTouch = false;
speedMultiplier = 1;
};
// --- Main Game Loop ---
game.update = function () {
if (isDead || isWin) return;
moveTimer += 1000 / 60;
var currentMoveInterval = MOVE_INTERVAL / speedMultiplier;
if (moveTimer < currentMoveInterval) return;
moveTimer = 0;
// Update direction
if (nextDir.x !== -snakeDir.x || nextDir.y !== -snakeDir.y) {
snakeDir = {
x: nextDir.x,
y: nextDir.y
};
}
// Calculate new head position
var head = snake[0];
var newCol = head.col + snakeDir.x;
var newRow = head.row + snakeDir.y;
// Check collisions
if (isWall(newCol, newRow) || isSnake(newCol, newRow, false)) {
triggerGameOver();
return;
}
// Move snake
// Remove old head and replace with segment
var oldHead = snake[0];
var newSegment = new SnakeSegment();
newSegment.col = oldHead.col;
newSegment.row = oldHead.row;
newSegment.x = oldHead.x;
newSegment.y = oldHead.y;
game.addChild(newSegment);
snake[0].destroy();
snake[0] = newSegment;
// Add new head at the front
var newHead = new SnakeHead();
newHead.col = newCol;
newHead.row = newRow;
var pos = gridToPos(newCol, newRow);
newHead.x = pos.x;
newHead.y = pos.y;
// Set rotation based on direction (asset is upright, so rotate by +90deg for right)
if (snakeDir.x === 1 && snakeDir.y === 0) {
newHead.rotation = Math.PI / 2; // Right
} else if (snakeDir.x === -1 && snakeDir.y === 0) {
newHead.rotation = -Math.PI / 2; // Left
} else if (snakeDir.x === 0 && snakeDir.y === -1) {
newHead.rotation = 0; // Up
} else if (snakeDir.x === 0 && snakeDir.y === 1) {
newHead.rotation = Math.PI; // Down
}
game.addChild(newHead);
snake.unshift(newHead);
// Check food collision
var ateFood = false;
var eatenFood = null;
for (var f = 0; f < foods.length; f++) {
if (foods[f].col === newCol && foods[f].row === newRow) {
ateFood = true;
eatenFood = foods[f];
break;
}
}
if (ateFood) {
// Remove the eaten food from the foods array and destroy it
for (var i = 0; i < foods.length; i++) {
if (foods[i] === eatenFood) {
foods[i].destroy();
foods.splice(i, 1);
break;
}
}
// Calculate score based on food type and multiplier
var pointsToAdd;
if (eatenFood instanceof Food4) {
pointsToAdd = 1 * scoreMultiplier; // Food4 gives 1 point
} else {
pointsToAdd = 2 * scoreMultiplier; // Food and Food2 give 2 points
}
setScore(LK.getScore() + pointsToAdd);
pendingGrowth += 1;
// Play feeding sound
LK.getSound('feeding').play();
// Handle Food3 special effect - give health (max 3 lives) or bonus points if at max health
if (eatenFood instanceof Food3) {
if (playerLives < 3) {
playerLives++;
healthTxt.setText('♥ ' + playerLives);
} else {
// Player is at max health (3 lives), give 50 bonus points
setScore(LK.getScore() + 50);
}
}
// Handle Food2 special effect - 10x multiplier for 5 seconds
if (eatenFood instanceof Food2) {
if (!isMultiplierActive) {
isMultiplierActive = true;
scoreMultiplier = 10;
// Clear any existing multiplier timer
if (multiplierTimer) {
LK.clearTimeout(multiplierTimer);
}
// Remove multiplier after exactly 5 seconds
multiplierTimer = LK.setTimeout(function () {
scoreMultiplier = 1;
isMultiplierActive = false;
multiplierTimer = null;
}, 5000);
}
// Yellow background effect for 5 seconds
if (!isYellowBackgroundActive) {
isYellowBackgroundActive = true;
game.setBackgroundColor(0xFFFF00); // Yellow color
// Clear any existing yellow background timer
if (yellowBackgroundTimer) {
LK.clearTimeout(yellowBackgroundTimer);
}
// Restore original background after exactly 5 seconds
yellowBackgroundTimer = LK.setTimeout(function () {
game.setBackgroundColor(originalBackgroundColor);
isYellowBackgroundActive = false;
yellowBackgroundTimer = null;
}, 5000);
}
}
// Play feeding2 sound only when Food4 is eaten
if (eatenFood instanceof Food4) {
// Clear any associated timer since Food4 was eaten
for (var t = food4Timers.length - 1; t >= 0; t--) {
LK.clearTimeout(food4Timers[t]);
food4Timers.splice(t, 1);
}
LK.getSound('feeding2').play();
// Create darkness effect for 10 seconds
if (!isDarkened) {
isDarkened = true;
darknessOverlay = LK.getAsset('snakeSegment', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732,
tint: 0x000000
});
darknessOverlay.alpha = 0;
game.addChild(darknessOverlay);
// Fade in darkness
tween(darknessOverlay, {
alpha: 0.7
}, {
duration: 500
});
// Clear any existing darkness timer
if (darknessTimer) {
LK.clearTimeout(darknessTimer);
}
// Remove darkness after exactly 5 seconds
darknessTimer = LK.setTimeout(function () {
if (darknessOverlay) {
tween(darknessOverlay, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
if (darknessOverlay) {
darknessOverlay.destroy();
darknessOverlay = null;
}
isDarkened = false;
darknessTimer = null;
}
});
}
}, 5000);
}
// Reduce snake speed by 50% for 10 seconds
if (!isSlowed) {
isSlowed = true;
MOVE_INTERVAL = originalMoveInterval * 1.5; // 50% slower
// Clear any existing speed timer
if (speedTimer) {
LK.clearTimeout(speedTimer);
}
// Restore original speed after exactly 5 seconds
speedTimer = LK.setTimeout(function () {
MOVE_INTERVAL = originalMoveInterval;
isSlowed = false;
speedTimer = null;
}, 5000);
}
}
spawnFood();
// Win condition
if (snake.length >= SNAKE_MAX_LENGTH) {
triggerWin();
return;
}
}
// Remove tail if not growing
if (pendingGrowth > 0) {
pendingGrowth--;
} else {
var tail = snake.pop();
tail.destroy();
}
};
// --- Draw Board Grid Background ---
var gridBg = new Container();
for (var r = 0; r < GRID_ROWS; r++) {
for (var c = 0; c < GRID_COLS; c++) {
// Use a light color for grid lines, and a slightly darker fill for the cell
var cell = LK.getAsset('snakeSegment', {
anchorX: 0.5,
anchorY: 0.5,
x: BOARD_OFFSET_X + c * GRID_SIZE + GRID_SIZE / 2,
y: BOARD_OFFSET_Y + r * GRID_SIZE + GRID_SIZE / 2,
width: GRID_SIZE,
height: GRID_SIZE,
tint: (r + c) % 2 === 0 ? 0x232323 : 0x1a1a1a // subtle checkerboard for clarity
});
// Add a border by overlaying a slightly smaller cell of the same color as background
var border = LK.getAsset('snakeSegment', {
anchorX: 0.5,
anchorY: 0.5,
x: BOARD_OFFSET_X + c * GRID_SIZE + GRID_SIZE / 2,
y: BOARD_OFFSET_Y + r * GRID_SIZE + GRID_SIZE / 2,
width: GRID_SIZE - 8,
height: GRID_SIZE - 8,
tint: (r + c) % 2 === 0 ? 0x181818 : 0x181818 // match background
});
gridBg.addChild(cell);
gridBg.addChild(border);
}
}
game.addChild(gridBg);
// --- Game Initialization ---
createWalls();
createSnake();
spawnFood();
setScore(1);
;
a woman head. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
2d pixel art red brick wall. In-Game asset. 2d. High contrast. No shadows
2d pixel art snake skin. High contrast. No shadows
add 10x text to this pic
kalp şekli pixel art 2d. In-Game asset. High contrast. No shadows