/**** * Classes ****/ //var storage = LK.import("@upit/storage.v1"); // Ball class: The player var Ball = Container.expand(function () { var self = Container.call(this); var ballAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ballAsset.width / 2; self.moveTween = null; return self; }); // Exit class: The goal var Exit = Container.expand(function () { var self = Container.call(this); var exitAsset = self.attachAsset('exit', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Wall class: Each wall is a Container with a box shape var Wall = Container.expand(function () { var self = Container.call(this); var wallAsset = self.attachAsset('wall', { anchorX: 0, anchorY: 0 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // MazeCell: Used for maze generation and logic, not a display object // --- Asset Initialization --- function MazeCell(x, y) { this.x = x; this.y = y; this.visited = false; this.walls = [true, true, true, true]; // top, right, bottom, left } // Maze: Handles maze generation and wall data function Maze(cols, rows) { this.cols = cols; this.rows = rows; this.grid = []; for (var y = 0; y < rows; y++) { for (var x = 0; x < cols; x++) { this.grid.push(new MazeCell(x, y)); } } this.stack = []; this.generate = function (seed) { // Simple seeded random function rand() { seed = (seed * 9301 + 49297) % 233280; return seed / 233280; } var index = function index(x, y) { if (x < 0 || y < 0 || x >= cols || y >= rows) { return -1; } return x + y * cols; }; for (var i = 0; i < this.grid.length; i++) { this.grid[i].visited = false; this.grid[i].walls = [true, true, true, true]; } var current = this.grid[0]; current.visited = true; this.stack = []; var total = cols * rows; var visitedCount = 1; while (visitedCount < total) { var neighbors = []; // top var top = this.grid[index(current.x, current.y - 1)]; if (top && !top.visited) { neighbors.push({ cell: top, dir: 0 }); } // right var right = this.grid[index(current.x + 1, current.y)]; if (right && !right.visited) { neighbors.push({ cell: right, dir: 1 }); } // bottom var bottom = this.grid[index(current.x, current.y + 1)]; if (bottom && !bottom.visited) { neighbors.push({ cell: bottom, dir: 2 }); } // left var left = this.grid[index(current.x - 1, current.y)]; if (left && !left.visited) { neighbors.push({ cell: left, dir: 3 }); } if (neighbors.length > 0) { var next = neighbors[Math.floor(rand() * neighbors.length)]; // Remove wall between current and next current.walls[next.dir] = false; next.cell.walls[(next.dir + 2) % 4] = false; this.stack.push(current); current = next.cell; current.visited = true; visitedCount++; } else if (this.stack.length > 0) { current = this.stack.pop(); } } }; this.cell = function (x, y) { if (x < 0 || y < 0 || x >= this.cols || y >= this.rows) { return null; } return this.grid[x + y * this.cols]; }; } // --- Game Variables --- var MAZE_MAX_LEVEL = 200; var mazeCols = 10; var mazeRows = 14; var cellSize = 140; // px var wallThickness = 20; var wallLength = cellSize; var mazeOffsetX = 0; var mazeOffsetY = 0; var maze = null; var walls = []; var ball = null; var exit = null; var currentLevel = 1; var cameraTargetX = 0; var cameraTargetY = 0; var cameraTween = null; var isMoving = false; var moveQueue = []; var lastBallCell = { x: 0, y: 0 }; // --- GUI --- var levelText = new Text2('Level 1', { size: 90, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0); LK.gui.top.addChild(levelText); // --- Helper Functions --- function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } function getCellFromPos(x, y) { var cx = Math.floor((x - mazeOffsetX) / cellSize); var cy = Math.floor((y - mazeOffsetY) / cellSize); return { x: clamp(cx, 0, mazeCols - 1), y: clamp(cy, 0, mazeRows - 1) }; } function getCellCenter(x, y) { return { x: mazeOffsetX + x * cellSize + cellSize / 2, y: mazeOffsetY + y * cellSize + cellSize / 2 }; } function getMazeSeed(level) { // Simple deterministic seed for each level return 12345 + level * 9876; } function clearMaze() { for (var i = 0; i < walls.length; i++) { walls[i].destroy(); } walls = []; if (ball) { ball.destroy(); ball = null; } if (exit) { exit.destroy(); exit = null; } } function buildMaze(level) { clearMaze(); // Increase maze size with level, up to a max mazeCols = clamp(8 + Math.floor(level / 10), 8, 18); mazeRows = clamp(12 + Math.floor(level / 8), 12, 24); cellSize = Math.floor(1800 / Math.max(mazeCols, mazeRows)); cellSize = clamp(cellSize, 80, 180); wallThickness = clamp(Math.floor(cellSize / 7), 12, 28); wallLength = cellSize; mazeOffsetX = Math.floor((2048 - mazeCols * cellSize) / 2); mazeOffsetY = Math.floor((2732 - mazeRows * cellSize) / 2); maze = new Maze(mazeCols, mazeRows); maze.generate(getMazeSeed(level)); // Draw walls for (var y = 0; y < mazeRows; y++) { for (var x = 0; x < mazeCols; x++) { var cell = maze.cell(x, y); var px = mazeOffsetX + x * cellSize; var py = mazeOffsetY + y * cellSize; // Top wall if (cell.walls[0]) { var wall = new Wall(); wall.x = px; wall.y = py; wall.y0 = py; // store original y wall.width = wallLength; wall.height = wallThickness; walls.push(wall); game.addChild(wall); } // Left wall if (cell.walls[3]) { var wall = new Wall(); wall.x = px; wall.y = py; wall.y0 = py; // store original y wall.width = wallThickness; wall.height = wallLength; walls.push(wall); game.addChild(wall); } // Right wall (last column) if (x === mazeCols - 1 && cell.walls[1]) { var wall = new Wall(); wall.x = px + cellSize - wallThickness; wall.y = py; wall.y0 = py; // store original y wall.width = wallThickness; wall.height = wallLength; walls.push(wall); game.addChild(wall); } // Bottom wall (last row) if (y === mazeRows - 1 && cell.walls[2]) { var wall = new Wall(); wall.x = px; wall.y = py + cellSize - wallThickness; wall.y0 = py + cellSize - wallThickness; // store original y wall.width = wallLength; wall.height = wallThickness; walls.push(wall); game.addChild(wall); } } } // Place ball at top-left cell center ball = new Ball(); var start = getCellCenter(0, 0); ball.x = start.x; ball.y = start.y; ball.y0 = start.y; // store original y game.addChild(ball); lastBallCell.x = 0; lastBallCell.y = 0; // Place exit at bottom-right cell center exit = new Exit(); var exitPos = getCellCenter(mazeCols - 1, mazeRows - 1); exit.x = exitPos.x; exit.y = exitPos.y; exit.y0 = exitPos.y; // store original y game.addChild(exit); // Camera setup cameraTargetX = ball.x; cameraTargetY = ball.y; game.x = 0; game.y = 0; isMoving = false; moveQueue = []; levelText.setText("Level " + level); } // --- Collision Detection --- function ballIntersectsWall(nextX, nextY) { var r = ball.radius; for (var i = 0; i < walls.length; i++) { var wall = walls[i]; var wx = wall.x; var wy = wall.y; var ww = wall.width; var wh = wall.height; // Circle-rectangle collision var closestX = clamp(nextX, wx, wx + ww); var closestY = clamp(nextY, wy, wy + wh); var dx = nextX - closestX; var dy = nextY - closestY; if (dx * dx + dy * dy < r * r - 1) { return true; } } return false; } function ballAtExit() { var dx = ball.x - exit.x; var dy = ball.y - exit.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < ball.radius + exit.width / 2 - 8; } // --- Ball Movement --- function tryMoveBallTo(cellX, cellY) { if (isMoving) { moveQueue.push({ x: cellX, y: cellY }); return; } var from = getCellFromPos(ball.x, ball.y); if (cellX === from.x && cellY === from.y) { return; } // Only allow moving to adjacent cell (no diagonal) var dx = cellX - from.x; var dy = cellY - from.y; if (Math.abs(dx) + Math.abs(dy) !== 1) { return; } // Check for wall between cells var cell = maze.cell(from.x, from.y); if (dx === 1 && cell.walls[1]) { return; } if (dx === -1 && cell.walls[3]) { return; } if (dy === 1 && cell.walls[2]) { return; } if (dy === -1 && cell.walls[0]) { return; } // Target position var target = getCellCenter(cellX, cellY); // Check collision at target if (ballIntersectsWall(target.x, target.y)) { return; } isMoving = true; ball.x = target.x; ball.y = target.y; ball.y0 = target.y; // update original y isMoving = false; lastBallCell.x = cellX; lastBallCell.y = cellY; if (ballAtExit()) { LK.setScore(currentLevel); if (currentLevel >= MAZE_MAX_LEVEL) { LK.showYouWin(); } else { currentLevel++; buildMaze(currentLevel); } } else if (moveQueue.length > 0) { var next = moveQueue.shift(); tryMoveBallTo(next.x, next.y); } } // --- Camera Follow --- function updateCamera() { // Center ball in viewport var targetX = 2048 / 2 - ball.x; var targetY = 2732 / 2 - ball.y; // Clamp camera so maze stays in view var minX = 2048 - (mazeOffsetX + mazeCols * cellSize); var maxX = -mazeOffsetX; var minY = 2732 - (mazeOffsetY + mazeRows * cellSize); var maxY = -mazeOffsetY; targetX = clamp(targetX, minX, maxX); targetY = clamp(targetY, minY, maxY); // Smooth follow game.x += (targetX - game.x) * 0.18; game.y += (targetY - game.y) * 0.18; } // --- Input Handling --- game.down = function (x, y, obj) { if (isMoving) { return; } // Convert to game coordinates var local = game.toLocal({ x: x, y: y }); var from = getCellFromPos(ball.x, ball.y); var tx = local.x - ball.x; var ty = local.y - ball.y; var dir = null; if (Math.abs(tx) > Math.abs(ty)) { // Horizontal if (tx > 0) { dir = { x: from.x + 1, y: from.y }; } else { dir = { x: from.x - 1, y: from.y }; } } else { // Vertical if (ty > 0) { dir = { x: from.x, y: from.y + 1 }; } else { dir = { x: from.x, y: from.y - 1 }; } } if (dir) { tryMoveBallTo(dir.x, dir.y); } }; // --- Arrow Button Controls --- // Ok tuşları container var controlsContainer = game.addChild(new Container()); // Tuşların boyutu ve aralık var btnSize = 120; var spacing = 20; ; // --- Direction Labels at Top of Map --- // --- Start Game --- currentLevel = 1; buildMaze(currentLevel); LK.setScore(0); ;
/****
* Classes
****/
//var storage = LK.import("@upit/storage.v1");
// Ball class: The player
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballAsset = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = ballAsset.width / 2;
self.moveTween = null;
return self;
});
// Exit class: The goal
var Exit = Container.expand(function () {
var self = Container.call(this);
var exitAsset = self.attachAsset('exit', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Wall class: Each wall is a Container with a box shape
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallAsset = self.attachAsset('wall', {
anchorX: 0,
anchorY: 0
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// MazeCell: Used for maze generation and logic, not a display object
// --- Asset Initialization ---
function MazeCell(x, y) {
this.x = x;
this.y = y;
this.visited = false;
this.walls = [true, true, true, true]; // top, right, bottom, left
}
// Maze: Handles maze generation and wall data
function Maze(cols, rows) {
this.cols = cols;
this.rows = rows;
this.grid = [];
for (var y = 0; y < rows; y++) {
for (var x = 0; x < cols; x++) {
this.grid.push(new MazeCell(x, y));
}
}
this.stack = [];
this.generate = function (seed) {
// Simple seeded random
function rand() {
seed = (seed * 9301 + 49297) % 233280;
return seed / 233280;
}
var index = function index(x, y) {
if (x < 0 || y < 0 || x >= cols || y >= rows) {
return -1;
}
return x + y * cols;
};
for (var i = 0; i < this.grid.length; i++) {
this.grid[i].visited = false;
this.grid[i].walls = [true, true, true, true];
}
var current = this.grid[0];
current.visited = true;
this.stack = [];
var total = cols * rows;
var visitedCount = 1;
while (visitedCount < total) {
var neighbors = [];
// top
var top = this.grid[index(current.x, current.y - 1)];
if (top && !top.visited) {
neighbors.push({
cell: top,
dir: 0
});
}
// right
var right = this.grid[index(current.x + 1, current.y)];
if (right && !right.visited) {
neighbors.push({
cell: right,
dir: 1
});
}
// bottom
var bottom = this.grid[index(current.x, current.y + 1)];
if (bottom && !bottom.visited) {
neighbors.push({
cell: bottom,
dir: 2
});
}
// left
var left = this.grid[index(current.x - 1, current.y)];
if (left && !left.visited) {
neighbors.push({
cell: left,
dir: 3
});
}
if (neighbors.length > 0) {
var next = neighbors[Math.floor(rand() * neighbors.length)];
// Remove wall between current and next
current.walls[next.dir] = false;
next.cell.walls[(next.dir + 2) % 4] = false;
this.stack.push(current);
current = next.cell;
current.visited = true;
visitedCount++;
} else if (this.stack.length > 0) {
current = this.stack.pop();
}
}
};
this.cell = function (x, y) {
if (x < 0 || y < 0 || x >= this.cols || y >= this.rows) {
return null;
}
return this.grid[x + y * this.cols];
};
}
// --- Game Variables ---
var MAZE_MAX_LEVEL = 200;
var mazeCols = 10;
var mazeRows = 14;
var cellSize = 140; // px
var wallThickness = 20;
var wallLength = cellSize;
var mazeOffsetX = 0;
var mazeOffsetY = 0;
var maze = null;
var walls = [];
var ball = null;
var exit = null;
var currentLevel = 1;
var cameraTargetX = 0;
var cameraTargetY = 0;
var cameraTween = null;
var isMoving = false;
var moveQueue = [];
var lastBallCell = {
x: 0,
y: 0
};
// --- GUI ---
var levelText = new Text2('Level 1', {
size: 90,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
// --- Helper Functions ---
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
function getCellFromPos(x, y) {
var cx = Math.floor((x - mazeOffsetX) / cellSize);
var cy = Math.floor((y - mazeOffsetY) / cellSize);
return {
x: clamp(cx, 0, mazeCols - 1),
y: clamp(cy, 0, mazeRows - 1)
};
}
function getCellCenter(x, y) {
return {
x: mazeOffsetX + x * cellSize + cellSize / 2,
y: mazeOffsetY + y * cellSize + cellSize / 2
};
}
function getMazeSeed(level) {
// Simple deterministic seed for each level
return 12345 + level * 9876;
}
function clearMaze() {
for (var i = 0; i < walls.length; i++) {
walls[i].destroy();
}
walls = [];
if (ball) {
ball.destroy();
ball = null;
}
if (exit) {
exit.destroy();
exit = null;
}
}
function buildMaze(level) {
clearMaze();
// Increase maze size with level, up to a max
mazeCols = clamp(8 + Math.floor(level / 10), 8, 18);
mazeRows = clamp(12 + Math.floor(level / 8), 12, 24);
cellSize = Math.floor(1800 / Math.max(mazeCols, mazeRows));
cellSize = clamp(cellSize, 80, 180);
wallThickness = clamp(Math.floor(cellSize / 7), 12, 28);
wallLength = cellSize;
mazeOffsetX = Math.floor((2048 - mazeCols * cellSize) / 2);
mazeOffsetY = Math.floor((2732 - mazeRows * cellSize) / 2);
maze = new Maze(mazeCols, mazeRows);
maze.generate(getMazeSeed(level));
// Draw walls
for (var y = 0; y < mazeRows; y++) {
for (var x = 0; x < mazeCols; x++) {
var cell = maze.cell(x, y);
var px = mazeOffsetX + x * cellSize;
var py = mazeOffsetY + y * cellSize;
// Top wall
if (cell.walls[0]) {
var wall = new Wall();
wall.x = px;
wall.y = py;
wall.y0 = py; // store original y
wall.width = wallLength;
wall.height = wallThickness;
walls.push(wall);
game.addChild(wall);
}
// Left wall
if (cell.walls[3]) {
var wall = new Wall();
wall.x = px;
wall.y = py;
wall.y0 = py; // store original y
wall.width = wallThickness;
wall.height = wallLength;
walls.push(wall);
game.addChild(wall);
}
// Right wall (last column)
if (x === mazeCols - 1 && cell.walls[1]) {
var wall = new Wall();
wall.x = px + cellSize - wallThickness;
wall.y = py;
wall.y0 = py; // store original y
wall.width = wallThickness;
wall.height = wallLength;
walls.push(wall);
game.addChild(wall);
}
// Bottom wall (last row)
if (y === mazeRows - 1 && cell.walls[2]) {
var wall = new Wall();
wall.x = px;
wall.y = py + cellSize - wallThickness;
wall.y0 = py + cellSize - wallThickness; // store original y
wall.width = wallLength;
wall.height = wallThickness;
walls.push(wall);
game.addChild(wall);
}
}
}
// Place ball at top-left cell center
ball = new Ball();
var start = getCellCenter(0, 0);
ball.x = start.x;
ball.y = start.y;
ball.y0 = start.y; // store original y
game.addChild(ball);
lastBallCell.x = 0;
lastBallCell.y = 0;
// Place exit at bottom-right cell center
exit = new Exit();
var exitPos = getCellCenter(mazeCols - 1, mazeRows - 1);
exit.x = exitPos.x;
exit.y = exitPos.y;
exit.y0 = exitPos.y; // store original y
game.addChild(exit);
// Camera setup
cameraTargetX = ball.x;
cameraTargetY = ball.y;
game.x = 0;
game.y = 0;
isMoving = false;
moveQueue = [];
levelText.setText("Level " + level);
}
// --- Collision Detection ---
function ballIntersectsWall(nextX, nextY) {
var r = ball.radius;
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var wx = wall.x;
var wy = wall.y;
var ww = wall.width;
var wh = wall.height;
// Circle-rectangle collision
var closestX = clamp(nextX, wx, wx + ww);
var closestY = clamp(nextY, wy, wy + wh);
var dx = nextX - closestX;
var dy = nextY - closestY;
if (dx * dx + dy * dy < r * r - 1) {
return true;
}
}
return false;
}
function ballAtExit() {
var dx = ball.x - exit.x;
var dy = ball.y - exit.y;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < ball.radius + exit.width / 2 - 8;
}
// --- Ball Movement ---
function tryMoveBallTo(cellX, cellY) {
if (isMoving) {
moveQueue.push({
x: cellX,
y: cellY
});
return;
}
var from = getCellFromPos(ball.x, ball.y);
if (cellX === from.x && cellY === from.y) {
return;
}
// Only allow moving to adjacent cell (no diagonal)
var dx = cellX - from.x;
var dy = cellY - from.y;
if (Math.abs(dx) + Math.abs(dy) !== 1) {
return;
}
// Check for wall between cells
var cell = maze.cell(from.x, from.y);
if (dx === 1 && cell.walls[1]) {
return;
}
if (dx === -1 && cell.walls[3]) {
return;
}
if (dy === 1 && cell.walls[2]) {
return;
}
if (dy === -1 && cell.walls[0]) {
return;
}
// Target position
var target = getCellCenter(cellX, cellY);
// Check collision at target
if (ballIntersectsWall(target.x, target.y)) {
return;
}
isMoving = true;
ball.x = target.x;
ball.y = target.y;
ball.y0 = target.y; // update original y
isMoving = false;
lastBallCell.x = cellX;
lastBallCell.y = cellY;
if (ballAtExit()) {
LK.setScore(currentLevel);
if (currentLevel >= MAZE_MAX_LEVEL) {
LK.showYouWin();
} else {
currentLevel++;
buildMaze(currentLevel);
}
} else if (moveQueue.length > 0) {
var next = moveQueue.shift();
tryMoveBallTo(next.x, next.y);
}
}
// --- Camera Follow ---
function updateCamera() {
// Center ball in viewport
var targetX = 2048 / 2 - ball.x;
var targetY = 2732 / 2 - ball.y;
// Clamp camera so maze stays in view
var minX = 2048 - (mazeOffsetX + mazeCols * cellSize);
var maxX = -mazeOffsetX;
var minY = 2732 - (mazeOffsetY + mazeRows * cellSize);
var maxY = -mazeOffsetY;
targetX = clamp(targetX, minX, maxX);
targetY = clamp(targetY, minY, maxY);
// Smooth follow
game.x += (targetX - game.x) * 0.18;
game.y += (targetY - game.y) * 0.18;
}
// --- Input Handling ---
game.down = function (x, y, obj) {
if (isMoving) {
return;
}
// Convert to game coordinates
var local = game.toLocal({
x: x,
y: y
});
var from = getCellFromPos(ball.x, ball.y);
var tx = local.x - ball.x;
var ty = local.y - ball.y;
var dir = null;
if (Math.abs(tx) > Math.abs(ty)) {
// Horizontal
if (tx > 0) {
dir = {
x: from.x + 1,
y: from.y
};
} else {
dir = {
x: from.x - 1,
y: from.y
};
}
} else {
// Vertical
if (ty > 0) {
dir = {
x: from.x,
y: from.y + 1
};
} else {
dir = {
x: from.x,
y: from.y - 1
};
}
}
if (dir) {
tryMoveBallTo(dir.x, dir.y);
}
};
// --- Arrow Button Controls ---
// Ok tuşları container
var controlsContainer = game.addChild(new Container());
// Tuşların boyutu ve aralık
var btnSize = 120;
var spacing = 20;
;
// --- Direction Labels at Top of Map ---
// --- Start Game ---
currentLevel = 1;
buildMaze(currentLevel);
LK.setScore(0);
;