#3884: pacman.js

projectforge-wicket/src/main/webapp/scripts/pacman/pacman.js

Path: projectforge-wicket/src/main/webapp/scripts/pacman/pacman.js · Lines: 1269 · Type: JavaScript game engine

Purpose: A FULL PAC-MAN GAME embedded in ProjectForge's Wicket webapp. Complete arcade game with ghost AI, collision detection, pellet scoring, 20+ audio files, and HTML5 Canvas rendering. An Easter egg accessible from error pages — turns a 500 error into a playable game.

Source: GitHub

1269 lines · 1008 code · 18 comments · 243 blank
CommitMessage
9ebb885222016-07-18 Initial commit

Part of the original ProjectForge codebase since 2016. Survived the 7.x rewrite, the CRA→Vite migration, and the Wicket→React transition.

Why is there a game in project management software? This is a developer Easter egg — when the Wicket webapp encounters an unhandled exception (500 error), instead of a boring stack trace, the error page offers to play Pac-Man. It turns frustration into fun. The game is also accessible directly at /wa/pacman for quick breaks during development.

Game architecture — 11 states, 4 ghosts, 30 FPS

var NONE = 4, UP = 3, LEFT = 2, DOWN = 1, RIGHT = 11,
    WAITING = 5, PAUSE = 6, PLAYING = 7,
    COUNTDOWN = 8, EATEN_PAUSE = 9, DYING = 10;
Pacman.FPS = 30;

11 game states: The game tracks state via numeric constants — not an enum (JS predates widespread enum usage). States transition: WAITING → COUNTDOWN → PLAYING → (DYING | EATEN_PAUSE) → PLAYING. The EATEN_PAUSE state is the brief freeze when Pac-Man eats a ghost — a classic arcade detail.

Ghost AI system: Each ghost (Pacman.Ghost) tracks: position, direction, eatable (vulnerable after power pellet), eaten (returning to base), and due (spawn timer). Movement speed varies by state — vulnerable ghosts move at speed 1 (slow), hidden ghosts at 4 (fast to return to base), normal at 2.

30 FPS game loop: The game runs at 30 frames per second via setInterval. Collision detection uses grid snapping — ghosts snap to 10px grid cells to prevent skipping over walls. The addBounded() function handles edge cases where a ghost's speed would overshoot a wall boundary.

The rendering layer — Canvas + audio sprites

The game renders to an HTML5 <canvas> element. The maze, pellets, Pac-Man, and ghosts are all drawn programmatically. Audio uses 20+ .ogg and .mp3 files in scripts/pacman/audio/ — siren (ghost chase), chomp (pellet eating), die (Pac-Man death), eatghost (ghost consumption), extra lives (1-up). Both formats are provided for browser compatibility (OGG for Firefox, MP3 for Chrome/Safari).

Known bugs (from source comments, lines 4-9): Looped audio doesn't work correctly, fruits and levels are incomplete, ghost eaten state is broken (ghost should return to base but doesn't), and proper ghost personality mechanics (Blinky chases, Pinky ambushes, Inky is random, Clyde wanders) are not fully implemented.