User prompt
Move it to the left
User prompt
The select menu is off screen
User prompt
It’s off screen
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'pacman.nextDir = dir;' Line Number: 568
User prompt
Move it on screen
User prompt
Add a maze selection screen before the maze loads
User prompt
More content
User prompt
When the Shostakovich respawn after being eaten, they phase through the walls off screen
User prompt
Now the fright ghosts kill yoy
User prompt
Now the fright ghosts kill yoy
User prompt
The ghosts leave the screen when you attack them in fright mode
User prompt
Make that gap bigger
User prompt
Can you add an area where the enemies can escape from spawn
User prompt
Fix what you just explained
User prompt
Make a way to get in the middle and the top center of the maze
User prompt
Open up every area
User prompt
Delete the wall at (column 10, row 17)
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'MAZE[row][col]')' in or related to this line: 'if (typeof MAZE[row] === "undefined") {' Line Number: 205
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'MAZE[row][col]')' in or related to this line: 'var cell = MAZE[row][col];' Line Number: 204
User prompt
Delete 10,10
User prompt
Delete the wall at 8,10
User prompt
Delete 10,8
User prompt
Can you delete the wall at (column 10, row 7)
User prompt
Prevent points from spawning where enemies spawn (middle)
User prompt
I still can access some areas; too, middle, bottom
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ghost class var Ghost = Container.expand(function () { var self = Container.call(this); self.colorId = 'ghost_red'; // default, will be set on init self.frightened = false; self.dir = { x: 0, y: 0 }; self.speed = 7; self.gridX = 0; self.gridY = 0; self.scatterTarget = { x: 0, y: 0 }; // for future AI self.mode = 'chase'; // 'chase', 'scatter', 'frightened' self.frightTimer = 0; self.attach = function (colorId) { self.colorId = colorId; self.removeChildren(); var ghostGfx = self.attachAsset(colorId, { anchorX: 0.5, anchorY: 0.5 }); }; self.setFrightened = function (on) { self.frightened = on; self.removeChildren(); if (on) { self.attachAsset('ghost_fright', { anchorX: 0.5, anchorY: 0.5 }); } else { self.attachAsset(self.colorId, { anchorX: 0.5, anchorY: 0.5 }); } }; self.update = function () { self.x += self.dir.x * self.speed; self.y += self.dir.y * self.speed; }; return self; }); // Pacman class var Pacman = Container.expand(function () { var self = Container.call(this); var pacmanGfx = self.attachAsset('pacman', { anchorX: 0.5, anchorY: 0.5 }); self.radius = pacmanGfx.width / 2; self.dir = { x: 1, y: 0 }; // Start moving right self.nextDir = { x: 1, y: 0 }; self.speed = 8; // pixels per tick self.gridX = 0; self.gridY = 0; self.moving = true; self.update = function () { if (!self.moving) return; // Move Pacman self.x += self.dir.x * self.speed; self.y += self.dir.y * self.speed; }; return self; }); // Pellet class var Pellet = Container.expand(function () { var self = Container.call(this); self.isPower = false; self.attach = function (isPower) { self.isPower = isPower; self.removeChildren(); if (isPower) { self.attachAsset('powerpellet', { anchorX: 0.5, anchorY: 0.5 }); } else { self.attachAsset('pellet', { anchorX: 0.5, anchorY: 0.5 }); } }; return self; }); // Wall class var Wall = Container.expand(function () { var self = Container.call(this); self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // 19x23 grid (classic Pacman aspect, fits 2048x2732 well) // 0: empty, 1: wall, 2: pellet, 3: power pellet // --- Maze Layout --- // Pacman (yellow circle) // Pellet (small white dot) // Power Pellet (bigger blue dot) // Wall (blue box) // Ghosts (four colors) // Frightened ghost (white) var MAZE_ROWS = 23; var MAZE_COLS = 19; var CELL_SIZE = 112; // 19*112=2128, 23*112=2576, fits in 2048x2732 with margin var MAZE_OFFSET_X = (2048 - MAZE_COLS * CELL_SIZE) / 2; var MAZE_OFFSET_Y = (2732 - MAZE_ROWS * CELL_SIZE) / 2 + 40; // Simple Pacman-like maze (minimal, for MVP) var MAZE = [ // Open up top center (row 0, col 9) [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1], [1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1], [1, 3, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 1, 3, 1, 1], [1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 2, 1], [1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1], // Open up middle (row 9, col 9,10,11) [1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1], // Open up center of row 11 (row 11, col 9) [1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1], // Open up middle (row 15, col 9) [1, 3, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 3, 1], // Open up bottom center (row 16, col 9) [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 1, 2, 2, 1], [1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1], // Open up bottom center (row 21, col 9) [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], // Bottom row (row 22) remains closed for border [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]; // --- Game State --- var pellets = []; var walls = []; var ghosts = []; var pacman = null; var score = 0; var pelletsLeft = 0; var frightTicks = 0; var frightActive = false; var frightDuration = 360; // 6 seconds at 60fps var swipeStart = null; var dragNode = null; var lastGameOver = false; // --- Score Display --- var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Maze Build --- function buildMaze() { // Clear previous for (var i = 0; i < pellets.length; ++i) pellets[i].destroy(); for (var i = 0; i < walls.length; ++i) walls[i].destroy(); pellets = []; walls = []; pelletsLeft = 0; for (var row = 0; row < MAZE_ROWS; ++row) { for (var col = 0; col < MAZE_COLS; ++col) { var cell = MAZE[row][col]; var x = MAZE_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2; var y = MAZE_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2; if (cell === 1) { var wall = new Wall(); wall.x = x; wall.y = y; game.addChild(wall); walls.push(wall); } else if (cell === 2 || cell === 3) { var pellet = new Pellet(); pellet.x = x; pellet.y = y; pellet.attach(cell === 3); game.addChild(pellet); pellets.push(pellet); pelletsLeft++; } } } } // --- Pacman Init --- function spawnPacman() { if (pacman) pacman.destroy(); pacman = new Pacman(); // Start position: center of maze, row 15, col 9 pacman.gridX = 9; pacman.gridY = 15; pacman.x = MAZE_OFFSET_X + pacman.gridX * CELL_SIZE + CELL_SIZE / 2; pacman.y = MAZE_OFFSET_Y + pacman.gridY * CELL_SIZE + CELL_SIZE / 2; pacman.dir = { x: 1, y: 0 }; pacman.nextDir = { x: 1, y: 0 }; pacman.moving = true; game.addChild(pacman); } // --- Ghosts Init --- function spawnGhosts() { // Remove old ghosts for (var i = 0; i < ghosts.length; ++i) ghosts[i].destroy(); ghosts = []; // Four ghosts, different colors, start in center var ghostColors = ['ghost_red', 'ghost_pink', 'ghost_blue', 'ghost_orange']; var ghostStarts = [{ col: 9, row: 11 }, { col: 8, row: 11 }, { col: 10, row: 11 }, { col: 9, row: 12 }]; for (var i = 0; i < 4; ++i) { var ghost = new Ghost(); ghost.attach(ghostColors[i]); ghost.gridX = ghostStarts[i].col; ghost.gridY = ghostStarts[i].row; ghost.x = MAZE_OFFSET_X + ghost.gridX * CELL_SIZE + CELL_SIZE / 2; ghost.y = MAZE_OFFSET_Y + ghost.gridY * CELL_SIZE + CELL_SIZE / 2; // Initial direction: up or left/right if (i === 0) ghost.dir = { x: 0, y: -1 };else if (i === 1) ghost.dir = { x: -1, y: 0 };else if (i === 2) ghost.dir = { x: 1, y: 0 };else ghost.dir = { x: 0, y: 1 }; ghost.frightened = false; ghosts.push(ghost); game.addChild(ghost); } } // --- Utility: Grid <-> Pixel --- function posToGrid(x, y) { var col = Math.floor((x - MAZE_OFFSET_X) / CELL_SIZE); var row = Math.floor((y - MAZE_OFFSET_Y) / CELL_SIZE); return { col: col, row: row }; } function gridToPos(col, row) { return { x: MAZE_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2, y: MAZE_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2 }; } function isWall(col, row) { if (col < 0 || col >= MAZE_COLS || row < 0 || row >= MAZE_ROWS) return true; return MAZE[row][col] === 1; } // --- Pacman Movement & Input --- function canMove(col, row, dir) { var nextCol = col + dir.x; var nextRow = row + dir.y; return !isWall(nextCol, nextRow); } function updatePacmanDirection() { var grid = posToGrid(pacman.x, pacman.y); // Snap to center of cell var center = gridToPos(grid.col, grid.row); var dx = Math.abs(pacman.x - center.x); var dy = Math.abs(pacman.y - center.y); if (dx < 4 && dy < 4) { // At center, can change direction if (canMove(grid.col, grid.row, pacman.nextDir)) { pacman.dir = { x: pacman.nextDir.x, y: pacman.nextDir.y }; } // If can't move in current dir, stop if (!canMove(grid.col, grid.row, pacman.dir)) { pacman.moving = false; } else { pacman.moving = true; } // Snap to center pacman.x = center.x; pacman.y = center.y; } } // --- Ghost Movement (Random for MVP, smarter later) --- function getValidGhostDirs(ghost) { var grid = posToGrid(ghost.x, ghost.y); var dirs = [{ x: 1, y: 0 }, { x: -1, y: 0 }, { x: 0, y: 1 }, { x: 0, y: -1 }]; var valid = []; for (var i = 0; i < dirs.length; ++i) { var d = dirs[i]; // Don't reverse direction if (ghost.dir.x === -d.x && ghost.dir.y === -d.y) continue; if (!isWall(grid.col + d.x, grid.row + d.y)) valid.push(d); } return valid; } function updateGhostDirection(ghost) { var grid = posToGrid(ghost.x, ghost.y); var center = gridToPos(grid.col, grid.row); var dx = Math.abs(ghost.x - center.x); var dy = Math.abs(ghost.y - center.y); if (dx < 4 && dy < 4) { // At center, pick new direction var valid = getValidGhostDirs(ghost); if (valid.length === 0) { ghost.dir = { x: -ghost.dir.x, y: -ghost.dir.y }; } else { // MVP: random direction var idx = Math.floor(Math.random() * valid.length); ghost.dir = { x: valid[idx].x, y: valid[idx].y }; } // Snap to center ghost.x = center.x; ghost.y = center.y; } } // --- Pellet Eating --- function checkPelletEat() { for (var i = pellets.length - 1; i >= 0; --i) { var pellet = pellets[i]; var dx = pacman.x - pellet.x; var dy = pacman.y - pellet.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 48) { // Eat pellet if (pellet.isPower) { frightTicks = frightDuration; frightActive = true; for (var g = 0; g < ghosts.length; ++g) { ghosts[g].setFrightened(true); } } pellet.destroy(); pellets.splice(i, 1); pelletsLeft--; score += pellet.isPower ? 50 : 10; scoreTxt.setText(score); LK.setScore(score); if (pelletsLeft === 0) { LK.showYouWin(); } } } } // --- Ghost Collisions --- function checkGhostCollisions() { for (var i = 0; i < ghosts.length; ++i) { var ghost = ghosts[i]; var dx = pacman.x - ghost.x; var dy = pacman.y - ghost.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 60) { if (ghost.frightened) { // Eat ghost ghost.setFrightened(false); // Send ghost to home var home = gridToPos(9, 11); ghost.x = home.x; ghost.y = home.y; ghost.dir = { x: 0, y: -1 }; score += 200; scoreTxt.setText(score); LK.setScore(score); } else { // Pacman dies if (!lastGameOver) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); lastGameOver = true; } } } } } // --- Frightened Mode Timer --- function updateFrightened() { if (frightActive) { frightTicks--; if (frightTicks <= 0) { frightActive = false; for (var g = 0; g < ghosts.length; ++g) { ghosts[g].setFrightened(false); } } } } // --- Input: Swipe to set direction --- function getSwipeDir(start, end) { var dx = end.x - start.x; var dy = end.y - start.y; if (Math.abs(dx) > Math.abs(dy)) { if (dx > 32) return { x: 1, y: 0 }; if (dx < -32) return { x: -1, y: 0 }; } else { if (dy > 32) return { x: 0, y: 1 }; if (dy < -32) return { x: 0, y: -1 }; } return null; } game.down = function (x, y, obj) { swipeStart = { x: x, y: y }; dragNode = pacman; }; game.move = function (x, y, obj) { if (!swipeStart) return; var swipeEnd = { x: x, y: y }; var dir = getSwipeDir(swipeStart, swipeEnd); if (dir) { pacman.nextDir = dir; swipeStart = null; } }; game.up = function (x, y, obj) { swipeStart = null; dragNode = null; }; // --- Game Update Loop --- game.update = function () { if (!pacman) return; lastGameOver = false; updatePacmanDirection(); pacman.update(); for (var i = 0; i < ghosts.length; ++i) { updateGhostDirection(ghosts[i]); ghosts[i].update(); } checkPelletEat(); checkGhostCollisions(); updateFrightened(); }; // --- Game Start --- function startGame() { score = 0; LK.setScore(0); scoreTxt.setText('0'); buildMaze(); spawnPacman(); spawnGhosts(); frightTicks = 0; frightActive = false; lastGameOver = false; } startGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ghost class
var Ghost = Container.expand(function () {
var self = Container.call(this);
self.colorId = 'ghost_red'; // default, will be set on init
self.frightened = false;
self.dir = {
x: 0,
y: 0
};
self.speed = 7;
self.gridX = 0;
self.gridY = 0;
self.scatterTarget = {
x: 0,
y: 0
}; // for future AI
self.mode = 'chase'; // 'chase', 'scatter', 'frightened'
self.frightTimer = 0;
self.attach = function (colorId) {
self.colorId = colorId;
self.removeChildren();
var ghostGfx = self.attachAsset(colorId, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.setFrightened = function (on) {
self.frightened = on;
self.removeChildren();
if (on) {
self.attachAsset('ghost_fright', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
self.attachAsset(self.colorId, {
anchorX: 0.5,
anchorY: 0.5
});
}
};
self.update = function () {
self.x += self.dir.x * self.speed;
self.y += self.dir.y * self.speed;
};
return self;
});
// Pacman class
var Pacman = Container.expand(function () {
var self = Container.call(this);
var pacmanGfx = self.attachAsset('pacman', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = pacmanGfx.width / 2;
self.dir = {
x: 1,
y: 0
}; // Start moving right
self.nextDir = {
x: 1,
y: 0
};
self.speed = 8; // pixels per tick
self.gridX = 0;
self.gridY = 0;
self.moving = true;
self.update = function () {
if (!self.moving) return;
// Move Pacman
self.x += self.dir.x * self.speed;
self.y += self.dir.y * self.speed;
};
return self;
});
// Pellet class
var Pellet = Container.expand(function () {
var self = Container.call(this);
self.isPower = false;
self.attach = function (isPower) {
self.isPower = isPower;
self.removeChildren();
if (isPower) {
self.attachAsset('powerpellet', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
self.attachAsset('pellet', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
return self;
});
// Wall class
var Wall = Container.expand(function () {
var self = Container.call(this);
self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// 19x23 grid (classic Pacman aspect, fits 2048x2732 well)
// 0: empty, 1: wall, 2: pellet, 3: power pellet
// --- Maze Layout ---
// Pacman (yellow circle)
// Pellet (small white dot)
// Power Pellet (bigger blue dot)
// Wall (blue box)
// Ghosts (four colors)
// Frightened ghost (white)
var MAZE_ROWS = 23;
var MAZE_COLS = 19;
var CELL_SIZE = 112; // 19*112=2128, 23*112=2576, fits in 2048x2732 with margin
var MAZE_OFFSET_X = (2048 - MAZE_COLS * CELL_SIZE) / 2;
var MAZE_OFFSET_Y = (2732 - MAZE_ROWS * CELL_SIZE) / 2 + 40;
// Simple Pacman-like maze (minimal, for MVP)
var MAZE = [
// Open up top center (row 0, col 9)
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1], [1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1], [1, 3, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 1, 3, 1, 1], [1, 2, 1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 2, 1], [1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1],
// Open up middle (row 9, col 9,10,11)
[1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1],
// Open up center of row 11 (row 11, col 9)
[1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1],
// Open up middle (row 15, col 9)
[1, 3, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 3, 1],
// Open up bottom center (row 16, col 9)
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 1, 2, 2, 1], [1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1],
// Open up bottom center (row 21, col 9)
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
// Bottom row (row 22) remains closed for border
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]];
// --- Game State ---
var pellets = [];
var walls = [];
var ghosts = [];
var pacman = null;
var score = 0;
var pelletsLeft = 0;
var frightTicks = 0;
var frightActive = false;
var frightDuration = 360; // 6 seconds at 60fps
var swipeStart = null;
var dragNode = null;
var lastGameOver = false;
// --- Score Display ---
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Maze Build ---
function buildMaze() {
// Clear previous
for (var i = 0; i < pellets.length; ++i) pellets[i].destroy();
for (var i = 0; i < walls.length; ++i) walls[i].destroy();
pellets = [];
walls = [];
pelletsLeft = 0;
for (var row = 0; row < MAZE_ROWS; ++row) {
for (var col = 0; col < MAZE_COLS; ++col) {
var cell = MAZE[row][col];
var x = MAZE_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2;
var y = MAZE_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2;
if (cell === 1) {
var wall = new Wall();
wall.x = x;
wall.y = y;
game.addChild(wall);
walls.push(wall);
} else if (cell === 2 || cell === 3) {
var pellet = new Pellet();
pellet.x = x;
pellet.y = y;
pellet.attach(cell === 3);
game.addChild(pellet);
pellets.push(pellet);
pelletsLeft++;
}
}
}
}
// --- Pacman Init ---
function spawnPacman() {
if (pacman) pacman.destroy();
pacman = new Pacman();
// Start position: center of maze, row 15, col 9
pacman.gridX = 9;
pacman.gridY = 15;
pacman.x = MAZE_OFFSET_X + pacman.gridX * CELL_SIZE + CELL_SIZE / 2;
pacman.y = MAZE_OFFSET_Y + pacman.gridY * CELL_SIZE + CELL_SIZE / 2;
pacman.dir = {
x: 1,
y: 0
};
pacman.nextDir = {
x: 1,
y: 0
};
pacman.moving = true;
game.addChild(pacman);
}
// --- Ghosts Init ---
function spawnGhosts() {
// Remove old ghosts
for (var i = 0; i < ghosts.length; ++i) ghosts[i].destroy();
ghosts = [];
// Four ghosts, different colors, start in center
var ghostColors = ['ghost_red', 'ghost_pink', 'ghost_blue', 'ghost_orange'];
var ghostStarts = [{
col: 9,
row: 11
}, {
col: 8,
row: 11
}, {
col: 10,
row: 11
}, {
col: 9,
row: 12
}];
for (var i = 0; i < 4; ++i) {
var ghost = new Ghost();
ghost.attach(ghostColors[i]);
ghost.gridX = ghostStarts[i].col;
ghost.gridY = ghostStarts[i].row;
ghost.x = MAZE_OFFSET_X + ghost.gridX * CELL_SIZE + CELL_SIZE / 2;
ghost.y = MAZE_OFFSET_Y + ghost.gridY * CELL_SIZE + CELL_SIZE / 2;
// Initial direction: up or left/right
if (i === 0) ghost.dir = {
x: 0,
y: -1
};else if (i === 1) ghost.dir = {
x: -1,
y: 0
};else if (i === 2) ghost.dir = {
x: 1,
y: 0
};else ghost.dir = {
x: 0,
y: 1
};
ghost.frightened = false;
ghosts.push(ghost);
game.addChild(ghost);
}
}
// --- Utility: Grid <-> Pixel ---
function posToGrid(x, y) {
var col = Math.floor((x - MAZE_OFFSET_X) / CELL_SIZE);
var row = Math.floor((y - MAZE_OFFSET_Y) / CELL_SIZE);
return {
col: col,
row: row
};
}
function gridToPos(col, row) {
return {
x: MAZE_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2,
y: MAZE_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2
};
}
function isWall(col, row) {
if (col < 0 || col >= MAZE_COLS || row < 0 || row >= MAZE_ROWS) return true;
return MAZE[row][col] === 1;
}
// --- Pacman Movement & Input ---
function canMove(col, row, dir) {
var nextCol = col + dir.x;
var nextRow = row + dir.y;
return !isWall(nextCol, nextRow);
}
function updatePacmanDirection() {
var grid = posToGrid(pacman.x, pacman.y);
// Snap to center of cell
var center = gridToPos(grid.col, grid.row);
var dx = Math.abs(pacman.x - center.x);
var dy = Math.abs(pacman.y - center.y);
if (dx < 4 && dy < 4) {
// At center, can change direction
if (canMove(grid.col, grid.row, pacman.nextDir)) {
pacman.dir = {
x: pacman.nextDir.x,
y: pacman.nextDir.y
};
}
// If can't move in current dir, stop
if (!canMove(grid.col, grid.row, pacman.dir)) {
pacman.moving = false;
} else {
pacman.moving = true;
}
// Snap to center
pacman.x = center.x;
pacman.y = center.y;
}
}
// --- Ghost Movement (Random for MVP, smarter later) ---
function getValidGhostDirs(ghost) {
var grid = posToGrid(ghost.x, ghost.y);
var dirs = [{
x: 1,
y: 0
}, {
x: -1,
y: 0
}, {
x: 0,
y: 1
}, {
x: 0,
y: -1
}];
var valid = [];
for (var i = 0; i < dirs.length; ++i) {
var d = dirs[i];
// Don't reverse direction
if (ghost.dir.x === -d.x && ghost.dir.y === -d.y) continue;
if (!isWall(grid.col + d.x, grid.row + d.y)) valid.push(d);
}
return valid;
}
function updateGhostDirection(ghost) {
var grid = posToGrid(ghost.x, ghost.y);
var center = gridToPos(grid.col, grid.row);
var dx = Math.abs(ghost.x - center.x);
var dy = Math.abs(ghost.y - center.y);
if (dx < 4 && dy < 4) {
// At center, pick new direction
var valid = getValidGhostDirs(ghost);
if (valid.length === 0) {
ghost.dir = {
x: -ghost.dir.x,
y: -ghost.dir.y
};
} else {
// MVP: random direction
var idx = Math.floor(Math.random() * valid.length);
ghost.dir = {
x: valid[idx].x,
y: valid[idx].y
};
}
// Snap to center
ghost.x = center.x;
ghost.y = center.y;
}
}
// --- Pellet Eating ---
function checkPelletEat() {
for (var i = pellets.length - 1; i >= 0; --i) {
var pellet = pellets[i];
var dx = pacman.x - pellet.x;
var dy = pacman.y - pellet.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 48) {
// Eat pellet
if (pellet.isPower) {
frightTicks = frightDuration;
frightActive = true;
for (var g = 0; g < ghosts.length; ++g) {
ghosts[g].setFrightened(true);
}
}
pellet.destroy();
pellets.splice(i, 1);
pelletsLeft--;
score += pellet.isPower ? 50 : 10;
scoreTxt.setText(score);
LK.setScore(score);
if (pelletsLeft === 0) {
LK.showYouWin();
}
}
}
}
// --- Ghost Collisions ---
function checkGhostCollisions() {
for (var i = 0; i < ghosts.length; ++i) {
var ghost = ghosts[i];
var dx = pacman.x - ghost.x;
var dy = pacman.y - ghost.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 60) {
if (ghost.frightened) {
// Eat ghost
ghost.setFrightened(false);
// Send ghost to home
var home = gridToPos(9, 11);
ghost.x = home.x;
ghost.y = home.y;
ghost.dir = {
x: 0,
y: -1
};
score += 200;
scoreTxt.setText(score);
LK.setScore(score);
} else {
// Pacman dies
if (!lastGameOver) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
lastGameOver = true;
}
}
}
}
}
// --- Frightened Mode Timer ---
function updateFrightened() {
if (frightActive) {
frightTicks--;
if (frightTicks <= 0) {
frightActive = false;
for (var g = 0; g < ghosts.length; ++g) {
ghosts[g].setFrightened(false);
}
}
}
}
// --- Input: Swipe to set direction ---
function getSwipeDir(start, end) {
var dx = end.x - start.x;
var dy = end.y - start.y;
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 32) return {
x: 1,
y: 0
};
if (dx < -32) return {
x: -1,
y: 0
};
} else {
if (dy > 32) return {
x: 0,
y: 1
};
if (dy < -32) return {
x: 0,
y: -1
};
}
return null;
}
game.down = function (x, y, obj) {
swipeStart = {
x: x,
y: y
};
dragNode = pacman;
};
game.move = function (x, y, obj) {
if (!swipeStart) return;
var swipeEnd = {
x: x,
y: y
};
var dir = getSwipeDir(swipeStart, swipeEnd);
if (dir) {
pacman.nextDir = dir;
swipeStart = null;
}
};
game.up = function (x, y, obj) {
swipeStart = null;
dragNode = null;
};
// --- Game Update Loop ---
game.update = function () {
if (!pacman) return;
lastGameOver = false;
updatePacmanDirection();
pacman.update();
for (var i = 0; i < ghosts.length; ++i) {
updateGhostDirection(ghosts[i]);
ghosts[i].update();
}
checkPelletEat();
checkGhostCollisions();
updateFrightened();
};
// --- Game Start ---
function startGame() {
score = 0;
LK.setScore(0);
scoreTxt.setText('0');
buildMaze();
spawnPacman();
spawnGhosts();
frightTicks = 0;
frightActive = false;
lastGameOver = false;
}
startGame();
Pacman red ghost. In-Game asset. 2d. High contrast. No shadows
Pacman pink ghost. In-Game asset. 2d. High contrast. No shadows
Pacman blue ghost. In-Game asset. 2d. High contrast. No shadows
Pacman orange ghost. In-Game asset. 2d. High contrast. No shadows
Pacman dark blue ghost with plain white eyes. In-Game asset. 2d. High contrast. No shadows
Pacman with mouth closed pixilated . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Pacman. In-Game asset. 2d. High contrast. shadows. Outline. Pixilated
Pacman with mouth opened pixilated . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Pacman cherry. In-Game asset. 2d. High contrast. No shadows
Pixilated ice cream. In-Game asset. 2d. High contrast. No shadows
Pixilated pizza. In-Game asset. 2d. High contrast. No shadows