Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit c42f3cefc5371ff8f14c05cad6e5604bda7824e1 @daleharvey committed Jul 26, 2010
BIN BD_Cartoon_Shout-webfont.ttf
Binary file not shown.
BIN audio/die.ogg
Binary file not shown.
BIN audio/eatghost.ogg
Binary file not shown.
BIN audio/eating.ogg
Binary file not shown.
BIN audio/eating.short.ogg
Binary file not shown.
BIN audio/eatpill.ogg
Binary file not shown.
BIN audio/extra lives.ogg
Binary file not shown.
BIN audio/intermission.ogg
Binary file not shown.
BIN audio/opening_song.ogg
Binary file not shown.
BIN audio/siren.ogg
Binary file not shown.
BIN audio/vcs_90.ogg
Binary file not shown.
65 index.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>HTML5 Pacman</title>
+
+ <style type="text/css">
+ @font-face {
+ font-family: 'BDCartoonShoutRegular';
+ src: url('BD_Cartoon_Shout-webfont.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+ }
+ #pacman {
+ height:450px;
+ width:342px;
+ margin:20px auto;
+ }
+ #shim {
+ font-family: BDCartoonShoutRegular;
+ position:absolute;
+ visibility:hidden
+ }
+ h1 { font-family: BDCartoonShoutRegular; text-align:center; }
+ body { width:342px; margin:0px auto; font-family:sans-serif; }
+ a { text-decoration:none; }
+ </style>
+
+</head>
+
+<body>
+
+ <div id="shim">shim for font face</div>
+
+ <h1>HTML5 Pacman</h1>
+
+ <a href="http://arandomurl.com/">Writeup</a> |
+ Code on <a href="http://arandomurl.com/">Github</a>
+
+ <div id="pacman"></div>
+
+ <script src="pacman_util.js"></script>
+ <script src="pacman.js"></script>
+
+ <script>
+ var el = document.getElementById("pacman");
+
+ function supports_canvas() {
+ return !!document.createElement('canvas').getContext;
+ };
+
+ function supports_local_storage() {
+ return ('localStorage' in window) && window['localStorage'] !== null;
+ };
+
+ if (supports_canvas() && supports_local_storage()) {
+ window.setTimeout(function () { PACMAN.init(el, "./"); }, 0);
+ } else {
+ el.innerHTML = "Sorry, needs a decent browser<br /><small>" +
+ "(firefox 3.6+, Chrome 4+, Opera 10+ and probably Safari)</small>";
+ }
+ </script>
+
+</body>
+</html>
1,235 pacman.js
@@ -0,0 +1,1235 @@
+/*jslint browser: true, undef: true, eqeqeq: true, nomen: true, white: true */
+/*global window: false, document: false */
+
+/*
+ * fix audio loop
+ * writeup publish
+ * add fruits + levels
+ * fix what happens when a ghost is eaten (should go back to base)
+ * do proper speeds
+ * do proper ghost mechanics (blinky/wimpy etc)
+ */
+
+Object.prototype.clone = function () {
+ var i, newObj = (this instanceof Array) ? [] : {};
+ for (i in this) {
+ if (i === 'clone') {
+ continue;
+ }
+ if (this[i] && typeof this[i] === "object") {
+ newObj[i] = this[i].clone();
+ } else {
+ newObj[i] = this[i];
+ }
+ }
+ return newObj;
+};
+
+var NONE = 4,
+ UP = 3,
+ LEFT = 2,
+ DOWN = 1,
+ RIGHT = 0,
+ WAITING = 5,
+ PAUSE = 6,
+ PLAYING = 7,
+ COUNTDOWN = 8,
+ EATEN_PAUSE = 9,
+ DYING = 10,
+ Pacman = {};
+
+Pacman.WALL = 0;
+Pacman.BISCUIT = 1;
+Pacman.EMPTY = 2;
+Pacman.BLOCK = 3;
+Pacman.PILL = 4;
+
+Pacman.MAP = [
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
+ [0, 4, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 4, 0],
+ [0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0],
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
+ [0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0],
+ [0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
+ [0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
+ [2, 2, 2, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 2, 2, 2],
+ [0, 0, 0, 0, 1, 0, 1, 0, 0, 3, 0, 0, 1, 0, 1, 0, 0, 0, 0],
+ [2, 2, 2, 2, 1, 1, 1, 0, 3, 3, 3, 0, 1, 1, 1, 2, 2, 2, 2],
+ [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
+ [2, 2, 2, 0, 1, 0, 1, 1, 1, 2, 1, 1, 1, 0, 1, 0, 2, 2, 2],
+ [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
+ [0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0],
+ [0, 4, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 4, 0],
+ [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0],
+ [0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0],
+ [0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0],
+ [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
+];
+
+Pacman.WALLS = [
+
+ [{"move": [0, 9.5]}, {"line": [3, 9.5]},
+ {"curve": [3.5, 9.5, 3.5, 9]}, {"line": [3.5, 8]},
+ {"curve": [3.5, 7.5, 3, 7.5]}, {"line": [1, 7.5]},
+ {"curve": [0.5, 7.5, 0.5, 7]}, {"line": [0.5, 1]},
+ {"curve": [0.5, 0.5, 1, 0.5]}, {"line": [9, 0.5]},
+ {"curve": [9.5, 0.5, 9.5, 1]}, {"line": [9.5, 3.5]}],
+
+ [{"move": [9.5, 1]},
+ {"curve": [9.5, 0.5, 10, 0.5]}, {"line": [18, 0.5]},
+ {"curve": [18.5, 0.5, 18.5, 1]}, {"line": [18.5, 7]},
+ {"curve": [18.5, 7.5, 18, 7.5]}, {"line": [16, 7.5]},
+ {"curve": [15.5, 7.5, 15.5, 8]}, {"line": [15.5, 9]},
+ {"curve": [15.5, 9.5, 16, 9.5]}, {"line": [19, 9.5]}],
+
+ [{"move": [2.5, 5.5]}, {"line": [3.5, 5.5]}],
+
+ [{"move": [3, 2.5]},
+ {"curve": [3.5, 2.5, 3.5, 3]},
+ {"curve": [3.5, 3.5, 3, 3.5]},
+ {"curve": [2.5, 3.5, 2.5, 3]},
+ {"curve": [2.5, 2.5, 3, 2.5]}],
+
+ [{"move": [15.5, 5.5]}, {"line": [16.5, 5.5]}],
+
+ [{"move": [16, 2.5]}, {"curve": [16.5, 2.5, 16.5, 3]},
+ {"curve": [16.5, 3.5, 16, 3.5]}, {"curve": [15.5, 3.5, 15.5, 3]},
+ {"curve": [15.5, 2.5, 16, 2.5]}],
+
+ [{"move": [6, 2.5]}, {"line": [7, 2.5]}, {"curve": [7.5, 2.5, 7.5, 3]},
+ {"curve": [7.5, 3.5, 7, 3.5]}, {"line": [6, 3.5]},
+ {"curve": [5.5, 3.5, 5.5, 3]}, {"curve": [5.5, 2.5, 6, 2.5]}],
+
+ [{"move": [12, 2.5]}, {"line": [13, 2.5]}, {"curve": [13.5, 2.5, 13.5, 3]},
+ {"curve": [13.5, 3.5, 13, 3.5]}, {"line": [12, 3.5]},
+ {"curve": [11.5, 3.5, 11.5, 3]}, {"curve": [11.5, 2.5, 12, 2.5]}],
+
+ [{"move": [7.5, 5.5]}, {"line": [9, 5.5]}, {"curve": [9.5, 5.5, 9.5, 6]},
+ {"line": [9.5, 7.5]}],
+ [{"move": [9.5, 6]}, {"curve": [9.5, 5.5, 10.5, 5.5]},
+ {"line": [11.5, 5.5]}],
+
+
+ [{"move": [5.5, 5.5]}, {"line": [5.5, 7]}, {"curve": [5.5, 7.5, 6, 7.5]},
+ {"line": [7.5, 7.5]}],
+ [{"move": [6, 7.5]}, {"curve": [5.5, 7.5, 5.5, 8]}, {"line": [5.5, 9.5]}],
+
+ [{"move": [13.5, 5.5]}, {"line": [13.5, 7]},
+ {"curve": [13.5, 7.5, 13, 7.5]}, {"line": [11.5, 7.5]}],
+ [{"move": [13, 7.5]}, {"curve": [13.5, 7.5, 13.5, 8]},
+ {"line": [13.5, 9.5]}],
+
+ [{"move": [0, 11.5]}, {"line": [3, 11.5]}, {"curve": [3.5, 11.5, 3.5, 12]},
+ {"line": [3.5, 13]}, {"curve": [3.5, 13.5, 3, 13.5]}, {"line": [1, 13.5]},
+ {"curve": [0.5, 13.5, 0.5, 14]}, {"line": [0.5, 17]},
+ {"curve": [0.5, 17.5, 1, 17.5]}, {"line": [1.5, 17.5]}],
+ [{"move": [1, 17.5]}, {"curve": [0.5, 17.5, 0.5, 18]}, {"line": [0.5, 21]},
+ {"curve": [0.5, 21.5, 1, 21.5]}, {"line": [18, 21.5]},
+ {"curve": [18.5, 21.5, 18.5, 21]}, {"line": [18.5, 18]},
+ {"curve": [18.5, 17.5, 18, 17.5]}, {"line": [17.5, 17.5]}],
+ [{"move": [18, 17.5]}, {"curve": [18.5, 17.5, 18.5, 17]},
+ {"line": [18.5, 14]}, {"curve": [18.5, 13.5, 18, 13.5]},
+ {"line": [16, 13.5]}, {"curve": [15.5, 13.5, 15.5, 13]},
+ {"line": [15.5, 12]}, {"curve": [15.5, 11.5, 16, 11.5]},
+ {"line": [19, 11.5]}],
+
+ [{"move": [5.5, 11.5]}, {"line": [5.5, 13.5]}],
+ [{"move": [13.5, 11.5]}, {"line": [13.5, 13.5]}],
+
+ [{"move": [2.5, 15.5]}, {"line": [3, 15.5]},
+ {"curve": [3.5, 15.5, 3.5, 16]}, {"line": [3.5, 17.5]}],
+ [{"move": [16.5, 15.5]}, {"line": [16, 15.5]},
+ {"curve": [15.5, 15.5, 15.5, 16]}, {"line": [15.5, 17.5]}],
+
+ [{"move": [5.5, 15.5]}, {"line": [7.5, 15.5]}],
+ [{"move": [11.5, 15.5]}, {"line": [13.5, 15.5]}],
+
+ [{"move": [2.5, 19.5]}, {"line": [5, 19.5]},
+ {"curve": [5.5, 19.5, 5.5, 19]}, {"line": [5.5, 17.5]}],
+ [{"move": [5.5, 19]}, {"curve": [5.5, 19.5, 6, 19.5]},
+ {"line": [7.5, 19.5]}],
+
+ [{"move": [11.5, 19.5]}, {"line": [13, 19.5]},
+ {"curve": [13.5, 19.5, 13.5, 19]}, {"line": [13.5, 17.5]}],
+ [{"move": [13.5, 19]}, {"curve": [13.5, 19.5, 14, 19.5]},
+ {"line": [16.5, 19.5]}],
+
+ [{"move": [7.5, 13.5]}, {"line": [9, 13.5]},
+ {"curve": [9.5, 13.5, 9.5, 14]}, {"line": [9.5, 15.5]}],
+ [{"move": [9.5, 14]}, {"curve": [9.5, 13.5, 10, 13.5]},
+ {"line": [11.5, 13.5]}],
+
+ [{"move": [7.5, 17.5]}, {"line": [9, 17.5]},
+ {"curve": [9.5, 17.5, 9.5, 18]}, {"line": [9.5, 19.5]}],
+ [{"move": [9.5, 18]}, {"curve": [9.5, 17.5, 10, 17.5]},
+ {"line": [11.5, 17.5]}],
+
+ [{"move": [8.5, 9.5]}, {"line": [8, 9.5]}, {"curve": [7.5, 9.5, 7.5, 10]},
+ {"line": [7.5, 11]}, {"curve": [7.5, 11.5, 8, 11.5]},
+ {"line": [11, 11.5]}, {"curve": [11.5, 11.5, 11.5, 11]},
+ {"line": [11.5, 10]}, {"curve": [11.5, 9.5, 11, 9.5]},
+ {"line": [10.5, 9.5]}]
+];
+
+Pacman.Ghost = function (game, map, colour) {
+
+ var position = null,
+ direction = null,
+ eatable = null,
+ eaten = null,
+ due = null;
+
+ function getNewCoord(dir, current) {
+ var speed = isVunerable() ? 1 : isHidden() ? 4 : 2;
+ return {
+ "x": addWithBounds(current.x,
+ (dir === LEFT && -speed ||
+ dir === RIGHT && speed || 0)),
+ "y": addWithBounds(current.y,
+ (dir === DOWN && speed ||
+ dir === UP && -speed || 0))
+ };
+ }
+
+ function addWithBounds(x1, x2) {
+ var rem = x1 % 10, result = rem + x2;
+ if (rem !== 0 && result > 10) {
+ return x1 + (10 - rem);
+ } else if(rem > 0 && result < 0) {
+ return x1 - rem;
+ }
+ return x1 + x2;
+ };
+
+ function isVunerable() {
+ return eatable !== null;
+ };
+
+ function isDangerous() {
+ return eaten === null;
+ };
+
+ function isHidden() {
+ return eatable === null && eaten !== null;
+ };
+
+ function getRandomDirection() {
+ var moves = (direction === LEFT || direction === RIGHT) ?
+ [UP, DOWN] : [LEFT, RIGHT];
+ return moves[Math.floor(Math.random() * 2)];
+ };
+
+ function reset() {
+ eaten = null;
+ eatable = null;
+ position = {"x": 90, "y": 80};
+ direction = getRandomDirection();
+ due = getRandomDirection();
+ };
+
+ function onWholeSquare(x) {
+ return x % 10 === 0;
+ };
+
+ function makeEatable() {
+ direction = (direction === LEFT) ? RIGHT :
+ (direction === RIGHT) ? LEFT :
+ (direction === UP) ? DOWN : UP;
+
+ eatable = game.getTick();
+ };
+
+ function eat() {
+ eatable = null;
+ eaten = game.getTick();
+ };
+
+ function pointToCoord(x) {
+ return Math.round(x/10);
+ };
+
+ function nextSquare(x, dir) {
+ var rem = x % 10;
+ if (rem === 0) {
+ return x;
+ } else if (dir === RIGHT || dir === DOWN) {
+ return x + (10 - rem);
+ } else {
+ return x - rem;
+ }
+ };
+
+ function onGridSquare(pos) {
+ return onWholeSquare(pos.y) && onWholeSquare(pos.x);
+ };
+
+ function secondsAgo(tick) {
+ return (game.getTick() - tick) / 30;
+ };
+
+ function getColour() {
+ if (eatable) {
+ if (secondsAgo(eatable) > 5) {
+ return game.getTick() % 20 > 10 ? "#FFF" : "#0000BB";
+ } else {
+ return "#0000BB";
+ }
+ } else if(eaten) {
+ return "#222";
+ }
+ return colour;
+ };
+
+ function draw(ctx) {
+
+ var top = (position.y/10) * map.blockSize,
+ left = (position.x/10) * map.blockSize;
+
+ if (eatable && secondsAgo(eatable) > 8) {
+ eatable = null;
+ }
+
+ if (eaten && secondsAgo(eaten) > 3) {
+ eaten = null;
+ }
+
+ var tl = left+map.blockSize;
+ var base = top+map.blockSize-3;
+ var s = map.blockSize;
+ var inc = map.blockSize / 10;
+
+ var high = game.getTick() % 10 > 5 ? 3 : -3;
+ var low = game.getTick() % 10 > 5 ? -3 : 3;
+
+ ctx.fillStyle = getColour();
+ ctx.beginPath();
+
+ ctx.moveTo(left, base);
+
+ ctx.quadraticCurveTo(left, top, left + (s/2), top);
+ ctx.quadraticCurveTo(left+s, top, left+s, base);
+
+ // Wavy things at the bottom
+ ctx.quadraticCurveTo(tl-(inc*1), base+high, tl-(inc*2), base);
+ ctx.quadraticCurveTo(tl-(inc*3), base+low, tl-(inc*4), base);
+ ctx.quadraticCurveTo(tl-(inc*5), base+high, tl-(inc*6), base);
+ ctx.quadraticCurveTo(tl-(inc*7), base+low, tl-(inc*8), base);
+ ctx.quadraticCurveTo(tl-(inc*9), base+high, tl-(inc*10), base);
+
+ ctx.closePath();
+ ctx.fill();
+
+ ctx.beginPath();
+ ctx.fillStyle = "#FFF";
+ ctx.arc(left+6,top+6, map.blockSize / 6, 0, 300, false);
+ ctx.arc((left+s)-6,top+6, map.blockSize / 6, 0, 300, false);
+ ctx.closePath();
+ ctx.fill();
+
+ var f = map.blockSize / 12;
+ var off = {};
+ off[RIGHT] = [f, 0];
+ off[LEFT] = [-f, 0];
+ off[UP] = [0, -f];
+ off[DOWN] = [0, f];
+
+ ctx.beginPath();
+ ctx.fillStyle = "#000";
+ ctx.arc(left+6+off[direction][0], top+6+off[direction][1],
+ map.blockSize / 15, 0, 300, false);
+ ctx.arc((left+s)-6+off[direction][0], top+6+off[direction][1],
+ map.blockSize / 15, 0, 300, false);
+ ctx.closePath();
+ ctx.fill();
+
+ };
+
+ function pane(pos) {
+
+ if (pos.y === 100 && pos.x >= 190 && direction === RIGHT) {
+ return {"y": 100, "x": -10};
+ }
+
+ if (pos.y === 100 && pos.x <= -10 && direction === LEFT) {
+ return position = {"y": 100, "x": 190};
+ }
+
+ return false;
+ };
+
+ function move(ctx) {
+
+ var oldPos = position,
+ onGrid = onGridSquare(position),
+ npos = null;
+
+ if (due !== direction) {
+
+ npos = getNewCoord(due, position);
+
+ if (onGrid &&
+ map.isFloorSpace(pointToCoord(nextSquare(npos.y, due)),
+ pointToCoord(nextSquare(npos.x, due)))) {
+ direction = due;
+ } else {
+ npos = null;
+ }
+ }
+
+ if (npos === null) {
+ npos = getNewCoord(direction, position);
+ }
+
+ if (onGrid &&
+ map.isWallSpace(pointToCoord(nextSquare(npos.y, direction)),
+ pointToCoord(nextSquare(npos.x, direction)))) {
+
+ due = getRandomDirection();
+ return move(ctx);
+ }
+
+ position = npos;
+
+ var tmp = pane(position);
+ if (tmp) {
+ position = tmp;
+ }
+
+ due = getRandomDirection();
+
+ return {
+ "new" : position,
+ "old" : oldPos
+ };
+ };
+
+ return {
+ "eat" : eat,
+ "isVunerable" : isVunerable,
+ "isDangerous" : isDangerous,
+ "makeEatable" : makeEatable,
+ "reset" : reset,
+ "move" : move,
+ "draw" : draw
+ };
+};
+
+Pacman.User = function (game, map) {
+
+ var position = null,
+ direction = null,
+ eaten = null,
+ due = null,
+ lives = null,
+ score = 5,
+ keyMap = {};
+
+ keyMap[37] = LEFT;
+ keyMap[38] = UP;
+ keyMap[39] = RIGHT;
+ keyMap[40] = DOWN;
+
+ function addScore(nScore) {
+ score += nScore;
+ if (score >= 10000 && score - nScore < 10000) {
+ lives += 1;
+ }
+ };
+
+ function theScore() {
+ return score;
+ };
+
+ function loseLife() {
+ lives -= 1;
+ };
+
+ function getLives() {
+ return lives;
+ };
+
+ function initUser() {
+ score = 0;
+ lives = 3;
+ newLevel();
+ }
+
+ function newLevel() {
+ resetPosition();
+ eaten = 0;
+ };
+
+ function resetPosition() {
+ position = {"x": 90, "y": 120};
+ direction = LEFT;
+ due = LEFT;
+ };
+
+ function reset() {
+ initUser();
+ resetPosition();
+ };
+
+ function keyDown(e) {
+ if (typeof keyMap[e.keyCode] !== "undefined") {
+ due = keyMap[e.keyCode];
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ return true;
+ };
+
+ function getNewCoord(dir, current) {
+ return {
+ "x": current.x + (dir === LEFT && -2 || dir === RIGHT && 2 || 0),
+ "y": current.y + (dir === DOWN && 2 || dir === UP && -2 || 0)
+ };
+ };
+
+ function onWholeSquare(x) {
+ return x % 10 === 0;
+ };
+
+ function pointToCoord(x) {
+ return Math.round(x/10);
+ };
+
+ function nextSquare(x, dir) {
+ var rem = x % 10;
+ if (rem === 0) {
+ return x;
+ } else if (dir === RIGHT || dir === DOWN) {
+ return x + (10 - rem);
+ } else {
+ return x - rem;
+ }
+ };
+
+ function onGridSquare(pos) {
+ return onWholeSquare(pos.y) && onWholeSquare(pos.x);
+ };
+
+ function isOnSamePlane(due, dir) {
+ return ((due === LEFT || due === RIGHT) &&
+ (dir === LEFT || dir === RIGHT)) ||
+ ((due === UP || due === DOWN) &&
+ (dir === UP || dir === DOWN));
+ };
+
+ function move(ctx) {
+
+ var npos = null;
+
+ if (due !== direction) {
+ npos = getNewCoord(due, position);
+
+ if (isOnSamePlane(due, direction) ||
+ (onGridSquare(position) &&
+ map.isFloorSpace(pointToCoord(nextSquare(npos.y, due)),
+ pointToCoord(nextSquare(npos.x, due))))) {
+ direction = due;
+ } else {
+ npos = null;
+ }
+ }
+
+ if (npos === null) {
+ npos = getNewCoord(direction, position);
+ }
+
+ if (onGridSquare(position) &&
+ map.isWallSpace(pointToCoord(nextSquare(npos.y, direction)),
+ pointToCoord(nextSquare(npos.x, direction)))) {
+ direction = NONE;
+ }
+
+ if (direction === NONE) {
+ return {"new" : position, "old" : position};
+ }
+
+ if (npos.y === 100 && npos.x >= 190 && direction === RIGHT) {
+ position = {"y": 100, "x": -10};
+ return {"new" : position, "old" : position};
+ }
+
+ if (npos.y === 100 && npos.x <= -12 && direction === LEFT) {
+ position = {"y": 100, "x": 190};
+ return {"new" : position, "old" : position};
+ }
+
+ var oldPosition = position;
+ position = npos;
+
+ var block = map.block(pointToCoord(nextSquare(position.y, direction)),
+ pointToCoord(nextSquare(position.x, direction)));
+
+
+ if ((isMidSquare(position.y) || isMidSquare(position.x)) &&
+ block === Pacman.BISCUIT || block === Pacman.PILL) {
+
+ map.setBlock(pointToCoord(nextSquare(position.y, direction)),
+ pointToCoord(nextSquare(position.x, direction)),
+ Pacman.EMPTY);
+
+ addScore(10);
+ eaten += 1;
+
+ if (eaten === 182) {
+ game.completedLevel();
+ }
+
+ if (block === Pacman.PILL) {
+ game.eatenPill();
+ }
+ }
+
+ return {
+ "new" : position,
+ "old" : oldPosition
+ };
+ };
+
+ function isMidSquare(x) {
+ var rem = x % 10;
+ return rem > 3 || rem < 7;
+ };
+
+ function calcAngle(dir, pos) {
+ if (dir == RIGHT && (pos.x % 10 < 5)) {
+ return {"start":0.25, "end":1.75, "direction": false};
+ } else if (dir === DOWN && (pos.y % 10 < 5)) {
+ return {"start":0.75, "end":2.25, "direction": false};
+ } else if (dir === UP && (pos.y % 10 < 5)) {
+ return {"start":1.25, "end":1.75, "direction": true};
+ } else if (dir === LEFT && (pos.x % 10 < 5)) {
+ return {"start":0.75, "end":1.25, "direction": true};
+ }
+ return {"start":0, "end":2, "direction": false};
+ };
+
+ function drawDead(ctx, amount) {
+
+ var size = map.blockSize,
+ half = size / 2;
+
+ if (amount >= 1) {
+ return;
+ }
+ ctx.fillStyle = "#FFFF00";
+ ctx.beginPath();
+ ctx.moveTo(((position.x/10) * size) + half,
+ ((position.y/10) * size) + half);
+
+ ctx.arc(((position.x/10) * size) + half,
+ ((position.y/10) * size) + half,
+ half, 0, Math.PI * 2 * amount, true);
+
+ ctx.fill();
+ };
+
+ function draw(ctx) {
+
+ var angle = calcAngle(direction, position);
+
+ ctx.fillStyle = "#FFFF00";
+
+ ctx.beginPath();
+
+ ctx.moveTo(((position.x/10) * map.blockSize) + map.blockSize / 2,
+ ((position.y/10) * map.blockSize) + map.blockSize / 2);
+
+ ctx.arc(((position.x/10) * map.blockSize) + map.blockSize / 2,
+ ((position.y/10) * map.blockSize) + map.blockSize / 2,
+ map.blockSize / 2,
+ Math.PI * angle.start,
+ Math.PI * angle.end, angle.direction);
+
+ ctx.fill();
+ };
+
+ initUser();
+
+ return {
+ "draw" : draw,
+ "drawDead" : drawDead,
+ "loseLife" : loseLife,
+ "getLives" : getLives,
+ "score" : score,
+ "addScore" : addScore,
+ "theScore" : theScore,
+ "keyDown" : keyDown,
+ "move" : move,
+ "newLevel" : newLevel,
+ "reset" : reset,
+ "resetPosition" : resetPosition
+ };
+};
+
+Pacman.Map = function (size) {
+
+ var height = null,
+ width = null,
+ blockSize = size,
+ pillSize = 0,
+ map = null;
+
+ function withinBounds(y, x) {
+ return y >= 0 && y < height && x >= 0 && x < width;
+ }
+
+ function isWall(y, x) {
+ return withinBounds(y, x) && map[y][x] === Pacman.WALL;
+ }
+
+ function isFloorSpace(y, x) {
+ if (!withinBounds(y, x)) {
+ return false;
+ }
+ var peice = map[y][x];
+ return peice === Pacman.EMPTY ||
+ peice === Pacman.BISCUIT ||
+ peice === Pacman.PILL;
+ }
+
+ function drawWall(ctx) {
+
+ var i, j, p, line;
+
+ ctx.strokeStyle = "#0000FF";
+ ctx.lineWidth = 5;
+ ctx.lineCap = "round";
+
+ for (i = 0; i < Pacman.WALLS.length; i += 1) {
+ line = Pacman.WALLS[i];
+ ctx.beginPath();
+
+ for (j = 0; j < line.length; j += 1) {
+
+ p = line[j];
+
+ if (p.move) {
+ ctx.moveTo(p.move[0] * blockSize, p.move[1] * blockSize);
+ } else if (p.line) {
+ ctx.lineTo(p.line[0] * blockSize, p.line[1] * blockSize);
+ } else if (p.curve) {
+ ctx.quadraticCurveTo(p.curve[0] * blockSize,
+ p.curve[1] * blockSize,
+ p.curve[2] * blockSize,
+ p.curve[3] * blockSize);
+ }
+ }
+ ctx.stroke();
+ }
+ }
+
+ function reset() {
+ map = Pacman.MAP.clone();
+ height = map.length;
+ width = map[0].length;
+ };
+
+ function block(y, x) {
+ return map[y][x];
+ };
+
+ function setBlock(y, x, type) {
+ map[y][x] = type;
+ };
+
+ function drawPills(ctx) {
+
+ if (++pillSize > 30) {
+ pillSize = 0;
+ }
+
+ for (i = 0; i < height; i += 1) {
+ for (j = 0; j < width; j += 1) {
+ if (map[i][j] === Pacman.PILL) {
+ ctx.beginPath();
+
+ ctx.fillStyle = "#000";
+ ctx.fillRect((j * blockSize), (i * blockSize),
+ blockSize, blockSize);
+
+ ctx.fillStyle = "#FFF";
+ ctx.arc((j * blockSize) + blockSize / 2,
+ (i * blockSize) + blockSize / 2,
+ Math.abs(5 - (pillSize/3)),
+ 0,
+ Math.PI * 2, false);
+ ctx.fill();
+ ctx.closePath();
+ }
+ }
+ }
+ };
+
+ function draw(ctx) {
+
+ var i, j, size = blockSize;
+
+ ctx.fillStyle = "#000";
+ ctx.fillRect(0, 0, width * size, height * size);
+
+ drawWall(ctx);
+
+ for (i = 0; i < height; i += 1) {
+ for (j = 0; j < width; j += 1) {
+ drawBlock(i, j, ctx);
+ }
+ }
+ };
+
+ function drawBlock(y, x, ctx) {
+
+ var layout = map[y][x];
+
+ if (layout === Pacman.PILL) {
+ return;
+ }
+
+ ctx.beginPath();
+
+ if (layout === Pacman.EMPTY ||
+ layout === Pacman.BLOCK ||
+ layout === Pacman.BISCUIT) {
+
+ ctx.fillStyle = "#000";
+ ctx.fillRect((x * blockSize), (y * blockSize),
+ blockSize, blockSize);
+
+ if (layout === Pacman.BISCUIT) {
+ ctx.fillStyle = "#FFF";
+ ctx.fillRect((x * blockSize) + (blockSize / 2.5),
+ (y * blockSize) + (blockSize / 2.5),
+ blockSize / 6, blockSize / 6);
+ }
+ }
+ ctx.closePath();
+ };
+
+ reset();
+
+ return {
+ "draw" : draw,
+ "drawBlock" : drawBlock,
+ "drawPills" : drawPills,
+ "block" : block,
+ "setBlock" : setBlock,
+ "reset" : reset,
+ "isWallSpace" : isWall,
+ "isFloorSpace" : isFloorSpace,
+ "height" : height,
+ "width" : width,
+ "blockSize" : blockSize
+ };
+};
+
+Pacman.Audio = function(game) {
+
+ var files = [],
+ endEvents = [],
+ progressEvents = [],
+ playing = [];
+
+ function load(name, path, cb) {
+
+ var f = files[name] = document.createElement("audio");
+
+ progressEvents[name] = function(event) { progress(event, name, cb); };
+
+ f.addEventListener("canplaythrough", progressEvents[name], true);
+ f.setAttribute("preload", "true");
+ f.setAttribute("autobuffer", "true");
+ f.setAttribute("src", path);
+ f.pause();
+ };
+
+ function progress(event, name, callback) {
+ if (event.loaded === event.total && typeof callback === "function") {
+ callback();
+ files[name].removeEventListener("canplaythrough",
+ progressEvents[name], true);
+ }
+ };
+
+ function disableSound() {
+ for (var i = 0; i < playing.length; i++) {
+ files[playing[i]].pause();
+ files[playing[i]].currentTime = 0;
+ }
+ playing = [];
+ };
+
+ function ended(name) {
+
+ var i, tmp = [], found = false;
+
+ files[name].removeEventListener("ended", endEvents[name], true);
+
+ for (i = 0; i < playing.length; i++) {
+ if (!found && playing[i]) {
+ found = true;
+ } else {
+ tmp.push(playing[i]);
+ }
+ }
+ playing = tmp;
+ };
+
+ function play(name) {
+ if (!game.soundDisabled()) {
+ endEvents[name] = function() { ended(name); };
+ playing.push(name);
+ files[name].addEventListener("ended", endEvents[name], true);
+ files[name].play();
+ }
+ };
+
+ function pause() {
+ for (var i = 0; i < playing.length; i++) {
+ files[playing[i]].pause();
+ }
+ };
+
+ function resume() {
+ for (var i = 0; i < playing.length; i++) {
+ files[playing[i]].play();
+ }
+ };
+
+ return {
+ "disableSound" : disableSound,
+ "load" : load,
+ "play" : play,
+ "pause" : pause,
+ "resume" : resume
+ };
+};
+
+var PACMAN = (function () {
+
+ var state = WAITING,
+ audio = null,
+ ghosts = [],
+ ghostSpecs = ["#00FFDE", "#FF0000", "#FFB8DE", "#FFB847"],
+ eatenCount = 0,
+ level = 0,
+ tick = 0,
+ ghostPos, userPos,
+ stateChanged = true,
+ timerStart = null,
+ lastTime = 0,
+ ctx = null,
+ timer = null,
+ map = null,
+ user = null,
+ stored = null;
+
+ function getTick() {
+ return tick;
+ };
+
+ function drawScore(text, position) {
+ ctx.fillStyle = "#FFFFFF";
+ ctx.font = "12px BDCartoonShoutRegular";
+ ctx.fillText(text,
+ (position["new"]["x"] / 10) * map.blockSize,
+ ((position["new"]["y"] + 5) / 10) * map.blockSize);
+ }
+
+ function dialog(text) {
+ ctx.fillStyle = "#FFFF00";
+ ctx.font = "14px BDCartoonShoutRegular";
+ var width = ctx.measureText(text).width,
+ x = ((map.width * map.blockSize) - width) / 2;
+ ctx.fillText(text, x, (map.height * 10) + 8);
+ }
+
+ function soundDisabled() {
+ return localStorage["soundDisabled"] === "true";
+ };
+
+ function startLevel() {
+ user.resetPosition();
+ for (var i = 0; i < ghosts.length; i += 1) {
+ ghosts[i].reset();
+ }
+ audio.play("start");
+ timerStart = tick;
+ setState(COUNTDOWN);
+ }
+
+ function startNewGame() {
+ setState(WAITING);
+ level = 1;
+ user.reset();
+ map.reset();
+ map.draw(ctx);
+ startLevel();
+ }
+
+ function keyDown(e) {
+ if (e.keyCode === 78) {
+ startNewGame();
+ } else if (e.keyCode === 83) {
+ audio.disableSound();
+ localStorage["soundDisabled"] = !soundDisabled();
+ } else if (e.keyCode === 80 && state === PAUSE) {
+ audio.resume();
+ map.draw(ctx);
+ setState(stored);
+ } else if (e.keyCode === 80) {
+ stored = state;
+ setState(PAUSE);
+ audio.pause();
+ map.draw(ctx);
+ dialog("Paused");
+ } else if (state !== PAUSE) {
+ return user.keyDown(e);
+ }
+ return true;
+ }
+
+ function loseLife() {
+ setState(WAITING);
+ user.loseLife();
+ if (user.getLives() > 0) {
+ startLevel();
+ }
+ }
+
+ function setState(nState) {
+ state = nState;
+ stateChanged = true;
+ };
+
+ function collided(user, ghost) {
+ return (Math.sqrt(Math.pow(ghost.x - user.x, 2) +
+ Math.pow(ghost.y - user.y, 2))) < 10;
+ };
+
+ function drawFooter() {
+
+ var topLeft = (map.height * map.blockSize),
+ textBase = topLeft + 17;
+
+ ctx.fillStyle = "#000000";
+ ctx.fillRect(0, topLeft, (map.width * map.blockSize), 30);
+
+ ctx.fillStyle = "#FFFF00";
+
+ for (var i = 0, len = user.getLives(); i < len; i++) {
+ ctx.fillStyle = "#FFFF00";
+ ctx.beginPath();
+ ctx.moveTo(150 + (25 * i) + map.blockSize / 2,
+ (topLeft+1) + map.blockSize / 2);
+
+ ctx.arc(150 + (25 * i) + map.blockSize / 2,
+ (topLeft+1) + map.blockSize / 2,
+ map.blockSize / 2, Math.PI * 0.25, Math.PI * 1.75, false);
+ ctx.fill();
+ }
+
+ ctx.fillStyle = !soundDisabled() ? "#00FF00" : "#FF0000";
+ ctx.font = "bold 16px sans-serif";
+ ctx.fillText("", 10, textBase);
+
+ ctx.fillStyle = "#FFFF00";
+ ctx.font = "14px BDCartoonShoutRegular";
+ ctx.fillText("Score: " + user.theScore(), 30, textBase);
+ ctx.fillText("Level: " + level, 260, textBase);
+ }
+
+ function redrawBlock(pos) {
+ map.drawBlock(Math.floor(pos.y/10), Math.floor(pos.x/10), ctx);
+ map.drawBlock(Math.ceil(pos.y/10), Math.ceil(pos.x/10), ctx);
+ }
+
+ function mainDraw() {
+
+ var diff, u, i, len, nScore;
+
+ ghostPos = [];
+
+ for (i = 0, len = ghosts.length; i < len; i += 1) {
+ ghostPos.push(ghosts[i].move(ctx));
+ }
+ u = user.move(ctx);
+
+ for (i = 0, len = ghosts.length; i < len; i += 1) {
+ redrawBlock(ghostPos[i].old);
+ }
+ redrawBlock(u.old);
+
+ for (i = 0, len = ghosts.length; i < len; i += 1) {
+ ghosts[i].draw(ctx);
+ }
+ user.draw(ctx);
+
+ userPos = u["new"];
+
+ for (i = 0, len = ghosts.length; i < len; i += 1) {
+ if (collided(userPos, ghostPos[i]["new"])) {
+ if (ghosts[i].isVunerable()) {
+ audio.play("eatghost");
+ ghosts[i].eat();
+ eatenCount += 1;
+ nScore = eatenCount * 50;
+ drawScore(nScore, ghostPos[i]);
+ user.addScore(nScore);
+ setState(EATEN_PAUSE);
+ timerStart = tick;
+ } else if (ghosts[i].isDangerous()) {
+ audio.play("die");
+ setState(DYING);
+ timerStart = tick;
+ }
+ }
+ }
+ };
+
+ function mainLoop() {
+
+ var diff;
+
+ if (state !== PAUSE) {
+ ++tick;
+ }
+
+ map.drawPills(ctx);
+
+ if (state === PLAYING) {
+ mainDraw();
+ } else if (state === WAITING && stateChanged) {
+ stateChanged = false;
+ map.draw(ctx);
+ dialog("Press N to start a New game");
+ } else if (state === EATEN_PAUSE && tick - timerStart > 11) {
+ map.draw(ctx);
+ setState(PLAYING);
+ } else if (state === DYING) {
+ if (tick - timerStart > 60) {
+ loseLife();
+ } else {
+ redrawBlock(userPos);
+ for (i = 0, len = ghosts.length; i < len; i += 1) {
+ redrawBlock(ghostPos[i].old);
+ ghostPos.push(ghosts[i].draw(ctx));
+ }
+ user.drawDead(ctx, (tick - timerStart) / 60);
+ }
+ } else if (state === COUNTDOWN) {
+
+ diff = 5 + Math.floor((timerStart - tick) / 30);
+
+ if (diff === 0) {
+ map.draw(ctx);
+ setState(PLAYING);
+ } else {
+ if (diff !== lastTime) {
+ lastTime = diff;
+ map.draw(ctx);
+ dialog("Starting in: " + diff);
+ }
+ }
+ }
+
+ drawFooter();
+ }
+
+ function eatenPill() {
+ audio.play("eatpill");
+ timerStart = tick;
+ eatenCount = 0;
+ for (i = 0; i < ghosts.length; i += 1) {
+ ghosts[i].makeEatable(ctx);
+ }
+ };
+
+ function completedLevel() {
+ setState(WAITING);
+ level += 1;
+ map.reset();
+ user.newLevel();
+ startLevel();
+ };
+
+ function keyPress(e) {
+ if (state !== WAITING && state !== PAUSE) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ };
+
+ function init(wrapper, root) {
+
+ var i, len, ghost,
+ maxHeight = (wrapper.offsetHeight - 30) / 22,
+ maxWidth = wrapper.offsetWidth / 19,
+ blockSize = Math.floor(Math.min(maxWidth, maxHeight)),
+ canvas = document.createElement("canvas");
+
+ canvas.setAttribute("width", (blockSize * 19) + "px");
+ canvas.setAttribute("height", (blockSize * 22) + 30 + "px");
+
+ wrapper.appendChild(canvas);
+
+ ctx = canvas.getContext('2d');
+
+ audio = new Pacman.Audio({"soundDisabled":soundDisabled});
+ map = new Pacman.Map(blockSize);
+ user = new Pacman.User({
+ "completedLevel" : completedLevel,
+ "eatenPill" : eatenPill
+ }, map);
+
+ for (i = 0, len = ghostSpecs.length; i < len; i += 1) {
+ ghost = new Pacman.Ghost({"getTick":getTick}, map, ghostSpecs[i]);
+ ghosts.push(ghost);
+ }
+
+ map.draw(ctx);
+ dialog("Loading ...");
+
+ var audio_files = [
+ ["start", root + "audio/opening_song.ogg"],
+ ["die", root + "audio/die.ogg"],
+ ["eatghost", root + "audio/eatghost.ogg"],
+ ["eatpill", root + "audio/eatpill.ogg"],
+ ["eating", root + "audio/eating.short.ogg"],
+ ["eating2", root + "audio/eating.short.ogg"]
+ ];
+
+ load(audio_files);
+ };
+
+ function load(arr) {
+
+ if (arr.length === 0) {
+ loaded();
+ } else {
+ var x = arr.pop();
+ audio.load(x[0], x[1], function() { load(arr); });
+ }
+ };
+
+ function loaded() {
+
+ dialog("Press N to Start");
+
+ document.addEventListener("keydown", keyDown, true);
+ document.addEventListener("keypress", keyPress, true);
+
+ timer = window.setInterval(mainLoop, 35);
+ };
+
+ return {
+ "init" : init
+ };
+
+}());

0 comments on commit c42f3ce

Please sign in to comment.
Something went wrong with that request. Please try again.