mirror of
https://github.com/pierre42100/ComunicWeb
synced 2024-11-24 13:09:20 +00:00
1271 lines
37 KiB
JavaScript
1271 lines
37 KiB
JavaScript
/*jslint browser: true, undef: true, eqeqeq: true, nomen: true, white: true */
|
|
/*global window: false, document: false */
|
|
|
|
/*
|
|
* fix looped audio
|
|
* add fruits + levels
|
|
* fix what happens when a ghost is eaten (should go back to base)
|
|
* do proper ghost mechanics (blinky/wimpy etc)
|
|
*/
|
|
|
|
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 = {};
|
|
|
|
Pacman.FPS = 30;
|
|
|
|
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,
|
|
xSpeed = (dir === LEFT && -speed || dir === RIGHT && speed || 0),
|
|
ySpeed = (dir === DOWN && speed || dir === UP && -speed || 0);
|
|
|
|
return {
|
|
"x": addBounded(current.x, xSpeed),
|
|
"y": addBounded(current.y, ySpeed)
|
|
};
|
|
};
|
|
|
|
/* Collision detection(walls) is done when a ghost lands on an
|
|
* exact block, make sure they dont skip over it
|
|
*/
|
|
function addBounded(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 oppositeDirection(dir) {
|
|
return dir === LEFT && RIGHT ||
|
|
dir === RIGHT && LEFT ||
|
|
dir === UP && DOWN || UP;
|
|
};
|
|
|
|
function makeEatable() {
|
|
direction = oppositeDirection(direction);
|
|
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) / Pacman.FPS;
|
|
};
|
|
|
|
function getColour() {
|
|
if (eatable) {
|
|
if (secondsAgo(eatable) > 5) {
|
|
return game.getTick() % 20 > 10 ? "#FFFFFF" : "#0000BB";
|
|
} else {
|
|
return "#0000BB";
|
|
}
|
|
} else if(eaten) {
|
|
return "#222";
|
|
}
|
|
return colour;
|
|
};
|
|
|
|
function draw(ctx) {
|
|
|
|
var s = map.blockSize,
|
|
top = (position.y/10) * s,
|
|
left = (position.x/10) * s;
|
|
|
|
if (eatable && secondsAgo(eatable) > 8) {
|
|
eatable = null;
|
|
}
|
|
|
|
if (eaten && secondsAgo(eaten) > 3) {
|
|
eaten = null;
|
|
}
|
|
|
|
var tl = left + s;
|
|
var base = top + s - 3;
|
|
var inc = s / 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, s / 6, 0, 300, false);
|
|
ctx.arc((left + s) - 6,top + 6, s / 6, 0, 300, false);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
var f = s / 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],
|
|
s / 15, 0, 300, false);
|
|
ctx.arc((left+s)-6+off[direction][0], top+6+off[direction][1],
|
|
s / 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({
|
|
"y":pointToCoord(nextSquare(npos.y, due)),
|
|
"x":pointToCoord(nextSquare(npos.x, due))})) {
|
|
direction = due;
|
|
} else {
|
|
npos = null;
|
|
}
|
|
}
|
|
|
|
if (npos === null) {
|
|
npos = getNewCoord(direction, position);
|
|
}
|
|
|
|
if (onGrid &&
|
|
map.isWallSpace({
|
|
"y" : pointToCoord(nextSquare(npos.y, direction)),
|
|
"x" : 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[KEY.ARROW_LEFT] = LEFT;
|
|
keyMap[KEY.ARROW_UP] = UP;
|
|
keyMap[KEY.ARROW_RIGHT] = RIGHT;
|
|
keyMap[KEY.ARROW_DOWN] = 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 next(pos, dir) {
|
|
return {
|
|
"y" : pointToCoord(nextSquare(pos.y, dir)),
|
|
"x" : pointToCoord(nextSquare(pos.x, dir)),
|
|
};
|
|
};
|
|
|
|
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,
|
|
nextWhole = null,
|
|
oldPosition = position,
|
|
block = null;
|
|
|
|
if (due !== direction) {
|
|
npos = getNewCoord(due, position);
|
|
|
|
if (isOnSamePlane(due, direction) ||
|
|
(onGridSquare(position) &&
|
|
map.isFloorSpace(next(npos, due)))) {
|
|
direction = due;
|
|
} else {
|
|
npos = null;
|
|
}
|
|
}
|
|
|
|
if (npos === null) {
|
|
npos = getNewCoord(direction, position);
|
|
}
|
|
|
|
if (onGridSquare(position) && map.isWallSpace(next(npos, direction))) {
|
|
direction = NONE;
|
|
}
|
|
|
|
if (direction === NONE) {
|
|
return {"new" : position, "old" : position};
|
|
}
|
|
|
|
if (npos.y === 100 && npos.x >= 190 && direction === RIGHT) {
|
|
npos = {"y": 100, "x": -10};
|
|
}
|
|
|
|
if (npos.y === 100 && npos.x <= -12 && direction === LEFT) {
|
|
npos = {"y": 100, "x": 190};
|
|
}
|
|
|
|
position = npos;
|
|
nextWhole = next(position, direction);
|
|
|
|
block = map.block(nextWhole);
|
|
|
|
if ((isMidSquare(position.y) || isMidSquare(position.x)) &&
|
|
block === Pacman.BISCUIT || block === Pacman.PILL) {
|
|
|
|
map.setBlock(nextWhole, Pacman.EMPTY);
|
|
addScore((block === Pacman.BISCUIT) ? 10 : 50);
|
|
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 s = map.blockSize,
|
|
angle = calcAngle(direction, position);
|
|
|
|
ctx.fillStyle = "#FFFF00";
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.moveTo(((position.x/10) * s) + s / 2,
|
|
((position.y/10) * s) + s / 2);
|
|
|
|
ctx.arc(((position.x/10) * s) + s / 2,
|
|
((position.y/10) * s) + s / 2,
|
|
s / 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(pos) {
|
|
return withinBounds(pos.y, pos.x) && map[pos.y][pos.x] === Pacman.WALL;
|
|
}
|
|
|
|
function isFloorSpace(pos) {
|
|
if (!withinBounds(pos.y, pos.x)) {
|
|
return false;
|
|
}
|
|
var peice = map[pos.y][pos.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 = cloneObj(Pacman.MAP);
|
|
height = map.length;
|
|
width = map[0].length;
|
|
};
|
|
|
|
function block(pos) {
|
|
return map[pos.y][pos.x];
|
|
};
|
|
|
|
function setBlock(pos, type) {
|
|
map[pos.y][pos.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 === KEY.N) {
|
|
startNewGame();
|
|
} else if (e.keyCode === KEY.S) {
|
|
audio.disableSound();
|
|
localStorage["soundDisabled"] = !soundDisabled();
|
|
} else if (e.keyCode === KEY.P && state === PAUSE) {
|
|
audio.resume();
|
|
map.draw(ctx);
|
|
setState(stored);
|
|
} else if (e.keyCode === KEY.P) {
|
|
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.fillText("s", 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) > (Pacman.FPS / 3)) {
|
|
map.draw(ctx);
|
|
setState(PLAYING);
|
|
} else if (state === DYING) {
|
|
if (tick - timerStart > (Pacman.FPS * 2)) {
|
|
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) / (Pacman.FPS * 2));
|
|
}
|
|
} else if (state === COUNTDOWN) {
|
|
|
|
diff = 5 + Math.floor((timerStart - tick) / Pacman.FPS);
|
|
|
|
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,
|
|
blockSize = wrapper.offsetWidth / 19,
|
|
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 extension = Modernizr.audio.ogg ? 'ogg' : 'mp3';
|
|
|
|
var audio_files = [
|
|
["start", root + "audio/opening_song." + extension],
|
|
["die", root + "audio/die." + extension],
|
|
["eatghost", root + "audio/eatghost." + extension],
|
|
["eatpill", root + "audio/eatpill." + extension],
|
|
["eating", root + "audio/eating.short." + extension],
|
|
["eating2", root + "audio/eating.short." + extension]
|
|
];
|
|
|
|
load(audio_files, function() { loaded(); });
|
|
};
|
|
|
|
function load(arr, callback) {
|
|
|
|
if (arr.length === 0) {
|
|
callback();
|
|
} else {
|
|
var x = arr.pop();
|
|
audio.load(x[0], x[1], function() { load(arr, callback); });
|
|
}
|
|
};
|
|
|
|
function loaded() {
|
|
|
|
dialog("Press N to Start");
|
|
|
|
document.addEventListener("keydown", keyDown, true);
|
|
document.addEventListener("keypress", keyPress, true);
|
|
|
|
timer = window.setInterval(mainLoop, 1000 / Pacman.FPS);
|
|
};
|
|
|
|
return {
|
|
"init" : init
|
|
};
|
|
|
|
}());
|
|
|
|
/* Human readable keyCode index */
|
|
var KEY = {'BACKSPACE': 8, 'TAB': 9, 'NUM_PAD_CLEAR': 12, 'ENTER': 13, 'SHIFT': 16, 'CTRL': 17, 'ALT': 18, 'PAUSE': 19, 'CAPS_LOCK': 20, 'ESCAPE': 27, 'SPACEBAR': 32, 'PAGE_UP': 33, 'PAGE_DOWN': 34, 'END': 35, 'HOME': 36, 'ARROW_LEFT': 37, 'ARROW_UP': 38, 'ARROW_RIGHT': 39, 'ARROW_DOWN': 40, 'PRINT_SCREEN': 44, 'INSERT': 45, 'DELETE': 46, 'SEMICOLON': 59, 'WINDOWS_LEFT': 91, 'WINDOWS_RIGHT': 92, 'SELECT': 93, 'NUM_PAD_ASTERISK': 106, 'NUM_PAD_PLUS_SIGN': 107, 'NUM_PAD_HYPHEN-MINUS': 109, 'NUM_PAD_FULL_STOP': 110, 'NUM_PAD_SOLIDUS': 111, 'NUM_LOCK': 144, 'SCROLL_LOCK': 145, 'SEMICOLON': 186, 'EQUALS_SIGN': 187, 'COMMA': 188, 'HYPHEN-MINUS': 189, 'FULL_STOP': 190, 'SOLIDUS': 191, 'GRAVE_ACCENT': 192, 'LEFT_SQUARE_BRACKET': 219, 'REVERSE_SOLIDUS': 220, 'RIGHT_SQUARE_BRACKET': 221, 'APOSTROPHE': 222};
|
|
|
|
(function () {
|
|
/* 0 - 9 */
|
|
for (var i = 48; i <= 57; i++) {
|
|
KEY['' + (i - 48)] = i;
|
|
}
|
|
/* A - Z */
|
|
for (i = 65; i <= 90; i++) {
|
|
KEY['' + String.fromCharCode(i)] = i;
|
|
}
|
|
/* NUM_PAD_0 - NUM_PAD_9 */
|
|
for (i = 96; i <= 105; i++) {
|
|
KEY['NUM_PAD_' + (i - 96)] = i;
|
|
}
|
|
/* F1 - F12 */
|
|
for (i = 112; i <= 123; i++) {
|
|
KEY['F' + (i - 112 + 1)] = i;
|
|
}
|
|
})();
|
|
|
|
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]}]
|
|
];
|
|
|
|
function cloneObj(obj) {
|
|
var i, newObj = (obj instanceof Array) ? [] : {};
|
|
for (var i in obj) {
|
|
if (i === 'clone') {
|
|
continue;
|
|
}
|
|
if (obj[i] && typeof obj[i] === "object") {
|
|
newObj[i] = cloneObj(obj[i]);
|
|
} else {
|
|
newObj[i] = obj[i];
|
|
}
|
|
}
|
|
return newObj;
|
|
};
|
|
|