Path: blob/main/static/src/gs/public/pacman/pacman-canvas.js
1344 views
/*-------------------------------------------------------------------12___________ ____ _____ _____ ____3\____ \__ \ _/ ___\ / \\__ \ / \4| |_> > __ \\ \___| Y Y \/ __ \| | \5| __(____ /\___ >__|_| (____ /___| /6|__| \/ \/ \/ \/ \/ .platzh1rsch.ch78author: platzh1rsch (www.platzh1rsch.ch)910-------------------------------------------------------------------*/1112"use strict";1314function geronimo() {15/* ----- Global Variables ---------------------------------------- */16var canvas;17var joystick;18var context;19var game;20var canvas_walls, context_walls;21var inky, blinky, clyde, pinky;2223var mapConfig = "data/map.json";242526/* AJAX stuff */27function getHighscore() {28setTimeout(ajax_get,30);29}30function ajax_get() {31var date = new Date().getTime();32$.ajax({33datatype: "json",34type: "GET",35url: "data/db-handler.php",36data: {37timestamp: date,38action: "get"39},40success: function(msg){41$("#highscore-list").text("");42for (var i = 0; i < msg.length; i++) {43$("#highscore-list").append("<li>"+msg[i]['name']+"<span id='score'>"+msg[i]['score']+"</span></li>");44}45}46});47}48function ajax_add(n, s, l) {4950$.ajax({51type: 'POST',52url: 'data/db-handler.php',53data: {54action: 'add',55name: n,56score: s,57level: l58},59dataType: 'json',60success: function(data) {61console.log('Highscore added: ' + data);62$('#highscore-form').html('<span class="button" id="show-highscore">View Highscore List</span>');63},64error: function(errorThrown) {65console.log(errorThrown);66}67});68}6970function addHighscore() {71var name = $("input[type=text]").val();72$("#highscore-form").html("Saving highscore...");73ajax_add(name ,game.score.score, game.level);74}7576function buildWall(context,gridX,gridY,width,height) {77console.log("BuildWall");78width = width*2-1;79height = height*2-1;80context.fillRect(pacman.radius/2+gridX*2*pacman.radius,pacman.radius/2+gridY*2*pacman.radius, width*pacman.radius, height*pacman.radius);81}8283function between(x, min, max) {84return x >= min && x <= max;85}868788// Logger89var logger = function() {90var oldConsoleLog = null;91var pub = {};9293pub.enableLogger = function enableLogger()94{95if(oldConsoleLog === null)96return;9798window['console']['log'] = oldConsoleLog;99};100101pub.disableLogger = function disableLogger()102{103oldConsoleLog = console.log;104window['console']['log'] = function() {};105};106107return pub;108}();109110// stop watch to measure the time111function Timer() {112this.time_diff = 0;113this.time_start = 0;114this.time_stop = 0;115this.start = function() {116this.time_start = new Date().getTime();117}118this.stop = function() {119this.time_stop = new Date().getTime();120this.time_diff += this.time_stop - this.time_start;121this.time_stop = 0;122this.time_start = 0;123}124this.reset = function() {125this.time_diff = 0;126this.time_start = 0;127this.time_stop = 0;128}129this.get_time_diff = function() {130return this.time_diff;131}132}133134// Manages the whole game ("God Object")135function Game() {136this.timer = new Timer();137this.refreshRate = 33; // speed of the game, will increase in higher levels138this.running = false;139this.pause = true;140this.score = new Score();141this.soundfx = 0;142this.map;143this.pillCount; // number of pills144this.monsters;145this.level = 1;146this.refreshLevel = function(h) {147$(h).html("Lvl: "+this.level);148};149this.gameOver = false;150this.canvas = $("#myCanvas").get(0);151this.wallColor = "Blue";152this.width = this.canvas.width;153this.height = this.canvas.height;154155this.pillSize = 3;156this.powerpillSizeMin = 2;157this.powerpillSizeMax = 6;158this.powerpillSizeCurrent = this.powerpillSizeMax;159this.powerPillAnimationCounter = 0;160this.nextPowerPillSize = function() {161/*if (this.powerPillAnimationCounter === 3) {162this.powerPillAnimationCounter = 0;163this.powerpillSizeCurrent = this.powerpillSizeMin + this.powerpillSizeCurrent % (this.powerpillSizeMax-this.powerpillSizeMin);164} else {165this.powerPillAnimationCounter++;166}*/167return this.powerpillSizeCurrent;168};169170this.ghostFrightened = false;171this.ghostFrightenedTimer = 240;172this.ghostMode = 0; // 0 = Scatter, 1 = Chase173this.ghostModeTimer = 200; // decrements each animationLoop execution174this.ghostSpeedNormal = (this.level > 4 ? 3 : 2); // global default for ghost speed175this.ghostSpeedDazzled = 2; // global default for ghost speed when dazzled176177/* Game Functions */178this.startGhostFrightened = function() {179console.log("ghost frigthened");180this.ghostFrightened = true;181this.ghostFrightenedTimer = 240;182inky.dazzle();183pinky.dazzle();184blinky.dazzle();185clyde.dazzle();186};187188this.endGhostFrightened = function() {189console.log("ghost frigthened end");190this.ghostFrightened = false;191inky.undazzle();192pinky.undazzle();193blinky.undazzle();194clyde.undazzle();195};196197198this.checkGhostMode = function() {199if (this.ghostFrightened) {200201this.ghostFrightenedTimer--;202if (this.ghostFrightenedTimer === 0) {203this.endGhostFrightened();204this.ghostFrigthenedTimer = 240;205/*inky.reverseDirection();206pinky.reverseDirection();207clyde.reverseDirection();208blinky.reverseDirection();*/209}210}211// always decrement ghostMode timer212this.ghostModeTimer--;213if (this.ghostModeTimer === 0 && game.level > 1) {214this.ghostMode ^= 1;215this.ghostModeTimer = 200 + this.ghostMode * 450;216console.log("ghostMode=" + this.ghostMode);217218game.buildWalls();219220inky.reverseDirection();221pinky.reverseDirection();222clyde.reverseDirection();223blinky.reverseDirection();224}225};226227this.getMapContent = function (x, y) {228var maxX = game.width / 30 -1;229var maxY = game.height / 30 -1;230if (x < 0) x = maxX + x;231if (x > maxX) x = x-maxX;232if (y < 0) y = maxY + y;233if (y > maxY) y = y-maxY;234return this.map.posY[y].posX[x].type;235};236237this.setMapContent = function (x,y,val) {238this.map.posY[y].posX[x].type = val;239};240241this.toggleSound = function() {242this.soundfx === 0 ? this.soundfx = 1 : this.soundfx = 0;243$('#mute').toggle();244};245246this.reset = function() {247};248249this.newGame = function() {250var r = confirm("Are you sure you want to restart?");251if (r) {252console.log("new Game");253this.init(0);254this.pauseResume();255}256};257258this.nextLevel = function() {259this.level++;260console.log("Level "+game.level);261game.showMessage("Level "+game.level, this.getLevelTitle() + "<br/>(Click to continue!)");262game.refreshLevel(".level");263this.init(1);264};265266this.drawHearts = function (count) {267var html = "";268for (var i = 0; i<count; i++) {269html += " <img src='img/heart.png'>";270}271$(".lives").html("Lives: "+html);272273};274275this.showContent = function (id) {276$('.content').hide();277$('#'+id).show();278};279280this.getLevelTitle = function() {281switch(this.level) {282case 2:283return '"The chase begins"';284// activate chase / scatter switching285case 3:286return '"Inky\s awakening"';287// Inky starts leaving the ghost house288case 4:289return '"Clyde\s awakening"';290// Clyde starts leaving the ghost house291case 5:292return '"need for speed"';293// All the ghosts get faster from now on294case 6:295return '"hunting season 1"';296// TODO: No scatter mood this time297case 7:298return '"the big calm"';299// TODO: Only scatter mood this time300case 8:301return '"hunting season 2"';302// TODO: No scatter mood and all ghosts leave instantly303case 9:304return '"ghosts on speed"';305// TODO: Ghosts get even faster for this level306default:307return '"nothing new"';308}309}310311this.showMessage = function(title, text) {312this.timer.stop();313this.pause = true;314$('#canvas-overlay-container').fadeIn(200);315if ($('.controls').css('display') != "none") $('.controls').slideToggle(200);316$('#canvas-overlay-content #title').text(title);317$('#canvas-overlay-content #text').html(text);318};319320this.closeMessage = function() {321$('#canvas-overlay-container').fadeOut(200);322$('.controls').slideToggle(200);323};324325this.pauseResume = function () {326if (!this.running) {327// start timer328this.timer.start();329330this.pause = false;331this.running = true;332this.closeMessage();333animationLoop();334}335else if (this.pause) {336// stop timer337this.timer.stop();338339this.pause = false;340this.closeMessage();341}342else {343this.showMessage("Pause","Click to Resume");344}345};346347this.init = function (state) {348349console.log("init game "+state);350351// reset timer if restart352if( state === 0 ) {353this.timer.reset();354}355356// get Level Map357$.ajax({358url: mapConfig,359async: false,360beforeSend: function(xhr){361if (xhr.overrideMimeType) xhr.overrideMimeType("application/json");362},363dataType: "json",364success: function (data) {365game.map = data;366}367});368369var temp = 0;370$.each(this.map.posY, function(i, item) {371$.each(this.posX, function() {372if (this.type == "pill") {373temp++;374//console.log("Pill Count++. temp="+temp+". PillCount="+this.pillCount+".");375}376});377});378379this.pillCount = temp;380381if (state === 0) {382this.score.set(0);383this.score.refresh(".score");384pacman.lives = 3;385game.level = 1;386this.refreshLevel(".level");387game.gameOver = false;388}389pacman.reset();390391game.drawHearts(pacman.lives);392393this.ghostFrightened = false;394this.ghostFrightenedTimer = 240;395this.ghostMode = 0; // 0 = Scatter, 1 = Chase396this.ghostModeTimer = 200; // decrements each animationLoop execution397398// initalize Ghosts, avoid memory flooding399if (pinky === null || pinky === undefined) {400pinky = new Ghost("pinky",7,5,'img/pinky.svg',2,2);401inky = new Ghost("inky",8,5,'img/inky.svg',13,11);402blinky = new Ghost("blinky",9,5,'img/blinky.svg',13,0);403clyde = new Ghost("clyde",10,5,'img/clyde.svg',2,11);404}405else {406//console.log("ghosts reset");407pinky.reset();408inky.reset();409blinky.reset();410clyde.reset();411}412blinky.start(); // blinky is the first to leave ghostHouse413inky.start();414pinky.start();415clyde.start();416};417418this.check = function() {419if ((this.pillCount === 0) && game.running) {420this.nextLevel();421}422};423424this.win = function () {};425this.gameover = function () {};426427this.toPixelPos = function (gridPos) {428return gridPos*30;429};430431this.toGridPos = function (pixelPos) {432return ((pixelPos % 30)/30);433};434435/* ------------ Start Pre-Build Walls ------------ */436this.buildWalls = function() {437if (this.ghostMode === 0) game.wallColor = "Blue";438else game.wallColor = "Red";439canvas_walls = document.createElement('canvas');440canvas_walls.width = game.canvas.width;441canvas_walls.height = game.canvas.height;442context_walls = canvas_walls.getContext("2d");443444context_walls.fillStyle = game.wallColor;445context_walls.strokeStyle = game.wallColor;446447//horizontal outer448buildWall(context_walls,0,0,18,1);449buildWall(context_walls,0,12,18,1);450451// vertical outer452buildWall(context_walls,0,0,1,6);453buildWall(context_walls,0,7,1,6);454buildWall(context_walls,17,0,1,6);455buildWall(context_walls,17,7,1,6);456457// ghost base458buildWall(context_walls,7,4,1,1);459buildWall(context_walls,6,5,1,2);460buildWall(context_walls,10,4,1,1);461buildWall(context_walls,11,5,1,2);462buildWall(context_walls,6,6,6,1);463464// ghost base door465context_walls.fillRect(8*2*pacman.radius,pacman.radius/2+4*2*pacman.radius+5, 4*pacman.radius, 1);466467// single blocks468buildWall(context_walls,4,0,1,2);469buildWall(context_walls,13,0,1,2);470471buildWall(context_walls,2,2,1,2);472buildWall(context_walls,6,2,2,1);473buildWall(context_walls,15,2,1,2);474buildWall(context_walls,10,2,2,1);475476buildWall(context_walls,2,3,2,1);477buildWall(context_walls,14,3,2,1);478buildWall(context_walls,5,3,1,1);479buildWall(context_walls,12,3,1,1);480buildWall(context_walls,3,3,1,3);481buildWall(context_walls,14,3,1,3);482483buildWall(context_walls,3,4,1,1);484buildWall(context_walls,14,4,1,1);485486buildWall(context_walls,0,5,2,1);487buildWall(context_walls,3,5,2,1);488buildWall(context_walls,16,5,2,1);489buildWall(context_walls,13,5,2,1);490491buildWall(context_walls,0,7,2,2);492buildWall(context_walls,16,7,2,2);493buildWall(context_walls,3,7,2,2);494buildWall(context_walls,13,7,2,2);495496buildWall(context_walls,4,8,2,2);497buildWall(context_walls,12,8,2,2);498buildWall(context_walls,5,8,3,1);499buildWall(context_walls,10,8,3,1);500501buildWall(context_walls,2,10,1,1);502buildWall(context_walls,15,10,1,1);503buildWall(context_walls,7,10,4,1);504buildWall(context_walls,4,11,2,2);505buildWall(context_walls,12,11,2,2);506/* ------------ End Pre-Build Walls ------------ */507};508509}510511game = new Game();512513514515function Score() {516this.score = 0;517this.set = function(i) {518this.score = i;519};520this.add = function(i) {521this.score += i;522};523this.refresh = function(h) {524$(h).html("Score: "+this.score);525};526527}528529530531// used to play sounds during the game532var Sound = {};533Sound.play = function (sound) {534if (game.soundfx == 1) {535var audio = document.getElementById(sound);536(audio !== null) ? audio.play() : console.log(sound+" not found");537}538};539540541// Direction object in Constructor notation542function Direction(name,angle1,angle2,dirX,dirY) {543this.name = name;544this.angle1 = angle1;545this.angle2 = angle2;546this.dirX = dirX;547this.dirY = dirY;548this.equals = function(dir) {549return JSON.stringify(this) == JSON.stringify(dir);550};551}552553// Direction Objects554var up = new Direction("up",1.75,1.25,0,-1); // UP555var left = new Direction("left",1.25,0.75,-1,0); // LEFT556var down = new Direction("down",0.75,0.25,0,1); // DOWN557var right = new Direction("right",0.25,1.75,1,0); //558/*var directions = [{},{},{},{}];559directions[0] = up;560directions[1] = down;561directions[2] = right;562directions[3] = left;*/563564565// DirectionWatcher566function directionWatcher() {567this.dir = null;568this.set = function(dir) {569this.dir = dir;570};571this.get = function() {572return this.dir;573};574}575576//var directionWatcher = new directionWatcher();577578// Ghost object in Constructor notation579function Ghost(name, gridPosX, gridPosY, image, gridBaseX, gridBaseY) {580this.name = name;581this.posX = gridPosX * 30;582this.posY = gridPosY * 30;583this.startPosX = gridPosX * 30;584this.startPosY = gridPosY * 30;585this.gridBaseX = gridBaseX;586this.gridBaseY = gridBaseY;587this.speed = game.ghostSpeedNormal;588this.images = JSON.parse(589'{"normal" : {'590+ '"inky" : "0",'591+ '"pinky" : "1",'592+ '"blinky" : "2",'593+ '"clyde" : "3"'594+ '},'595+596'"frightened1" : {'597+598'"left" : "", "up": "", "right" : "", "down": ""},'599+600'"frightened2" : {'601+602'"left" : "", "up": "", "right" : "", "down": ""},'603+604'"dead" : {'605+606'"left" : "", "up": "", "right" : "", "down": ""}}'607);608this.image = new Image();609this.image.src = image;610this.ghostHouse = true;611this.dazzled = false;612this.dead = false;613this.dazzle = function() {614this.changeSpeed(game.ghostSpeedDazzled);615// ensure ghost doesnt leave grid616if (this.posX > 0) this.posX = this.posX - this.posX % this.speed;617if (this.posY > 0) this.posY = this.posY - this.posY % this.speed;618this.dazzled = true;619}620this.undazzle = function() {621// only change speed if ghost is not "dead"622if (!this.dead) this.changeSpeed(game.ghostSpeedNormal);623// ensure ghost doesnt leave grid624if (this.posX > 0) this.posX = this.posX - this.posX % this.speed;625if (this.posY > 0) this.posY = this.posY - this.posY % this.speed;626this.dazzled = false;627}628this.dazzleImg = new Image();629this.dazzleImg.src = 'img/dazzled.svg';630this.dazzleImg2 = new Image();631this.dazzleImg2.src = 'img/dazzled2.svg';632this.deadImg = new Image();633this.deadImg.src = 'img/dead.svg';634this.direction = right;635this.radius = pacman.radius;636this.draw = function (context) {637if (this.dead) {638context.drawImage(this.deadImg, this.posX, this.posY, 2*this.radius, 2*this.radius);639}640else if (this.dazzled) {641if (pacman.beastModeTimer < 50 && pacman.beastModeTimer % 8 > 1) {642context.drawImage(this.dazzleImg2, this.posX, this.posY, 2*this.radius, 2*this.radius);643} else {644context.drawImage(this.dazzleImg, this.posX, this.posY, 2*this.radius, 2*this.radius);645}646}647else context.drawImage(this.image, this.posX, this.posY, 2*this.radius, 2*this.radius);648}649this.getCenterX = function () {650return this.posX+this.radius;651}652this.getCenterY = function () {653return this.posY+this.radius;654}655656this.reset = function() {657this.dead = false;658this.posX = this.startPosX;659this.posY = this.startPosY;660this.ghostHouse = true;661this.undazzle();662}663664this.die = function() {665if (!this.dead) {666game.score.add(100);667//this.reset();668this.dead = true;669this.changeSpeed(game.ghostSpeedNormal);670}671}672this.changeSpeed = function(s) {673// adjust gridPosition to new speed674this.posX = Math.round(this.posX / s) * s;675this.posY = Math.round(this.posY / s) * s;676this.speed = s;677}678679this.move = function() {680681this.checkDirectionChange();682this.checkCollision();683684// leave Ghost House685if (this.ghostHouse == true) {686687// Clyde does not start chasing before 2/3 of all pills are eaten and if level is < 4688if (this.name == "clyde") {689if ((game.level < 4) || ((game.pillCount > 104/3))) this.stop = true;690else this.stop = false;691}692// Inky starts after 30 pills and only from the third level on693if (this.name == "inky") {694if ((game.level < 3) || ((game.pillCount > 104-30))) this.stop = true;695else this.stop = false;696}697698if ((this.getGridPosY() == 5) && this.inGrid()) {699if ((this.getGridPosX() == 7)) this.setDirection(right);700if ((this.getGridPosX() == 8) || this.getGridPosX() == 9) this.setDirection(up);701if ((this.getGridPosX() == 10)) this.setDirection(left);702}703if ((this.getGridPosY() == 4) && ((this.getGridPosX() == 8) || (this.getGridPosX() == 9)) && this.inGrid()) {704console.log("ghosthouse -> false");705this.ghostHouse = false;706}707}708709if (!this.stop) {710// Move711this.posX += this.speed * this.dirX;712this.posY += this.speed * this.dirY;713714// Check if out of canvas715if (this.posX >= game.width-this.radius) this.posX = this.speed-this.radius;716if (this.posX <= 0-this.radius) this.posX = game.width-this.speed-this.radius;717if (this.posY >= game.height-this.radius) this.posY = this.speed-this.radius;718if (this.posY <= 0-this.radius) this.posY = game.height-this.speed-this.radius;719}720}721722this.checkCollision = function() {723724/* Check Back to Home */725if (this.dead && (this.getGridPosX() == this.startPosX /30) && (this.getGridPosY() == this.startPosY / 30)) this.reset();726else {727728/* Check Ghost / Pacman Collision */729if ((between(pacman.getCenterX(), this.getCenterX()-10, this.getCenterX()+10))730&& (between(pacman.getCenterY(), this.getCenterY()-10, this.getCenterY()+10)))731{732if ((!this.dazzled) && (!this.dead)) {733pacman.die();734}735else {736this.die();737}738}739}740}741742/* Pathfinding */743this.getNextDirection = function() {744// get next field745var pX = this.getGridPosX();746var pY= this.getGridPosY();747game.getMapContent(pX,pY);748var u, d, r, l; // option up, down, right, left749750// get target751if (this.dead) { // go Home752var tX = this.startPosX / 30;753var tY = this.startPosY / 30;754}755else if (game.ghostMode == 0) { // Scatter Mode756var tX = this.gridBaseX;757var tY = this.gridBaseY;758} else if (game.ghostMode == 1) { // Chase Mode759760switch (this.name) {761762// target: 4 ahead and 4 left of pacman763case "pinky":764var pdir = pacman.direction;765var pdirX = pdir.dirX == 0 ? - pdir.dirY : pdir.dirX;766var pdirY = pdir.dirY == 0 ? - pdir.dirX : pdir.dirY;767768var tX = (pacman.getGridPosX() + pdirX*4) % (game.width / pacman.radius +1);769var tY = (pacman.getGridPosY() + pdirY*4) % (game.height / pacman.radius +1);770break;771772// target: pacman773case "blinky":774var tX = pacman.getGridPosX();775var tY = pacman.getGridPosY();776break;777778// target:779case "inky":780var tX = pacman.getGridPosX() + 2*pacman.direction.dirX;781var tY = pacman.getGridPosY() + 2*pacman.direction.dirY;782var vX = tX - blinky.getGridPosX();783var vY = tY - blinky.getGridPosY();784tX = Math.abs(blinky.getGridPosX() + vX*2);785tY = Math.abs(blinky.getGridPosY() + vY*2);786break;787788// target: pacman, until pacman is closer than 5 grid fields, then back to scatter789case "clyde":790var tX = pacman.getGridPosX();791var tY = pacman.getGridPosY();792var dist = Math.sqrt(Math.pow((pX-tX),2) + Math.pow((pY - tY),2));793794if (dist < 5) {795tX = this.gridBaseX;796tY = this.gridBaseY;797}798break;799800}801}802803804var oppDir = this.getOppositeDirection(); // ghosts are not allowed to change direction 180�805806var dirs = [{},{},{},{}];807dirs[0].field = game.getMapContent(pX,pY-1);808dirs[0].dir = up;809dirs[0].distance = Math.sqrt(Math.pow((pX-tX),2) + Math.pow((pY -1 - tY),2));810811dirs[1].field = game.getMapContent(pX,pY+1);812dirs[1].dir = down;813dirs[1].distance = Math.sqrt(Math.pow((pX-tX),2) + Math.pow((pY+1 - tY),2));814815dirs[2].field = game.getMapContent(pX+1,pY);816dirs[2].dir = right;817dirs[2].distance = Math.sqrt(Math.pow((pX+1-tX),2) + Math.pow((pY - tY),2));818819dirs[3].field = game.getMapContent(pX-1,pY);820dirs[3].dir = left;821dirs[3].distance = Math.sqrt(Math.pow((pX-1-tX),2) + Math.pow((pY - tY),2));822823// Sort possible directions by distance824function compare(a,b) {825if (a.distance < b.distance)826return -1;827if (a.distance > b.distance)828return 1;829return 0;830}831var dirs2 = dirs.sort(compare);832833var r = this.dir;834var j;835836if (this.dead) {837for (var i = dirs2.length-1; i >= 0; i--) {838if ((dirs2[i].field != "wall") && !(dirs2[i].dir.equals(this.getOppositeDirection()))) {839r = dirs2[i].dir;840}841}842}843else {844for (var i = dirs2.length-1; i >= 0; i--) {845if ((dirs2[i].field != "wall") && (dirs2[i].field != "door") && !(dirs2[i].dir.equals(this.getOppositeDirection()))) {846r = dirs2[i].dir;847}848}849}850this.directionWatcher.set(r);851return r;852}853this.setRandomDirection = function() {854var dir = Math.floor((Math.random()*10)+1)%5;855856switch(dir) {857case 1:858if (this.getOppositeDirection().equals(up)) this.setDirection(down);859else this.setDirection(up);860break;861case 2:862if (this.getOppositeDirection().equals(down)) this.setDirection(up);863else this.setDirection(down);864break;865case 3:866if (this.getOppositeDirection().equals(right)) this.setDirection(left);867else this.setDirection(right);868break;869case 4:870if (this.getOppositeDirection().equals(left)) this.setDirection(right);871else this.setDirection(left);872break;873}874}875this.reverseDirection = function() {876console.log("reverseDirection: "+this.direction.name+" to "+this.getOppositeDirection().name);877this.directionWatcher.set(this.getOppositeDirection());878}879880}881882Ghost.prototype = new Figure();883884// Super Class for Pacman & Ghosts885function Figure() {886this.posX;887this.posY;888this.speed;889this.dirX = right.dirX;890this.dirY = right.dirY;891this.direction;892this.stop = true;893this.directionWatcher = new directionWatcher();894this.getNextDirection = function() { console.log("Figure getNextDirection");};895this.checkDirectionChange = function() {896if (this.inGrid() && (this.directionWatcher.get() == null)) this.getNextDirection();897if ((this.directionWatcher.get() != null) && this.inGrid()) {898//console.log("changeDirection to "+this.directionWatcher.get().name);899this.setDirection(this.directionWatcher.get());900this.directionWatcher.set(null);901}902903}904905906this.inGrid = function() {907if((this.posX % (2*this.radius) === 0) && (this.posY % (2*this.radius) === 0)) return true;908return false;909}910this.getOppositeDirection = function() {911if (this.direction.equals(up)) return down;912else if (this.direction.equals(down)) return up;913else if (this.direction.equals(right)) return left;914else if (this.direction.equals(left)) return right;915}916this.move = function() {917918if (!this.stop) {919this.posX += this.speed * this.dirX;920this.posY += this.speed * this.dirY;921922// Check if out of canvas923if (this.posX >= game.width-this.radius) this.posX = this.speed-this.radius;924if (this.posX <= 0-this.radius) this.posX = game.width-this.speed-this.radius;925if (this.posY >= game.height-this.radius) this.posY = this.speed-this.radius;926if (this.posY <= 0-this.radius) this.posY = game.height-this.speed-this.radius;927}928}929this.stop = function() { this.stop = true;}930this.start = function() { this.stop = false;}931932this.getGridPosX = function() {933return (this.posX - (this.posX % 30))/30;934}935this.getGridPosY = function() {936return (this.posY - (this.posY % 30))/30;937}938this.setDirection = function(dir) {939this.dirX = dir.dirX;940this.dirY = dir.dirY;941this.angle1 = dir.angle1;942this.angle2 = dir.angle2;943this.direction = dir;944}945this.setPosition = function(x, y) {946this.posX = x;947this.posY = y;948}949}950951function pacman() {952this.radius = 15;953this.posX = 0;954this.posY = 6*2*this.radius;955this.speed = 5;956this.angle1 = 0.25;957this.angle2 = 1.75;958this.mouth = 1; /* Switches between 1 and -1, depending on mouth closing / opening */959this.dirX = right.dirX;960this.dirY = right.dirY;961this.lives = 3;962this.stuckX = 0;963this.stuckY = 0;964this.frozen = false; // used to play die Animation965this.freeze = function () {966this.frozen = true;967}968this.unfreeze = function() {969this.frozen = false;970}971this.getCenterX = function () {972return this.posX+this.radius;973}974this.getCenterY = function () {975return this.posY+this.radius;976}977this.directionWatcher = new directionWatcher();978979this.direction = right;980981this.beastMode = false;982this.beastModeTimer = 0;983984this.checkCollisions = function () {985986if ((this.stuckX == 0) && (this.stuckY == 0) && this.frozen == false) {987988// Get the Grid Position of Pac989var gridX = this.getGridPosX();990var gridY = this.getGridPosY();991var gridAheadX = gridX;992var gridAheadY = gridY;993994var field = game.getMapContent(gridX, gridY);995996// get the field 1 ahead to check wall collisions997if ((this.dirX == 1) && (gridAheadX < 17)) gridAheadX += 1;998if ((this.dirY == 1) && (gridAheadY < 12)) gridAheadY += 1;999var fieldAhead = game.getMapContent(gridAheadX, gridAheadY);100010011002/* Check Pill Collision */1003if ((field === "pill") || (field === "powerpill")) {1004//console.log("Pill found at ("+gridX+"/"+gridY+"). Pacman at ("+this.posX+"/"+this.posY+")");1005if (1006((this.dirX == 1) && (between(this.posX, game.toPixelPos(gridX)+this.radius-5, game.toPixelPos(gridX+1))))1007|| ((this.dirX == -1) && (between(this.posX, game.toPixelPos(gridX), game.toPixelPos(gridX)+5)))1008|| ((this.dirY == 1) && (between(this.posY, game.toPixelPos(gridY)+this.radius-5, game.toPixelPos(gridY+1))))1009|| ((this.dirY == -1) && (between(this.posY, game.toPixelPos(gridY), game.toPixelPos(gridY)+5)))1010|| (fieldAhead === "wall")1011)1012{ var s;1013if (field === "powerpill") {1014Sound.play("powerpill");1015s = 50;1016this.enableBeastMode();1017game.startGhostFrightened();1018}1019else {1020Sound.play("waka");1021s = 10;1022game.pillCount--;1023}1024game.map.posY[gridY].posX[gridX].type = "null";1025game.score.add(s);1026}1027}10281029/* Check Wall Collision */1030if ((fieldAhead === "wall") || (fieldAhead === "door")) {1031this.stuckX = this.dirX;1032this.stuckY = this.dirY;1033pacman.stop();1034// get out of the wall1035if ((this.stuckX == 1) && ((this.posX % 2*this.radius) != 0)) this.posX -= 5;1036if ((this.stuckY == 1) && ((this.posY % 2*this.radius) != 0)) this.posY -= 5;1037if (this.stuckX == -1) this.posX += 5;1038if (this.stuckY == -1) this.posY += 5;1039}10401041}1042}1043this.checkDirectionChange = function() {1044if (this.directionWatcher.get() != null) {1045//console.log("next Direction: "+directionWatcher.get().name);10461047if ((this.stuckX == 1) && this.directionWatcher.get() == right) this.directionWatcher.set(null);1048else {1049// reset stuck events1050this.stuckX = 0;1051this.stuckY = 0;105210531054// only allow direction changes inside the grid1055if ((this.inGrid())) {1056//console.log("changeDirection to "+directionWatcher.get().name);10571058// check if possible to change direction without getting stuck1059console.log("x: "+this.getGridPosX()+" + "+this.directionWatcher.get().dirX);1060console.log("y: "+this.getGridPosY()+" + "+this.directionWatcher.get().dirY);1061var x = this.getGridPosX()+this.directionWatcher.get().dirX;1062var y = this.getGridPosY()+this.directionWatcher.get().dirY;1063if (x <= -1) x = game.width/(this.radius*2)-1;1064if (x >= game.width/(this.radius*2)) x = 0;1065if (y <= -1) x = game.height/(this.radius*2)-1;1066if (y >= game.heigth/(this.radius*2)) y = 0;10671068console.log("x: "+x);1069console.log("y: "+y);1070var nextTile = game.map.posY[y].posX[x].type;1071console.log("checkNextTile: "+nextTile);10721073if (nextTile != "wall") {1074this.setDirection(this.directionWatcher.get());1075this.directionWatcher.set(null);1076}1077}1078}1079}1080}1081this.setDirection = function(dir) {1082if (!this.frozen) {1083this.dirX = dir.dirX;1084this.dirY = dir.dirY;1085this.angle1 = dir.angle1;1086this.angle2 = dir.angle2;1087this.direction = dir;1088}1089}1090this.enableBeastMode = function() {1091this.beastMode = true;1092this.beastModeTimer = 240;1093//console.log("Beast Mode activated!");1094inky.dazzle();1095pinky.dazzle();1096blinky.dazzle();1097clyde.dazzle();1098};1099this.disableBeastMode = function() {1100this.beastMode = false;1101//console.log("Beast Mode is over!");1102inky.undazzle();1103pinky.undazzle();1104blinky.undazzle();1105clyde.undazzle();1106};1107this.move = function() {11081109if (!this.frozen) {1110if (this.beastModeTimer > 0) {1111this.beastModeTimer--;1112//console.log("Beast Mode: "+this.beastModeTimer);1113}1114if ((this.beastModeTimer == 0) && (this.beastMode == true)) this.disableBeastMode();11151116this.posX += this.speed * this.dirX;1117this.posY += this.speed * this.dirY;11181119// Check if out of canvas1120if (this.posX >= game.width-this.radius) this.posX = 5-this.radius;1121if (this.posX <= 0-this.radius) this.posX = game.width-5-this.radius;1122if (this.posY >= game.height-this.radius) this.posY = 5-this.radius;1123if (this.posY <= 0-this.radius) this.posY = game.height-5-this.radius;1124}1125else this.dieAnimation();1126}11271128this.eat = function () {11291130if (!this.frozen) {1131if (this.dirX == this.dirY == 0) {11321133this.angle1 -= this.mouth*0.07;1134this.angle2 += this.mouth*0.07;11351136var limitMax1 = this.direction.angle1;1137var limitMax2 = this.direction.angle2;1138var limitMin1 = this.direction.angle1 - 0.21;1139var limitMin2 = this.direction.angle2 + 0.21;11401141if (this.angle1 < limitMin1 || this.angle2 > limitMin2)1142{1143this.mouth = -1;1144}1145if (this.angle1 >= limitMax1 || this.angle2 <= limitMax2)1146{1147this.mouth = 1;1148}1149}1150}1151}1152this.stop = function() {1153this.dirX = 0;1154this.dirY = 0;1155}1156this.reset = function() {1157this.unfreeze();1158this.posX = 0;1159this.posY = 6*2*this.radius;1160this.setDirection(right);1161this.stop();1162this.stuckX = 0;1163this.stuckY = 0;1164//console.log("reset pacman");1165}1166this.dieAnimation = function() {1167this.angle1 += 0.05;1168this.angle2 -= 0.05;1169if (this.angle1 >= this.direction.angle1+0.7 || this.angle2 <= this.direction.angle2-0.7) {1170this.dieFinal();1171}1172}1173this.die = function() {1174Sound.play("die");1175this.freeze();1176this.dieAnimation();1177}1178this.dieFinal = function() {1179this.reset();1180pinky.reset();1181inky.reset();1182blinky.reset();1183clyde.reset();1184this.lives--;1185console.log("pacman died, "+this.lives+" lives left");1186if (this.lives <= 0) {1187var input = "<div id='highscore-form'><span id='form-validater'></span><input type='text' id='playerName'/><span class='button' id='score-submit'>save</span></div>";1188game.showMessage("Game over","Total Score: "+game.score.score+input);1189game.gameOver = true;1190$('#playerName').focus();1191}1192game.drawHearts(this.lives);1193}1194this.getGridPosX = function() {1195return (this.posX - (this.posX % 30))/30;1196}1197this.getGridPosY = function() {1198return (this.posY - (this.posY % 30))/30;1199}1200}1201pacman.prototype = new Figure();1202var pacman = new pacman();1203game.buildWalls();120412051206// Check if a new cache is available on page load.1207function checkAppCache() {1208console.log('check AppCache');1209window.applicationCache.addEventListener('updateready', function(e)1210{1211console.log("AppCache: updateready");1212if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {12131214// Browser downloaded a new app cache.1215// Swap it in and reload the page to get the new hotness.1216window.applicationCache.swapCache();1217if (confirm('A new version of this site is available. Load it?')) {1218window.location.reload();1219}12201221} else {1222// Manifest didn't change. Nothing new to server.1223}1224}, false);12251226window.applicationCache.addEventListener('cached', function(e)1227{1228console.log("AppCache: cached");1229}, false);12301231}123212331234// Action starts here:12351236function hideAdressbar() {1237console.log("hide adressbar");1238$("html").scrollTop(1);1239$("body").scrollTop(1);1240}12411242$(document).ready(function() {12431244$.ajaxSetup({ mimeType: "application/json" });12451246$.ajaxSetup({beforeSend: function(xhr){1247if (xhr.overrideMimeType){1248xhr.overrideMimeType("application/json");1249//console.log("mimetype set to json");1250}1251}1252});12531254// Hide address bar1255hideAdressbar();12561257if (window.applicationCache != null) checkAppCache();12581259/* -------------------- EVENT LISTENERS -------------------------- */12601261// Listen for resize changes1262/*window.addEventListener("resize", function() {1263// Get screen size (inner/outerWidth, inner/outerHeight)1264// deactivated because of problems1265if ((window.outerHeight < window.outerWidth) && (window.outerHeight < 720)) {1266game.showMessage("Rotate Device","Your screen is too small to play in landscape view.");1267console.log("rotate your device to portrait!");1268}1269}, false);*/127012711272// --------------- Controls127312741275// Keyboard1276window.addEventListener('keydown',doKeyDown,true);12771278$('#canvas-container').click(function() {1279if (!(game.gameOver == true)) game.pauseResume();1280});12811282$('body').on('click', '#score-submit', function(){1283console.log("submit highscore pressed");1284if ($('#playerName').val() === "" || $('#playerName').val() === undefined) {1285$('#form-validater').html("Please enter a name<br/>");1286} else {1287$('#form-validater').html("");1288addHighscore();1289}1290});12911292$('body').on('click', '#show-highscore', function(){1293game.showContent('highscore-content');1294getHighscore();1295});12961297// Hammerjs Touch Events1298/*Hammer('#canvas-container').on("tap", function(event) {1299if (!(game.gameOver == true)) game.pauseResume();1300});*/1301Hammer('.container').on("swiperight", function(event) {1302if ($('#game-content').is(":visible")) {1303event.gesture.preventDefault();1304pacman.directionWatcher.set(right);1305}1306});1307Hammer('.container').on("swipeleft", function(event) {1308if ($('#game-content').is(":visible")) {1309event.gesture.preventDefault();1310pacman.directionWatcher.set(left);1311}1312});1313Hammer('.container').on("swipeup", function(event) {1314if ($('#game-content').is(":visible")) {1315event.gesture.preventDefault();1316pacman.directionWatcher.set(up);1317}1318});1319Hammer('.container').on("swipedown", function(event) {1320if ($('#game-content').is(":visible")) {1321event.gesture.preventDefault();1322pacman.directionWatcher.set(down);1323}1324});13251326// Mobile Control Buttons1327$(document).on('touchend mousedown','#up',function(event) {1328event.preventDefault();1329pacman.directionWatcher.set(up);1330});1331$(document).on('touchend mousedown','#down',function(event) {1332event.preventDefault();1333pacman.directionWatcher.set(down);1334});1335$(document).on('touchend mousedown','#left',function(event) {1336event.preventDefault();1337pacman.directionWatcher.set(left);1338});1339$(document).on('touchend mousedown','#right',function(event) {1340event.preventDefault();1341pacman.directionWatcher.set(right);1342});13431344// Menu1345$(document).on('click','.button#newGame',function(event) {1346game.newGame();1347});1348$(document).on('click','.button#highscore',function(event) {1349game.showContent('highscore-content');1350getHighscore();1351});1352$(document).on('click','.button#instructions',function(event) {1353game.showContent('instructions-content');1354});1355$(document).on('click','.button#info',function(event) {1356game.showContent('info-content');1357});1358// back button1359$(document).on('click','.button#back',function(event) {1360game.showContent('game-content');1361});1362// toggleSound1363$(document).on('click','.controlSound',function(event) {1364game.toggleSound();1365});1366// get latest1367$(document).on('click', '#updateCode', function(event) {1368console.log('check for new version');1369event.preventDefault();1370window.applicationCache.update();1371});13721373// checkAppCache();13741375canvas = $("#myCanvas").get(0);1376context = canvas.getContext("2d");1377137813791380/* --------------- GAME INITIALISATION ------------------------------------13811382TODO: put this into Game object and change code to accept different setups / levels13831384-------------------------------------------------------------------------- */13851386game.init(0);1387logger.disableLogger();13881389renderContent();1390});13911392function renderContent()1393{1394//context.save()13951396// Refresh Score1397game.score.refresh(".score");13981399// Pills1400context.beginPath();1401context.fillStyle = "White";1402context.strokeStyle = "White";14031404var dotPosY;1405$.each(game.map.posY, function(i, item) {1406dotPosY = this.row;1407$.each(this.posX, function() {1408if (this.type == "pill") {1409context.arc(game.toPixelPos(this.col-1)+pacman.radius,game.toPixelPos(dotPosY-1)+pacman.radius,game.pillSize,0*Math.PI,2*Math.PI);1410context.moveTo(game.toPixelPos(this.col-1), game.toPixelPos(dotPosY-1));1411}1412else if (this.type == "powerpill") {1413context.arc(game.toPixelPos(this.col-1)+pacman.radius,game.toPixelPos(dotPosY-1)+pacman.radius,game.powerpillSizeCurrent,0*Math.PI,2*Math.PI);1414context.moveTo(game.toPixelPos(this.col-1), game.toPixelPos(dotPosY-1));1415}1416});1417});1418console.log("pps: " + game.nextPowerPillSize());1419context.fill();14201421// Walls1422context.drawImage(canvas_walls, 0, 0);142314241425if (game.running == true) {1426// Ghosts1427pinky.draw(context);1428blinky.draw(context);1429inky.draw(context);1430clyde.draw(context);143114321433// Pac Man1434context.beginPath();1435context.fillStyle = "Yellow";1436context.strokeStyle = "Yellow";1437context.arc(pacman.posX+pacman.radius,pacman.posY+pacman.radius,pacman.radius,pacman.angle1*Math.PI,pacman.angle2*Math.PI);1438context.lineTo(pacman.posX+pacman.radius, pacman.posY+pacman.radius);1439context.stroke();1440context.fill();1441}14421443}14441445function renderGrid(gridPixelSize, color)1446{1447context.save();1448context.lineWidth = 0.5;1449context.strokeStyle = color;14501451// horizontal grid lines1452for(var i = 0; i <= canvas.height; i = i + gridPixelSize)1453{1454context.beginPath();1455context.moveTo(0, i);1456context.lineTo(canvas.width, i);1457context.closePath();1458context.stroke();1459}14601461// vertical grid lines1462for(var i = 0; i <= canvas.width; i = i + gridPixelSize)1463{1464context.beginPath();1465context.moveTo(i, 0);1466context.lineTo(i, canvas.height);1467context.closePath();1468context.stroke();1469}14701471context.restore();1472}14731474function animationLoop()1475{1476canvas.width = canvas.width;1477//renderGrid(pacman.radius, "red");1478renderContent();14791480if (game.dieAnimation == 1) pacman.dieAnimation();1481if (game.pause != true){1482// Make changes before next loop1483pacman.move();1484pacman.eat();1485pacman.checkDirectionChange();1486pacman.checkCollisions(); // has to be the LAST method called on pacman1487148814891490blinky.move();1491inky.move();1492pinky.move();1493clyde.move();14941495game.checkGhostMode();1496}14971498// All dots collected?1499game.check();150015011502//requestAnimationFrame(animationLoop);1503setTimeout(animationLoop, game.refreshRate);150415051506}1507150815091510function doKeyDown(evt){15111512switch (evt.keyCode)1513{1514case 38: // UP Arrow Key pressed1515evt.preventDefault();1516case 87: // W pressed1517pacman.directionWatcher.set(up);1518break;1519case 40: // DOWN Arrow Key pressed1520evt.preventDefault();1521case 83: // S pressed1522pacman.directionWatcher.set(down);1523break;1524case 37: // LEFT Arrow Key pressed1525evt.preventDefault();1526case 65: // A pressed1527pacman.directionWatcher.set(left);1528break;1529case 39: // RIGHT Arrow Key pressed1530evt.preventDefault();1531case 68: // D pressed1532pacman.directionWatcher.set(right);1533break;1534case 78: // N pressed1535if (!$('#playerName').is(':focus')) {1536game.pause = 1;1537game.newGame();1538}1539break;1540case 77: // M pressed1541game.toggleSound();1542break;1543case 8: // Backspace pressed -> show Game Content1544case 27: // ESC pressed -> show Game Content1545if (!$('#playerName').is(':focus')) {1546evt.preventDefault();1547game.showContent('game-content');1548}1549break;1550case 32: // SPACE pressed -> pause Game1551evt.preventDefault();1552if (!(game.gameOver == true)1553&& $('#game-content').is(':visible')1554) game.pauseResume();1555break;1556}1557}1558}15591560geronimo();15611562