User prompt
Make him disappear if you have the correct amount of pellets and take away your pellets
User prompt
Make him spawn on specific tiles that block off paths that Pacman has to get to
User prompt
Make it impossible to go through him like a wall
User prompt
Make him just spawn on an empty tile and stay there
User prompt
Pizza Delivery Ghost: Randomly appears in the maze, gets in the way, asks for tip in pellets 2000-3000. Just go along with what you did for the cherry but not despawn
User prompt
A lot bigger text
User prompt
Make the text slit bigger
User prompt
Make the text a more pixelated and bold font
User prompt
Add text to the maze menu that says Choose a maze
User prompt
Move the maze buttons up more
User prompt
Make the maze menu bugger
User prompt
Make the maze menu just a bit bugger
User prompt
Make the maze menu a bit bigger to fit all the buttons
User prompt
Move the maze selection button down a bit
User prompt
Pacman and ghosts spawn in maze 5 walls
User prompt
Add more content (no shop)
User prompt
Prevent enemies from phasing through walls in any way w
User prompt
They are still just floating off screen. Make them reset like the biggining of the gsme
User prompt
Fix enemies spawning after being killed by Pacman by putting the enemy back to spawn and act as normal instead of leaving the screen
User prompt
Make Pacman flip 180 degrees when moving left
User prompt
Make him upright when going left
User prompt
Make the player flip 90 degrees when moving left
User prompt
Make it disappear after consuming
User prompt
Make it so it doesn’t get stuck very easily
User prompt
Make it move in the maze like a ghost
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // BonusObject class: animates through 5 assets and moves around, gives 1000-2000 points var BonusObject = Container.expand(function () { var self = Container.call(this); self.assetNames = ['bonus1', 'bonus2', 'bonus3', 'bonus4', 'bonus5']; self.assetIndex = 0; self.bonusGfx = self.attachAsset(self.assetNames[0], { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.bonusGfx.width / 2; self.moveDir = { x: 1, y: 1 }; self.speed = 10 + Math.floor(Math.random() * 6); // 10-15 px/tick self.lastX = self.x; self.lastY = self.y; self._animTimer = LK.setInterval(function () { self.assetIndex = (self.assetIndex + 1) % self.assetNames.length; self.removeChildren(); self.bonusGfx = self.attachAsset(self.assetNames[self.assetIndex], { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.bonusGfx.width / 2; }, 250); self.destroy = function () { if (self._animTimer) { LK.clearInterval(self._animTimer); self._animTimer = null; } Container.prototype.destroy.call(self); }; self.update = function () { // Save last position for edge/collision logic self.lastX = self.x; self.lastY = self.y; // Move self.x += self.moveDir.x * self.speed; self.y += self.moveDir.y * self.speed; // Bounce off maze edges (keep inside play area) var minX = MAZE_OFFSET_X + self.radius; var maxX = MAZE_OFFSET_X + MAZE_COLS * CELL_SIZE - self.radius; var minY = MAZE_OFFSET_Y + self.radius; var maxY = MAZE_OFFSET_Y + MAZE_ROWS * CELL_SIZE - self.radius; if (self.x <= minX && self.moveDir.x < 0 || self.x >= maxX && self.moveDir.x > 0) { self.moveDir.x *= -1; self.x = Math.max(minX, Math.min(self.x, maxX)); } if (self.y <= minY && self.moveDir.y < 0 || self.y >= maxY && self.moveDir.y > 0) { self.moveDir.y *= -1; self.y = Math.max(minY, Math.min(self.y, maxY)); } }; return self; }); // Cherry class var Cherry = Container.expand(function () { var self = Container.call(this); self.attachAsset('cherry', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // 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 () { // Save last position for collision checks var prevX = self.x; var prevY = self.y; self.x += self.dir.x * self.speed; self.y += self.dir.y * self.speed; // Wall collision: check if ghost is inside a wall after moving, and correct if so var grid = posToGrid(self.x, self.y); if (isWall(grid.col, grid.row)) { // Undo movement and stop at wall edge self.x = prevX; self.y = prevY; // Snap to center of previous cell to avoid jitter var center = gridToPos(posToGrid(self.x, self.y).col, posToGrid(self.x, self.y).row); self.x = center.x; self.y = center.y; // Optionally, force ghost to pick a new direction next update } }; return self; }); // Pacman class var Pacman = Container.expand(function () { var self = Container.call(this); self.spriteIndex = 0; self.pacmanGfx = self.attachAsset('pacman', { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.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; // Animation: swap sprite every 0.5s self._animTimer = LK.setInterval(function () { self.spriteIndex = 1 - self.spriteIndex; self.removeChildren(); if (self.spriteIndex === 0) { self.pacmanGfx = self.attachAsset('pacman', { anchorX: 0.5, anchorY: 0.5 }); } else { self.pacmanGfx = self.attachAsset('pacman2', { anchorX: 0.5, anchorY: 0.5 }); } self.radius = self.pacmanGfx.width / 2; }, 500); self.destroy = function () { // Clean up animation timer if (self._animTimer) { LK.clearInterval(self._animTimer); self._animTimer = null; } Container.prototype.destroy.call(self); }; self.update = function () { if (!self.moving) return; // Move Pacman self.x += self.dir.x * self.speed; self.y += self.dir.y * self.speed; // Flip Pacman when moving left, reset otherwise if (self.dir.x < 0) { self.pacmanGfx.scaleX = -1; } else { self.pacmanGfx.scaleX = 1; } }; 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 ****/ // Frightened ghost (white) // Ghosts (four colors) // Wall (blue box) // Power Pellet (bigger blue dot) // Pellet (small white dot) // Pacman (yellow circle) // --- Maze Layout --- // 0: empty, 1: wall, 2: pellet, 3: power pellet // 19x23 grid (classic Pacman aspect, fits 2048x2732 well) 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; // --- Multiple Mazes for More Content --- var MAZES = []; // Maze 1 (original) MAZES[0] = [ // Open up top row for pellets: add pellets (2) and open up more wall spaces for access [1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 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], [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0], [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 0, 9,10,11, 18) [0, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 0], [0, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 0], // 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 0, 9, 18) [0, 3, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 3, 0], // Open up bottom center (row 16, col 0, 9, 18) [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 0], [0, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 2, 0], [0, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 1, 2, 2, 0], // Open up (column 10, row 17) [1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 0, 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], // Open up bottom row (row 22, col 9) [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]]; // Remove wall below ghost spawn (row 12, col 9) to allow escape MAZES[0][12][9] = 0; // Widen the gap below ghost spawn (row 13, cols 8, 9, 10) MAZES[0][13][8] = 0; MAZES[0][13][9] = 0; MAZES[0][13][10] = 0; // Maze 2 (significantly different, spiral and open center, more power pellets, fewer walls) MAZES[1] = [[1, 3, 2, 1, 2, 1, 2, 1, 2, 0, 2, 1, 2, 1, 2, 1, 2, 3, 1], [1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1], [1, 2, 1, 3, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 3, 1, 2, 1], [1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 2, 2, 1, 2, 1, 2, 2, 0, 2, 2, 1, 2, 1, 2, 2, 2, 1], [1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1], [0, 2, 2, 2, 1, 2, 2, 2, 2, 0, 2, 2, 2, 2, 1, 2, 2, 2, 0], [0, 1, 1, 2, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 2, 1, 1, 0], [0, 2, 2, 2, 2, 2, 2, 1, 2, 0, 2, 1, 2, 2, 2, 2, 2, 2, 0], [1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1], [1, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 3, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 3, 1, 2, 1], [1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1], [1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1], [1, 3, 2, 1, 2, 1, 2, 1, 2, 0, 2, 1, 2, 1, 2, 1, 2, 3, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]]; // Remove wall below ghost spawn (row 12, col 9) to allow escape MAZES[1][12][9] = 0; // Widen the gap below ghost spawn (row 13, cols 8, 9, 10) MAZES[1][13][8] = 0; MAZES[1][13][9] = 0; MAZES[1][13][10] = 0; // Maze 3 (new, more open, with corridors and more power pellets) MAZES[2] = [[1, 3, 2, 2, 2, 1, 1, 1, 2, 0, 2, 1, 1, 1, 2, 2, 2, 3, 1], [1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1], [1, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1], [1, 2, 1, 3, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 3, 1, 2, 1], [1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 2, 2, 1, 2, 1, 2, 2, 0, 2, 2, 1, 2, 1, 2, 2, 2, 1], [1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1], [0, 2, 2, 2, 1, 2, 2, 2, 2, 0, 2, 2, 2, 2, 1, 2, 2, 2, 0], [0, 1, 1, 2, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 2, 1, 1, 0], [0, 2, 2, 2, 2, 2, 2, 1, 2, 0, 2, 1, 2, 2, 2, 2, 2, 2, 0], [1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1], [1, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 3, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 3, 1, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1], [1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1], [1, 3, 2, 2, 2, 1, 1, 1, 2, 0, 2, 1, 1, 1, 2, 2, 2, 3, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]]; // Remove wall below ghost spawn (row 12, col 9) to allow escape MAZES[2][12][9] = 0; // Widen the gap below ghost spawn (row 13, cols 8, 9, 10) MAZES[2][13][8] = 0; MAZES[2][13][9] = 0; MAZES[2][13][10] = 0; // Maze 4 (unique: zig-zag corridors, more open, more power pellets, new pattern) MAZES[3] = [[1, 3, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2, 3, 1], [1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1], [1, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1], [1, 2, 1, 3, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 3, 1, 2, 1], [1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 2, 2, 1, 2, 1, 2, 2, 0, 2, 2, 1, 2, 1, 2, 2, 2, 1], [1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1], [0, 2, 2, 2, 1, 2, 2, 2, 2, 0, 2, 2, 2, 2, 1, 2, 2, 2, 0], [0, 1, 1, 2, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 2, 1, 1, 0], [0, 2, 2, 2, 2, 2, 2, 1, 2, 0, 2, 1, 2, 2, 2, 2, 2, 2, 0], [1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1], [1, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 3, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 3, 1, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1], [1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1], [1, 3, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2, 3, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]]; // Remove wall below ghost spawn (row 12, col 9) to allow escape MAZES[3][12][9] = 0; // Widen the gap below ghost spawn (row 13, cols 8, 9, 10) MAZES[3][13][8] = 0; MAZES[3][13][9] = 0; MAZES[3][13][10] = 0; // Maze 5 (new: diamond pattern, more open, more power pellets, unique layout) MAZES[4] = [[1, 3, 2, 2, 2, 1, 1, 1, 2, 0, 2, 1, 1, 1, 2, 2, 2, 3, 1], [1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1], [1, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1], [1, 2, 1, 3, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 3, 1, 2, 1], [1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 2, 2, 1, 2, 1, 2, 2, 0, 2, 2, 1, 2, 1, 2, 2, 2, 1], [1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1], [0, 2, 2, 2, 1, 2, 2, 2, 2, 0, 2, 2, 2, 2, 1, 2, 2, 2, 0], [0, 1, 1, 2, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 2, 1, 1, 0], [0, 2, 2, 2, 2, 2, 2, 1, 2, 0, 2, 1, 2, 2, 2, 2, 2, 2, 0], [1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1], [1, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 1], [1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1], [1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1], [1, 2, 1, 3, 2, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2, 3, 1, 2, 1], [1, 2, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 2, 1], [1, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1], [1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1], [1, 3, 2, 2, 2, 1, 1, 1, 2, 0, 2, 1, 1, 1, 2, 2, 2, 3, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]]; // Remove wall below ghost spawn (row 12, col 9) to allow escape MAZES[4][12][9] = 0; // Widen the gap below ghost spawn (row 13, cols 8, 9, 10) MAZES[4][13][8] = 0; MAZES[4][13][9] = 0; MAZES[4][13][10] = 0; // Current maze index var currentMazeIndex = 0; var MAZE = MAZES[currentMazeIndex]; // --- Game State --- var pellets = []; var walls = []; var ghosts = []; var cherries = []; 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; // Cherry spawn state var cherryActive = false; var cherryTimer = 0; var cherryObj = null; // --- Score Display --- var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Bonus Object State --- var bonusObject = null; var bonusObjectActive = false; var bonusObjectPoints = 0; // --- Maze Selection UI --- var mazeSelectBtn = new Container(); var mazeBtnGfx = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); mazeBtnGfx.width = 90; mazeBtnGfx.height = 90; mazeBtnGfx.tint = 0x00bfff; mazeSelectBtn.addChild(mazeBtnGfx); var mazeBtnTxt = new Text2('≡', { size: 70, fill: "#fff" }); mazeBtnTxt.anchor.set(0.5, 0.5); mazeBtnTxt.x = 0; mazeBtnTxt.y = 0; mazeSelectBtn.addChild(mazeBtnTxt); // Place in top right, with margin mazeSelectBtn.x = -100; mazeSelectBtn.y = 0; LK.gui.topRight.addChild(mazeSelectBtn); // Maze selection popup var mazePopup = new Container(); mazePopup.visible = false; mazePopup.zIndex = 1000; // ensure on top var popupBg = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); popupBg.width = 600; popupBg.height = 400; popupBg.tint = 0x222244; mazePopup.addChild(popupBg); var maze1Btn = new Container(); var maze1Gfx = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); maze1Gfx.width = 220; maze1Gfx.height = 100; maze1Gfx.tint = 0x00bfff; maze1Btn.addChild(maze1Gfx); var maze1Txt = new Text2('Maze 1', { size: 60, fill: "#fff" }); maze1Txt.anchor.set(0.5, 0.5); maze1Btn.addChild(maze1Txt); maze1Btn.x = 0; maze1Btn.y = -70; mazePopup.addChild(maze1Btn); var maze2Btn = new Container(); var maze2Gfx = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); maze2Gfx.width = 220; maze2Gfx.height = 100; maze2Gfx.tint = 0x00bfff; maze2Btn.addChild(maze2Gfx); var maze2Txt = new Text2('Maze 2', { size: 60, fill: "#fff" }); maze2Txt.anchor.set(0.5, 0.5); maze2Btn.addChild(maze2Txt); maze2Btn.x = 0; maze2Btn.y = 70; mazePopup.addChild(maze2Btn); // Maze 3 button var maze3Btn = new Container(); var maze3Gfx = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); maze3Gfx.width = 220; maze3Gfx.height = 100; maze3Gfx.tint = 0x00bfff; maze3Btn.addChild(maze3Gfx); var maze3Txt = new Text2('Maze 3', { size: 60, fill: "#fff" }); maze3Txt.anchor.set(0.5, 0.5); maze3Btn.addChild(maze3Txt); maze3Btn.x = 0; maze3Btn.y = 210; mazePopup.addChild(maze3Btn); // Maze 4 button var maze4Btn = new Container(); var maze4Gfx = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); maze4Gfx.width = 220; maze4Gfx.height = 100; maze4Gfx.tint = 0x00bfff; maze4Btn.addChild(maze4Gfx); var maze4Txt = new Text2('Maze 4', { size: 60, fill: "#fff" }); maze4Txt.anchor.set(0.5, 0.5); maze4Btn.addChild(maze4Txt); maze4Btn.x = 0; maze4Btn.y = 350; mazePopup.addChild(maze4Btn); // Maze 5 button var maze5Btn = new Container(); var maze5Gfx = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); maze5Gfx.width = 220; maze5Gfx.height = 100; maze5Gfx.tint = 0x00bfff; maze5Btn.addChild(maze5Gfx); var maze5Txt = new Text2('Maze 5', { size: 60, fill: "#fff" }); maze5Txt.anchor.set(0.5, 0.5); maze5Btn.addChild(maze5Txt); maze5Btn.x = 0; maze5Btn.y = 490; mazePopup.addChild(maze5Btn); // Center popup in GUI mazePopup.x = 0; mazePopup.y = 0; LK.gui.center.addChild(mazePopup); // Button event handlers mazeSelectBtn.down = function (x, y, obj) { mazePopup.visible = true; }; maze1Btn.down = function (x, y, obj) { if (currentMazeIndex !== 0) { currentMazeIndex = 0; MAZE = MAZES[currentMazeIndex]; startGame(); } mazePopup.visible = false; }; maze2Btn.down = function (x, y, obj) { if (currentMazeIndex !== 1) { currentMazeIndex = 1; MAZE = MAZES[currentMazeIndex]; startGame(); } mazePopup.visible = false; }; // Maze 3 button event handler maze3Btn.down = function (x, y, obj) { if (currentMazeIndex !== 2) { currentMazeIndex = 2; MAZE = MAZES[currentMazeIndex]; startGame(); } mazePopup.visible = false; }; // Maze 4 button event handler maze4Btn.down = function (x, y, obj) { if (currentMazeIndex !== 3) { currentMazeIndex = 3; MAZE = MAZES[currentMazeIndex]; startGame(); } mazePopup.visible = false; }; // Maze 5 button event handler maze5Btn.down = function (x, y, obj) { if (currentMazeIndex !== 4) { currentMazeIndex = 4; MAZE = MAZES[currentMazeIndex]; startGame(); } mazePopup.visible = false; }; // Hide popup if user taps outside popup area mazePopup.down = function (x, y, obj) { // Only close if not on a button var localX = x - mazePopup.x; var localY = y - mazePopup.y; // If not inside any button, close var inBtn = function inBtn(btn) { return localX > btn.x - 110 && localX < btn.x + 110 && localY > btn.y - 50 && localY < btn.y + 50; }; if (!(inBtn(maze1Btn) || inBtn(maze2Btn) || inBtn(maze3Btn) || inBtn(maze4Btn) || inBtn(maze5Btn))) { mazePopup.visible = false; } }; // --- 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(); for (var i = 0; i < cherries.length; ++i) cherries[i].destroy(); pellets = []; walls = []; cherries = []; cherryActive = false; cherryTimer = 0; cherryObj = null; pelletsLeft = 0; for (var row = 0; row < MAZE_ROWS; ++row) { // Defensive: skip if MAZE or row is not defined if (!MAZE || !MAZE[row]) continue; for (var col = 0; col < MAZE_COLS; ++col) { // Defensive: skip if column is not defined if (typeof MAZE[row][col] === "undefined") continue; 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) { // Prevent pellets from spawning at ghost spawn locations (center of maze) // Ghosts spawn at (row 11, col 9), (row 11, col 8), (row 11, col 10), (row 12, col 9) var isGhostSpawn = row === 11 && (col === 8 || col === 9 || col === 10) || row === 12 && col === 9; if (!isGhostSpawn) { 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 for maze 1 // For maze 2, pick a valid open cell (not a wall) if (currentMazeIndex === 1 || currentMazeIndex === 2) { // Find a non-wall cell near the center, prefer row 15, col 9, else search outward var found = false; var tryOrder = [{ col: 9, row: 15 }, { col: 9, row: 14 }, { col: 9, row: 16 }, { col: 8, row: 15 }, { col: 10, row: 15 }, { col: 8, row: 14 }, { col: 10, row: 14 }, { col: 8, row: 16 }, { col: 10, row: 16 }]; for (var i = 0; i < tryOrder.length; ++i) { var c = tryOrder[i].col, r = tryOrder[i].row; if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { pacman.gridX = c; pacman.gridY = r; found = true; break; } } if (!found) { // fallback: scan for first open cell for (var r = 0; r < MAZE_ROWS; ++r) { for (var c = 0; c < MAZE_COLS; ++c) { if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { pacman.gridX = c; pacman.gridY = r; found = true; break; } } if (found) break; } } } else if (currentMazeIndex === 3 || currentMazeIndex === 4) { // For maze 4 and 5, pick a valid open cell (not a wall), like maze 1/2 var found = false; var tryOrder = [{ col: 9, row: 15 }, { col: 9, row: 14 }, { col: 9, row: 16 }, { col: 8, row: 15 }, { col: 10, row: 15 }, { col: 8, row: 14 }, { col: 10, row: 14 }, { col: 8, row: 16 }, { col: 10, row: 16 }]; for (var i = 0; i < tryOrder.length; ++i) { var c = tryOrder[i].col, r = tryOrder[i].row; if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { pacman.gridX = c; pacman.gridY = r; found = true; break; } } if (!found) { // fallback: scan for first open cell for (var r = 0; r < MAZE_ROWS; ++r) { for (var c = 0; c < MAZE_COLS; ++c) { if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { pacman.gridX = c; pacman.gridY = r; found = true; break; } } if (found) break; } } } else { 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 maze 5 (index 4), ensure ghosts spawn in open cells near center if (currentMazeIndex === 4) { // Try to find open cells near the classic spawn points var preferred = [{ col: 9, row: 11 }, { col: 8, row: 11 }, { col: 10, row: 11 }, { col: 9, row: 12 }, { col: 8, row: 12 }, { col: 10, row: 12 }, { col: 9, row: 10 }, { col: 8, row: 10 }, { col: 10, row: 10 }]; var found = []; for (var i = 0; i < preferred.length && found.length < 4; ++i) { var c = preferred[i].col, r = preferred[i].row; if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { // Only add if not already used var already = false; for (var j = 0; j < found.length; ++j) { if (found[j].col === c && found[j].row === r) { already = true; break; } } if (!already) found.push({ col: c, row: r }); } } // If not enough, fill with any open cell if (found.length < 4) { for (var r = 0; r < MAZE_ROWS && found.length < 4; ++r) { for (var c = 0; c < MAZE_COLS && found.length < 4; ++c) { if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { // Only add if not already used var already = false; for (var j = 0; j < found.length; ++j) { if (found[j].col === c && found[j].row === r) { already = true; break; } } if (!already) found.push({ col: c, row: r }); } } } } // Use found as ghostStarts for (var i = 0; i < 4; ++i) { if (found[i]) ghostStarts[i] = found[i]; } } 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; // Defensive: check MAZE and MAZE[row] exist, and col is defined if (!MAZE || !MAZE[row] || typeof MAZE[row][col] === "undefined") 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) { // Switch to next maze if available, else loop back to first currentMazeIndex = (currentMazeIndex + 1) % MAZES.length; MAZE = MAZES[currentMazeIndex]; 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 (frightActive && ghost.frightened && ghost.visible !== false) { // Eat ghost: make invisible, respawn after fright ends ghost.visible = false; score += 200; scoreTxt.setText(score); LK.setScore(score); } else if (!frightActive && ghost.visible !== false) { // 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); // If ghost was eaten (invisible), respawn at home if (ghosts[g].visible === false) { // Respawn at original spawn for this ghost var spawnIdx = g; var ghostStarts = [{ col: 9, row: 11 }, { col: 8, row: 11 }, { col: 10, row: 11 }, { col: 9, row: 12 }]; // For maze 5 (index 4), ensure ghosts respawn in open cells near center if (currentMazeIndex === 4) { var preferred = [{ col: 9, row: 11 }, { col: 8, row: 11 }, { col: 10, row: 11 }, { col: 9, row: 12 }, { col: 8, row: 12 }, { col: 10, row: 12 }, { col: 9, row: 10 }, { col: 8, row: 10 }, { col: 10, row: 10 }]; var found = []; for (var i = 0; i < preferred.length && found.length < 4; ++i) { var c = preferred[i].col, r = preferred[i].row; if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { // Only add if not already used var already = false; for (var j = 0; j < found.length; ++j) { if (found[j].col === c && found[j].row === r) { already = true; break; } } if (!already) found.push({ col: c, row: r }); } } // If not enough, fill with any open cell if (found.length < 4) { for (var r = 0; r < MAZE_ROWS && found.length < 4; ++r) { for (var c = 0; c < MAZE_COLS && found.length < 4; ++c) { if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { // Only add if not already used var already = false; for (var j = 0; j < found.length; ++j) { if (found[j].col === c && found[j].row === r) { already = true; break; } } if (!already) found.push({ col: c, row: r }); } } } } if (found[spawnIdx]) ghostStarts[spawnIdx] = found[spawnIdx]; } var spawn = ghostStarts[spawnIdx]; var home = gridToPos(spawn.col, spawn.row); ghosts[g].x = home.x; ghosts[g].y = home.y; ghosts[g].dir = { x: 0, y: -1 }; ghosts[g].visible = true; ghosts[g].gridX = spawn.col; ghosts[g].gridY = spawn.row; } } } } } // --- 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(); } // --- Cherry spawn logic --- // Spawn cherry after 1/3 pellets are eaten, if not already spawned if (!cherryActive && pelletsLeft > 0 && pelletsLeft <= Math.floor(pelletsLeft + cherries.length + pellets.length) * 2 / 3) { // Find a random empty cell (not wall, not pellet, not ghost spawn) var openCells = []; for (var row = 0; row < MAZE_ROWS; ++row) { for (var col = 0; col < MAZE_COLS; ++col) { if (MAZE[row] && typeof MAZE[row][col] !== "undefined" && MAZE[row][col] !== 1) { // Not wall var isGhostSpawn = row === 11 && (col === 8 || col === 9 || col === 10) || row === 12 && col === 9; if (!isGhostSpawn) { // Not already a pellet var pelletHere = false; for (var p = 0; p < pellets.length; ++p) { var grid = posToGrid(pellets[p].x, pellets[p].y); if (grid.col === col && grid.row === row) { pelletHere = true; break; } } // Not already a cherry var cherryHere = false; for (var c = 0; c < cherries.length; ++c) { var grid = posToGrid(cherries[c].x, cherries[c].y); if (grid.col === col && grid.row === row) { cherryHere = true; break; } } if (!pelletHere && !cherryHere) { openCells.push({ col: col, row: row }); } } } } } if (openCells.length > 0) { var idx = Math.floor(Math.random() * openCells.length); var pos = gridToPos(openCells[idx].col, openCells[idx].row); cherryObj = new Cherry(); cherryObj.x = pos.x; cherryObj.y = pos.y; game.addChild(cherryObj); cherries.push(cherryObj); cherryActive = true; cherryTimer = 600; // 10 seconds at 60fps } } // Cherry timer and removal if (cherryActive && cherryObj) { cherryTimer--; if (cherryTimer <= 0) { cherryObj.destroy(); cherries.splice(cherries.indexOf(cherryObj), 1); cherryObj = null; cherryActive = false; } } checkPelletEat(); // --- Cherry eat logic --- if (cherryActive && cherryObj) { var dx = pacman.x - cherryObj.x; var dy = pacman.y - cherryObj.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 60) { // Eat cherry! cherryObj.destroy(); cherries.splice(cherries.indexOf(cherryObj), 1); cherryObj = null; cherryActive = false; score += 200; scoreTxt.setText(score); LK.setScore(score); } } // --- BonusObject spawn and update --- // Only spawn once per game session if (!bonusObjectActive) { // Spawn in a random open cell var openCells = []; for (var row = 0; row < MAZE_ROWS; ++row) { for (var col = 0; col < MAZE_COLS; ++col) { if (MAZE[row] && typeof MAZE[row][col] !== "undefined" && MAZE[row][col] !== 1) { // Not wall var isGhostSpawn = row === 11 && (col === 8 || col === 9 || col === 10) || row === 12 && col === 9; if (!isGhostSpawn) { openCells.push({ col: col, row: row }); } } } } if (openCells.length > 0) { var idx = Math.floor(Math.random() * openCells.length); var pos = gridToPos(openCells[idx].col, openCells[idx].row); bonusObject = new BonusObject(); bonusObject.x = pos.x; bonusObject.y = pos.y; // Randomize initial direction bonusObject.moveDir = { x: Math.random() < 0.5 ? 1 : -1, y: Math.random() < 0.5 ? 1 : -1 }; game.addChild(bonusObject); bonusObjectActive = true; bonusObjectPoints = 1000 + Math.floor(Math.random() * 1001); // 1000-2000 } } // Update BonusObject movement if (bonusObjectActive && bonusObject) { bonusObject.update(); // Check collision with Pacman var dx = pacman.x - bonusObject.x; var dy = pacman.y - bonusObject.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 60) { // Pacman eats bonus object! if (bonusObject) { bonusObject.destroy(); bonusObject = null; bonusObjectActive = false; score += bonusObjectPoints; scoreTxt.setText(score); LK.setScore(score); } } } checkGhostCollisions(); updateFrightened(); }; // --- Game Start --- function startGame() { score = 0; LK.setScore(0); scoreTxt.setText('0'); // Do not reset currentMazeIndex here, so maze selection works MAZE = MAZES[currentMazeIndex]; buildMaze(); spawnPacman(); spawnGhosts(); // Reset all ghosts to spawn positions and visible, like at the beginning of the game var ghostStarts = [{ col: 9, row: 11 }, { col: 8, row: 11 }, { col: 10, row: 11 }, { col: 9, row: 12 }]; // For maze 5 (index 4), ensure ghosts reset in open cells near center if (currentMazeIndex === 4) { var preferred = [{ col: 9, row: 11 }, { col: 8, row: 11 }, { col: 10, row: 11 }, { col: 9, row: 12 }, { col: 8, row: 12 }, { col: 10, row: 12 }, { col: 9, row: 10 }, { col: 8, row: 10 }, { col: 10, row: 10 }]; var found = []; for (var i = 0; i < preferred.length && found.length < 4; ++i) { var c = preferred[i].col, r = preferred[i].row; if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { // Only add if not already used var already = false; for (var j = 0; j < found.length; ++j) { if (found[j].col === c && found[j].row === r) { already = true; break; } } if (!already) found.push({ col: c, row: r }); } } // If not enough, fill with any open cell if (found.length < 4) { for (var r = 0; r < MAZE_ROWS && found.length < 4; ++r) { for (var c = 0; c < MAZE_COLS && found.length < 4; ++c) { if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) { // Only add if not already used var already = false; for (var j = 0; j < found.length; ++j) { if (found[j].col === c && found[j].row === r) { already = true; break; } } if (!already) found.push({ col: c, row: r }); } } } } for (var i = 0; i < 4; ++i) { if (found[i]) ghostStarts[i] = found[i]; } } for (var g = 0; g < ghosts.length; ++g) { var spawn = ghostStarts[g]; var home = gridToPos(spawn.col, spawn.row); ghosts[g].x = home.x; ghosts[g].y = home.y; ghosts[g].dir = { x: 0, y: -1 }; ghosts[g].visible = true; ghosts[g].gridX = spawn.col; ghosts[g].gridY = spawn.row; ghosts[g].setFrightened(false); } frightTicks = 0; frightActive = false; lastGameOver = false; // Reset cherry state for (var i = 0; i < cherries.length; ++i) cherries[i].destroy(); cherries = []; cherryActive = false; cherryTimer = 0; cherryObj = null; } startGame();
===================================================================
--- original.js
+++ change.js
@@ -634,27 +634,63 @@
}
if (found) break;
}
}
- } else if (currentMazeIndex === 3) {
- // Force Pacman to spawn in a wall cell for level 4
- // Find the first wall cell (cell === 1)
- var foundWall = false;
- for (var r = 0; r < MAZE_ROWS; ++r) {
- for (var c = 0; c < MAZE_COLS; ++c) {
- if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] === 1) {
- pacman.gridX = c;
- pacman.gridY = r;
- foundWall = true;
- break;
+ } else if (currentMazeIndex === 3 || currentMazeIndex === 4) {
+ // For maze 4 and 5, pick a valid open cell (not a wall), like maze 1/2
+ var found = false;
+ var tryOrder = [{
+ col: 9,
+ row: 15
+ }, {
+ col: 9,
+ row: 14
+ }, {
+ col: 9,
+ row: 16
+ }, {
+ col: 8,
+ row: 15
+ }, {
+ col: 10,
+ row: 15
+ }, {
+ col: 8,
+ row: 14
+ }, {
+ col: 10,
+ row: 14
+ }, {
+ col: 8,
+ row: 16
+ }, {
+ col: 10,
+ row: 16
+ }];
+ for (var i = 0; i < tryOrder.length; ++i) {
+ var c = tryOrder[i].col,
+ r = tryOrder[i].row;
+ if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) {
+ pacman.gridX = c;
+ pacman.gridY = r;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // fallback: scan for first open cell
+ for (var r = 0; r < MAZE_ROWS; ++r) {
+ for (var c = 0; c < MAZE_COLS; ++c) {
+ if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) {
+ pacman.gridX = c;
+ pacman.gridY = r;
+ found = true;
+ break;
+ }
}
+ if (found) break;
}
- if (foundWall) break;
}
- if (!foundWall) {
- pacman.gridX = 9;
- pacman.gridY = 15;
- }
} else {
pacman.gridX = 9;
pacman.gridY = 15;
}
@@ -690,8 +726,84 @@
}, {
col: 9,
row: 12
}];
+ // For maze 5 (index 4), ensure ghosts spawn in open cells near center
+ if (currentMazeIndex === 4) {
+ // Try to find open cells near the classic spawn points
+ var preferred = [{
+ col: 9,
+ row: 11
+ }, {
+ col: 8,
+ row: 11
+ }, {
+ col: 10,
+ row: 11
+ }, {
+ col: 9,
+ row: 12
+ }, {
+ col: 8,
+ row: 12
+ }, {
+ col: 10,
+ row: 12
+ }, {
+ col: 9,
+ row: 10
+ }, {
+ col: 8,
+ row: 10
+ }, {
+ col: 10,
+ row: 10
+ }];
+ var found = [];
+ for (var i = 0; i < preferred.length && found.length < 4; ++i) {
+ var c = preferred[i].col,
+ r = preferred[i].row;
+ if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) {
+ // Only add if not already used
+ var already = false;
+ for (var j = 0; j < found.length; ++j) {
+ if (found[j].col === c && found[j].row === r) {
+ already = true;
+ break;
+ }
+ }
+ if (!already) found.push({
+ col: c,
+ row: r
+ });
+ }
+ }
+ // If not enough, fill with any open cell
+ if (found.length < 4) {
+ for (var r = 0; r < MAZE_ROWS && found.length < 4; ++r) {
+ for (var c = 0; c < MAZE_COLS && found.length < 4; ++c) {
+ if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) {
+ // Only add if not already used
+ var already = false;
+ for (var j = 0; j < found.length; ++j) {
+ if (found[j].col === c && found[j].row === r) {
+ already = true;
+ break;
+ }
+ }
+ if (!already) found.push({
+ col: c,
+ row: r
+ });
+ }
+ }
+ }
+ }
+ // Use found as ghostStarts
+ for (var i = 0; i < 4; ++i) {
+ if (found[i]) ghostStarts[i] = found[i];
+ }
+ }
for (var i = 0; i < 4; ++i) {
var ghost = new Ghost();
ghost.attach(ghostColors[i]);
ghost.gridX = ghostStarts[i].col;
@@ -900,8 +1012,80 @@
}, {
col: 9,
row: 12
}];
+ // For maze 5 (index 4), ensure ghosts respawn in open cells near center
+ if (currentMazeIndex === 4) {
+ var preferred = [{
+ col: 9,
+ row: 11
+ }, {
+ col: 8,
+ row: 11
+ }, {
+ col: 10,
+ row: 11
+ }, {
+ col: 9,
+ row: 12
+ }, {
+ col: 8,
+ row: 12
+ }, {
+ col: 10,
+ row: 12
+ }, {
+ col: 9,
+ row: 10
+ }, {
+ col: 8,
+ row: 10
+ }, {
+ col: 10,
+ row: 10
+ }];
+ var found = [];
+ for (var i = 0; i < preferred.length && found.length < 4; ++i) {
+ var c = preferred[i].col,
+ r = preferred[i].row;
+ if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) {
+ // Only add if not already used
+ var already = false;
+ for (var j = 0; j < found.length; ++j) {
+ if (found[j].col === c && found[j].row === r) {
+ already = true;
+ break;
+ }
+ }
+ if (!already) found.push({
+ col: c,
+ row: r
+ });
+ }
+ }
+ // If not enough, fill with any open cell
+ if (found.length < 4) {
+ for (var r = 0; r < MAZE_ROWS && found.length < 4; ++r) {
+ for (var c = 0; c < MAZE_COLS && found.length < 4; ++c) {
+ if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) {
+ // Only add if not already used
+ var already = false;
+ for (var j = 0; j < found.length; ++j) {
+ if (found[j].col === c && found[j].row === r) {
+ already = true;
+ break;
+ }
+ }
+ if (!already) found.push({
+ col: c,
+ row: r
+ });
+ }
+ }
+ }
+ }
+ if (found[spawnIdx]) ghostStarts[spawnIdx] = found[spawnIdx];
+ }
var spawn = ghostStarts[spawnIdx];
var home = gridToPos(spawn.col, spawn.row);
ghosts[g].x = home.x;
ghosts[g].y = home.y;
@@ -1133,8 +1317,82 @@
}, {
col: 9,
row: 12
}];
+ // For maze 5 (index 4), ensure ghosts reset in open cells near center
+ if (currentMazeIndex === 4) {
+ var preferred = [{
+ col: 9,
+ row: 11
+ }, {
+ col: 8,
+ row: 11
+ }, {
+ col: 10,
+ row: 11
+ }, {
+ col: 9,
+ row: 12
+ }, {
+ col: 8,
+ row: 12
+ }, {
+ col: 10,
+ row: 12
+ }, {
+ col: 9,
+ row: 10
+ }, {
+ col: 8,
+ row: 10
+ }, {
+ col: 10,
+ row: 10
+ }];
+ var found = [];
+ for (var i = 0; i < preferred.length && found.length < 4; ++i) {
+ var c = preferred[i].col,
+ r = preferred[i].row;
+ if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) {
+ // Only add if not already used
+ var already = false;
+ for (var j = 0; j < found.length; ++j) {
+ if (found[j].col === c && found[j].row === r) {
+ already = true;
+ break;
+ }
+ }
+ if (!already) found.push({
+ col: c,
+ row: r
+ });
+ }
+ }
+ // If not enough, fill with any open cell
+ if (found.length < 4) {
+ for (var r = 0; r < MAZE_ROWS && found.length < 4; ++r) {
+ for (var c = 0; c < MAZE_COLS && found.length < 4; ++c) {
+ if (MAZE[r] && typeof MAZE[r][c] !== "undefined" && MAZE[r][c] !== 1) {
+ // Only add if not already used
+ var already = false;
+ for (var j = 0; j < found.length; ++j) {
+ if (found[j].col === c && found[j].row === r) {
+ already = true;
+ break;
+ }
+ }
+ if (!already) found.push({
+ col: c,
+ row: r
+ });
+ }
+ }
+ }
+ }
+ for (var i = 0; i < 4; ++i) {
+ if (found[i]) ghostStarts[i] = found[i];
+ }
+ }
for (var g = 0; g < ghosts.length; ++g) {
var spawn = ghostStarts[g];
var home = gridToPos(spawn.col, spawn.row);
ghosts[g].x = home.x;
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