/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var DirectionalButton = Container.expand(function (direction, assetId) {
var self = Container.call(this);
var graphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.direction = direction;
// Add visual feedback when pressed
self.down = function (x, y, obj) {
if (!isGameRunning) {
return;
}
// Prevent multiple rapid presses
if (self.isPressed) {
return;
}
self.isPressed = true;
// Visual feedback - make button slightly smaller
tween(self, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
// Change snake direction immediately
changeDirection(self.direction);
};
self.up = function (x, y, obj) {
// Reset pressed state
self.isPressed = false;
// Return button to normal size
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100
});
};
return self;
});
var EnemySnake = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemySnake', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xff4444; // Red tint for enemy
self.moveSpeed = 1.5;
self.direction = Math.random() * Math.PI * 2;
self.chaseTimer = 0;
self.isBeingRepelled = false;
self.repelTimer = 0;
self.update = function () {
// Check if being repelled by friendly snakes
self.isBeingRepelled = false;
for (var i = 0; i < friendlySnakes.length; i++) {
var friendlySnake = friendlySnakes[i];
var dist = Math.sqrt(Math.pow(self.x - friendlySnake.x, 2) + Math.pow(self.y - friendlySnake.y, 2));
if (dist < 150) {
// Repel range
self.isBeingRepelled = true;
// Move away from friendly snake
var angle = Math.atan2(self.y - friendlySnake.y, self.x - friendlySnake.x);
self.direction = angle;
break;
}
}
// If not being repelled, chase the player
if (!self.isBeingRepelled && snake.length > 0) {
self.chaseTimer++;
if (self.chaseTimer >= 30) {
// Update chase direction every 0.5 seconds
var playerHead = snake[0];
var playerScreenPos = gridToScreen(playerHead.x, playerHead.y);
var angle = Math.atan2(playerScreenPos.y - self.y, playerScreenPos.x - self.x);
self.direction = angle;
self.chaseTimer = 0;
}
}
// Move in current direction
self.x += Math.cos(self.direction) * self.moveSpeed;
self.y += Math.sin(self.direction) * self.moveSpeed;
// Bounce off walls
if (self.x < boardOffsetX || self.x > boardOffsetX + BOARD_WIDTH * GRID_SIZE) {
self.direction = Math.PI - self.direction;
}
if (self.y < boardOffsetY || self.y > boardOffsetY + BOARD_HEIGHT * GRID_SIZE) {
self.direction = -self.direction;
}
// Keep within bounds
self.x = Math.max(boardOffsetX, Math.min(boardOffsetX + BOARD_WIDTH * GRID_SIZE, self.x));
self.y = Math.max(boardOffsetY, Math.min(boardOffsetY + BOARD_HEIGHT * GRID_SIZE, self.y));
};
return self;
});
var Food = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Simple pulsing animation
var scale = 1 + Math.sin(LK.ticks * 0.1) * 0.1;
self.scaleX = scale;
self.scaleY = scale;
};
return self;
});
var FriendlySnake = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('friendlySnake', {
anchorX: 0.5,
anchorY: 0.5
});
self.moveSpeed = 2;
self.direction = Math.random() * Math.PI * 2;
self.dropTimer = 0;
self.maxDrops = 3;
self.dropsLeft = 3;
self.hasKilledEnemy = false;
self.update = function () {
// Check for nearby enemy snakes to chase
var isChasing = false;
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemySnakes.length; i++) {
var enemySnake = enemySnakes[i];
var dist = Math.sqrt(Math.pow(self.x - enemySnake.x, 2) + Math.pow(self.y - enemySnake.y, 2));
if (dist < 200) {
// Detection range - notify GIGACHAD!
if (!gigaChadNotified && enemySnakes.length > 0) {
gigaChadNotified = true;
if (!gigaChad) {
gigaChad = game.addChild(new GigaChad());
}
gigaChad.summon();
}
if (dist < closestDistance) {
closestDistance = dist;
closestEnemy = enemySnake;
isChasing = true;
}
}
}
// If chasing an enemy, move towards it
if (isChasing && closestEnemy) {
var angle = Math.atan2(closestEnemy.y - self.y, closestEnemy.x - self.x);
self.direction = angle;
self.moveSpeed = 3; // Increase speed when chasing
} else {
self.moveSpeed = 2; // Normal speed when not chasing
}
// Move in current direction
self.x += Math.cos(self.direction) * self.moveSpeed;
self.y += Math.sin(self.direction) * self.moveSpeed;
// Check if we killed an enemy snake
for (var i = enemySnakes.length - 1; i >= 0; i--) {
var enemySnake = enemySnakes[i];
var dist = Math.sqrt(Math.pow(self.x - enemySnake.x, 2) + Math.pow(self.y - enemySnake.y, 2));
if (dist < 50) {
// Kill range
// Enemy snake is killed
enemySnake.destroy();
enemySnakes.splice(i, 1);
var coins = storage.coins || 0;
storage.coins = coins + 100; // Bonus for friendly snake killing enemy
scoreTxt.setText('SCOİN: ' + storage.coins);
// Mark this friendly snake as having killed an enemy
self.hasKilledEnemy = true;
break;
}
}
// Bounce off walls
if (self.x < boardOffsetX || self.x > boardOffsetX + BOARD_WIDTH * GRID_SIZE) {
self.direction = Math.PI - self.direction;
}
if (self.y < boardOffsetY || self.y > boardOffsetY + BOARD_HEIGHT * GRID_SIZE) {
self.direction = -self.direction;
}
// Keep within bounds
self.x = Math.max(boardOffsetX, Math.min(boardOffsetX + BOARD_WIDTH * GRID_SIZE, self.x));
self.y = Math.max(boardOffsetY, Math.min(boardOffsetY + BOARD_HEIGHT * GRID_SIZE, self.y));
// Drop food periodically
self.dropTimer++;
if (self.dropTimer >= 120 && self.dropsLeft > 0) {
// Every 2 seconds
self.dropFood();
self.dropTimer = 0;
self.dropsLeft--;
}
// Remove after all drops are done - but only if hasn't killed an enemy
if (self.dropsLeft <= 0 && !self.hasKilledEnemy) {
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
var index = friendlySnakes.indexOf(self);
if (index > -1) {
friendlySnakes.splice(index, 1);
}
}
});
}
};
self.dropFood = function () {
var dropType = Math.random() < 0.3 ? 'level2' : 'normal';
var newFood;
if (dropType === 'level2') {
newFood = game.addChild(new Level2Food());
} else {
newFood = game.addChild(new Food());
}
// Convert screen position to grid position and clamp to board bounds
var gridX = Math.round((self.x - boardOffsetX) / GRID_SIZE);
var gridY = Math.round((self.y - boardOffsetY) / GRID_SIZE);
// Ensure food stays within board bounds
gridX = Math.max(0, Math.min(BOARD_WIDTH - 1, gridX));
gridY = Math.max(0, Math.min(BOARD_HEIGHT - 1, gridY));
// Set food position based on clamped grid coordinates
var screenPos = gridToScreen(gridX, gridY);
newFood.x = screenPos.x;
newFood.y = screenPos.y;
newFood.gridX = gridX;
newFood.gridY = gridY;
// Add to food array
foodItems.push(newFood);
};
return self;
});
var GigaChad = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('gigachad', {
anchorX: 0.5,
anchorY: 0.5
});
self.isActive = false;
self.destroyTimer = 0;
self.destroyDelay = shopEffects.speedGigachad.active ? 180 : 600; // 3 seconds if fast, 10 seconds normal
self.summon = function () {
if (self.isActive) {
return;
} // Already summoned
self.isActive = true;
self.destroyTimer = 0;
// Position GIGACHAD at center of board
self.x = boardOffsetX + BOARD_WIDTH * GRID_SIZE / 2;
self.y = boardOffsetY + BOARD_HEIGHT * GRID_SIZE / 2;
// Make GIGACHAD appear with dramatic effect
self.alpha = 0;
self.scaleX = 0.1;
self.scaleY = 0.1;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.bounceOut
});
// Play summon sound
LK.getSound('gigachadSummon').play();
// Flash screen to show GIGACHAD arrival
LK.effects.flashScreen(0xFFD700, 1000); // Gold flash
// Destroy all enemies immediately upon arrival
self.destroyAllEnemies();
};
self.update = function () {
if (!self.isActive) {
return;
}
self.destroyTimer++;
// Update destroy delay based on current speed effect status
self.destroyDelay = shopEffects.speedGigachad.active ? 180 : 600;
// Pulsing effect while active
var pulse = 1 + Math.sin(LK.ticks * 0.2) * 0.1;
self.scaleX = pulse;
self.scaleY = pulse;
// Auto-deactivate after completing task (enemies destroyed immediately on summon)
if (self.destroyTimer >= self.destroyDelay) {
// GIGACHAD disappears after completing his task
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isActive = false;
self.destroyTimer = 0;
}
});
}
};
self.destroyAllEnemies = function () {
// Destroy all enemy snakes immediately
for (var i = enemySnakes.length - 1; i >= 0; i--) {
var enemySnake = enemySnakes[i];
// Flash enemy red before destroying
tween(enemySnake, {
tint: 0xFF0000,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
enemySnake.destroy();
}
});
}
// Clear enemy snakes array
enemySnakes = [];
// Play destroy sound
LK.getSound('gigachadDestroy').play();
// Flash screen red for destruction
LK.effects.flashScreen(0xFF0000, 1500);
// Add massive score bonus
var coins = storage.coins || 0;
storage.coins = coins + 500;
scoreTxt.setText('SCOİN: ' + storage.coins);
};
return self;
});
var Level2Food = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('level2Food', {
anchorX: 0.5,
anchorY: 0.5
});
self.foodType = 'level2';
self.update = function () {
// Special pulsing animation for level 2 food
var scale = 1 + Math.sin(LK.ticks * 0.15) * 0.3;
self.scaleX = scale;
self.scaleY = scale;
// Color cycling effect
var colorPhase = LK.ticks * 0.1 % (Math.PI * 2);
var red = Math.floor(128 + 127 * Math.sin(colorPhase));
var green = Math.floor(128 + 127 * Math.sin(colorPhase + Math.PI * 2 / 3));
var blue = Math.floor(128 + 127 * Math.sin(colorPhase + Math.PI * 4 / 3));
graphics.tint = red << 16 | green << 8 | blue;
};
return self;
});
var Merchant = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('friendlySnake', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Gold tint for merchant
// Shop items with costs
self.shopItems = {
slowdown: {
cost: 150,
name: "Slowdown (5min)",
active: false,
duration: 18000
},
// 5 minutes at 60fps
speedGigachad: {
cost: 200,
name: "Fast GIGACHAD",
active: false
},
doublePoints: {
cost: 200,
name: "2x Points",
active: false,
duration: 18000
} // 5 minutes at 60fps
};
self.down = function (x, y, obj) {
self.openShop();
};
self.openShop = function () {
// Simple shop interface using score display
var playerCoins = storage.coins || 0;
var shopText = "SHOP (SCOİN: " + playerCoins + ")\n";
shopText += "1. Slowdown 5min - " + self.shopItems.slowdown.cost + " SCOİN\n";
shopText += "2. Fast GIGACHAD - " + self.shopItems.speedGigachad.cost + " SCOİN\n";
shopText += "3. 2x Points - " + self.shopItems.doublePoints.cost + " SCOİN";
// Flash screen to show shop is open
LK.effects.flashScreen(0xFFD700, 500);
};
return self;
});
var ShopButton = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x4CAF50; // Green tint for shop button
self.down = function (x, y, obj) {
if (!isGameRunning && !isShopOpen) {
return;
}
// Visual feedback - make button slightly smaller
tween(self, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
// Toggle shop - close if open, open if closed
if (isShopOpen) {
self.closeShop();
} else {
isShopOpen = true;
self.openFullScreenShop();
}
};
self.up = function (x, y, obj) {
// Return button to normal size
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100
});
};
self.openFullScreenShop = function () {
// Create full-screen shop container
shopContainer = new Container();
shopContainer.x = 0;
shopContainer.y = 0;
game.addChild(shopContainer);
// Create semi-transparent background
var shopBg = LK.getAsset('shopButton', {
anchorX: 0,
anchorY: 0,
scaleX: 10.24,
scaleY: 34.15
});
shopBg.tint = 0x000000;
shopBg.alpha = 0.8;
shopContainer.addChild(shopBg);
// Get player coins
var playerCoins = storage.coins || 0;
// Create shop title
var titleText = new Text2('SHOP', {
size: 150,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
shopContainer.addChild(titleText);
// Create coins display
var coinsText = new Text2('SCOİN: ' + playerCoins, {
size: 80,
fill: 0xFFFFFF
});
coinsText.anchor.set(0.5, 0.5);
coinsText.x = 1024;
coinsText.y = 450;
shopContainer.addChild(coinsText);
// Create shop items
var yPos = 700;
var itemSpacing = 200;
// Slowdown item
var slowdownButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5
});
slowdownButton.x = 1024;
slowdownButton.y = yPos;
slowdownButton.tint = playerCoins >= 150 ? 0x4CAF50 : 0x666666;
shopContainer.addChild(slowdownButton);
var slowdownText = new Text2('Fast GIGACHAD - 200 SCOİN', {
size: 50,
fill: 0xFFFFFF
});
slowdownText.anchor.set(0.5, 0.5);
slowdownText.x = 1024;
slowdownText.y = yPos;
shopContainer.addChild(slowdownText);
// Speed GIGACHAD item
yPos += itemSpacing;
var speedButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5
});
speedButton.x = 1024;
speedButton.y = yPos;
speedButton.tint = playerCoins >= 200 ? 0x4CAF50 : 0x666666;
shopContainer.addChild(speedButton);
var speedText = new Text2('Slowdown 5min - 150 SCOİN', {
size: 50,
fill: 0xFFFFFF
});
speedText.anchor.set(0.5, 0.5);
speedText.x = 1024;
speedText.y = yPos;
shopContainer.addChild(speedText);
// Double Points item
yPos += itemSpacing;
var doubleButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5
});
doubleButton.x = 1024;
doubleButton.y = yPos;
doubleButton.tint = playerCoins >= 200 ? 0x4CAF50 : 0x666666;
shopContainer.addChild(doubleButton);
var doubleText = new Text2('2x Points - 200 SCOİN', {
size: 50,
fill: 0xFFFFFF
});
doubleText.anchor.set(0.5, 0.5);
doubleText.x = 1024;
doubleText.y = yPos;
shopContainer.addChild(doubleText);
// Add shop interaction handlers
self.handleShopClick = function (x, y) {
// Get fresh coin data for purchase validation
var currentCoins = storage.coins || 0;
// Check which button was clicked using global coordinates
var itemY = 700;
// First button (Fast GIGACHAD) - covers almost entire screen width
if (Math.abs(y - itemY) < 100) {
if (currentCoins >= 200) {
// Initialize inventory if needed
if (!storage.inventory) {
storage.inventory = {
slowdown: 0,
speedGigachad: 0,
doublePoints: 0
};
}
// Complete the purchase
storage.coins = currentCoins - 200;
storage.inventory.speedGigachad = (storage.inventory.speedGigachad || 0) + 1;
// Close shop and give feedback
self.closeShop();
LK.effects.flashScreen(0x4CAF50, 500); // Green flash for successful purchase
} else {
// Not enough coins - red flash
LK.effects.flashScreen(0xFF0000, 300);
}
return;
}
// Second button (Slowdown)
itemY += 200;
if (Math.abs(y - itemY) < 100) {
if (currentCoins >= 150) {
// Initialize inventory if needed
if (!storage.inventory) {
storage.inventory = {
slowdown: 0,
speedGigachad: 0,
doublePoints: 0
};
}
// Complete the purchase
storage.coins = currentCoins - 150;
storage.inventory.slowdown = (storage.inventory.slowdown || 0) + 1;
// Close shop and give feedback
self.closeShop();
LK.effects.flashScreen(0x4CAF50, 500); // Green flash for successful purchase
} else {
// Not enough coins - red flash
LK.effects.flashScreen(0xFF0000, 300);
}
return;
}
// Double points button
itemY += 200;
if (Math.abs(y - itemY) < 100) {
if (currentCoins >= 200) {
// Initialize inventory if needed
if (!storage.inventory) {
storage.inventory = {
slowdown: 0,
speedGigachad: 0,
doublePoints: 0
};
}
// Complete the purchase
storage.coins = currentCoins - 200;
storage.inventory.doublePoints = (storage.inventory.doublePoints || 0) + 1;
// Close shop and give feedback
self.closeShop();
LK.effects.flashScreen(0x4CAF50, 500); // Green flash for successful purchase
} else {
// Not enough coins - red flash
LK.effects.flashScreen(0xFF0000, 300);
}
return;
}
};
self.closeShop = function () {
isShopOpen = false;
isGameRunning = true; // Resume game when closing shop
if (shopContainer) {
shopContainer.destroy();
shopContainer = null;
}
};
};
return self;
});
var Snake = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game configuration
var GRID_SIZE = 40;
var BOARD_WIDTH = 35;
var BOARD_HEIGHT = 50;
var MOVE_DELAY = 8; // Frames between moves
// Game state variables
var snake = [];
var snakeDirection = {
x: 1,
y: 0
};
var nextDirection = {
x: 1,
y: 0
};
var food = null;
var moveCounter = 0;
var isGameRunning = true;
var friendlySnakes = [];
var friendlySnakeTimer = 0;
var enemySnakes = [];
var enemySnakeTimer = 0;
var foodItems = [];
var gigaChad = null;
var gigaChadNotified = false;
var shopButton = null;
var shopEffects = {
slowdown: {
active: false,
timer: 0
},
speedGigachad: {
active: false
},
doublePoints: {
active: false,
timer: 0
}
};
var isShopOpen = false;
var shopContainer = null;
// Calculate board position to center it horizontally and leave space for controls at bottom
var boardOffsetX = (2048 - BOARD_WIDTH * GRID_SIZE) / 2;
var boardOffsetY = 100; // Start closer to top, leaving space for controls at bottom
// Initialize SCOİN storage
if (!storage.coins) {
storage.coins = 0;
}
// Create score display
var scoreTxt = new Text2('SCOİN: 0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
// Position score centered horizontally and at control buttons level
scoreTxt.x = 2048 / 2; // Center horizontally
scoreTxt.y = 2732 - 300; // Same vertical level as control buttons
game.addChild(scoreTxt);
// Create power-up timer display
var powerUpTimerTxt = new Text2('', {
size: 60,
fill: 0x4CAF50
});
powerUpTimerTxt.anchor.set(0.5, 0.5);
powerUpTimerTxt.x = 2048 / 2; // Center horizontally
powerUpTimerTxt.y = 2732 - 200; // Above score display (score is at 2732 - 300, so this puts timer above it)
game.addChild(powerUpTimerTxt);
// Initialize snake with only head
function initializeSnake() {
snake = [];
// Clear death flag when starting fresh game
hadPowerUpsOnDeath = false;
// Create head using new Snake class
var head = game.addChild(new Snake());
head.x = boardOffsetX + 12 * GRID_SIZE;
head.y = boardOffsetY + 25 * GRID_SIZE;
snake.push({
x: 12,
y: 25,
obj: head
});
}
// Convert grid coordinates to screen coordinates
function gridToScreen(gridX, gridY) {
return {
x: boardOffsetX + gridX * GRID_SIZE,
y: boardOffsetY + gridY * GRID_SIZE
};
}
// Generate random food position
function spawnFood() {
var validPositions = [];
// Find all valid positions (not occupied by snake)
for (var x = 0; x < BOARD_WIDTH; x++) {
for (var y = 0; y < BOARD_HEIGHT; y++) {
var occupied = false;
for (var i = 0; i < snake.length; i++) {
if (snake[i].x === x && snake[i].y === y) {
occupied = true;
break;
}
}
if (!occupied) {
validPositions.push({
x: x,
y: y
});
}
}
}
if (validPositions.length > 0) {
var randomIndex = Math.floor(Math.random() * validPositions.length);
var pos = validPositions[randomIndex];
if (food) {
food.destroy();
}
food = game.addChild(new Food());
var screenPos = gridToScreen(pos.x, pos.y);
food.x = screenPos.x;
food.y = screenPos.y;
food.gridX = pos.x;
food.gridY = pos.y;
foodItems.push(food);
}
}
// Check if position is valid (within bounds and not colliding with snake)
function isValidPosition(x, y) {
// Check bounds
if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) {
return false;
}
// No body collision check needed for head-only snake
return true;
}
// Move snake
function moveSnake() {
if (!isGameRunning) {
return;
}
// Direction is already updated immediately in changeDirection function
// Calculate new head position
var head = snake[0];
var newHeadX = head.x + snakeDirection.x;
var newHeadY = head.y + snakeDirection.y;
// Check if new position is valid
if (!isValidPosition(newHeadX, newHeadY)) {
// Game over
isGameRunning = false;
// Check if player had any active power-ups before clearing them
hadPowerUpsOnDeath = shopEffects.slowdown.active || shopEffects.speedGigachad.active || shopEffects.doublePoints.active;
// Clear all active power-up effects on death
shopEffects.slowdown.active = false;
shopEffects.slowdown.timer = 0;
shopEffects.speedGigachad.active = false;
shopEffects.doublePoints.active = false;
shopEffects.doublePoints.timer = 0;
LK.getSound('gameOver').play();
LK.showGameOver();
return;
}
// Check if any food is eaten
var ateFood = false;
for (var i = foodItems.length - 1; i >= 0; i--) {
var currentFood = foodItems[i];
if (currentFood && newHeadX === currentFood.gridX && newHeadY === currentFood.gridY) {
ateFood = true;
var baseCoins = currentFood.foodType === 'level2' ? 50 : 10;
var coinGain = baseCoins;
// Apply double points effect if active
if (shopEffects.doublePoints.active) {
coinGain = baseCoins * 2;
}
var coins = storage.coins || 0;
storage.coins = coins + coinGain;
scoreTxt.setText('SCOİN: ' + storage.coins);
LK.getSound('eat').play();
// Flash food before removing
tween(currentFood, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
currentFood.destroy();
}
});
foodItems.splice(i, 1);
if (currentFood === food) {
food = null;
spawnFood();
}
break;
}
}
// Simply move the existing head to new position (no growth)
var headObj = snake[0].obj;
var screenPos = gridToScreen(newHeadX, newHeadY);
headObj.x = screenPos.x;
headObj.y = screenPos.y;
snake[0].x = newHeadX;
snake[0].y = newHeadY;
}
// Handle direction changes
function changeDirection(newDir) {
// Prevent immediate reversal
if (newDir.x === -snakeDirection.x && newDir.y === -snakeDirection.y) {
return;
}
// Prevent diagonal movement - only allow pure horizontal or vertical movement
if (newDir.x !== 0 && newDir.y !== 0) {
return;
}
// Apply direction change immediately for responsive controls
snakeDirection.x = newDir.x;
snakeDirection.y = newDir.y;
nextDirection.x = newDir.x;
nextDirection.y = newDir.y;
}
// Remove touch controls - snake can only be controlled by buttons
// Create visible walls around the game board
function createWalls() {
// Top and bottom walls
for (var x = -1; x <= BOARD_WIDTH; x++) {
// Top wall
var topWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
topWall.x = boardOffsetX + x * GRID_SIZE;
topWall.y = boardOffsetY - GRID_SIZE;
// Bottom wall
var bottomWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
bottomWall.x = boardOffsetX + x * GRID_SIZE;
bottomWall.y = boardOffsetY + BOARD_HEIGHT * GRID_SIZE;
}
// Left and right walls
for (var y = -1; y <= BOARD_HEIGHT; y++) {
// Left wall
var leftWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
leftWall.x = boardOffsetX - GRID_SIZE;
leftWall.y = boardOffsetY + y * GRID_SIZE;
// Right wall
var rightWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
rightWall.x = boardOffsetX + BOARD_WIDTH * GRID_SIZE;
rightWall.y = boardOffsetY + y * GRID_SIZE;
}
}
// Create directional control buttons
var upButton = game.addChild(new DirectionalButton({
x: 0,
y: -1
}, 'upButton'));
var downButton = game.addChild(new DirectionalButton({
x: 0,
y: 1
}, 'downButton'));
var leftButton = game.addChild(new DirectionalButton({
x: -1,
y: 0
}, 'leftButton'));
var rightButton = game.addChild(new DirectionalButton({
x: 1,
y: 0
}, 'rightButton'));
// Position buttons in bottom right corner
var buttonSpacing = 140;
var buttonBaseX = 2048 - 200;
var buttonBaseY = 2732 - 300;
// Up button
upButton.x = buttonBaseX;
upButton.y = buttonBaseY - buttonSpacing;
// Down button
downButton.x = buttonBaseX;
downButton.y = buttonBaseY + buttonSpacing;
// Left button
leftButton.x = buttonBaseX - buttonSpacing;
leftButton.y = buttonBaseY;
// Right button
rightButton.x = buttonBaseX + buttonSpacing;
rightButton.y = buttonBaseY;
// Add text labels to buttons
var upText = new Text2('↑', {
size: 60,
fill: 0xFFFFFF
});
upText.anchor.set(0.5, 0.5);
upText.x = upButton.x;
upText.y = upButton.y;
game.addChild(upText);
var downText = new Text2('↓', {
size: 60,
fill: 0xFFFFFF
});
downText.anchor.set(0.5, 0.5);
downText.x = downButton.x;
downText.y = downButton.y;
game.addChild(downText);
var leftText = new Text2('←', {
size: 60,
fill: 0xFFFFFF
});
leftText.anchor.set(0.5, 0.5);
leftText.x = leftButton.x;
leftText.y = leftButton.y;
game.addChild(leftText);
var rightText = new Text2('→', {
size: 60,
fill: 0xFFFFFF
});
rightText.anchor.set(0.5, 0.5);
rightText.x = rightButton.x;
rightText.y = rightButton.y;
game.addChild(rightText);
// Initialize game
createWalls();
initializeSnake();
spawnFood();
// Game mode variables
var gameMode = 'menu'; // 'menu', 'normal'
// Create main menu overlay
var instructionContainer = new Container();
instructionContainer.x = 0;
instructionContainer.y = 0;
game.addChild(instructionContainer);
// Create semi-transparent background
var instructionBg = LK.getAsset('shopButton', {
anchorX: 0,
anchorY: 0,
scaleX: 10.24,
scaleY: 34.15
});
instructionBg.tint = 0x000000;
instructionBg.alpha = 0.9;
instructionContainer.addChild(instructionBg);
// Create main menu title
var menuTitle = new Text2('THE SNAKE GAME 2077', {
size: 150,
fill: 0xFFD700
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 300;
instructionContainer.addChild(menuTitle);
// Create game mode buttons
var normalModeButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.0
});
normalModeButton.x = 1024;
normalModeButton.y = 800;
normalModeButton.tint = 0x4CAF50;
instructionContainer.addChild(normalModeButton);
var normalModeText = new Text2('NORMAL MODE', {
size: 60,
fill: 0xFFFFFF
});
normalModeText.anchor.set(0.5, 0.5);
normalModeText.x = normalModeButton.x;
normalModeText.y = normalModeButton.y;
instructionContainer.addChild(normalModeText);
var otherButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.0
});
otherButton.x = 1024;
otherButton.y = 1000;
otherButton.tint = 0xFF5722;
instructionContainer.addChild(otherButton);
var otherText = new Text2('DİE', {
size: 60,
fill: 0xFFFFFF
});
otherText.anchor.set(0.5, 0.5);
otherText.x = otherButton.x;
otherText.y = otherButton.y;
instructionContainer.addChild(otherText);
// How to play button
var howToPlayButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.8
});
howToPlayButton.x = 1024;
howToPlayButton.y = 1200;
howToPlayButton.tint = 0x9C27B0;
instructionContainer.addChild(howToPlayButton);
var howToPlayText = new Text2('HOW TO PLAY', {
size: 50,
fill: 0xFFFFFF
});
howToPlayText.anchor.set(0.5, 0.5);
howToPlayText.x = howToPlayButton.x;
howToPlayText.y = howToPlayButton.y;
instructionContainer.addChild(howToPlayText);
// Pause game initially to show menu
isGameRunning = false;
gameMode = 'menu';
// Add click handler for menu
instructionContainer.down = function (x, y, obj) {
// Check normal mode button
if (Math.abs(x - normalModeButton.x) < 150 && Math.abs(y - normalModeButton.y) < 50) {
startNormalMode();
return;
}
// Check other button
if (Math.abs(x - otherButton.x) < 150 && Math.abs(y - otherButton.y) < 50) {
showOther();
return;
}
// Check how to play button
if (Math.abs(x - howToPlayButton.x) < 120 && Math.abs(y - howToPlayButton.y) < 40) {
showHowToPlayWithOther();
return;
}
};
// Function to start normal mode
function startNormalMode() {
gameMode = 'normal';
isCompetitionMode = false;
// Remove menu overlay
instructionContainer.destroy();
instructionContainer = null;
// Start the game
isGameRunning = true;
// Start background music
LK.playMusic('backgroundMusic');
// Flash screen green to indicate game start
LK.effects.flashScreen(0x4CAF50, 500);
}
// Function to show other
function showOther() {
// Flash screen to indicate URL opening attempt
LK.effects.flashScreen(0xFF5722, 500);
// Since window.open doesn't work in this environment, use LK.showGameOver as workaround
// In a real implementation, this would open https://upit.com/@karasavasci127
LK.showGameOver();
}
// Function to show how to play with other URL
function showHowToPlayWithOther() {
// Clear current menu content
while (instructionContainer.children.length > 0) {
instructionContainer.removeChild(instructionContainer.children[0]);
}
// Add background back
instructionContainer.addChild(instructionBg);
// Create instruction title
var instructionTitle = new Text2('HOW TO PLAY', {
size: 120,
fill: 0xFFD700
});
instructionTitle.anchor.set(0.5, 0.5);
instructionTitle.x = 1024;
instructionTitle.y = 300;
instructionContainer.addChild(instructionTitle);
// Create instruction text
var instructionText = new Text2('• Use arrow buttons to move snake\n• Collect food to earn SCOİN\n• Normal food = 10 SCOİN\n• Level 2 food = 50 SCOİN\n• Avoid red enemy snakes\n• Gold friendly snakes protect you\n• Buy power-ups in SHOP\n• Use items from INVENTORY', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 1100;
instructionContainer.addChild(instructionText);
// Create Other URL text below instructions
var otherUrlText = new Text2('Other: https://upit.com/@karasavasci127', {
size: 60,
fill: 0xFF5722
});
otherUrlText.anchor.set(0.5, 0.5);
otherUrlText.x = 1024;
otherUrlText.y = 1400;
instructionContainer.addChild(otherUrlText);
// Back to menu button
var backButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 0.8
});
backButton.x = 1024;
backButton.y = 1900;
backButton.tint = 0x666666;
instructionContainer.addChild(backButton);
var backText = new Text2('BACK TO MENU', {
size: 50,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = backButton.x;
backText.y = backButton.y;
instructionContainer.addChild(backText);
// Update click handler for back button and Other URL
instructionContainer.down = function (x, y, obj) {
if (Math.abs(x - backButton.x) < 100 && Math.abs(y - backButton.y) < 40) {
// Go back to main menu
LK.showGameOver(); // Reset game state and return to main menu
} else if (Math.abs(x - otherUrlText.x) < 300 && Math.abs(y - otherUrlText.y) < 50) {
// Other URL clicked
LK.effects.flashScreen(0xFF5722, 500);
LK.showGameOver();
}
};
}
// Don't start background music immediately - wait for instructions to be dismissed
// Merchant functionality is handled through the shop button
// Create shop button at bottom center
shopButton = game.addChild(new ShopButton());
shopButton.x = 2048 / 2; // Center horizontally
shopButton.y = 2732 - 150; // Bottom of screen with some margin
// Add shop button text
var shopText = new Text2('SHOP', {
size: 40,
fill: 0xFFFFFF
});
shopText.anchor.set(0.5, 0.5);
shopText.x = shopButton.x;
shopText.y = shopButton.y;
game.addChild(shopText);
// Create hidden money cheat button above score
var hiddenMoneyButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.3
});
hiddenMoneyButton.x = scoreTxt.x; // Same x position as score
hiddenMoneyButton.y = scoreTxt.y - 100; // 100 pixels above score
hiddenMoneyButton.alpha = 0; // Make completely invisible
game.addChild(hiddenMoneyButton);
// Add click handler for hidden money button
hiddenMoneyButton.down = function (x, y, obj) {
// Give player 999999 coins
storage.coins = (storage.coins || 0) + 999999;
// Flash screen gold to indicate cheat activated
LK.effects.flashScreen(0xFFD700, 500);
};
// Initialize inventory storage
if (!storage.inventory) {
storage.inventory = {
slowdown: 0,
speedGigachad: 0,
doublePoints: 0
};
}
// Create inventory button
var inventoryButton = game.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
}));
inventoryButton.x = 2048 / 2 - 250; // Left of shop button
inventoryButton.y = 2732 - 150; // Same level as shop button
inventoryButton.tint = 0x9C27B0; // Purple tint for inventory
// Add inventory button text
var inventoryText = new Text2('INVENTORY', {
size: 30,
fill: 0xFFFFFF
});
inventoryText.anchor.set(0.5, 0.5);
inventoryText.x = inventoryButton.x;
inventoryText.y = inventoryButton.y;
game.addChild(inventoryText);
// Create reset SCOİN button
var resetCoinButton = game.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
}));
resetCoinButton.x = 2048 / 2 + 250; // Right of shop button (opposite side from inventory)
resetCoinButton.y = 2732 - 150; // Same level as shop and inventory buttons
resetCoinButton.tint = 0xFF5722; // Orange-red tint for reset button
// Add reset button text
var resetText = new Text2('RESET SCOİN', {
size: 30,
fill: 0xFFFFFF
});
resetText.anchor.set(0.5, 0.5);
resetText.x = resetCoinButton.x;
resetText.y = resetCoinButton.y;
game.addChild(resetText);
// Add reset button functionality
resetCoinButton.down = function (x, y, obj) {
// Visual feedback
tween(resetCoinButton, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100
});
// Reset SCOİN to 0
storage.coins = 0;
// Update score display
scoreTxt.setText('SCOİN: ' + storage.coins);
// Flash screen orange to indicate reset
LK.effects.flashScreen(0xFF5722, 500);
};
resetCoinButton.up = function (x, y, obj) {
// Return to normal size
tween(resetCoinButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
};
// Inventory system variables
var isInventoryOpen = false;
var inventoryContainer = null;
var hadPowerUpsOnDeath = false; // Track if player had power-ups when they died
// Add inventory button functionality
inventoryButton.down = function (x, y, obj) {
// Visual feedback
tween(inventoryButton, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100
});
// Toggle inventory
if (isInventoryOpen) {
closeInventory();
} else {
openInventory();
}
};
inventoryButton.up = function (x, y, obj) {
// Return to normal size
tween(inventoryButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
};
// Function to open inventory
function openInventory() {
if (isInventoryOpen) {
return;
}
isInventoryOpen = true;
isGameRunning = false; // Pause game
// Create inventory container
inventoryContainer = new Container();
inventoryContainer.x = 0;
inventoryContainer.y = 0;
game.addChild(inventoryContainer);
// Create background
var inventoryBg = LK.getAsset('shopButton', {
anchorX: 0,
anchorY: 0,
scaleX: 10.24,
scaleY: 34.15
});
inventoryBg.tint = 0x2E2E2E;
inventoryBg.alpha = 0.9;
inventoryContainer.addChild(inventoryBg);
// Title
var titleText = new Text2('INVENTORY', {
size: 120,
fill: 0x9C27B0
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
inventoryContainer.addChild(titleText);
// Current coins display
var coinsText = new Text2('SCOİN: ' + (storage.coins || 0), {
size: 60,
fill: 0xFFD700
});
coinsText.anchor.set(0.5, 0.5);
coinsText.x = 1024;
coinsText.y = 400;
inventoryContainer.addChild(coinsText);
// Inventory items section - GIGACHAD at top center, others below
var yPos = 500;
var itemSpacing = 120;
// Fast GIGACHAD items - positioned at top center
var speedGigachadCount = storage.inventory.speedGigachad || 0;
var speedStatusText = shopEffects.speedGigachad.active ? ' (ACTIVE)' : '';
var speedItemText = new Text2('Fast GIGACHAD: ' + speedGigachadCount + speedStatusText, {
size: 60,
fill: shopEffects.speedGigachad.active ? 0x4CAF50 : 0xFFFFFF
});
speedItemText.anchor.set(0.5, 0.5);
speedItemText.x = 1024; // Centered
speedItemText.y = yPos;
inventoryContainer.addChild(speedItemText);
// Use button for speed GIGACHAD - centered below text
if (speedGigachadCount > 0 && !shopEffects.speedGigachad.active && !hadPowerUpsOnDeath) {
var useSpeedBtn = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
useSpeedBtn.x = 1024; // Centered
useSpeedBtn.y = yPos + 60;
useSpeedBtn.tint = 0x4CAF50;
inventoryContainer.addChild(useSpeedBtn);
var useSpeedText = new Text2('USE', {
size: 40,
fill: 0xFFFFFF
});
useSpeedText.anchor.set(0.5, 0.5);
useSpeedText.x = useSpeedBtn.x;
useSpeedText.y = useSpeedBtn.y;
inventoryContainer.addChild(useSpeedText);
}
// Slowdown items - positioned below GIGACHAD
yPos += itemSpacing + 60;
var slowdownCount = storage.inventory.slowdown || 0;
var slowdownStatusText = shopEffects.slowdown.active ? ' (ACTIVE - ' + Math.ceil((18000 - shopEffects.slowdown.timer) / 60) + 's left)' : '';
var slowdownItemText = new Text2('Slowdown 5min: ' + slowdownCount + slowdownStatusText, {
size: 45,
fill: shopEffects.slowdown.active ? 0x4CAF50 : 0xFFFFFF
});
slowdownItemText.anchor.set(0.5, 0.5);
slowdownItemText.x = 1024;
slowdownItemText.y = yPos;
inventoryContainer.addChild(slowdownItemText);
// Use button for slowdown
if (slowdownCount > 0 && !shopEffects.slowdown.active && !hadPowerUpsOnDeath) {
var useSlowdownBtn = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
useSlowdownBtn.x = 1024 + 200;
useSlowdownBtn.y = yPos;
useSlowdownBtn.tint = 0x4CAF50;
inventoryContainer.addChild(useSlowdownBtn);
var useSlowdownText = new Text2('USE', {
size: 30,
fill: 0xFFFFFF
});
useSlowdownText.anchor.set(0.5, 0.5);
useSlowdownText.x = useSlowdownBtn.x;
useSlowdownText.y = useSlowdownBtn.y;
inventoryContainer.addChild(useSlowdownText);
}
// Double Points items - positioned at bottom
yPos += itemSpacing;
var doublePointsCount = storage.inventory.doublePoints || 0;
var doubleStatusText = shopEffects.doublePoints.active ? ' (ACTIVE - ' + Math.ceil((18000 - shopEffects.doublePoints.timer) / 60) + 's left)' : '';
var doubleItemText = new Text2('2x Points: ' + doublePointsCount + doubleStatusText, {
size: 45,
fill: shopEffects.doublePoints.active ? 0x4CAF50 : 0xFFFFFF
});
doubleItemText.anchor.set(0.5, 0.5);
doubleItemText.x = 1024;
doubleItemText.y = yPos;
inventoryContainer.addChild(doubleItemText);
// Use button for double points
if (doublePointsCount > 0 && !shopEffects.doublePoints.active && !hadPowerUpsOnDeath) {
var useDoubleBtn = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
useDoubleBtn.x = 1024 + 200;
useDoubleBtn.y = yPos;
useDoubleBtn.tint = 0x4CAF50;
inventoryContainer.addChild(useDoubleBtn);
var useDoubleText = new Text2('USE', {
size: 30,
fill: 0xFFFFFF
});
useDoubleText.anchor.set(0.5, 0.5);
useDoubleText.x = useDoubleBtn.x;
useDoubleText.y = useDoubleBtn.y;
inventoryContainer.addChild(useDoubleText);
}
}
// Function to close inventory
function closeInventory() {
isInventoryOpen = false;
isGameRunning = true;
if (inventoryContainer) {
inventoryContainer.destroy();
inventoryContainer = null;
}
}
// Function to handle inventory item usage
function handleInventoryClick(x, y) {
if (!isInventoryOpen || !inventoryContainer) {
return;
}
// Prevent using inventory if player had power-ups when they died
if (hadPowerUpsOnDeath) {
LK.effects.flashScreen(0xFF0000, 300); // Red flash to indicate blocked
return;
}
// Check use buttons with correct global positions
var itemSpacing = 120;
var startY = 500;
// Speed GIGACHAD use button - centered at top (1024, startY + 60)
var speedButtonX = 1024;
var speedButtonY = startY + 60;
if (Math.abs(x - speedButtonX) < 150 && Math.abs(y - speedButtonY) < 50) {
if ((storage.inventory.speedGigachad || 0) > 0 && !shopEffects.speedGigachad.active) {
storage.inventory.speedGigachad--;
shopEffects.speedGigachad.active = true;
// Save inventory changes
storage.inventory = {
slowdown: storage.inventory.slowdown || 0,
speedGigachad: storage.inventory.speedGigachad,
doublePoints: storage.inventory.doublePoints || 0
};
closeInventory();
LK.effects.flashScreen(0x4CAF50, 500);
}
return;
}
// Slowdown use button - positioned at (1024 + 200, slowdownY)
var slowdownY = startY + itemSpacing + 60;
var slowdownButtonX = 1024 + 200;
if (Math.abs(x - slowdownButtonX) < 100 && Math.abs(y - slowdownY) < 50) {
if ((storage.inventory.slowdown || 0) > 0 && !shopEffects.slowdown.active) {
storage.inventory.slowdown--;
shopEffects.slowdown.active = true;
shopEffects.slowdown.timer = 0;
// Save inventory changes
storage.inventory = {
slowdown: storage.inventory.slowdown,
speedGigachad: storage.inventory.speedGigachad || 0,
doublePoints: storage.inventory.doublePoints || 0
};
closeInventory();
LK.effects.flashScreen(0x4CAF50, 500);
}
return;
}
// Double Points use button - positioned at (1024 + 200, doubleY)
var doubleY = startY + itemSpacing * 2 + 60;
var doubleButtonX = 1024 + 200;
if (Math.abs(x - doubleButtonX) < 100 && Math.abs(y - doubleY) < 50) {
if ((storage.inventory.doublePoints || 0) > 0 && !shopEffects.doublePoints.active) {
storage.inventory.doublePoints--;
shopEffects.doublePoints.active = true;
shopEffects.doublePoints.timer = 0;
// Save inventory changes
storage.inventory = {
slowdown: storage.inventory.slowdown || 0,
speedGigachad: storage.inventory.speedGigachad || 0,
doublePoints: storage.inventory.doublePoints
};
closeInventory();
LK.effects.flashScreen(0x4CAF50, 500);
}
return;
}
}
// Add shop interaction handler
game.down = function (x, y, obj) {
if (isShopOpen && shopButton) {
shopButton.handleShopClick(x, y);
} else if (isInventoryOpen) {
handleInventoryClick(x, y);
}
};
// Main game loop
game.update = function () {
if (!isGameRunning || isShopOpen || isInventoryOpen) {
return;
}
moveCounter++;
var currentMoveDelay = MOVE_DELAY;
if (shopEffects.slowdown.active) {
currentMoveDelay = MOVE_DELAY * 2; // Double the delay for slowdown effect
}
if (moveCounter >= currentMoveDelay) {
moveCounter = 0;
moveSnake();
}
// Spawn friendly snakes every 10 seconds (600 frames at 60fps)
friendlySnakeTimer++;
if (friendlySnakeTimer >= 600) {
friendlySnakeTimer = 0;
var friendlySnake = game.addChild(new FriendlySnake());
// Spawn at random edge position within board bounds only
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top - ensure spawn within board width
friendlySnake.x = boardOffsetX + (Math.random() * (BOARD_WIDTH - 2) + 1) * GRID_SIZE;
friendlySnake.y = boardOffsetY + GRID_SIZE;
break;
case 1:
// Right - ensure spawn within board height
friendlySnake.x = boardOffsetX + (BOARD_WIDTH - 1) * GRID_SIZE;
friendlySnake.y = boardOffsetY + (Math.random() * (BOARD_HEIGHT - 2) + 1) * GRID_SIZE;
break;
case 2:
// Bottom - ensure spawn within board width
friendlySnake.x = boardOffsetX + (Math.random() * (BOARD_WIDTH - 2) + 1) * GRID_SIZE;
friendlySnake.y = boardOffsetY + (BOARD_HEIGHT - 1) * GRID_SIZE;
break;
case 3:
// Left - ensure spawn within board height
friendlySnake.x = boardOffsetX + GRID_SIZE;
friendlySnake.y = boardOffsetY + (Math.random() * (BOARD_HEIGHT - 2) + 1) * GRID_SIZE;
break;
}
friendlySnakes.push(friendlySnake);
}
// Spawn enemy snakes every 8 seconds (480 frames at 60fps)
enemySnakeTimer++;
if (enemySnakeTimer >= 480) {
enemySnakeTimer = 0;
var enemySnake = game.addChild(new EnemySnake());
// Spawn at random edge position within board bounds only
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top - ensure spawn within board width
enemySnake.x = boardOffsetX + (Math.random() * (BOARD_WIDTH - 2) + 1) * GRID_SIZE;
enemySnake.y = boardOffsetY + GRID_SIZE;
break;
case 1:
// Right - ensure spawn within board height
enemySnake.x = boardOffsetX + (BOARD_WIDTH - 1) * GRID_SIZE;
enemySnake.y = boardOffsetY + (Math.random() * (BOARD_HEIGHT - 2) + 1) * GRID_SIZE;
break;
case 2:
// Bottom - ensure spawn within board width
enemySnake.x = boardOffsetX + (Math.random() * (BOARD_WIDTH - 2) + 1) * GRID_SIZE;
enemySnake.y = boardOffsetY + (BOARD_HEIGHT - 1) * GRID_SIZE;
break;
case 3:
// Left - ensure spawn within board height
enemySnake.x = boardOffsetX + GRID_SIZE;
enemySnake.y = boardOffsetY + (Math.random() * (BOARD_HEIGHT - 2) + 1) * GRID_SIZE;
break;
}
enemySnakes.push(enemySnake);
}
// Check collision between player and enemy snakes
if (snake.length > 0) {
var playerHead = snake[0];
var playerScreenPos = gridToScreen(playerHead.x, playerHead.y);
for (var i = enemySnakes.length - 1; i >= 0; i--) {
var enemySnake = enemySnakes[i];
var dist = Math.sqrt(Math.pow(playerScreenPos.x - enemySnake.x, 2) + Math.pow(playerScreenPos.y - enemySnake.y, 2));
if (dist < 60) {
// Collision detected
// Check if friendly snake is protecting
var isProtected = false;
for (var j = 0; j < friendlySnakes.length; j++) {
var friendlySnake = friendlySnakes[j];
var friendlyDist = Math.sqrt(Math.pow(playerScreenPos.x - friendlySnake.x, 2) + Math.pow(playerScreenPos.y - friendlySnake.y, 2));
if (friendlyDist < 100) {
// Protection range
isProtected = true;
// Remove the enemy snake as it's defeated by friendly snake
enemySnake.destroy();
enemySnakes.splice(i, 1);
var coins = storage.coins || 0;
storage.coins = coins + 50; // Bonus for protection
scoreTxt.setText('SCOİN: ' + storage.coins);
break;
}
}
if (!isProtected) {
// Game over - player eaten by enemy snake
isGameRunning = false;
// Check if player had any active power-ups before clearing them
hadPowerUpsOnDeath = shopEffects.slowdown.active || shopEffects.speedGigachad.active || shopEffects.doublePoints.active;
// Clear all active power-up effects on death
shopEffects.slowdown.active = false;
shopEffects.slowdown.timer = 0;
shopEffects.speedGigachad.active = false;
shopEffects.doublePoints.active = false;
shopEffects.doublePoints.timer = 0;
LK.getSound('gameOver').play();
LK.showGameOver();
return;
}
}
}
}
// Reset GIGACHAD notification if no enemies remain
if (enemySnakes.length === 0) {
gigaChadNotified = false;
}
// Update shop effects
if (shopEffects.slowdown.active) {
shopEffects.slowdown.timer++;
if (shopEffects.slowdown.timer >= 18000) {
// 5 minutes
shopEffects.slowdown.active = false;
shopEffects.slowdown.timer = 0;
}
}
if (shopEffects.doublePoints.active) {
shopEffects.doublePoints.timer++;
if (shopEffects.doublePoints.timer >= 18000) {
// 5 minutes
shopEffects.doublePoints.active = false;
shopEffects.doublePoints.timer = 0;
}
}
// Update power-up timer display
var timerText = "";
var activeEffects = [];
if (shopEffects.slowdown.active) {
var slowdownTimeLeft = Math.ceil((18000 - shopEffects.slowdown.timer) / 60);
activeEffects.push("Slowdown: " + slowdownTimeLeft + "s");
}
if (shopEffects.speedGigachad.active) {
activeEffects.push("Fast GIGACHAD: ACTIVE");
}
if (shopEffects.doublePoints.active) {
var doublePointsTimeLeft = Math.ceil((18000 - shopEffects.doublePoints.timer) / 60);
activeEffects.push("2x Points: " + doublePointsTimeLeft + "s");
}
if (activeEffects.length > 0) {
timerText = activeEffects.join(" | ");
powerUpTimerTxt.setText(timerText);
powerUpTimerTxt.alpha = 1;
} else {
powerUpTimerTxt.setText("");
powerUpTimerTxt.alpha = 0;
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var DirectionalButton = Container.expand(function (direction, assetId) {
var self = Container.call(this);
var graphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.direction = direction;
// Add visual feedback when pressed
self.down = function (x, y, obj) {
if (!isGameRunning) {
return;
}
// Prevent multiple rapid presses
if (self.isPressed) {
return;
}
self.isPressed = true;
// Visual feedback - make button slightly smaller
tween(self, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
// Change snake direction immediately
changeDirection(self.direction);
};
self.up = function (x, y, obj) {
// Reset pressed state
self.isPressed = false;
// Return button to normal size
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100
});
};
return self;
});
var EnemySnake = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemySnake', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xff4444; // Red tint for enemy
self.moveSpeed = 1.5;
self.direction = Math.random() * Math.PI * 2;
self.chaseTimer = 0;
self.isBeingRepelled = false;
self.repelTimer = 0;
self.update = function () {
// Check if being repelled by friendly snakes
self.isBeingRepelled = false;
for (var i = 0; i < friendlySnakes.length; i++) {
var friendlySnake = friendlySnakes[i];
var dist = Math.sqrt(Math.pow(self.x - friendlySnake.x, 2) + Math.pow(self.y - friendlySnake.y, 2));
if (dist < 150) {
// Repel range
self.isBeingRepelled = true;
// Move away from friendly snake
var angle = Math.atan2(self.y - friendlySnake.y, self.x - friendlySnake.x);
self.direction = angle;
break;
}
}
// If not being repelled, chase the player
if (!self.isBeingRepelled && snake.length > 0) {
self.chaseTimer++;
if (self.chaseTimer >= 30) {
// Update chase direction every 0.5 seconds
var playerHead = snake[0];
var playerScreenPos = gridToScreen(playerHead.x, playerHead.y);
var angle = Math.atan2(playerScreenPos.y - self.y, playerScreenPos.x - self.x);
self.direction = angle;
self.chaseTimer = 0;
}
}
// Move in current direction
self.x += Math.cos(self.direction) * self.moveSpeed;
self.y += Math.sin(self.direction) * self.moveSpeed;
// Bounce off walls
if (self.x < boardOffsetX || self.x > boardOffsetX + BOARD_WIDTH * GRID_SIZE) {
self.direction = Math.PI - self.direction;
}
if (self.y < boardOffsetY || self.y > boardOffsetY + BOARD_HEIGHT * GRID_SIZE) {
self.direction = -self.direction;
}
// Keep within bounds
self.x = Math.max(boardOffsetX, Math.min(boardOffsetX + BOARD_WIDTH * GRID_SIZE, self.x));
self.y = Math.max(boardOffsetY, Math.min(boardOffsetY + BOARD_HEIGHT * GRID_SIZE, self.y));
};
return self;
});
var Food = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('food', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Simple pulsing animation
var scale = 1 + Math.sin(LK.ticks * 0.1) * 0.1;
self.scaleX = scale;
self.scaleY = scale;
};
return self;
});
var FriendlySnake = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('friendlySnake', {
anchorX: 0.5,
anchorY: 0.5
});
self.moveSpeed = 2;
self.direction = Math.random() * Math.PI * 2;
self.dropTimer = 0;
self.maxDrops = 3;
self.dropsLeft = 3;
self.hasKilledEnemy = false;
self.update = function () {
// Check for nearby enemy snakes to chase
var isChasing = false;
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemySnakes.length; i++) {
var enemySnake = enemySnakes[i];
var dist = Math.sqrt(Math.pow(self.x - enemySnake.x, 2) + Math.pow(self.y - enemySnake.y, 2));
if (dist < 200) {
// Detection range - notify GIGACHAD!
if (!gigaChadNotified && enemySnakes.length > 0) {
gigaChadNotified = true;
if (!gigaChad) {
gigaChad = game.addChild(new GigaChad());
}
gigaChad.summon();
}
if (dist < closestDistance) {
closestDistance = dist;
closestEnemy = enemySnake;
isChasing = true;
}
}
}
// If chasing an enemy, move towards it
if (isChasing && closestEnemy) {
var angle = Math.atan2(closestEnemy.y - self.y, closestEnemy.x - self.x);
self.direction = angle;
self.moveSpeed = 3; // Increase speed when chasing
} else {
self.moveSpeed = 2; // Normal speed when not chasing
}
// Move in current direction
self.x += Math.cos(self.direction) * self.moveSpeed;
self.y += Math.sin(self.direction) * self.moveSpeed;
// Check if we killed an enemy snake
for (var i = enemySnakes.length - 1; i >= 0; i--) {
var enemySnake = enemySnakes[i];
var dist = Math.sqrt(Math.pow(self.x - enemySnake.x, 2) + Math.pow(self.y - enemySnake.y, 2));
if (dist < 50) {
// Kill range
// Enemy snake is killed
enemySnake.destroy();
enemySnakes.splice(i, 1);
var coins = storage.coins || 0;
storage.coins = coins + 100; // Bonus for friendly snake killing enemy
scoreTxt.setText('SCOİN: ' + storage.coins);
// Mark this friendly snake as having killed an enemy
self.hasKilledEnemy = true;
break;
}
}
// Bounce off walls
if (self.x < boardOffsetX || self.x > boardOffsetX + BOARD_WIDTH * GRID_SIZE) {
self.direction = Math.PI - self.direction;
}
if (self.y < boardOffsetY || self.y > boardOffsetY + BOARD_HEIGHT * GRID_SIZE) {
self.direction = -self.direction;
}
// Keep within bounds
self.x = Math.max(boardOffsetX, Math.min(boardOffsetX + BOARD_WIDTH * GRID_SIZE, self.x));
self.y = Math.max(boardOffsetY, Math.min(boardOffsetY + BOARD_HEIGHT * GRID_SIZE, self.y));
// Drop food periodically
self.dropTimer++;
if (self.dropTimer >= 120 && self.dropsLeft > 0) {
// Every 2 seconds
self.dropFood();
self.dropTimer = 0;
self.dropsLeft--;
}
// Remove after all drops are done - but only if hasn't killed an enemy
if (self.dropsLeft <= 0 && !self.hasKilledEnemy) {
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
var index = friendlySnakes.indexOf(self);
if (index > -1) {
friendlySnakes.splice(index, 1);
}
}
});
}
};
self.dropFood = function () {
var dropType = Math.random() < 0.3 ? 'level2' : 'normal';
var newFood;
if (dropType === 'level2') {
newFood = game.addChild(new Level2Food());
} else {
newFood = game.addChild(new Food());
}
// Convert screen position to grid position and clamp to board bounds
var gridX = Math.round((self.x - boardOffsetX) / GRID_SIZE);
var gridY = Math.round((self.y - boardOffsetY) / GRID_SIZE);
// Ensure food stays within board bounds
gridX = Math.max(0, Math.min(BOARD_WIDTH - 1, gridX));
gridY = Math.max(0, Math.min(BOARD_HEIGHT - 1, gridY));
// Set food position based on clamped grid coordinates
var screenPos = gridToScreen(gridX, gridY);
newFood.x = screenPos.x;
newFood.y = screenPos.y;
newFood.gridX = gridX;
newFood.gridY = gridY;
// Add to food array
foodItems.push(newFood);
};
return self;
});
var GigaChad = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('gigachad', {
anchorX: 0.5,
anchorY: 0.5
});
self.isActive = false;
self.destroyTimer = 0;
self.destroyDelay = shopEffects.speedGigachad.active ? 180 : 600; // 3 seconds if fast, 10 seconds normal
self.summon = function () {
if (self.isActive) {
return;
} // Already summoned
self.isActive = true;
self.destroyTimer = 0;
// Position GIGACHAD at center of board
self.x = boardOffsetX + BOARD_WIDTH * GRID_SIZE / 2;
self.y = boardOffsetY + BOARD_HEIGHT * GRID_SIZE / 2;
// Make GIGACHAD appear with dramatic effect
self.alpha = 0;
self.scaleX = 0.1;
self.scaleY = 0.1;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.bounceOut
});
// Play summon sound
LK.getSound('gigachadSummon').play();
// Flash screen to show GIGACHAD arrival
LK.effects.flashScreen(0xFFD700, 1000); // Gold flash
// Destroy all enemies immediately upon arrival
self.destroyAllEnemies();
};
self.update = function () {
if (!self.isActive) {
return;
}
self.destroyTimer++;
// Update destroy delay based on current speed effect status
self.destroyDelay = shopEffects.speedGigachad.active ? 180 : 600;
// Pulsing effect while active
var pulse = 1 + Math.sin(LK.ticks * 0.2) * 0.1;
self.scaleX = pulse;
self.scaleY = pulse;
// Auto-deactivate after completing task (enemies destroyed immediately on summon)
if (self.destroyTimer >= self.destroyDelay) {
// GIGACHAD disappears after completing his task
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isActive = false;
self.destroyTimer = 0;
}
});
}
};
self.destroyAllEnemies = function () {
// Destroy all enemy snakes immediately
for (var i = enemySnakes.length - 1; i >= 0; i--) {
var enemySnake = enemySnakes[i];
// Flash enemy red before destroying
tween(enemySnake, {
tint: 0xFF0000,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
enemySnake.destroy();
}
});
}
// Clear enemy snakes array
enemySnakes = [];
// Play destroy sound
LK.getSound('gigachadDestroy').play();
// Flash screen red for destruction
LK.effects.flashScreen(0xFF0000, 1500);
// Add massive score bonus
var coins = storage.coins || 0;
storage.coins = coins + 500;
scoreTxt.setText('SCOİN: ' + storage.coins);
};
return self;
});
var Level2Food = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('level2Food', {
anchorX: 0.5,
anchorY: 0.5
});
self.foodType = 'level2';
self.update = function () {
// Special pulsing animation for level 2 food
var scale = 1 + Math.sin(LK.ticks * 0.15) * 0.3;
self.scaleX = scale;
self.scaleY = scale;
// Color cycling effect
var colorPhase = LK.ticks * 0.1 % (Math.PI * 2);
var red = Math.floor(128 + 127 * Math.sin(colorPhase));
var green = Math.floor(128 + 127 * Math.sin(colorPhase + Math.PI * 2 / 3));
var blue = Math.floor(128 + 127 * Math.sin(colorPhase + Math.PI * 4 / 3));
graphics.tint = red << 16 | green << 8 | blue;
};
return self;
});
var Merchant = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('friendlySnake', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFFD700; // Gold tint for merchant
// Shop items with costs
self.shopItems = {
slowdown: {
cost: 150,
name: "Slowdown (5min)",
active: false,
duration: 18000
},
// 5 minutes at 60fps
speedGigachad: {
cost: 200,
name: "Fast GIGACHAD",
active: false
},
doublePoints: {
cost: 200,
name: "2x Points",
active: false,
duration: 18000
} // 5 minutes at 60fps
};
self.down = function (x, y, obj) {
self.openShop();
};
self.openShop = function () {
// Simple shop interface using score display
var playerCoins = storage.coins || 0;
var shopText = "SHOP (SCOİN: " + playerCoins + ")\n";
shopText += "1. Slowdown 5min - " + self.shopItems.slowdown.cost + " SCOİN\n";
shopText += "2. Fast GIGACHAD - " + self.shopItems.speedGigachad.cost + " SCOİN\n";
shopText += "3. 2x Points - " + self.shopItems.doublePoints.cost + " SCOİN";
// Flash screen to show shop is open
LK.effects.flashScreen(0xFFD700, 500);
};
return self;
});
var ShopButton = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0x4CAF50; // Green tint for shop button
self.down = function (x, y, obj) {
if (!isGameRunning && !isShopOpen) {
return;
}
// Visual feedback - make button slightly smaller
tween(self, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
// Toggle shop - close if open, open if closed
if (isShopOpen) {
self.closeShop();
} else {
isShopOpen = true;
self.openFullScreenShop();
}
};
self.up = function (x, y, obj) {
// Return button to normal size
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100
});
};
self.openFullScreenShop = function () {
// Create full-screen shop container
shopContainer = new Container();
shopContainer.x = 0;
shopContainer.y = 0;
game.addChild(shopContainer);
// Create semi-transparent background
var shopBg = LK.getAsset('shopButton', {
anchorX: 0,
anchorY: 0,
scaleX: 10.24,
scaleY: 34.15
});
shopBg.tint = 0x000000;
shopBg.alpha = 0.8;
shopContainer.addChild(shopBg);
// Get player coins
var playerCoins = storage.coins || 0;
// Create shop title
var titleText = new Text2('SHOP', {
size: 150,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
shopContainer.addChild(titleText);
// Create coins display
var coinsText = new Text2('SCOİN: ' + playerCoins, {
size: 80,
fill: 0xFFFFFF
});
coinsText.anchor.set(0.5, 0.5);
coinsText.x = 1024;
coinsText.y = 450;
shopContainer.addChild(coinsText);
// Create shop items
var yPos = 700;
var itemSpacing = 200;
// Slowdown item
var slowdownButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5
});
slowdownButton.x = 1024;
slowdownButton.y = yPos;
slowdownButton.tint = playerCoins >= 150 ? 0x4CAF50 : 0x666666;
shopContainer.addChild(slowdownButton);
var slowdownText = new Text2('Fast GIGACHAD - 200 SCOİN', {
size: 50,
fill: 0xFFFFFF
});
slowdownText.anchor.set(0.5, 0.5);
slowdownText.x = 1024;
slowdownText.y = yPos;
shopContainer.addChild(slowdownText);
// Speed GIGACHAD item
yPos += itemSpacing;
var speedButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5
});
speedButton.x = 1024;
speedButton.y = yPos;
speedButton.tint = playerCoins >= 200 ? 0x4CAF50 : 0x666666;
shopContainer.addChild(speedButton);
var speedText = new Text2('Slowdown 5min - 150 SCOİN', {
size: 50,
fill: 0xFFFFFF
});
speedText.anchor.set(0.5, 0.5);
speedText.x = 1024;
speedText.y = yPos;
shopContainer.addChild(speedText);
// Double Points item
yPos += itemSpacing;
var doubleButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5
});
doubleButton.x = 1024;
doubleButton.y = yPos;
doubleButton.tint = playerCoins >= 200 ? 0x4CAF50 : 0x666666;
shopContainer.addChild(doubleButton);
var doubleText = new Text2('2x Points - 200 SCOİN', {
size: 50,
fill: 0xFFFFFF
});
doubleText.anchor.set(0.5, 0.5);
doubleText.x = 1024;
doubleText.y = yPos;
shopContainer.addChild(doubleText);
// Add shop interaction handlers
self.handleShopClick = function (x, y) {
// Get fresh coin data for purchase validation
var currentCoins = storage.coins || 0;
// Check which button was clicked using global coordinates
var itemY = 700;
// First button (Fast GIGACHAD) - covers almost entire screen width
if (Math.abs(y - itemY) < 100) {
if (currentCoins >= 200) {
// Initialize inventory if needed
if (!storage.inventory) {
storage.inventory = {
slowdown: 0,
speedGigachad: 0,
doublePoints: 0
};
}
// Complete the purchase
storage.coins = currentCoins - 200;
storage.inventory.speedGigachad = (storage.inventory.speedGigachad || 0) + 1;
// Close shop and give feedback
self.closeShop();
LK.effects.flashScreen(0x4CAF50, 500); // Green flash for successful purchase
} else {
// Not enough coins - red flash
LK.effects.flashScreen(0xFF0000, 300);
}
return;
}
// Second button (Slowdown)
itemY += 200;
if (Math.abs(y - itemY) < 100) {
if (currentCoins >= 150) {
// Initialize inventory if needed
if (!storage.inventory) {
storage.inventory = {
slowdown: 0,
speedGigachad: 0,
doublePoints: 0
};
}
// Complete the purchase
storage.coins = currentCoins - 150;
storage.inventory.slowdown = (storage.inventory.slowdown || 0) + 1;
// Close shop and give feedback
self.closeShop();
LK.effects.flashScreen(0x4CAF50, 500); // Green flash for successful purchase
} else {
// Not enough coins - red flash
LK.effects.flashScreen(0xFF0000, 300);
}
return;
}
// Double points button
itemY += 200;
if (Math.abs(y - itemY) < 100) {
if (currentCoins >= 200) {
// Initialize inventory if needed
if (!storage.inventory) {
storage.inventory = {
slowdown: 0,
speedGigachad: 0,
doublePoints: 0
};
}
// Complete the purchase
storage.coins = currentCoins - 200;
storage.inventory.doublePoints = (storage.inventory.doublePoints || 0) + 1;
// Close shop and give feedback
self.closeShop();
LK.effects.flashScreen(0x4CAF50, 500); // Green flash for successful purchase
} else {
// Not enough coins - red flash
LK.effects.flashScreen(0xFF0000, 300);
}
return;
}
};
self.closeShop = function () {
isShopOpen = false;
isGameRunning = true; // Resume game when closing shop
if (shopContainer) {
shopContainer.destroy();
shopContainer = null;
}
};
};
return self;
});
var Snake = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('snakeHead', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game configuration
var GRID_SIZE = 40;
var BOARD_WIDTH = 35;
var BOARD_HEIGHT = 50;
var MOVE_DELAY = 8; // Frames between moves
// Game state variables
var snake = [];
var snakeDirection = {
x: 1,
y: 0
};
var nextDirection = {
x: 1,
y: 0
};
var food = null;
var moveCounter = 0;
var isGameRunning = true;
var friendlySnakes = [];
var friendlySnakeTimer = 0;
var enemySnakes = [];
var enemySnakeTimer = 0;
var foodItems = [];
var gigaChad = null;
var gigaChadNotified = false;
var shopButton = null;
var shopEffects = {
slowdown: {
active: false,
timer: 0
},
speedGigachad: {
active: false
},
doublePoints: {
active: false,
timer: 0
}
};
var isShopOpen = false;
var shopContainer = null;
// Calculate board position to center it horizontally and leave space for controls at bottom
var boardOffsetX = (2048 - BOARD_WIDTH * GRID_SIZE) / 2;
var boardOffsetY = 100; // Start closer to top, leaving space for controls at bottom
// Initialize SCOİN storage
if (!storage.coins) {
storage.coins = 0;
}
// Create score display
var scoreTxt = new Text2('SCOİN: 0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
// Position score centered horizontally and at control buttons level
scoreTxt.x = 2048 / 2; // Center horizontally
scoreTxt.y = 2732 - 300; // Same vertical level as control buttons
game.addChild(scoreTxt);
// Create power-up timer display
var powerUpTimerTxt = new Text2('', {
size: 60,
fill: 0x4CAF50
});
powerUpTimerTxt.anchor.set(0.5, 0.5);
powerUpTimerTxt.x = 2048 / 2; // Center horizontally
powerUpTimerTxt.y = 2732 - 200; // Above score display (score is at 2732 - 300, so this puts timer above it)
game.addChild(powerUpTimerTxt);
// Initialize snake with only head
function initializeSnake() {
snake = [];
// Clear death flag when starting fresh game
hadPowerUpsOnDeath = false;
// Create head using new Snake class
var head = game.addChild(new Snake());
head.x = boardOffsetX + 12 * GRID_SIZE;
head.y = boardOffsetY + 25 * GRID_SIZE;
snake.push({
x: 12,
y: 25,
obj: head
});
}
// Convert grid coordinates to screen coordinates
function gridToScreen(gridX, gridY) {
return {
x: boardOffsetX + gridX * GRID_SIZE,
y: boardOffsetY + gridY * GRID_SIZE
};
}
// Generate random food position
function spawnFood() {
var validPositions = [];
// Find all valid positions (not occupied by snake)
for (var x = 0; x < BOARD_WIDTH; x++) {
for (var y = 0; y < BOARD_HEIGHT; y++) {
var occupied = false;
for (var i = 0; i < snake.length; i++) {
if (snake[i].x === x && snake[i].y === y) {
occupied = true;
break;
}
}
if (!occupied) {
validPositions.push({
x: x,
y: y
});
}
}
}
if (validPositions.length > 0) {
var randomIndex = Math.floor(Math.random() * validPositions.length);
var pos = validPositions[randomIndex];
if (food) {
food.destroy();
}
food = game.addChild(new Food());
var screenPos = gridToScreen(pos.x, pos.y);
food.x = screenPos.x;
food.y = screenPos.y;
food.gridX = pos.x;
food.gridY = pos.y;
foodItems.push(food);
}
}
// Check if position is valid (within bounds and not colliding with snake)
function isValidPosition(x, y) {
// Check bounds
if (x < 0 || x >= BOARD_WIDTH || y < 0 || y >= BOARD_HEIGHT) {
return false;
}
// No body collision check needed for head-only snake
return true;
}
// Move snake
function moveSnake() {
if (!isGameRunning) {
return;
}
// Direction is already updated immediately in changeDirection function
// Calculate new head position
var head = snake[0];
var newHeadX = head.x + snakeDirection.x;
var newHeadY = head.y + snakeDirection.y;
// Check if new position is valid
if (!isValidPosition(newHeadX, newHeadY)) {
// Game over
isGameRunning = false;
// Check if player had any active power-ups before clearing them
hadPowerUpsOnDeath = shopEffects.slowdown.active || shopEffects.speedGigachad.active || shopEffects.doublePoints.active;
// Clear all active power-up effects on death
shopEffects.slowdown.active = false;
shopEffects.slowdown.timer = 0;
shopEffects.speedGigachad.active = false;
shopEffects.doublePoints.active = false;
shopEffects.doublePoints.timer = 0;
LK.getSound('gameOver').play();
LK.showGameOver();
return;
}
// Check if any food is eaten
var ateFood = false;
for (var i = foodItems.length - 1; i >= 0; i--) {
var currentFood = foodItems[i];
if (currentFood && newHeadX === currentFood.gridX && newHeadY === currentFood.gridY) {
ateFood = true;
var baseCoins = currentFood.foodType === 'level2' ? 50 : 10;
var coinGain = baseCoins;
// Apply double points effect if active
if (shopEffects.doublePoints.active) {
coinGain = baseCoins * 2;
}
var coins = storage.coins || 0;
storage.coins = coins + coinGain;
scoreTxt.setText('SCOİN: ' + storage.coins);
LK.getSound('eat').play();
// Flash food before removing
tween(currentFood, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
currentFood.destroy();
}
});
foodItems.splice(i, 1);
if (currentFood === food) {
food = null;
spawnFood();
}
break;
}
}
// Simply move the existing head to new position (no growth)
var headObj = snake[0].obj;
var screenPos = gridToScreen(newHeadX, newHeadY);
headObj.x = screenPos.x;
headObj.y = screenPos.y;
snake[0].x = newHeadX;
snake[0].y = newHeadY;
}
// Handle direction changes
function changeDirection(newDir) {
// Prevent immediate reversal
if (newDir.x === -snakeDirection.x && newDir.y === -snakeDirection.y) {
return;
}
// Prevent diagonal movement - only allow pure horizontal or vertical movement
if (newDir.x !== 0 && newDir.y !== 0) {
return;
}
// Apply direction change immediately for responsive controls
snakeDirection.x = newDir.x;
snakeDirection.y = newDir.y;
nextDirection.x = newDir.x;
nextDirection.y = newDir.y;
}
// Remove touch controls - snake can only be controlled by buttons
// Create visible walls around the game board
function createWalls() {
// Top and bottom walls
for (var x = -1; x <= BOARD_WIDTH; x++) {
// Top wall
var topWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
topWall.x = boardOffsetX + x * GRID_SIZE;
topWall.y = boardOffsetY - GRID_SIZE;
// Bottom wall
var bottomWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
bottomWall.x = boardOffsetX + x * GRID_SIZE;
bottomWall.y = boardOffsetY + BOARD_HEIGHT * GRID_SIZE;
}
// Left and right walls
for (var y = -1; y <= BOARD_HEIGHT; y++) {
// Left wall
var leftWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
leftWall.x = boardOffsetX - GRID_SIZE;
leftWall.y = boardOffsetY + y * GRID_SIZE;
// Right wall
var rightWall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
}));
rightWall.x = boardOffsetX + BOARD_WIDTH * GRID_SIZE;
rightWall.y = boardOffsetY + y * GRID_SIZE;
}
}
// Create directional control buttons
var upButton = game.addChild(new DirectionalButton({
x: 0,
y: -1
}, 'upButton'));
var downButton = game.addChild(new DirectionalButton({
x: 0,
y: 1
}, 'downButton'));
var leftButton = game.addChild(new DirectionalButton({
x: -1,
y: 0
}, 'leftButton'));
var rightButton = game.addChild(new DirectionalButton({
x: 1,
y: 0
}, 'rightButton'));
// Position buttons in bottom right corner
var buttonSpacing = 140;
var buttonBaseX = 2048 - 200;
var buttonBaseY = 2732 - 300;
// Up button
upButton.x = buttonBaseX;
upButton.y = buttonBaseY - buttonSpacing;
// Down button
downButton.x = buttonBaseX;
downButton.y = buttonBaseY + buttonSpacing;
// Left button
leftButton.x = buttonBaseX - buttonSpacing;
leftButton.y = buttonBaseY;
// Right button
rightButton.x = buttonBaseX + buttonSpacing;
rightButton.y = buttonBaseY;
// Add text labels to buttons
var upText = new Text2('↑', {
size: 60,
fill: 0xFFFFFF
});
upText.anchor.set(0.5, 0.5);
upText.x = upButton.x;
upText.y = upButton.y;
game.addChild(upText);
var downText = new Text2('↓', {
size: 60,
fill: 0xFFFFFF
});
downText.anchor.set(0.5, 0.5);
downText.x = downButton.x;
downText.y = downButton.y;
game.addChild(downText);
var leftText = new Text2('←', {
size: 60,
fill: 0xFFFFFF
});
leftText.anchor.set(0.5, 0.5);
leftText.x = leftButton.x;
leftText.y = leftButton.y;
game.addChild(leftText);
var rightText = new Text2('→', {
size: 60,
fill: 0xFFFFFF
});
rightText.anchor.set(0.5, 0.5);
rightText.x = rightButton.x;
rightText.y = rightButton.y;
game.addChild(rightText);
// Initialize game
createWalls();
initializeSnake();
spawnFood();
// Game mode variables
var gameMode = 'menu'; // 'menu', 'normal'
// Create main menu overlay
var instructionContainer = new Container();
instructionContainer.x = 0;
instructionContainer.y = 0;
game.addChild(instructionContainer);
// Create semi-transparent background
var instructionBg = LK.getAsset('shopButton', {
anchorX: 0,
anchorY: 0,
scaleX: 10.24,
scaleY: 34.15
});
instructionBg.tint = 0x000000;
instructionBg.alpha = 0.9;
instructionContainer.addChild(instructionBg);
// Create main menu title
var menuTitle = new Text2('THE SNAKE GAME 2077', {
size: 150,
fill: 0xFFD700
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 300;
instructionContainer.addChild(menuTitle);
// Create game mode buttons
var normalModeButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.0
});
normalModeButton.x = 1024;
normalModeButton.y = 800;
normalModeButton.tint = 0x4CAF50;
instructionContainer.addChild(normalModeButton);
var normalModeText = new Text2('NORMAL MODE', {
size: 60,
fill: 0xFFFFFF
});
normalModeText.anchor.set(0.5, 0.5);
normalModeText.x = normalModeButton.x;
normalModeText.y = normalModeButton.y;
instructionContainer.addChild(normalModeText);
var otherButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.0
});
otherButton.x = 1024;
otherButton.y = 1000;
otherButton.tint = 0xFF5722;
instructionContainer.addChild(otherButton);
var otherText = new Text2('DİE', {
size: 60,
fill: 0xFFFFFF
});
otherText.anchor.set(0.5, 0.5);
otherText.x = otherButton.x;
otherText.y = otherButton.y;
instructionContainer.addChild(otherText);
// How to play button
var howToPlayButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.8
});
howToPlayButton.x = 1024;
howToPlayButton.y = 1200;
howToPlayButton.tint = 0x9C27B0;
instructionContainer.addChild(howToPlayButton);
var howToPlayText = new Text2('HOW TO PLAY', {
size: 50,
fill: 0xFFFFFF
});
howToPlayText.anchor.set(0.5, 0.5);
howToPlayText.x = howToPlayButton.x;
howToPlayText.y = howToPlayButton.y;
instructionContainer.addChild(howToPlayText);
// Pause game initially to show menu
isGameRunning = false;
gameMode = 'menu';
// Add click handler for menu
instructionContainer.down = function (x, y, obj) {
// Check normal mode button
if (Math.abs(x - normalModeButton.x) < 150 && Math.abs(y - normalModeButton.y) < 50) {
startNormalMode();
return;
}
// Check other button
if (Math.abs(x - otherButton.x) < 150 && Math.abs(y - otherButton.y) < 50) {
showOther();
return;
}
// Check how to play button
if (Math.abs(x - howToPlayButton.x) < 120 && Math.abs(y - howToPlayButton.y) < 40) {
showHowToPlayWithOther();
return;
}
};
// Function to start normal mode
function startNormalMode() {
gameMode = 'normal';
isCompetitionMode = false;
// Remove menu overlay
instructionContainer.destroy();
instructionContainer = null;
// Start the game
isGameRunning = true;
// Start background music
LK.playMusic('backgroundMusic');
// Flash screen green to indicate game start
LK.effects.flashScreen(0x4CAF50, 500);
}
// Function to show other
function showOther() {
// Flash screen to indicate URL opening attempt
LK.effects.flashScreen(0xFF5722, 500);
// Since window.open doesn't work in this environment, use LK.showGameOver as workaround
// In a real implementation, this would open https://upit.com/@karasavasci127
LK.showGameOver();
}
// Function to show how to play with other URL
function showHowToPlayWithOther() {
// Clear current menu content
while (instructionContainer.children.length > 0) {
instructionContainer.removeChild(instructionContainer.children[0]);
}
// Add background back
instructionContainer.addChild(instructionBg);
// Create instruction title
var instructionTitle = new Text2('HOW TO PLAY', {
size: 120,
fill: 0xFFD700
});
instructionTitle.anchor.set(0.5, 0.5);
instructionTitle.x = 1024;
instructionTitle.y = 300;
instructionContainer.addChild(instructionTitle);
// Create instruction text
var instructionText = new Text2('• Use arrow buttons to move snake\n• Collect food to earn SCOİN\n• Normal food = 10 SCOİN\n• Level 2 food = 50 SCOİN\n• Avoid red enemy snakes\n• Gold friendly snakes protect you\n• Buy power-ups in SHOP\n• Use items from INVENTORY', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 1100;
instructionContainer.addChild(instructionText);
// Create Other URL text below instructions
var otherUrlText = new Text2('Other: https://upit.com/@karasavasci127', {
size: 60,
fill: 0xFF5722
});
otherUrlText.anchor.set(0.5, 0.5);
otherUrlText.x = 1024;
otherUrlText.y = 1400;
instructionContainer.addChild(otherUrlText);
// Back to menu button
var backButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 0.8
});
backButton.x = 1024;
backButton.y = 1900;
backButton.tint = 0x666666;
instructionContainer.addChild(backButton);
var backText = new Text2('BACK TO MENU', {
size: 50,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = backButton.x;
backText.y = backButton.y;
instructionContainer.addChild(backText);
// Update click handler for back button and Other URL
instructionContainer.down = function (x, y, obj) {
if (Math.abs(x - backButton.x) < 100 && Math.abs(y - backButton.y) < 40) {
// Go back to main menu
LK.showGameOver(); // Reset game state and return to main menu
} else if (Math.abs(x - otherUrlText.x) < 300 && Math.abs(y - otherUrlText.y) < 50) {
// Other URL clicked
LK.effects.flashScreen(0xFF5722, 500);
LK.showGameOver();
}
};
}
// Don't start background music immediately - wait for instructions to be dismissed
// Merchant functionality is handled through the shop button
// Create shop button at bottom center
shopButton = game.addChild(new ShopButton());
shopButton.x = 2048 / 2; // Center horizontally
shopButton.y = 2732 - 150; // Bottom of screen with some margin
// Add shop button text
var shopText = new Text2('SHOP', {
size: 40,
fill: 0xFFFFFF
});
shopText.anchor.set(0.5, 0.5);
shopText.x = shopButton.x;
shopText.y = shopButton.y;
game.addChild(shopText);
// Create hidden money cheat button above score
var hiddenMoneyButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.3
});
hiddenMoneyButton.x = scoreTxt.x; // Same x position as score
hiddenMoneyButton.y = scoreTxt.y - 100; // 100 pixels above score
hiddenMoneyButton.alpha = 0; // Make completely invisible
game.addChild(hiddenMoneyButton);
// Add click handler for hidden money button
hiddenMoneyButton.down = function (x, y, obj) {
// Give player 999999 coins
storage.coins = (storage.coins || 0) + 999999;
// Flash screen gold to indicate cheat activated
LK.effects.flashScreen(0xFFD700, 500);
};
// Initialize inventory storage
if (!storage.inventory) {
storage.inventory = {
slowdown: 0,
speedGigachad: 0,
doublePoints: 0
};
}
// Create inventory button
var inventoryButton = game.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
}));
inventoryButton.x = 2048 / 2 - 250; // Left of shop button
inventoryButton.y = 2732 - 150; // Same level as shop button
inventoryButton.tint = 0x9C27B0; // Purple tint for inventory
// Add inventory button text
var inventoryText = new Text2('INVENTORY', {
size: 30,
fill: 0xFFFFFF
});
inventoryText.anchor.set(0.5, 0.5);
inventoryText.x = inventoryButton.x;
inventoryText.y = inventoryButton.y;
game.addChild(inventoryText);
// Create reset SCOİN button
var resetCoinButton = game.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
}));
resetCoinButton.x = 2048 / 2 + 250; // Right of shop button (opposite side from inventory)
resetCoinButton.y = 2732 - 150; // Same level as shop and inventory buttons
resetCoinButton.tint = 0xFF5722; // Orange-red tint for reset button
// Add reset button text
var resetText = new Text2('RESET SCOİN', {
size: 30,
fill: 0xFFFFFF
});
resetText.anchor.set(0.5, 0.5);
resetText.x = resetCoinButton.x;
resetText.y = resetCoinButton.y;
game.addChild(resetText);
// Add reset button functionality
resetCoinButton.down = function (x, y, obj) {
// Visual feedback
tween(resetCoinButton, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100
});
// Reset SCOİN to 0
storage.coins = 0;
// Update score display
scoreTxt.setText('SCOİN: ' + storage.coins);
// Flash screen orange to indicate reset
LK.effects.flashScreen(0xFF5722, 500);
};
resetCoinButton.up = function (x, y, obj) {
// Return to normal size
tween(resetCoinButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
};
// Inventory system variables
var isInventoryOpen = false;
var inventoryContainer = null;
var hadPowerUpsOnDeath = false; // Track if player had power-ups when they died
// Add inventory button functionality
inventoryButton.down = function (x, y, obj) {
// Visual feedback
tween(inventoryButton, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 100
});
// Toggle inventory
if (isInventoryOpen) {
closeInventory();
} else {
openInventory();
}
};
inventoryButton.up = function (x, y, obj) {
// Return to normal size
tween(inventoryButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100
});
};
// Function to open inventory
function openInventory() {
if (isInventoryOpen) {
return;
}
isInventoryOpen = true;
isGameRunning = false; // Pause game
// Create inventory container
inventoryContainer = new Container();
inventoryContainer.x = 0;
inventoryContainer.y = 0;
game.addChild(inventoryContainer);
// Create background
var inventoryBg = LK.getAsset('shopButton', {
anchorX: 0,
anchorY: 0,
scaleX: 10.24,
scaleY: 34.15
});
inventoryBg.tint = 0x2E2E2E;
inventoryBg.alpha = 0.9;
inventoryContainer.addChild(inventoryBg);
// Title
var titleText = new Text2('INVENTORY', {
size: 120,
fill: 0x9C27B0
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
inventoryContainer.addChild(titleText);
// Current coins display
var coinsText = new Text2('SCOİN: ' + (storage.coins || 0), {
size: 60,
fill: 0xFFD700
});
coinsText.anchor.set(0.5, 0.5);
coinsText.x = 1024;
coinsText.y = 400;
inventoryContainer.addChild(coinsText);
// Inventory items section - GIGACHAD at top center, others below
var yPos = 500;
var itemSpacing = 120;
// Fast GIGACHAD items - positioned at top center
var speedGigachadCount = storage.inventory.speedGigachad || 0;
var speedStatusText = shopEffects.speedGigachad.active ? ' (ACTIVE)' : '';
var speedItemText = new Text2('Fast GIGACHAD: ' + speedGigachadCount + speedStatusText, {
size: 60,
fill: shopEffects.speedGigachad.active ? 0x4CAF50 : 0xFFFFFF
});
speedItemText.anchor.set(0.5, 0.5);
speedItemText.x = 1024; // Centered
speedItemText.y = yPos;
inventoryContainer.addChild(speedItemText);
// Use button for speed GIGACHAD - centered below text
if (speedGigachadCount > 0 && !shopEffects.speedGigachad.active && !hadPowerUpsOnDeath) {
var useSpeedBtn = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
useSpeedBtn.x = 1024; // Centered
useSpeedBtn.y = yPos + 60;
useSpeedBtn.tint = 0x4CAF50;
inventoryContainer.addChild(useSpeedBtn);
var useSpeedText = new Text2('USE', {
size: 40,
fill: 0xFFFFFF
});
useSpeedText.anchor.set(0.5, 0.5);
useSpeedText.x = useSpeedBtn.x;
useSpeedText.y = useSpeedBtn.y;
inventoryContainer.addChild(useSpeedText);
}
// Slowdown items - positioned below GIGACHAD
yPos += itemSpacing + 60;
var slowdownCount = storage.inventory.slowdown || 0;
var slowdownStatusText = shopEffects.slowdown.active ? ' (ACTIVE - ' + Math.ceil((18000 - shopEffects.slowdown.timer) / 60) + 's left)' : '';
var slowdownItemText = new Text2('Slowdown 5min: ' + slowdownCount + slowdownStatusText, {
size: 45,
fill: shopEffects.slowdown.active ? 0x4CAF50 : 0xFFFFFF
});
slowdownItemText.anchor.set(0.5, 0.5);
slowdownItemText.x = 1024;
slowdownItemText.y = yPos;
inventoryContainer.addChild(slowdownItemText);
// Use button for slowdown
if (slowdownCount > 0 && !shopEffects.slowdown.active && !hadPowerUpsOnDeath) {
var useSlowdownBtn = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
useSlowdownBtn.x = 1024 + 200;
useSlowdownBtn.y = yPos;
useSlowdownBtn.tint = 0x4CAF50;
inventoryContainer.addChild(useSlowdownBtn);
var useSlowdownText = new Text2('USE', {
size: 30,
fill: 0xFFFFFF
});
useSlowdownText.anchor.set(0.5, 0.5);
useSlowdownText.x = useSlowdownBtn.x;
useSlowdownText.y = useSlowdownBtn.y;
inventoryContainer.addChild(useSlowdownText);
}
// Double Points items - positioned at bottom
yPos += itemSpacing;
var doublePointsCount = storage.inventory.doublePoints || 0;
var doubleStatusText = shopEffects.doublePoints.active ? ' (ACTIVE - ' + Math.ceil((18000 - shopEffects.doublePoints.timer) / 60) + 's left)' : '';
var doubleItemText = new Text2('2x Points: ' + doublePointsCount + doubleStatusText, {
size: 45,
fill: shopEffects.doublePoints.active ? 0x4CAF50 : 0xFFFFFF
});
doubleItemText.anchor.set(0.5, 0.5);
doubleItemText.x = 1024;
doubleItemText.y = yPos;
inventoryContainer.addChild(doubleItemText);
// Use button for double points
if (doublePointsCount > 0 && !shopEffects.doublePoints.active && !hadPowerUpsOnDeath) {
var useDoubleBtn = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
useDoubleBtn.x = 1024 + 200;
useDoubleBtn.y = yPos;
useDoubleBtn.tint = 0x4CAF50;
inventoryContainer.addChild(useDoubleBtn);
var useDoubleText = new Text2('USE', {
size: 30,
fill: 0xFFFFFF
});
useDoubleText.anchor.set(0.5, 0.5);
useDoubleText.x = useDoubleBtn.x;
useDoubleText.y = useDoubleBtn.y;
inventoryContainer.addChild(useDoubleText);
}
}
// Function to close inventory
function closeInventory() {
isInventoryOpen = false;
isGameRunning = true;
if (inventoryContainer) {
inventoryContainer.destroy();
inventoryContainer = null;
}
}
// Function to handle inventory item usage
function handleInventoryClick(x, y) {
if (!isInventoryOpen || !inventoryContainer) {
return;
}
// Prevent using inventory if player had power-ups when they died
if (hadPowerUpsOnDeath) {
LK.effects.flashScreen(0xFF0000, 300); // Red flash to indicate blocked
return;
}
// Check use buttons with correct global positions
var itemSpacing = 120;
var startY = 500;
// Speed GIGACHAD use button - centered at top (1024, startY + 60)
var speedButtonX = 1024;
var speedButtonY = startY + 60;
if (Math.abs(x - speedButtonX) < 150 && Math.abs(y - speedButtonY) < 50) {
if ((storage.inventory.speedGigachad || 0) > 0 && !shopEffects.speedGigachad.active) {
storage.inventory.speedGigachad--;
shopEffects.speedGigachad.active = true;
// Save inventory changes
storage.inventory = {
slowdown: storage.inventory.slowdown || 0,
speedGigachad: storage.inventory.speedGigachad,
doublePoints: storage.inventory.doublePoints || 0
};
closeInventory();
LK.effects.flashScreen(0x4CAF50, 500);
}
return;
}
// Slowdown use button - positioned at (1024 + 200, slowdownY)
var slowdownY = startY + itemSpacing + 60;
var slowdownButtonX = 1024 + 200;
if (Math.abs(x - slowdownButtonX) < 100 && Math.abs(y - slowdownY) < 50) {
if ((storage.inventory.slowdown || 0) > 0 && !shopEffects.slowdown.active) {
storage.inventory.slowdown--;
shopEffects.slowdown.active = true;
shopEffects.slowdown.timer = 0;
// Save inventory changes
storage.inventory = {
slowdown: storage.inventory.slowdown,
speedGigachad: storage.inventory.speedGigachad || 0,
doublePoints: storage.inventory.doublePoints || 0
};
closeInventory();
LK.effects.flashScreen(0x4CAF50, 500);
}
return;
}
// Double Points use button - positioned at (1024 + 200, doubleY)
var doubleY = startY + itemSpacing * 2 + 60;
var doubleButtonX = 1024 + 200;
if (Math.abs(x - doubleButtonX) < 100 && Math.abs(y - doubleY) < 50) {
if ((storage.inventory.doublePoints || 0) > 0 && !shopEffects.doublePoints.active) {
storage.inventory.doublePoints--;
shopEffects.doublePoints.active = true;
shopEffects.doublePoints.timer = 0;
// Save inventory changes
storage.inventory = {
slowdown: storage.inventory.slowdown || 0,
speedGigachad: storage.inventory.speedGigachad || 0,
doublePoints: storage.inventory.doublePoints
};
closeInventory();
LK.effects.flashScreen(0x4CAF50, 500);
}
return;
}
}
// Add shop interaction handler
game.down = function (x, y, obj) {
if (isShopOpen && shopButton) {
shopButton.handleShopClick(x, y);
} else if (isInventoryOpen) {
handleInventoryClick(x, y);
}
};
// Main game loop
game.update = function () {
if (!isGameRunning || isShopOpen || isInventoryOpen) {
return;
}
moveCounter++;
var currentMoveDelay = MOVE_DELAY;
if (shopEffects.slowdown.active) {
currentMoveDelay = MOVE_DELAY * 2; // Double the delay for slowdown effect
}
if (moveCounter >= currentMoveDelay) {
moveCounter = 0;
moveSnake();
}
// Spawn friendly snakes every 10 seconds (600 frames at 60fps)
friendlySnakeTimer++;
if (friendlySnakeTimer >= 600) {
friendlySnakeTimer = 0;
var friendlySnake = game.addChild(new FriendlySnake());
// Spawn at random edge position within board bounds only
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top - ensure spawn within board width
friendlySnake.x = boardOffsetX + (Math.random() * (BOARD_WIDTH - 2) + 1) * GRID_SIZE;
friendlySnake.y = boardOffsetY + GRID_SIZE;
break;
case 1:
// Right - ensure spawn within board height
friendlySnake.x = boardOffsetX + (BOARD_WIDTH - 1) * GRID_SIZE;
friendlySnake.y = boardOffsetY + (Math.random() * (BOARD_HEIGHT - 2) + 1) * GRID_SIZE;
break;
case 2:
// Bottom - ensure spawn within board width
friendlySnake.x = boardOffsetX + (Math.random() * (BOARD_WIDTH - 2) + 1) * GRID_SIZE;
friendlySnake.y = boardOffsetY + (BOARD_HEIGHT - 1) * GRID_SIZE;
break;
case 3:
// Left - ensure spawn within board height
friendlySnake.x = boardOffsetX + GRID_SIZE;
friendlySnake.y = boardOffsetY + (Math.random() * (BOARD_HEIGHT - 2) + 1) * GRID_SIZE;
break;
}
friendlySnakes.push(friendlySnake);
}
// Spawn enemy snakes every 8 seconds (480 frames at 60fps)
enemySnakeTimer++;
if (enemySnakeTimer >= 480) {
enemySnakeTimer = 0;
var enemySnake = game.addChild(new EnemySnake());
// Spawn at random edge position within board bounds only
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top - ensure spawn within board width
enemySnake.x = boardOffsetX + (Math.random() * (BOARD_WIDTH - 2) + 1) * GRID_SIZE;
enemySnake.y = boardOffsetY + GRID_SIZE;
break;
case 1:
// Right - ensure spawn within board height
enemySnake.x = boardOffsetX + (BOARD_WIDTH - 1) * GRID_SIZE;
enemySnake.y = boardOffsetY + (Math.random() * (BOARD_HEIGHT - 2) + 1) * GRID_SIZE;
break;
case 2:
// Bottom - ensure spawn within board width
enemySnake.x = boardOffsetX + (Math.random() * (BOARD_WIDTH - 2) + 1) * GRID_SIZE;
enemySnake.y = boardOffsetY + (BOARD_HEIGHT - 1) * GRID_SIZE;
break;
case 3:
// Left - ensure spawn within board height
enemySnake.x = boardOffsetX + GRID_SIZE;
enemySnake.y = boardOffsetY + (Math.random() * (BOARD_HEIGHT - 2) + 1) * GRID_SIZE;
break;
}
enemySnakes.push(enemySnake);
}
// Check collision between player and enemy snakes
if (snake.length > 0) {
var playerHead = snake[0];
var playerScreenPos = gridToScreen(playerHead.x, playerHead.y);
for (var i = enemySnakes.length - 1; i >= 0; i--) {
var enemySnake = enemySnakes[i];
var dist = Math.sqrt(Math.pow(playerScreenPos.x - enemySnake.x, 2) + Math.pow(playerScreenPos.y - enemySnake.y, 2));
if (dist < 60) {
// Collision detected
// Check if friendly snake is protecting
var isProtected = false;
for (var j = 0; j < friendlySnakes.length; j++) {
var friendlySnake = friendlySnakes[j];
var friendlyDist = Math.sqrt(Math.pow(playerScreenPos.x - friendlySnake.x, 2) + Math.pow(playerScreenPos.y - friendlySnake.y, 2));
if (friendlyDist < 100) {
// Protection range
isProtected = true;
// Remove the enemy snake as it's defeated by friendly snake
enemySnake.destroy();
enemySnakes.splice(i, 1);
var coins = storage.coins || 0;
storage.coins = coins + 50; // Bonus for protection
scoreTxt.setText('SCOİN: ' + storage.coins);
break;
}
}
if (!isProtected) {
// Game over - player eaten by enemy snake
isGameRunning = false;
// Check if player had any active power-ups before clearing them
hadPowerUpsOnDeath = shopEffects.slowdown.active || shopEffects.speedGigachad.active || shopEffects.doublePoints.active;
// Clear all active power-up effects on death
shopEffects.slowdown.active = false;
shopEffects.slowdown.timer = 0;
shopEffects.speedGigachad.active = false;
shopEffects.doublePoints.active = false;
shopEffects.doublePoints.timer = 0;
LK.getSound('gameOver').play();
LK.showGameOver();
return;
}
}
}
}
// Reset GIGACHAD notification if no enemies remain
if (enemySnakes.length === 0) {
gigaChadNotified = false;
}
// Update shop effects
if (shopEffects.slowdown.active) {
shopEffects.slowdown.timer++;
if (shopEffects.slowdown.timer >= 18000) {
// 5 minutes
shopEffects.slowdown.active = false;
shopEffects.slowdown.timer = 0;
}
}
if (shopEffects.doublePoints.active) {
shopEffects.doublePoints.timer++;
if (shopEffects.doublePoints.timer >= 18000) {
// 5 minutes
shopEffects.doublePoints.active = false;
shopEffects.doublePoints.timer = 0;
}
}
// Update power-up timer display
var timerText = "";
var activeEffects = [];
if (shopEffects.slowdown.active) {
var slowdownTimeLeft = Math.ceil((18000 - shopEffects.slowdown.timer) / 60);
activeEffects.push("Slowdown: " + slowdownTimeLeft + "s");
}
if (shopEffects.speedGigachad.active) {
activeEffects.push("Fast GIGACHAD: ACTIVE");
}
if (shopEffects.doublePoints.active) {
var doublePointsTimeLeft = Math.ceil((18000 - shopEffects.doublePoints.timer) / 60);
activeEffects.push("2x Points: " + doublePointsTimeLeft + "s");
}
if (activeEffects.length > 0) {
timerText = activeEffects.join(" | ");
powerUpTimerTxt.setText(timerText);
powerUpTimerTxt.alpha = 1;
} else {
powerUpTimerTxt.setText("");
powerUpTimerTxt.alpha = 0;
}
};
robotik yap
wall block. In-Game asset. 2d. High contrast. No shadows
energy ball. In-Game asset. 2d. High contrast. No shadows
frendly robotic snake. In-Game asset. 2d. High contrast. No shadows
rainbow energy. In-Game asset. 2d. High contrast. No shadows
enemy robotic snake. In-Game asset. 2d. High contrast. No shadows
Robotic GigaChad snake. In-Game asset. 2d. High contrast. No shadows
yellow and orange cube 2d. In-Game asset. 2d. High contrast. No shadows
button red 2d. In-Game asset. 2d. High contrast. No shadows