Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
FogNetwork
GitHub Repository: FogNetwork/Tsunami
Path: blob/main/public/games/files/pacman/pacman-canvas.js
1036 views
1
/*-------------------------------------------------------------------
2
3
___________ ____ _____ _____ ____
4
\____ \__ \ _/ ___\ / \\__ \ / \
5
| |_> > __ \\ \___| Y Y \/ __ \| | \
6
| __(____ /\___ >__|_| (____ /___| /
7
|__| \/ \/ \/ \/ \/ .platzh1rsch.ch
8
9
author: platzh1rsch (www.platzh1rsch.ch)
10
11
-------------------------------------------------------------------*/
12
13
"use strict";
14
15
// global enums
16
const GHOSTS = {
17
INKY: 'inky',
18
BLINKY: 'blinky',
19
PINKY: 'pinky',
20
CLYDE: 'clyde'
21
}
22
23
// global constants
24
const FINAL_LEVEL = 10;
25
const PILL_POINTS = 10;
26
const POWERPILL_POINTS = 50;
27
const GHOST_POINTS = 100;
28
const HIGHSCORE_ENABLED = true;
29
30
31
function geronimo() {
32
/* ----- Global Variables ---------------------------------------- */
33
var canvas;
34
var context;
35
var game;
36
var canvas_walls, context_walls;
37
var inky, blinky, clyde, pinky;
38
39
var mapConfig = "data/map.json";
40
41
42
/* AJAX stuff */
43
var getHighscore = () => {
44
setTimeout(ajax_get, 30);
45
}
46
var ajax_get = () => {
47
var date = new Date().getTime();
48
$.ajax({
49
datatype: "json",
50
type: "GET",
51
url: "data/db-handler.php",
52
data: {
53
timestamp: date,
54
action: "get"
55
},
56
success: function (msg) {
57
$("#highscore-list").text("");
58
for (var i = 0; i < msg.length; i++) {
59
$("#highscore-list").append("<li>" + msg[i]['name'] + "<span id='score'>" + msg[i]['score'] + "</span></li>");
60
}
61
}
62
});
63
}
64
var ajax_add = (n, s, l) => {
65
66
$.ajax({
67
type: 'POST',
68
url: 'data/db-handler.php',
69
data: {
70
action: 'add',
71
name: n,
72
score: s,
73
level: l
74
},
75
dataType: 'json',
76
success: function (data) {
77
console.log('Highscore added: ' + data);
78
$('#highscore-form').html('<span class="button" id="show-highscore">View Highscore List</span>');
79
},
80
error: function (errorThrown) {
81
console.log(errorThrown);
82
}
83
});
84
}
85
86
function addHighscore() {
87
var name = $("input[type=text]").val();
88
$("#highscore-form").html("Saving highscore...");
89
ajax_add(name, game.score.score, game.level);
90
}
91
92
function buildWall(context, gridX, gridY, width, height) {
93
console.log("BuildWall");
94
width = width * 2 - 1;
95
height = height * 2 - 1;
96
context.fillRect(pacman.radius / 2 + gridX * 2 * pacman.radius, pacman.radius / 2 + gridY * 2 * pacman.radius, width * pacman.radius, height * pacman.radius);
97
}
98
99
function between(x, min, max) {
100
return x >= min && x <= max;
101
}
102
103
// Logger
104
var logger = function () {
105
var originalConsoleLog = null;
106
var originalConsoleDebug = null;
107
var logger = {};
108
109
logger.enableLogger = function enableLogger() {
110
if (originalConsoleLog === null)
111
return;
112
113
window['console']['log'] = originalConsoleLog;
114
console.log('console.log enabled');
115
116
if (originalConsoleDebug === null)
117
return;
118
119
window['console']['debug'] = originalConsoleDebug;
120
console.log('console.debug enabled');
121
122
};
123
124
logger.disableLogger = function disableLogger() {
125
console.log('console.log disabled');
126
originalConsoleLog = console.log;
127
window['console']['log'] = function () {};
128
originalConsoleDebug = console.debug;
129
window['console']['debug'] = function () {};
130
};
131
132
return logger;
133
}();
134
135
// stop watch to measure the time
136
function Timer() {
137
this.time_diff = 0;
138
this.time_start = 0;
139
this.time_stop = 0;
140
this.start = function () {
141
this.time_start = new Date().getTime();
142
}
143
this.stop = function () {
144
this.time_stop = new Date().getTime();
145
this.time_diff += this.time_stop - this.time_start;
146
this.time_stop = 0;
147
this.time_start = 0;
148
}
149
this.reset = function () {
150
this.time_diff = 0;
151
this.time_start = 0;
152
this.time_stop = 0;
153
}
154
this.get_time_diff = function () {
155
return this.time_diff;
156
}
157
}
158
159
// Manages the whole game ("God Object")
160
function Game() {
161
this.timer = new Timer(); // TODO: implememnt properly, and submit with highscore
162
this.refreshRate = 33; // speed of the game, will increase in higher levels
163
164
this.started = false; // TODO: what's the purpose of this exactly?
165
this.pause = true;
166
this.gameOver = false;
167
168
this.score = new Score();
169
this.soundfx = 0;
170
this.map;
171
this.pillCount; // number of pills
172
this.monsters;
173
this.level = 1;
174
this.refreshLevel = function (h) {
175
$(h).html("Lvl: " + this.level);
176
};
177
this.canvas = $("#myCanvas").get(0);
178
this.wallColor = "Blue";
179
this.width = this.canvas.width;
180
this.height = this.canvas.height;
181
182
// global pill states
183
this.pillSize = 3;
184
this.powerpillSizeMin = 2;
185
this.powerpillSizeMax = 6;
186
this.powerpillSizeCurrent = this.powerpillSizeMax;
187
this.powerPillAnimationCounter = 0;
188
189
// TODO: vibrant power pills
190
this.nextPowerPillSize = function () {
191
/*if (this.powerPillAnimationCounter === 3) {
192
this.powerPillAnimationCounter = 0;
193
this.powerpillSizeCurrent = this.powerpillSizeMin + this.powerpillSizeCurrent % (this.powerpillSizeMax-this.powerpillSizeMin);
194
} else {
195
this.powerPillAnimationCounter++;
196
}*/
197
return this.powerpillSizeCurrent;
198
};
199
200
// global ghost states
201
this.ghostFrightened = false;
202
this.ghostFrightenedTimer = 240;
203
this.ghostMode = 0; // 0 = Scatter, 1 = Chase
204
this.ghostModeTimer = 200; // decrements each animationLoop execution
205
this.ghostSpeedNormal = (this.level > 4 ? 3 : 2); // global default for ghost speed
206
this.ghostSpeedDazzled = 2; // global default for ghost speed when dazzled
207
208
/* Game Functions */
209
this.startGhostFrightened = function () {
210
console.log("ghost frigthened");
211
this.ghostFrightened = true;
212
this.ghostFrightenedTimer = 240;
213
inky.dazzle();
214
pinky.dazzle();
215
blinky.dazzle();
216
clyde.dazzle();
217
};
218
219
this.endGhostFrightened = function () {
220
this.ghostFrightened = false;
221
inky.undazzle();
222
pinky.undazzle();
223
blinky.undazzle();
224
clyde.undazzle();
225
};
226
227
228
this.checkGhostMode = function () {
229
if (this.ghostFrightened) {
230
231
this.ghostFrightenedTimer--;
232
if (this.ghostFrightenedTimer === 0) {
233
this.endGhostFrightened();
234
this.ghostFrigthenedTimer = 240;
235
/*inky.reverseDirection();
236
pinky.reverseDirection();
237
clyde.reverseDirection();
238
blinky.reverseDirection();*/
239
}
240
}
241
// always decrement ghostMode timer
242
this.ghostModeTimer--;
243
if (this.ghostModeTimer === 0 && game.level > 1) {
244
this.ghostMode ^= 1;
245
this.ghostModeTimer = 200 + this.ghostMode * 450;
246
console.log("ghostMode=" + this.ghostMode);
247
248
game.buildWalls();
249
250
inky.reverseDirection();
251
pinky.reverseDirection();
252
clyde.reverseDirection();
253
blinky.reverseDirection();
254
}
255
};
256
257
this.getMapContent = function (x, y) {
258
var maxX = game.width / 30 - 1;
259
var maxY = game.height / 30 - 1;
260
if (x < 0) x = maxX + x;
261
if (x > maxX) x = x - maxX;
262
if (y < 0) y = maxY + y;
263
if (y > maxY) y = y - maxY;
264
return this.map.posY[y].posX[x].type;
265
};
266
267
this.setMapContent = function (x, y, val) {
268
this.map.posY[y].posX[x].type = val;
269
};
270
271
this.toggleSound = function () {
272
this.soundfx === 0 ? this.soundfx = 1 : this.soundfx = 0;
273
$('#mute').toggle();
274
};
275
276
// TODO: test
277
this.reset = function () {
278
this.score.set(0);
279
this.score.refresh(".score");
280
pacman.lives = 3;
281
game.level = 1;
282
this.refreshLevel(".level");
283
284
this.pause = false;
285
this.gameOver = false;
286
};
287
288
this.newGame = function () {
289
var r = confirm("Are you sure you want to restart?");
290
if (r) {
291
console.log("new Game");
292
this.init(0);
293
this.forceResume();
294
}
295
};
296
297
this.nextLevel = function () {
298
console.debug('nextLevel: current, final', this.level, FINAL_LEVEL);
299
if (this.level === FINAL_LEVEL) {
300
console.log('next level, ' + FINAL_LEVEL + ', end game');
301
game.endGame(true);
302
game.showHighscoreForm();
303
} else {
304
this.level++;
305
console.log("Level " + game.level);
306
game.pauseAndShowMessage("Level " + game.level, this.getLevelTitle() + "<br/>(Click to continue!)");
307
game.refreshLevel(".level");
308
this.init(1);
309
}
310
};
311
312
/* UI functions */
313
this.drawHearts = function (count) {
314
var html = "";
315
for (var i = 0; i < count; i++) {
316
html += " <img src='img/heart.png'>";
317
}
318
$(".lives").html("Lives: " + html);
319
320
};
321
322
this.showContent = function (id) {
323
$('.content').hide();
324
$('#' + id).show();
325
};
326
327
this.getLevelTitle = function () {
328
switch (this.level) {
329
case 2:
330
return '"The chase begins"';
331
// activate chase / scatter switching
332
case 3:
333
return '"Inky\s awakening"';
334
// Inky starts leaving the ghost house
335
case 4:
336
return '"Clyde\s awakening"';
337
// Clyde starts leaving the ghost house
338
case 5:
339
return '"need for speed"';
340
// All the ghosts get faster from now on
341
case 6:
342
return '"hunting season 1"';
343
// TODO: No scatter mood this time
344
case 7:
345
return '"the big calm"';
346
// TODO: Only scatter mood this time
347
case 8:
348
return '"hunting season 2"';
349
// TODO: No scatter mood and all ghosts leave instantly
350
case 9:
351
return '"ghosts on speed"';
352
// TODO: Ghosts get even faster for this level
353
case FINAL_LEVEL:
354
return '"The final chase"';
355
// TODO: Ghosts get even faster for this level
356
default:
357
return '"nothing new"';
358
}
359
}
360
361
this.showMessage = function (title, text) {
362
$('#canvas-overlay-container').fadeIn(200);
363
if ($('.controls').css('display') != "none") $('.controls').slideToggle(200);
364
$('#canvas-overlay-content #title').text(title);
365
$('#canvas-overlay-content #text').html(text);
366
}
367
368
this.pauseAndShowMessage = function (title, text) {
369
this.timer.stop();
370
this.pause = true;
371
this.showMessage(title, text);
372
};
373
374
this.closeMessage = function () {
375
$('#canvas-overlay-container').fadeOut(200);
376
$('.controls').slideToggle(200);
377
};
378
379
this.validateScoreWithLevel = function () {
380
const maxLevelPointsPills = 104 * PILL_POINTS;
381
const maxLevelPointsPowerpills = 4 * POWERPILL_POINTS;
382
const maxLevelPointsGhosts = 4 * 4 * GHOST_POINTS;
383
const maxLevelPoints = maxLevelPointsPills + maxLevelPointsPowerpills + maxLevelPointsGhosts;
384
385
const scoreIsValid = this.score.score / this.level <= maxLevelPoints;
386
console.log('validate score. score: ' + this.score.score + ', level: ' + this.level, scoreIsValid);
387
return scoreIsValid;
388
389
}
390
391
this.showHighscoreForm = function () {
392
var scoreIsValid = this.validateScoreWithLevel();
393
394
var inputHTML = scoreIsValid ? `<div id='highscore-form'>
395
<span id='form-validator'></span>
396
<input type='text' id='playerName'/>
397
<span class='button' id='score-submit'>save</span>
398
</div>` : `<div id='invalid-score'>Your score looks fake, the highscore list is only for honest players ;)</div>`;
399
this.pauseAndShowMessage("Game over", "Total Score: " + this.score.score + (HIGHSCORE_ENABLED ? inputHTML : ''));
400
$('#playerName').focus();
401
}
402
403
/* game controls */
404
405
this.forceStartAnimationLoop = function () {
406
// start timer
407
this.timer.start();
408
409
this.pause = false;
410
this.started = true;
411
this.closeMessage();
412
animationLoop();
413
}
414
415
this.forcePause = function () {
416
this.timer.stop();
417
this.pauseAndShowMessage("Pause", "Click to Resume");
418
}
419
420
this.forceResume = function () {
421
this.closeMessage();
422
this.pause = false;
423
this.timer.start();
424
}
425
426
this.pauseResume = function () {
427
if (this.gameOver) {
428
console.log('Cannot pause / resume. GameOver set to true.');
429
return;
430
}
431
if (!this.started) {
432
this.forceStartAnimationLoop();
433
} else if (this.pause) {
434
this.forceResume();
435
} else {
436
this.pauseAndShowMessage("Pause", "Click to Resume");
437
}
438
};
439
440
this.loadMapConfig = async () => {
441
console.log('load map config');
442
return new Promise((resolve, reject) => {
443
$.ajax({
444
url: mapConfig,
445
beforeSend: function (xhr) {
446
if (xhr.overrideMimeType) xhr.overrideMimeType("application/json");
447
},
448
dataType: "json",
449
success: (data) => {
450
console.log('map config loaded');
451
game.map = data;
452
resolve(data);
453
},
454
error: (response) => {
455
console.error('error fetching map config');
456
reject(response);
457
}
458
});
459
})
460
};
461
462
this.getPillCount = () => {
463
var temp = 0;
464
$.each(this.map.posY, function (i, item) {
465
$.each(this.posX, function () {
466
if (this.type == "pill") {
467
temp++;
468
//console.log("Pill Count++. temp="+temp+". PillCount="+this.pillCount+".");
469
}
470
});
471
});
472
return temp;
473
}
474
475
this.init = async (state) => {
476
477
console.log("init game " + state);
478
479
// get Level Map
480
this.map = await this.loadMapConfig();
481
482
this.pillCount = this.getPillCount();
483
484
// TODO: why are there 2 state checks?
485
if (state === 0) {
486
this.timer.reset();
487
game.reset();
488
}
489
pacman.reset();
490
491
game.drawHearts(pacman.lives);
492
493
this.ghostFrightened = false;
494
this.ghostFrightenedTimer = 240;
495
this.ghostMode = 0; // 0 = Scatter, 1 = Chase
496
this.ghostModeTimer = 200; // decrements each animationLoop execution
497
498
// initalize Ghosts, avoid memory flooding
499
if (pinky === null || pinky === undefined) {
500
pinky = new Ghost(GHOSTS.PINKY, 7, 5, 'img/pinky.svg', 2, 2);
501
inky = new Ghost(GHOSTS.INKY, 8, 5, 'img/inky.svg', 13, 11);
502
blinky = new Ghost(GHOSTS.BLINKY, 9, 5, 'img/blinky.svg', 13, 0);
503
clyde = new Ghost(GHOSTS.CLYDE, 10, 5, 'img/clyde.svg', 2, 11);
504
} else {
505
pinky.reset();
506
inky.reset();
507
blinky.reset();
508
clyde.reset();
509
}
510
blinky.start(); // blinky is the first to leave ghostHouse
511
inky.start();
512
pinky.start();
513
clyde.start();
514
};
515
516
this.checkForLevelUp = function () {
517
if ((this.pillCount === 0) && game.started) {
518
this.nextLevel();
519
}
520
};
521
522
this.endGame = function (allLevelsCompleted = false) {
523
console.log('Game Over by ' + (allLevelsCompleted ? 'WIN' : 'LOSS'));
524
this.pause = true;
525
this.gameOver = true;
526
}
527
528
this.toPixelPos = function (gridPos) {
529
return gridPos * 30;
530
};
531
532
this.toGridPos = function (pixelPos) {
533
return ((pixelPos % 30) / 30);
534
};
535
536
/* ------------ Start Pre-Build Walls ------------ */
537
this.buildWalls = function () {
538
if (this.ghostMode === 0) game.wallColor = "Blue";
539
else game.wallColor = "Red";
540
canvas_walls = document.createElement('canvas');
541
canvas_walls.width = game.canvas.width;
542
canvas_walls.height = game.canvas.height;
543
context_walls = canvas_walls.getContext("2d");
544
545
context_walls.fillStyle = game.wallColor;
546
context_walls.strokeStyle = game.wallColor;
547
548
//horizontal outer
549
buildWall(context_walls, 0, 0, 18, 1);
550
buildWall(context_walls, 0, 12, 18, 1);
551
552
// vertical outer
553
buildWall(context_walls, 0, 0, 1, 6);
554
buildWall(context_walls, 0, 7, 1, 6);
555
buildWall(context_walls, 17, 0, 1, 6);
556
buildWall(context_walls, 17, 7, 1, 6);
557
558
// ghost base
559
buildWall(context_walls, 7, 4, 1, 1);
560
buildWall(context_walls, 6, 5, 1, 2);
561
buildWall(context_walls, 10, 4, 1, 1);
562
buildWall(context_walls, 11, 5, 1, 2);
563
buildWall(context_walls, 6, 6, 6, 1);
564
565
// ghost base door
566
context_walls.fillRect(8 * 2 * pacman.radius, pacman.radius / 2 + 4 * 2 * pacman.radius + 5, 4 * pacman.radius, 1);
567
568
// single blocks
569
buildWall(context_walls, 4, 0, 1, 2);
570
buildWall(context_walls, 13, 0, 1, 2);
571
572
buildWall(context_walls, 2, 2, 1, 2);
573
buildWall(context_walls, 6, 2, 2, 1);
574
buildWall(context_walls, 15, 2, 1, 2);
575
buildWall(context_walls, 10, 2, 2, 1);
576
577
buildWall(context_walls, 2, 3, 2, 1);
578
buildWall(context_walls, 14, 3, 2, 1);
579
buildWall(context_walls, 5, 3, 1, 1);
580
buildWall(context_walls, 12, 3, 1, 1);
581
buildWall(context_walls, 3, 3, 1, 3);
582
buildWall(context_walls, 14, 3, 1, 3);
583
584
buildWall(context_walls, 3, 4, 1, 1);
585
buildWall(context_walls, 14, 4, 1, 1);
586
587
buildWall(context_walls, 0, 5, 2, 1);
588
buildWall(context_walls, 3, 5, 2, 1);
589
buildWall(context_walls, 16, 5, 2, 1);
590
buildWall(context_walls, 13, 5, 2, 1);
591
592
buildWall(context_walls, 0, 7, 2, 2);
593
buildWall(context_walls, 16, 7, 2, 2);
594
buildWall(context_walls, 3, 7, 2, 2);
595
buildWall(context_walls, 13, 7, 2, 2);
596
597
buildWall(context_walls, 4, 8, 2, 2);
598
buildWall(context_walls, 12, 8, 2, 2);
599
buildWall(context_walls, 5, 8, 3, 1);
600
buildWall(context_walls, 10, 8, 3, 1);
601
602
buildWall(context_walls, 2, 10, 1, 1);
603
buildWall(context_walls, 15, 10, 1, 1);
604
buildWall(context_walls, 7, 10, 4, 1);
605
buildWall(context_walls, 4, 11, 2, 2);
606
buildWall(context_walls, 12, 11, 2, 2);
607
/* ------------ End Pre-Build Walls ------------ */
608
};
609
610
}
611
612
game = new Game();
613
614
615
616
function Score() {
617
this.score = 0;
618
this.set = function (i) {
619
this.score = i;
620
};
621
this.add = function (i) {
622
this.score += i;
623
};
624
this.refresh = function (h) {
625
$(h).html("Score: " + this.score);
626
};
627
628
}
629
630
631
632
// used to play sounds during the game
633
var Sound = {};
634
Sound.play = function (sound) {
635
if (game.soundfx == 1) {
636
var audio = document.getElementById(sound);
637
(audio !== null) ? audio.play(): console.log(sound + " not found");
638
}
639
};
640
641
642
// Direction object in Constructor notation
643
function Direction(name, angle1, angle2, dirX, dirY) {
644
this.name = name;
645
this.angle1 = angle1;
646
this.angle2 = angle2;
647
this.dirX = dirX;
648
this.dirY = dirY;
649
this.equals = function (dir) {
650
return JSON.stringify(this) == JSON.stringify(dir);
651
};
652
}
653
654
// Direction Objects
655
var up = new Direction("up", 1.75, 1.25, 0, -1); // UP
656
var left = new Direction("left", 1.25, 0.75, -1, 0); // LEFT
657
var down = new Direction("down", 0.75, 0.25, 0, 1); // DOWN
658
var right = new Direction("right", 0.25, 1.75, 1, 0); // RIGHT
659
/*var directions = [{},{},{},{}];
660
directions[0] = up;
661
directions[1] = down;
662
directions[2] = right;
663
directions[3] = left;*/
664
665
666
// DirectionWatcher
667
function directionWatcher() {
668
this.dir = null;
669
this.set = function (dir) {
670
this.dir = dir;
671
};
672
this.get = function () {
673
return this.dir;
674
};
675
}
676
677
//var directionWatcher = new directionWatcher();
678
679
// Ghost object in Constructor notation
680
function Ghost(name, gridPosX, gridPosY, image, gridBaseX, gridBaseY) {
681
this.name = name;
682
this.posX = gridPosX * 30;
683
this.posY = gridPosY * 30;
684
this.startPosX = gridPosX * 30;
685
this.startPosY = gridPosY * 30;
686
this.gridBaseX = gridBaseX;
687
this.gridBaseY = gridBaseY;
688
this.speed = game.ghostSpeedNormal;
689
this.images = JSON.parse(
690
'{"normal" : {' +
691
`"${GHOSTS.INKY}" : "0",` +
692
`"${GHOSTS.PINKY}" : "1",` +
693
`"${GHOSTS.BLINKY}" : "2",` +
694
`"${GHOSTS.CLYDE}" : "3"` +
695
'},' +
696
'"frightened1" : {' +
697
'"left" : "", "up": "", "right" : "", "down": ""},' +
698
'"frightened2" : {' +
699
'"left" : "", "up": "", "right" : "", "down": ""},' +
700
'"dead" : {' +
701
'"left" : "", "up": "", "right" : "", "down": ""}}'
702
);
703
this.image = new Image();
704
this.image.src = image;
705
this.ghostHouse = true;
706
this.dazzled = false;
707
this.dead = false;
708
this.dazzle = function () {
709
this.changeSpeed(game.ghostSpeedDazzled);
710
// ensure ghost doesnt leave grid
711
if (this.posX > 0) this.posX = this.posX - this.posX % this.speed;
712
if (this.posY > 0) this.posY = this.posY - this.posY % this.speed;
713
this.dazzled = true;
714
}
715
this.undazzle = function () {
716
// only change speed if ghost is not "dead"
717
if (!this.dead) this.changeSpeed(game.ghostSpeedNormal);
718
// ensure ghost doesnt leave grid
719
if (this.posX > 0) this.posX = this.posX - this.posX % this.speed;
720
if (this.posY > 0) this.posY = this.posY - this.posY % this.speed;
721
this.dazzled = false;
722
}
723
this.dazzleImg = new Image();
724
this.dazzleImg.src = 'img/dazzled.svg';
725
this.dazzleImg2 = new Image();
726
this.dazzleImg2.src = 'img/dazzled2.svg';
727
this.deadImg = new Image();
728
this.deadImg.src = 'img/dead.svg';
729
this.direction = right;
730
this.radius = pacman.radius;
731
this.draw = function (context) {
732
if (this.dead) {
733
context.drawImage(this.deadImg, this.posX, this.posY, 2 * this.radius, 2 * this.radius);
734
} else if (this.dazzled) {
735
if (pacman.beastModeTimer < 50 && pacman.beastModeTimer % 8 > 1) {
736
context.drawImage(this.dazzleImg2, this.posX, this.posY, 2 * this.radius, 2 * this.radius);
737
} else {
738
context.drawImage(this.dazzleImg, this.posX, this.posY, 2 * this.radius, 2 * this.radius);
739
}
740
} else context.drawImage(this.image, this.posX, this.posY, 2 * this.radius, 2 * this.radius);
741
}
742
this.getCenterX = function () {
743
return this.posX + this.radius;
744
}
745
this.getCenterY = function () {
746
return this.posY + this.radius;
747
}
748
749
this.reset = function () {
750
this.dead = false;
751
this.posX = this.startPosX;
752
this.posY = this.startPosY;
753
this.ghostHouse = true;
754
this.undazzle();
755
}
756
757
this.die = function () {
758
if (!this.dead) {
759
game.score.add(GHOST_POINTS);
760
//this.reset();
761
this.dead = true;
762
this.changeSpeed(game.ghostSpeedNormal);
763
}
764
}
765
this.changeSpeed = function (s) {
766
// adjust gridPosition to new speed
767
this.posX = Math.round(this.posX / s) * s;
768
this.posY = Math.round(this.posY / s) * s;
769
this.speed = s;
770
}
771
772
this.move = function () {
773
774
this.checkDirectionChange();
775
this.checkCollision();
776
777
// leave Ghost House
778
if (this.ghostHouse == true) {
779
780
// Clyde does not start chasing before 2/3 of all pills are eaten and if level is < 4
781
if (this.name == GHOSTS.CLYDE) {
782
if ((game.level < 4) || ((game.pillCount > 104 / 3))) this.stop = true;
783
else this.stop = false;
784
}
785
// Inky starts after 30 pills and only from the third level on
786
if (this.name == GHOSTS.INKY) {
787
if ((game.level < 3) || ((game.pillCount > 104 - 30))) this.stop = true;
788
else this.stop = false;
789
}
790
791
if ((this.getGridPosY() == 5) && this.inGrid()) {
792
if ((this.getGridPosX() == 7)) this.setDirection(right);
793
if ((this.getGridPosX() == 8) || this.getGridPosX() == 9) this.setDirection(up);
794
if ((this.getGridPosX() == 10)) this.setDirection(left);
795
}
796
if ((this.getGridPosY() == 4) && ((this.getGridPosX() == 8) || (this.getGridPosX() == 9)) && this.inGrid()) {
797
console.log("ghosthouse -> false");
798
this.ghostHouse = false;
799
}
800
}
801
802
if (!this.stop) {
803
// Move
804
this.posX += this.speed * this.dirX;
805
this.posY += this.speed * this.dirY;
806
807
// Check if out of canvas
808
if (this.posX >= game.width - this.radius) this.posX = this.speed - this.radius;
809
if (this.posX <= 0 - this.radius) this.posX = game.width - this.speed - this.radius;
810
if (this.posY >= game.height - this.radius) this.posY = this.speed - this.radius;
811
if (this.posY <= 0 - this.radius) this.posY = game.height - this.speed - this.radius;
812
}
813
}
814
815
this.checkCollision = function () {
816
817
/* Check Back to Home */
818
if (this.dead && (this.getGridPosX() == this.startPosX / 30) && (this.getGridPosY() == this.startPosY / 30)) this.reset();
819
else {
820
821
/* Check Ghost / Pacman Collision */
822
if ((between(pacman.getCenterX(), this.getCenterX() - 10, this.getCenterX() + 10)) &&
823
(between(pacman.getCenterY(), this.getCenterY() - 10, this.getCenterY() + 10))) {
824
if ((!this.dazzled) && (!this.dead)) {
825
pacman.die();
826
} else {
827
this.die();
828
}
829
}
830
}
831
}
832
833
/* Pathfinding */
834
this.getNextDirection = function () {
835
// get next field
836
var pX = this.getGridPosX();
837
var pY = this.getGridPosY();
838
game.getMapContent(pX, pY);
839
var u, d, r, l; // option up, down, right, left
840
841
// get target
842
if (this.dead) { // go Home
843
var tX = this.startPosX / 30;
844
var tY = this.startPosY / 30;
845
} else if (game.ghostMode == 0) { // Scatter Mode
846
var tX = this.gridBaseX;
847
var tY = this.gridBaseY;
848
} else if (game.ghostMode == 1) { // Chase Mode
849
850
switch (this.name) {
851
852
// target: 4 ahead and 4 left of pacman
853
case GHOSTS.PINKY:
854
var pdir = pacman.direction;
855
var pdirX = pdir.dirX == 0 ? -pdir.dirY : pdir.dirX;
856
var pdirY = pdir.dirY == 0 ? -pdir.dirX : pdir.dirY;
857
858
var tX = (pacman.getGridPosX() + pdirX * 4) % (game.width / pacman.radius + 1);
859
var tY = (pacman.getGridPosY() + pdirY * 4) % (game.height / pacman.radius + 1);
860
break;
861
862
// target: pacman
863
case GHOSTS.BLINKY:
864
var tX = pacman.getGridPosX();
865
var tY = pacman.getGridPosY();
866
break;
867
868
// target:
869
case GHOSTS.INKY:
870
var tX = pacman.getGridPosX() + 2 * pacman.direction.dirX;
871
var tY = pacman.getGridPosY() + 2 * pacman.direction.dirY;
872
var vX = tX - blinky.getGridPosX();
873
var vY = tY - blinky.getGridPosY();
874
tX = Math.abs(blinky.getGridPosX() + vX * 2);
875
tY = Math.abs(blinky.getGridPosY() + vY * 2);
876
break;
877
878
// target: pacman, until pacman is closer than 5 grid fields, then back to scatter
879
case GHOSTS.CLYDE:
880
var tX = pacman.getGridPosX();
881
var tY = pacman.getGridPosY();
882
var dist = Math.sqrt(Math.pow((pX - tX), 2) + Math.pow((pY - tY), 2));
883
884
if (dist < 5) {
885
tX = this.gridBaseX;
886
tY = this.gridBaseY;
887
}
888
break;
889
890
}
891
}
892
var oppDir = this.getOppositeDirection(); // ghosts are not allowed to change direction 180�
893
894
var dirs = [{}, {}, {}, {}];
895
dirs[0].field = game.getMapContent(pX, pY - 1);
896
dirs[0].dir = up;
897
dirs[0].distance = Math.sqrt(Math.pow((pX - tX), 2) + Math.pow((pY - 1 - tY), 2));
898
899
dirs[1].field = game.getMapContent(pX, pY + 1);
900
dirs[1].dir = down;
901
dirs[1].distance = Math.sqrt(Math.pow((pX - tX), 2) + Math.pow((pY + 1 - tY), 2));
902
903
dirs[2].field = game.getMapContent(pX + 1, pY);
904
dirs[2].dir = right;
905
dirs[2].distance = Math.sqrt(Math.pow((pX + 1 - tX), 2) + Math.pow((pY - tY), 2));
906
907
dirs[3].field = game.getMapContent(pX - 1, pY);
908
dirs[3].dir = left;
909
dirs[3].distance = Math.sqrt(Math.pow((pX - 1 - tX), 2) + Math.pow((pY - tY), 2));
910
911
// Sort possible directions by distance
912
function compare(a, b) {
913
if (a.distance < b.distance)
914
return -1;
915
if (a.distance > b.distance)
916
return 1;
917
return 0;
918
}
919
var dirs2 = dirs.sort(compare);
920
921
var r = this.dir;
922
var j;
923
924
if (this.dead) {
925
for (var i = dirs2.length - 1; i >= 0; i--) {
926
if ((dirs2[i].field != "wall") && !(dirs2[i].dir.equals(this.getOppositeDirection()))) {
927
r = dirs2[i].dir;
928
}
929
}
930
} else {
931
for (var i = dirs2.length - 1; i >= 0; i--) {
932
if ((dirs2[i].field != "wall") && (dirs2[i].field != "door") && !(dirs2[i].dir.equals(this.getOppositeDirection()))) {
933
r = dirs2[i].dir;
934
}
935
}
936
}
937
this.directionWatcher.set(r);
938
return r;
939
}
940
this.setRandomDirection = function () {
941
var dir = Math.floor((Math.random() * 10) + 1) % 5;
942
943
switch (dir) {
944
case 1:
945
if (this.getOppositeDirection().equals(up)) this.setDirection(down);
946
else this.setDirection(up);
947
break;
948
case 2:
949
if (this.getOppositeDirection().equals(down)) this.setDirection(up);
950
else this.setDirection(down);
951
break;
952
case 3:
953
if (this.getOppositeDirection().equals(right)) this.setDirection(left);
954
else this.setDirection(right);
955
break;
956
case 4:
957
if (this.getOppositeDirection().equals(left)) this.setDirection(right);
958
else this.setDirection(left);
959
break;
960
}
961
}
962
this.reverseDirection = function () {
963
console.log("reverseDirection: " + this.direction.name + " to " + this.getOppositeDirection().name);
964
this.directionWatcher.set(this.getOppositeDirection());
965
}
966
967
}
968
969
Ghost.prototype = new Figure();
970
971
// Super Class for Pacman & Ghosts
972
function Figure() {
973
this.posX;
974
this.posY;
975
this.speed;
976
this.dirX = right.dirX;
977
this.dirY = right.dirY;
978
this.direction;
979
this.stop = true;
980
this.directionWatcher = new directionWatcher();
981
this.getNextDirection = function () {
982
console.log("Figure getNextDirection");
983
};
984
this.checkDirectionChange = function () {
985
if (this.inGrid() && (this.directionWatcher.get() == null)) this.getNextDirection();
986
if ((this.directionWatcher.get() != null) && this.inGrid()) {
987
//console.log("changeDirection to "+this.directionWatcher.get().name);
988
this.setDirection(this.directionWatcher.get());
989
this.directionWatcher.set(null);
990
}
991
992
}
993
994
995
this.inGrid = function () {
996
if ((this.posX % (2 * this.radius) === 0) && (this.posY % (2 * this.radius) === 0)) return true;
997
return false;
998
}
999
this.getOppositeDirection = function () {
1000
if (this.direction.equals(up)) return down;
1001
else if (this.direction.equals(down)) return up;
1002
else if (this.direction.equals(right)) return left;
1003
else if (this.direction.equals(left)) return right;
1004
}
1005
this.move = function () {
1006
1007
if (!this.stop) {
1008
this.posX += this.speed * this.dirX;
1009
this.posY += this.speed * this.dirY;
1010
1011
// Check if out of canvas
1012
if (this.posX >= game.width - this.radius) this.posX = this.speed - this.radius;
1013
if (this.posX <= 0 - this.radius) this.posX = game.width - this.speed - this.radius;
1014
if (this.posY >= game.height - this.radius) this.posY = this.speed - this.radius;
1015
if (this.posY <= 0 - this.radius) this.posY = game.height - this.speed - this.radius;
1016
}
1017
}
1018
this.stop = function () {
1019
this.stop = true;
1020
}
1021
this.start = function () {
1022
this.stop = false;
1023
}
1024
1025
this.getGridPosX = function () {
1026
return (this.posX - (this.posX % 30)) / 30;
1027
}
1028
this.getGridPosY = function () {
1029
return (this.posY - (this.posY % 30)) / 30;
1030
}
1031
this.setDirection = function (dir) {
1032
this.dirX = dir.dirX;
1033
this.dirY = dir.dirY;
1034
this.angle1 = dir.angle1;
1035
this.angle2 = dir.angle2;
1036
this.direction = dir;
1037
}
1038
this.setPosition = function (x, y) {
1039
this.posX = x;
1040
this.posY = y;
1041
}
1042
}
1043
1044
function pacman() {
1045
this.radius = 15;
1046
this.posX = 0;
1047
this.posY = 6 * 2 * this.radius;
1048
this.speed = 5;
1049
this.angle1 = 0.25;
1050
this.angle2 = 1.75;
1051
this.mouth = 1; /* Switches between 1 and -1, depending on mouth closing / opening */
1052
this.dirX = right.dirX;
1053
this.dirY = right.dirY;
1054
this.lives = 3;
1055
this.stuckX = 0;
1056
this.stuckY = 0;
1057
this.frozen = false; // used to play die Animation
1058
this.freeze = function () {
1059
this.frozen = true;
1060
}
1061
this.unfreeze = function () {
1062
this.frozen = false;
1063
}
1064
this.getCenterX = function () {
1065
return this.posX + this.radius;
1066
}
1067
this.getCenterY = function () {
1068
return this.posY + this.radius;
1069
}
1070
this.directionWatcher = new directionWatcher();
1071
1072
this.direction = right;
1073
1074
this.beastMode = false;
1075
this.beastModeTimer = 0;
1076
1077
this.checkCollisions = function () {
1078
1079
if ((this.stuckX == 0) && (this.stuckY == 0) && this.frozen == false) {
1080
1081
// Get the Grid Position of Pac
1082
var gridX = this.getGridPosX();
1083
var gridY = this.getGridPosY();
1084
var gridAheadX = gridX;
1085
var gridAheadY = gridY;
1086
1087
var field = game.getMapContent(gridX, gridY);
1088
1089
// get the field 1 ahead to check wall collisions
1090
if ((this.dirX == 1) && (gridAheadX < 17)) gridAheadX += 1;
1091
if ((this.dirY == 1) && (gridAheadY < 12)) gridAheadY += 1;
1092
var fieldAhead = game.getMapContent(gridAheadX, gridAheadY);
1093
1094
1095
/* Check Pill Collision */
1096
if ((field === "pill") || (field === "powerpill")) {
1097
//console.log("Pill found at ("+gridX+"/"+gridY+"). Pacman at ("+this.posX+"/"+this.posY+")");
1098
if (
1099
((this.dirX == 1) && (between(this.posX, game.toPixelPos(gridX) + this.radius - 5, game.toPixelPos(gridX + 1)))) ||
1100
((this.dirX == -1) && (between(this.posX, game.toPixelPos(gridX), game.toPixelPos(gridX) + 5))) ||
1101
((this.dirY == 1) && (between(this.posY, game.toPixelPos(gridY) + this.radius - 5, game.toPixelPos(gridY + 1)))) ||
1102
((this.dirY == -1) && (between(this.posY, game.toPixelPos(gridY), game.toPixelPos(gridY) + 5))) ||
1103
(fieldAhead === "wall")
1104
) {
1105
var s;
1106
if (field === "powerpill") {
1107
Sound.play("powerpill");
1108
s = POWERPILL_POINTS;
1109
this.enableBeastMode();
1110
game.startGhostFrightened();
1111
} else {
1112
Sound.play("waka");
1113
s = PILL_POINTS;
1114
game.pillCount--;
1115
}
1116
game.map.posY[gridY].posX[gridX].type = "null";
1117
game.score.add(s);
1118
}
1119
}
1120
1121
/* Check Wall Collision */
1122
if ((fieldAhead === "wall") || (fieldAhead === "door")) {
1123
this.stuckX = this.dirX;
1124
this.stuckY = this.dirY;
1125
pacman.stop();
1126
// get out of the wall
1127
if ((this.stuckX == 1) && ((this.posX % 2 * this.radius) != 0)) this.posX -= 5;
1128
if ((this.stuckY == 1) && ((this.posY % 2 * this.radius) != 0)) this.posY -= 5;
1129
if (this.stuckX == -1) this.posX += 5;
1130
if (this.stuckY == -1) this.posY += 5;
1131
}
1132
1133
}
1134
}
1135
this.checkDirectionChange = function () {
1136
if (this.directionWatcher.get() != null) {
1137
console.groupCollapsed('checkDirectionChange');
1138
//console.log("next Direction: "+directionWatcher.get().name);
1139
1140
if ((this.stuckX == 1) && this.directionWatcher.get() == right) this.directionWatcher.set(null);
1141
else {
1142
// reset stuck events
1143
this.stuckX = 0;
1144
this.stuckY = 0;
1145
1146
1147
// only allow direction changes inside the grid
1148
if ((this.inGrid())) {
1149
//console.log("changeDirection to "+directionWatcher.get().name);
1150
1151
// check if possible to change direction without getting stuck
1152
console.debug("x: " + this.getGridPosX() + " + " + this.directionWatcher.get().dirX);
1153
console.debug("y: " + this.getGridPosY() + " + " + this.directionWatcher.get().dirY);
1154
var x = this.getGridPosX() + this.directionWatcher.get().dirX;
1155
var y = this.getGridPosY() + this.directionWatcher.get().dirY;
1156
if (x <= -1) x = game.width / (this.radius * 2) - 1;
1157
if (x >= game.width / (this.radius * 2)) x = 0;
1158
if (y <= -1) x = game.height / (this.radius * 2) - 1;
1159
if (y >= game.heigth / (this.radius * 2)) y = 0;
1160
1161
console.debug("x: " + x);
1162
console.debug("y: " + y);
1163
var nextTile = game.map.posY[y].posX[x].type;
1164
console.debug("checkNextTile: " + nextTile);
1165
1166
if (nextTile != "wall") {
1167
this.setDirection(this.directionWatcher.get());
1168
this.directionWatcher.set(null);
1169
}
1170
}
1171
}
1172
console.groupEnd();
1173
}
1174
}
1175
this.setDirection = function (dir) {
1176
if (!this.frozen) {
1177
this.dirX = dir.dirX;
1178
this.dirY = dir.dirY;
1179
this.angle1 = dir.angle1;
1180
this.angle2 = dir.angle2;
1181
this.direction = dir;
1182
}
1183
}
1184
this.enableBeastMode = function () {
1185
this.beastMode = true;
1186
this.beastModeTimer = 240;
1187
console.debug("Beast Mode activated!");
1188
inky.dazzle();
1189
pinky.dazzle();
1190
blinky.dazzle();
1191
clyde.dazzle();
1192
};
1193
this.disableBeastMode = function () {
1194
this.beastMode = false;
1195
console.debug("Beast Mode is over!");
1196
inky.undazzle();
1197
pinky.undazzle();
1198
blinky.undazzle();
1199
clyde.undazzle();
1200
};
1201
this.move = function () {
1202
1203
if (!this.frozen) {
1204
if (this.beastModeTimer > 0) {
1205
this.beastModeTimer--;
1206
//console.log("Beast Mode: "+this.beastModeTimer);
1207
}
1208
if ((this.beastModeTimer == 0) && (this.beastMode == true)) this.disableBeastMode();
1209
1210
this.posX += this.speed * this.dirX;
1211
this.posY += this.speed * this.dirY;
1212
1213
// Check if out of canvas
1214
if (this.posX >= game.width - this.radius) this.posX = 5 - this.radius;
1215
if (this.posX <= 0 - this.radius) this.posX = game.width - 5 - this.radius;
1216
if (this.posY >= game.height - this.radius) this.posY = 5 - this.radius;
1217
if (this.posY <= 0 - this.radius) this.posY = game.height - 5 - this.radius;
1218
} else this.dieAnimation();
1219
}
1220
1221
this.eat = function () {
1222
1223
if (!this.frozen) {
1224
if (this.dirX == this.dirY == 0) {
1225
1226
this.angle1 -= this.mouth * 0.07;
1227
this.angle2 += this.mouth * 0.07;
1228
1229
var limitMax1 = this.direction.angle1;
1230
var limitMax2 = this.direction.angle2;
1231
var limitMin1 = this.direction.angle1 - 0.21;
1232
var limitMin2 = this.direction.angle2 + 0.21;
1233
1234
if (this.angle1 < limitMin1 || this.angle2 > limitMin2) {
1235
this.mouth = -1;
1236
}
1237
if (this.angle1 >= limitMax1 || this.angle2 <= limitMax2) {
1238
this.mouth = 1;
1239
}
1240
}
1241
}
1242
}
1243
this.stop = function () {
1244
this.dirX = 0;
1245
this.dirY = 0;
1246
}
1247
this.reset = function () {
1248
this.unfreeze();
1249
this.posX = 0;
1250
this.posY = 6 * 2 * this.radius;
1251
this.setDirection(right);
1252
this.stop();
1253
this.stuckX = 0;
1254
this.stuckY = 0;
1255
//console.log("reset pacman");
1256
}
1257
this.dieAnimation = function () {
1258
this.angle1 += 0.05;
1259
this.angle2 -= 0.05;
1260
if (this.angle1 >= this.direction.angle1 + 0.7 || this.angle2 <= this.direction.angle2 - 0.7) {
1261
this.dieFinal();
1262
}
1263
}
1264
this.die = function () {
1265
Sound.play("die");
1266
this.freeze();
1267
this.dieAnimation();
1268
}
1269
this.dieFinal = function () {
1270
this.reset();
1271
pinky.reset();
1272
inky.reset();
1273
blinky.reset();
1274
clyde.reset();
1275
this.lives--;
1276
console.log("pacman died, " + this.lives + " lives left");
1277
if (this.lives <= 0) {
1278
game.endGame();
1279
game.showHighscoreForm();
1280
}
1281
game.drawHearts(this.lives);
1282
}
1283
this.getGridPosX = function () {
1284
return (this.posX - (this.posX % 30)) / 30;
1285
}
1286
this.getGridPosY = function () {
1287
return (this.posY - (this.posY % 30)) / 30;
1288
}
1289
}
1290
pacman.prototype = new Figure();
1291
var pacman = new pacman();
1292
game.buildWalls();
1293
1294
1295
// Check if a new cache is available on page load.
1296
function checkAppCache() {
1297
console.log('check AppCache');
1298
window.applicationCache.addEventListener('updateready', function (e) {
1299
console.log("AppCache: updateready");
1300
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
1301
1302
// Browser downloaded a new app cache.
1303
// Swap it in and reload the page to get the new hotness.
1304
window.applicationCache.swapCache();
1305
if (confirm('A new version of this site is available. Load it?')) {
1306
window.location.reload();
1307
}
1308
1309
} else {
1310
// Manifest didn't change. Nothing new to server.
1311
}
1312
}, false);
1313
1314
window.applicationCache.addEventListener('cached', function (e) {
1315
console.log("AppCache: cached");
1316
}, false);
1317
1318
}
1319
1320
1321
// Action starts here:
1322
1323
function hideAdressbar() {
1324
console.log("hide adressbar");
1325
$("html").scrollTop(1);
1326
$("body").scrollTop(1);
1327
}
1328
1329
$(document).ready(function () {
1330
1331
if (!['localhost', '127.0.0.1'].includes(window.location.hostname)) {
1332
logger.disableLogger();
1333
}
1334
1335
$.ajaxSetup({
1336
mimeType: "application/json"
1337
});
1338
1339
$.ajaxSetup({
1340
beforeSend: function (xhr) {
1341
if (xhr.overrideMimeType) {
1342
xhr.overrideMimeType("application/json");
1343
}
1344
}
1345
});
1346
1347
// Hide address bar
1348
hideAdressbar();
1349
1350
if (window.applicationCache != null) checkAppCache();
1351
1352
/* -------------------- EVENT LISTENERS -------------------------- */
1353
1354
// Listen for resize changes
1355
/*window.addEventListener("resize", function() {
1356
// Get screen size (inner/outerWidth, inner/outerHeight)
1357
// deactivated because of problems
1358
if ((window.outerHeight < window.outerWidth) && (window.outerHeight < 720)) {
1359
game.pauseAndShowMessage("Rotate Device","Your screen is too small to play in landscape view.");
1360
console.log("rotate your device to portrait!");
1361
}
1362
}, false);*/
1363
1364
1365
// --------------- Controls
1366
1367
1368
// Keyboard
1369
window.addEventListener('keydown', doKeyDown, true);
1370
1371
// pause / resume game on canvas click
1372
$('#canvas-container').click(function () {
1373
if (!(game.gameOver === true)) game.pauseResume();
1374
});
1375
1376
// highscore form submit event listener
1377
$('body').on('click', '#score-submit', function () {
1378
console.log("submit highscore pressed");
1379
if ($('#playerName').val() === "" || $('#playerName').val() === undefined) {
1380
$('#form-validator').html("Please enter a name<br/>");
1381
} else {
1382
$('#form-validator').html("");
1383
addHighscore();
1384
}
1385
});
1386
1387
$('body').on('click', '#show-highscore', function () {
1388
game.showContent('highscore-content');
1389
getHighscore();
1390
});
1391
1392
// Hammerjs Touch Events
1393
Hammer('.container').on("swiperight", function (event) {
1394
if ($('#game-content').is(":visible")) {
1395
event.gesture.preventDefault();
1396
pacman.directionWatcher.set(right);
1397
}
1398
});
1399
Hammer('.container').on("swipeleft", function (event) {
1400
if ($('#game-content').is(":visible")) {
1401
event.gesture.preventDefault();
1402
pacman.directionWatcher.set(left);
1403
}
1404
});
1405
Hammer('.container').on("swipeup", function (event) {
1406
if ($('#game-content').is(":visible")) {
1407
event.gesture.preventDefault();
1408
pacman.directionWatcher.set(up);
1409
}
1410
});
1411
Hammer('.container').on("swipedown", function (event) {
1412
if ($('#game-content').is(":visible")) {
1413
event.gesture.preventDefault();
1414
pacman.directionWatcher.set(down);
1415
}
1416
});
1417
1418
// Mobile Control Buttons
1419
$(document).on('touchend mousedown', '#up', function (event) {
1420
event.preventDefault();
1421
pacman.directionWatcher.set(up);
1422
});
1423
$(document).on('touchend mousedown', '#down', function (event) {
1424
event.preventDefault();
1425
pacman.directionWatcher.set(down);
1426
});
1427
$(document).on('touchend mousedown', '#left', function (event) {
1428
event.preventDefault();
1429
pacman.directionWatcher.set(left);
1430
});
1431
$(document).on('touchend mousedown', '#right', function (event) {
1432
event.preventDefault();
1433
pacman.directionWatcher.set(right);
1434
});
1435
1436
// Menu
1437
$(document).on('click', '.button#newGame', function (event) {
1438
game.newGame();
1439
});
1440
$(document).on('click', '.button#highscore', function (event) {
1441
game.showContent('highscore-content');
1442
getHighscore();
1443
});
1444
$(document).on('click', '.button#instructions', function (event) {
1445
game.showContent('instructions-content');
1446
});
1447
$(document).on('click', '.button#info', function (event) {
1448
game.showContent('info-content');
1449
});
1450
// back button
1451
$(document).on('click', '.button#back', function (event) {
1452
game.showContent('game-content');
1453
});
1454
// toggleSound
1455
$(document).on('click', '.controlSound', function (event) {
1456
game.toggleSound();
1457
});
1458
// get latest
1459
$(document).on('click', '#updateCode', function (event) {
1460
console.log('check for new version');
1461
event.preventDefault();
1462
window.applicationCache.update();
1463
});
1464
1465
// checkAppCache();
1466
1467
canvas = $("#myCanvas").get(0);
1468
context = canvas.getContext("2d");
1469
1470
/* --------------- GAME INITIALISATION ------------------------------------
1471
1472
TODO: put this into Game object and change code to accept different setups / levels
1473
1474
-------------------------------------------------------------------------- */
1475
1476
game.init(0);
1477
1478
renderContent();
1479
});
1480
1481
function renderContent() {
1482
// Refresh Score
1483
game.score.refresh(".score");
1484
1485
// Pills
1486
context.beginPath();
1487
context.fillStyle = "White";
1488
context.strokeStyle = "White";
1489
1490
var dotPosY;
1491
if (game.map && game.map.posY && game.map.posY.length > 0) {
1492
$.each(game.map.posY, (i, row) => {
1493
dotPosY = row.row;
1494
$.each(row.posX, (j, column) => {
1495
if (column.type == "pill") {
1496
context.arc(game.toPixelPos(column.col - 1) + pacman.radius, game.toPixelPos(dotPosY - 1) + pacman.radius, game.pillSize, 0 * Math.PI, 2 * Math.PI);
1497
context.moveTo(game.toPixelPos(column.col - 1), game.toPixelPos(dotPosY - 1));
1498
} else if (column.type == "powerpill") {
1499
context.arc(game.toPixelPos(column.col - 1) + pacman.radius, game.toPixelPos(dotPosY - 1) + pacman.radius, game.powerpillSizeCurrent, 0 * Math.PI, 2 * Math.PI);
1500
context.moveTo(game.toPixelPos(column.col - 1), game.toPixelPos(dotPosY - 1));
1501
}
1502
});
1503
});
1504
} else {
1505
console.warn('Map not loaded (yet).')
1506
}
1507
1508
context.fill();
1509
1510
// Walls
1511
context.drawImage(canvas_walls, 0, 0);
1512
1513
1514
if (game.started) {
1515
// Ghosts
1516
pinky.draw(context);
1517
blinky.draw(context);
1518
inky.draw(context);
1519
clyde.draw(context);
1520
1521
1522
// Pac Man
1523
context.beginPath();
1524
context.fillStyle = "Yellow";
1525
context.strokeStyle = "Yellow";
1526
context.arc(pacman.posX + pacman.radius, pacman.posY + pacman.radius, pacman.radius, pacman.angle1 * Math.PI, pacman.angle2 * Math.PI);
1527
context.lineTo(pacman.posX + pacman.radius, pacman.posY + pacman.radius);
1528
context.stroke();
1529
context.fill();
1530
}
1531
1532
}
1533
1534
// TODO: only for debugging
1535
function renderGrid(gridPixelSize, color) {
1536
context.save();
1537
context.lineWidth = 0.5;
1538
context.strokeStyle = color;
1539
1540
// horizontal grid lines
1541
for (var i = 0; i <= canvas.height; i = i + gridPixelSize) {
1542
context.beginPath();
1543
context.moveTo(0, i);
1544
context.lineTo(canvas.width, i);
1545
context.closePath();
1546
context.stroke();
1547
}
1548
1549
// vertical grid lines
1550
for (var i = 0; i <= canvas.width; i = i + gridPixelSize) {
1551
context.beginPath();
1552
context.moveTo(i, 0);
1553
context.lineTo(i, canvas.height);
1554
context.closePath();
1555
context.stroke();
1556
}
1557
1558
context.restore();
1559
}
1560
1561
1562
function animationLoop() {
1563
1564
// if (gameOver) return;
1565
1566
canvas.width = canvas.width;
1567
// enable next line to show grid
1568
// renderGrid(pacman.radius, "red");
1569
renderContent();
1570
1571
if (game.dieAnimation == 1) pacman.dieAnimation();
1572
if (game.pause !== true) {
1573
// Make changes before next loop
1574
pacman.move();
1575
pacman.eat();
1576
pacman.checkDirectionChange();
1577
pacman.checkCollisions(); // has to be the LAST method called on pacman
1578
1579
blinky.move();
1580
inky.move();
1581
pinky.move();
1582
clyde.move();
1583
1584
game.checkGhostMode();
1585
1586
1587
// All dots collected?
1588
game.checkForLevelUp();
1589
}
1590
1591
//requestAnimationFrame(animationLoop);
1592
setTimeout(animationLoop, game.refreshRate);
1593
1594
}
1595
1596
1597
1598
function doKeyDown(evt) {
1599
1600
switch (evt.keyCode) {
1601
case 38: // UP Arrow Key pressed
1602
evt.preventDefault();
1603
case 87: // W pressed
1604
pacman.directionWatcher.set(up);
1605
break;
1606
case 40: // DOWN Arrow Key pressed
1607
evt.preventDefault();
1608
case 83: // S pressed
1609
pacman.directionWatcher.set(down);
1610
break;
1611
case 37: // LEFT Arrow Key pressed
1612
evt.preventDefault();
1613
case 65: // A pressed
1614
pacman.directionWatcher.set(left);
1615
break;
1616
case 39: // RIGHT Arrow Key pressed
1617
evt.preventDefault();
1618
case 68: // D pressed
1619
pacman.directionWatcher.set(right);
1620
break;
1621
case 78: // N pressed
1622
if (!$('#playerName').is(':focus')) {
1623
game.pause = 1;
1624
game.newGame();
1625
}
1626
break;
1627
case 77: // M pressed
1628
game.toggleSound();
1629
break;
1630
case 8: // Backspace pressed -> show Game Content
1631
case 27: // ESC pressed -> show Game Content
1632
if (!$('#playerName').is(':focus')) {
1633
evt.preventDefault();
1634
game.showContent('game-content');
1635
}
1636
break;
1637
case 32: // SPACE pressed -> pause Game
1638
evt.preventDefault();
1639
if (!(game.gameOver == true) &&
1640
$('#game-content').is(':visible')
1641
) game.pauseResume();
1642
break;
1643
}
1644
}
1645
}
1646
1647
geronimo();
1648