/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_X;
self.y = gridY * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y;
self.targetX = self.x;
self.targetY = self.y;
};
self.moveToGrid = function (gridX, gridY) {
if (self.isMoving) return;
self.gridX = gridX;
self.gridY = gridY;
self.targetX = gridX * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_X;
self.targetY = gridY * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y;
self.isMoving = true;
tween(self, {
x: self.targetX,
y: self.targetY
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isMoving = false;
}
});
};
return self;
});
var MazeCell = Container.expand(function () {
var self = Container.call(this);
self.cellType = 'wall';
self.gridX = 0;
self.gridY = 0;
self.cellGraphics = null;
self.setCellType = function (type) {
if (self.cellGraphics) {
self.removeChild(self.cellGraphics);
}
self.cellType = type;
var assetName = type;
self.cellGraphics = self.attachAsset(assetName, {
anchorX: 0,
anchorY: 0
});
};
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * CELL_SIZE + MAZE_OFFSET_X;
self.y = gridY * CELL_SIZE + MAZE_OFFSET_Y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1A1A1A
});
/****
* Game Code
****/
var MAZE_WIDTH = 13;
var MAZE_HEIGHT = 17;
var CELL_SIZE = 80;
var MAZE_OFFSET_X = (2048 - MAZE_WIDTH * CELL_SIZE) / 2;
var MAZE_OFFSET_Y = (2732 - MAZE_HEIGHT * CELL_SIZE) / 2;
var currentLevel = 1;
var maze = [];
var mazeCells = [];
var ball = null;
var entranceX = 1;
var entranceY = 1;
var nextLevelExits = [];
var prevLevelExits = [];
var dragNode = null;
var moveCount = 0;
var levelTxt = new Text2('Level: ' + currentLevel, {
size: 80,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 100;
var moveCountTxt = new Text2('Moves: ' + moveCount, {
size: 60,
fill: 0xFFFFFF
});
moveCountTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(moveCountTxt);
moveCountTxt.x = -20;
moveCountTxt.y = 20;
function generateMaze() {
maze = [];
for (var y = 0; y < MAZE_HEIGHT; y++) {
maze[y] = [];
for (var x = 0; x < MAZE_WIDTH; x++) {
maze[y][x] = 'wall';
}
}
// Generate 3 exits to next level and 3 exits to previous level
var nextLevelExits = [];
var prevLevelExits = [];
// Generate 3 exits to next level
for (var i = 0; i < 3; i++) {
var exitX, exitY;
do {
exitX = 1 + Math.floor(Math.random() * (MAZE_WIDTH - 2));
exitY = 1 + Math.floor(Math.random() * (MAZE_HEIGHT - 2));
} while (exitX === entranceX && exitY === entranceY || isExitPositionTaken(exitX, exitY, nextLevelExits, prevLevelExits));
nextLevelExits.push({
x: exitX,
y: exitY
});
}
// Generate 3 exits to previous level (only if not on level 1)
if (currentLevel > 1) {
for (var i = 0; i < 3; i++) {
var exitX, exitY;
do {
exitX = 1 + Math.floor(Math.random() * (MAZE_WIDTH - 2));
exitY = 1 + Math.floor(Math.random() * (MAZE_HEIGHT - 2));
} while (exitX === entranceX && exitY === entranceY || isExitPositionTaken(exitX, exitY, nextLevelExits, prevLevelExits));
prevLevelExits.push({
x: exitX,
y: exitY
});
}
}
var complexity = Math.min(currentLevel, 10);
var pathCount = 3 + complexity;
maze[entranceY][entranceX] = 'entrance';
// Place next level exits
for (var i = 0; i < nextLevelExits.length; i++) {
maze[nextLevelExits[i].y][nextLevelExits[i].x] = 'exit';
}
// Place previous level exits
for (var i = 0; i < prevLevelExits.length; i++) {
maze[prevLevelExits[i].y][prevLevelExits[i].x] = 'prev_exit';
}
var paths = [{
x: entranceX,
y: entranceY
}, {
x: exitX,
y: exitY
}];
for (var i = 0; i < pathCount; i++) {
var x = 1 + Math.floor(Math.random() * (MAZE_WIDTH - 2));
var y = 1 + Math.floor(Math.random() * (MAZE_HEIGHT - 2));
if (maze[y][x] === 'wall') {
maze[y][x] = 'path';
paths.push({
x: x,
y: y
});
}
}
for (var p = 0; p < paths.length - 1; p++) {
var start = paths[p];
var end = paths[p + 1];
createPath(start.x, start.y, end.x, end.y);
}
var extraConnections = Math.floor(complexity / 2);
for (var e = 0; e < extraConnections; e++) {
var start = paths[Math.floor(Math.random() * paths.length)];
var end = paths[Math.floor(Math.random() * paths.length)];
if (start !== end) {
createPath(start.x, start.y, end.x, end.y);
}
}
}
function isExitPositionTaken(x, y, nextExits, prevExits) {
for (var i = 0; i < nextExits.length; i++) {
if (nextExits[i].x === x && nextExits[i].y === y) return true;
}
for (var i = 0; i < prevExits.length; i++) {
if (prevExits[i].x === x && prevExits[i].y === y) return true;
}
return false;
}
function createPath(x1, y1, x2, y2) {
var currentX = x1;
var currentY = y1;
while (currentX !== x2 || currentY !== y2) {
if (maze[currentY] && maze[currentY][currentX] === 'wall') {
maze[currentY][currentX] = 'path';
}
if (currentX < x2) currentX++;else if (currentX > x2) currentX--;else if (currentY < y2) currentY++;else if (currentY > y2) currentY--;
}
if (maze[currentY] && maze[currentY][currentX] === 'wall') {
maze[currentY][currentX] = 'path';
}
}
function createMazeVisuals() {
for (var i = mazeCells.length - 1; i >= 0; i--) {
mazeCells[i].destroy();
}
mazeCells = [];
for (var y = 0; y < MAZE_HEIGHT; y++) {
for (var x = 0; x < MAZE_WIDTH; x++) {
var cell = new MazeCell();
cell.setGridPosition(x, y);
cell.setCellType(maze[y][x]);
mazeCells.push(cell);
game.addChild(cell);
}
}
}
function initializeLevel() {
generateMaze();
createMazeVisuals();
if (ball) {
ball.destroy();
}
ball = new Ball();
ball.setGridPosition(entranceX, entranceY);
game.addChild(ball);
levelTxt.setText('Level: ' + currentLevel);
moveCountTxt.setText('Moves: ' + moveCount);
}
function isValidMove(gridX, gridY) {
if (gridX < 0 || gridX >= MAZE_WIDTH || gridY < 0 || gridY >= MAZE_HEIGHT) {
return false;
}
return maze[gridY][gridX] !== 'wall';
}
function getGridFromPosition(x, y) {
var gridX = Math.floor((x - MAZE_OFFSET_X) / CELL_SIZE);
var gridY = Math.floor((y - MAZE_OFFSET_Y) / CELL_SIZE);
return {
x: gridX,
y: gridY
};
}
function handleMove(x, y, obj) {
if (dragNode && !ball.isMoving) {
var grid = getGridFromPosition(x, y);
if (isValidMove(grid.x, grid.y)) {
moveCount++;
moveCountTxt.setText('Moves: ' + moveCount);
ball.moveToGrid(grid.x, grid.y);
if (maze[grid.y][grid.x] === 'exit') {
LK.getSound('exitSound').play();
LK.setTimeout(function () {
currentLevel++;
storage.currentLevel = currentLevel;
LK.getSound('levelComplete').play();
initializeLevel();
LK.playMusic('backgroundMusic');
}, 300);
} else if (maze[grid.y][grid.x] === 'prev_exit' && currentLevel > 1) {
LK.getSound('prevExitSound').play();
LK.setTimeout(function () {
currentLevel--;
storage.currentLevel = currentLevel;
LK.getSound('levelComplete').play();
initializeLevel();
}, 300);
}
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = ball;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
initializeLevel(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_X;
self.y = gridY * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y;
self.targetX = self.x;
self.targetY = self.y;
};
self.moveToGrid = function (gridX, gridY) {
if (self.isMoving) return;
self.gridX = gridX;
self.gridY = gridY;
self.targetX = gridX * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_X;
self.targetY = gridY * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y;
self.isMoving = true;
tween(self, {
x: self.targetX,
y: self.targetY
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isMoving = false;
}
});
};
return self;
});
var MazeCell = Container.expand(function () {
var self = Container.call(this);
self.cellType = 'wall';
self.gridX = 0;
self.gridY = 0;
self.cellGraphics = null;
self.setCellType = function (type) {
if (self.cellGraphics) {
self.removeChild(self.cellGraphics);
}
self.cellType = type;
var assetName = type;
self.cellGraphics = self.attachAsset(assetName, {
anchorX: 0,
anchorY: 0
});
};
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * CELL_SIZE + MAZE_OFFSET_X;
self.y = gridY * CELL_SIZE + MAZE_OFFSET_Y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1A1A1A
});
/****
* Game Code
****/
var MAZE_WIDTH = 13;
var MAZE_HEIGHT = 17;
var CELL_SIZE = 80;
var MAZE_OFFSET_X = (2048 - MAZE_WIDTH * CELL_SIZE) / 2;
var MAZE_OFFSET_Y = (2732 - MAZE_HEIGHT * CELL_SIZE) / 2;
var currentLevel = 1;
var maze = [];
var mazeCells = [];
var ball = null;
var entranceX = 1;
var entranceY = 1;
var nextLevelExits = [];
var prevLevelExits = [];
var dragNode = null;
var moveCount = 0;
var levelTxt = new Text2('Level: ' + currentLevel, {
size: 80,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 100;
var moveCountTxt = new Text2('Moves: ' + moveCount, {
size: 60,
fill: 0xFFFFFF
});
moveCountTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(moveCountTxt);
moveCountTxt.x = -20;
moveCountTxt.y = 20;
function generateMaze() {
maze = [];
for (var y = 0; y < MAZE_HEIGHT; y++) {
maze[y] = [];
for (var x = 0; x < MAZE_WIDTH; x++) {
maze[y][x] = 'wall';
}
}
// Generate 3 exits to next level and 3 exits to previous level
var nextLevelExits = [];
var prevLevelExits = [];
// Generate 3 exits to next level
for (var i = 0; i < 3; i++) {
var exitX, exitY;
do {
exitX = 1 + Math.floor(Math.random() * (MAZE_WIDTH - 2));
exitY = 1 + Math.floor(Math.random() * (MAZE_HEIGHT - 2));
} while (exitX === entranceX && exitY === entranceY || isExitPositionTaken(exitX, exitY, nextLevelExits, prevLevelExits));
nextLevelExits.push({
x: exitX,
y: exitY
});
}
// Generate 3 exits to previous level (only if not on level 1)
if (currentLevel > 1) {
for (var i = 0; i < 3; i++) {
var exitX, exitY;
do {
exitX = 1 + Math.floor(Math.random() * (MAZE_WIDTH - 2));
exitY = 1 + Math.floor(Math.random() * (MAZE_HEIGHT - 2));
} while (exitX === entranceX && exitY === entranceY || isExitPositionTaken(exitX, exitY, nextLevelExits, prevLevelExits));
prevLevelExits.push({
x: exitX,
y: exitY
});
}
}
var complexity = Math.min(currentLevel, 10);
var pathCount = 3 + complexity;
maze[entranceY][entranceX] = 'entrance';
// Place next level exits
for (var i = 0; i < nextLevelExits.length; i++) {
maze[nextLevelExits[i].y][nextLevelExits[i].x] = 'exit';
}
// Place previous level exits
for (var i = 0; i < prevLevelExits.length; i++) {
maze[prevLevelExits[i].y][prevLevelExits[i].x] = 'prev_exit';
}
var paths = [{
x: entranceX,
y: entranceY
}, {
x: exitX,
y: exitY
}];
for (var i = 0; i < pathCount; i++) {
var x = 1 + Math.floor(Math.random() * (MAZE_WIDTH - 2));
var y = 1 + Math.floor(Math.random() * (MAZE_HEIGHT - 2));
if (maze[y][x] === 'wall') {
maze[y][x] = 'path';
paths.push({
x: x,
y: y
});
}
}
for (var p = 0; p < paths.length - 1; p++) {
var start = paths[p];
var end = paths[p + 1];
createPath(start.x, start.y, end.x, end.y);
}
var extraConnections = Math.floor(complexity / 2);
for (var e = 0; e < extraConnections; e++) {
var start = paths[Math.floor(Math.random() * paths.length)];
var end = paths[Math.floor(Math.random() * paths.length)];
if (start !== end) {
createPath(start.x, start.y, end.x, end.y);
}
}
}
function isExitPositionTaken(x, y, nextExits, prevExits) {
for (var i = 0; i < nextExits.length; i++) {
if (nextExits[i].x === x && nextExits[i].y === y) return true;
}
for (var i = 0; i < prevExits.length; i++) {
if (prevExits[i].x === x && prevExits[i].y === y) return true;
}
return false;
}
function createPath(x1, y1, x2, y2) {
var currentX = x1;
var currentY = y1;
while (currentX !== x2 || currentY !== y2) {
if (maze[currentY] && maze[currentY][currentX] === 'wall') {
maze[currentY][currentX] = 'path';
}
if (currentX < x2) currentX++;else if (currentX > x2) currentX--;else if (currentY < y2) currentY++;else if (currentY > y2) currentY--;
}
if (maze[currentY] && maze[currentY][currentX] === 'wall') {
maze[currentY][currentX] = 'path';
}
}
function createMazeVisuals() {
for (var i = mazeCells.length - 1; i >= 0; i--) {
mazeCells[i].destroy();
}
mazeCells = [];
for (var y = 0; y < MAZE_HEIGHT; y++) {
for (var x = 0; x < MAZE_WIDTH; x++) {
var cell = new MazeCell();
cell.setGridPosition(x, y);
cell.setCellType(maze[y][x]);
mazeCells.push(cell);
game.addChild(cell);
}
}
}
function initializeLevel() {
generateMaze();
createMazeVisuals();
if (ball) {
ball.destroy();
}
ball = new Ball();
ball.setGridPosition(entranceX, entranceY);
game.addChild(ball);
levelTxt.setText('Level: ' + currentLevel);
moveCountTxt.setText('Moves: ' + moveCount);
}
function isValidMove(gridX, gridY) {
if (gridX < 0 || gridX >= MAZE_WIDTH || gridY < 0 || gridY >= MAZE_HEIGHT) {
return false;
}
return maze[gridY][gridX] !== 'wall';
}
function getGridFromPosition(x, y) {
var gridX = Math.floor((x - MAZE_OFFSET_X) / CELL_SIZE);
var gridY = Math.floor((y - MAZE_OFFSET_Y) / CELL_SIZE);
return {
x: gridX,
y: gridY
};
}
function handleMove(x, y, obj) {
if (dragNode && !ball.isMoving) {
var grid = getGridFromPosition(x, y);
if (isValidMove(grid.x, grid.y)) {
moveCount++;
moveCountTxt.setText('Moves: ' + moveCount);
ball.moveToGrid(grid.x, grid.y);
if (maze[grid.y][grid.x] === 'exit') {
LK.getSound('exitSound').play();
LK.setTimeout(function () {
currentLevel++;
storage.currentLevel = currentLevel;
LK.getSound('levelComplete').play();
initializeLevel();
LK.playMusic('backgroundMusic');
}, 300);
} else if (maze[grid.y][grid.x] === 'prev_exit' && currentLevel > 1) {
LK.getSound('prevExitSound').play();
LK.setTimeout(function () {
currentLevel--;
storage.currentLevel = currentLevel;
LK.getSound('levelComplete').play();
initializeLevel();
}, 300);
}
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = ball;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
initializeLevel();