1
0

pacman-core.js~ 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. var Game = {
  2. id : null,
  3. type : "offline",
  4. player : 1,
  5. PACMAN_START_X : 14 * TILE_SIZE,
  6. PACMAN_START_Y : 24 * TILE_SIZE,
  7. GHOST_STATE_CHASE : 1,
  8. GHOST_STATE_SCATTER : 2,
  9. GHOST_STATE_FRIGHTENED : 3,
  10. GHOST_STATE_IN_JAIL : 4,
  11. GHOST_STATE_EATEN : 5,
  12. GHOST_STATE_FRIGHTENED_BLINK : 6,
  13. GHOST_EVENT_CHASE : "ghost_event_chase",
  14. GHOST_EVENT_SCATTER : "ghost_event_scatter",
  15. GHOST_EVENT_DOT_EATEN : "ghost_event_dot_eaten",
  16. DOT_POINTS : 10,
  17. BIG_DOT_POINTS : 50,
  18. totalDots : 0,
  19. dots : {},
  20. timer : null,
  21. frightTimer : null,
  22. bonusTimer : null,
  23. level : -1,
  24. levelData : null,
  25. step : 0,
  26. score : 0,
  27. eatenDots : 0,
  28. lives : 3,
  29. running : false,
  30. mode : 2, // Game.GHOST_STATE_SCATTER
  31. frightMode : false,
  32. eaten: 0,
  33. pacman : null,
  34. miss : null,
  35. hero : null,
  36. blinky : null,
  37. pinky : null,
  38. inky : null,
  39. clyde : null,
  40. ghosts : new Array(),
  41. actors : {},
  42. heroes : new Array(),
  43. maze : MAZE,
  44. init : function() {
  45. GUI.updateMessage("READY");
  46. $(".dot.hiddenDot").each( function(incr, elt) {
  47. Game.dots[elt.id] = "dot";
  48. });
  49. $(".dot.hiddenDot").removeClass("hiddenDot");
  50. $(".bigDot.hiddenDot").each( function(incr, elt) {
  51. Game.dots[elt.id] = "bigDot";
  52. });
  53. $(".bigDot.hiddenDot").removeClass("hiddenDot")
  54. Game.totalDots = $(".dot").length + $(".bigDot").length;
  55. SCOREBOARD.init();
  56. SCOREBOARD.set_score( Game.score );
  57. Game.level++;
  58. Game.step = 0;
  59. Game.eatenDots = 0;
  60. GUI.updateLevelNumber( Game.level + 1 );
  61. Game.build(LEVELS[Math.min(Game.level, LEVELS.length)]);
  62. },
  63. build : function(data) {
  64. Game.levelData = data;
  65. Game.addPacman();
  66. Game.addGhosts();
  67. Sound.play("opening");
  68. setTimeout("Game.start();", 4500);
  69. },
  70. start : function() {
  71. //if( $.browser.webkit )
  72. $(document).keydown( function( event ) {
  73. if( event.which > 36 && event.which < 41 )
  74. return false;
  75. } );
  76. // $(document).keypress(scrollPreventFct );
  77. GUI.updateMessage("");
  78. Game.timer = new PausableTimer(Game.timerManager, Game.levelData.mode[Game.step] * 1000);
  79. Game.running = true;
  80. },
  81. levelComplete : function() {
  82. Game.running = false;
  83. Game.timer.stop();
  84. Game.timer = null;
  85. setTimeout("Game.init();", 3000);
  86. },
  87. eat : function(type) {
  88. Game.eatenDots++;
  89. if( type === "bigDot" ) {
  90. Game.score += Game.BIG_DOT_POINTS;
  91. // console.log( "Eating big dot " + Game.score );
  92. SCOREBOARD.add( Game.BIG_DOT_POINTS );
  93. } else {
  94. Game.score += Game.DOT_POINTS;
  95. // console.log( "Eating dot " + Game.score );
  96. SCOREBOARD.add( Game.DOT_POINTS );
  97. }
  98. if( Game.eatenDots == 70 || Game.eatenDots == 170 ) {
  99. Game.bonusTimer = setTimeout("Game.hideBonus();", ( 9 + Math.random() ) * 1000 );
  100. $("#" + Game.maze.bonus_target).addClass( Game.levelData.bonus.type);
  101. }
  102. if( Game.eatenDots === Game.totalDots )
  103. Game.levelComplete();
  104. },
  105. eatGhost : function(ghost) {
  106. Sound.play("ghost");
  107. Game.eaten++;
  108. var points = Game.eaten * 200;
  109. Game.score += points;
  110. // console.log(new Date() + " Eating " + ghost.id + " " + (Game.eaten * 200) + " "+ Game.score );
  111. SCOREBOARD.add( points );
  112. },
  113. hideBonus : function() {
  114. $("#" + Game.maze.bonus_target).removeClass( Game.levelData.bonus.type);
  115. Game.bonusTimer = null;
  116. },
  117. die : function() {
  118. Game.running = false;
  119. $.each( Game.actors, function(index, actor) {
  120. actor.speed = 0;
  121. })
  122. Game.pacman.die();
  123. Game.timer.stop();
  124. Game.step = 0;
  125. Game.timer = null;
  126. $("#life" + Game.lives).effect( "pulsate", {times:3, mode:"hide"}, 500 );
  127. Game.lives--;
  128. if( Game.lives > 0 )
  129. setTimeout( "Game.startAfterDie();", 3000);
  130. else {
  131. GUI.drawText( $("#message"), "GAME OVER", true );
  132. Game.show_game_over();
  133. }
  134. },
  135. show_game_over: function() {
  136. },
  137. startAfterDie : function() {
  138. var dotsCounters = new Array();
  139. $.each(Game.ghosts, function(index, ghost ) {
  140. dotsCounters[index] = ghost.dotsCounter;
  141. });
  142. Game.addGhosts();
  143. Game.addPacman();
  144. //Game.addMissPacman();
  145. $.each(Game.ghosts, function(index, ghost ) {
  146. ghost.dotsCounter = dotsCounters[index];
  147. if( ghost.dotsCounter >= ghost.dotsLimits[Math.min(Game.level, ghost.dotsLimits.length - 1)] ) {
  148. ghost.speed = ghost.initialSpeed;
  149. ghost.state_to(Game.GHOST_STATE_SCATTER);
  150. }
  151. });
  152. Game.running = true;
  153. Game.step = 0;
  154. Game.timer = new PausableTimer(Game.timerManager, Game.levelData.mode[Game.step] * 1000);
  155. },
  156. timerManager : function() {
  157. Game.step++;
  158. if( Game.step % 2 == 1 ) {
  159. $(".actor").trigger(Game.GHOST_EVENT_CHASE);
  160. Game.mode = Game.GHOST_STATE_CHASE;
  161. } else {
  162. $(".actor").trigger(Game.GHOST_EVENT_SCATTER);
  163. Game.mode = Game.GHOST_STATE_SCATTER;
  164. }
  165. if( Game.step < Game.levelData.mode.length - 1 && Game.levelData.mode[Game.step] != INFINITY )
  166. Game.timer = new PausableTimer(Game.timerManager, Game.levelData.mode[Game.step] * 1000);
  167. },
  168. addPacman : function() {
  169. if( $("#pacman").length == 0) {
  170. Game.pacman = new Pacman();
  171. $("#actors").addSprite("pacman", {animation: Game.pacman.animations["right"], posx:Game.pacman.x, posy: Game.pacman.y, width: ACTOR_SIZE, height: ACTOR_SIZE});
  172. Game.pacman.node = $("#pacman");
  173. Game.pacman.node.addClass( "actor" );
  174. Game.actors[ "pacman" ] = Game.pacman;
  175. Game.heroes[ "pacman" ] = Game.pacman;
  176. Game.hero = Game.pacman;
  177. }
  178. Game.pacman.init();
  179. Game.pacman.speed = Game.levelData.pacman.speed;
  180. Game.pacman.left();
  181. },
  182. addMissPacman : function() {
  183. if( $("#miss_pacman").length == 0) {
  184. Game.miss = new Pacman();
  185. Game.miss.animations["right"] = new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 3, offsety: 272, delta: ACTOR_SIZE, rate: 120, type: $.gameQuery.ANIMATION_HORIZONTAL });
  186. Game.miss.animations["up"] = new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 3, offsetx: 96, offsety: 272, delta: ACTOR_SIZE, rate: 120, type: $.gameQuery.ANIMATION_HORIZONTAL });
  187. $("#actors").addSprite("miss_pacman", {animation: Game.miss.animations["right"], posx:Game.miss.x, posy: Game.miss.y, width: ACTOR_SIZE, height: ACTOR_SIZE});
  188. Game.miss.node = $("#miss_pacman");
  189. Game.miss.node.addClass( "actor" );
  190. Game.actors[ "miss_pacman" ] = Game.miss;
  191. Game.heroes[ "miss_pacman" ] = Game.miss;
  192. }
  193. Game.miss.init();
  194. Game.miss.x = Game.MISS_PACMAN_START_X;
  195. Game.miss.y = Game.MISS_PACMAN_START_Y;
  196. Game.miss.speed = Game.levelData.pacman.speed;
  197. Game.miss.right(true);
  198. Game.miss.left(true);
  199. Game.miss.node.x(Game.miss.x);
  200. Game.miss.node.y(Game.miss.y);
  201. Game.miss.right();
  202. },
  203. addGhosts : function() {
  204. Game.addBlinky();
  205. Game.addPinky();
  206. Game.addInky();
  207. Game.addClyde();
  208. },
  209. addBlinky : function() {
  210. if( $("#blinky").length == 0 ) {
  211. Game.blinky = new Ghost("blinky", 0, {x: 14 * TILE_SIZE, y: 14 * TILE_SIZE}, {x: 25, y: 0 }, function() {
  212. var prey = Game.actors[ "blinky" ].prey;
  213. return {x: prey.getTileX(), y: prey.getTileY()};
  214. }, [0,0,0], Game.GHOST_STATE_SCATTER);
  215. Game.blinky.center();
  216. $("#actors").addSprite("blinky", {animation: Game.blinky.animations["right"], posx:Game.blinky.x, posy: Game.blinky.y, width: ACTOR_SIZE, height: ACTOR_SIZE});
  217. Game.blinky.node = $("#blinky");
  218. Game.blinky.node.addClass( "actor" );
  219. Game.actors[ "blinky" ] = Game.blinky;
  220. Game.blinky.loadBindings();
  221. Game.blinky.originalTarget = Game.blinky.target;
  222. Game.blinky.target = function() {
  223. var remainingDots = Game.totalDots - Game.eatenDots;
  224. var elroySpecs = Game.levelData.ghost;
  225. if( ( Game.blinky.state == Game.GHOST_STATE_SCATTER || Game.blinky.state == Game.GHOST_STATE_CHASE ) && remainingDots <= elroySpecs.elroy1Dots ) {
  226. if( remainingDots <= elroySpecs.elroy2Dots ) {
  227. Game.blinky.speed = elroySpecs.elroy2Speed;
  228. }
  229. else {
  230. Game.blinky.speed = elroySpecs.elroy1Speed;
  231. }
  232. return Game.blinky.personnalTarget();
  233. }
  234. return Game.blinky.originalTarget();
  235. };
  236. Game.ghosts.push( Game.blinky );
  237. } else {
  238. Game.blinky.init();
  239. }
  240. Game.blinky.state = Game.GHOST_STATE_SCATTER;
  241. Game.blinky.left();
  242. Game.blinky.initialSpeed = Game.levelData.ghost.speed;
  243. Game.blinky.speed = Game.blinky.initialSpeed;
  244. },
  245. addPinky : function() {
  246. if( $("#pinky").length == 0 ) {
  247. Game.pinky = new Ghost("pinky", 1, {x: 14 * TILE_SIZE, y: 16 * TILE_SIZE}, {x: 2, y: 0 }, function() {
  248. var prey = Game.actors[ "pinky" ].prey;
  249. var direction = this.prey.direction;
  250. if( direction % 2 == 0 )
  251. return {x: prey.getTileX() + (direction == LEFT ? -4 : 4), y: prey.getTileY()};
  252. else
  253. return {x: prey.getTileX(), y: prey.getTileY() + (direction == UP ? -4 : 4) };
  254. }, [0,0,0], Game.GHOST_STATE_IN_JAIL);
  255. Game.pinky.center();
  256. $("#actors").addSprite("pinky", {animation: Game.pinky.animations["right"], posx: Game.pinky.x, posy: Game.pinky.y, width: ACTOR_SIZE, height: ACTOR_SIZE});
  257. Game.pinky.node = $("#pinky");
  258. Game.pinky.node.addClass( "actor" );
  259. Game.actors[ "pinky" ] = Game.pinky;
  260. Game.pinky.loadBindings();
  261. Game.ghosts.push( Game.pinky );
  262. } else {
  263. Game.pinky.init();
  264. }
  265. Game.pinky.initialSpeed = Game.levelData.ghost.speed;
  266. Game.pinky.left();
  267. },
  268. addInky : function() {
  269. if( $("#inky").length == 0 ) {
  270. Game.inky = new Ghost("inky", 2, {x: 12 * TILE_SIZE, y: 16 * TILE_SIZE}, {x: 27, y: 34 }, function() {
  271. var prey = Game.actors[ "inky" ].prey;
  272. var direction = prey.direction;
  273. if( direction % 2 == 0 )
  274. direction = {x: prey.getTileX() + (direction == LEFT ? -2 : 2) - Game.blinky.getTileX(), y: prey.getTileY() - Game.blinky.getTileY()};
  275. else
  276. direction = {x: prey.getTileX() - Game.blinky.getTileX(), y: prey.getTileY() + (direction == UP ? -2 : 2) - Game.blinky.getTileY()};
  277. return {x: direction.x * 2, y: direction.y * 2};
  278. }, [30,0,0], Game.GHOST_STATE_IN_JAIL);
  279. Game.inky.center();
  280. $("#actors").addSprite("inky", {animation: Game.inky.animations["right"], posx:Game.inky.x, posy: Game.inky.y, width: ACTOR_SIZE, height: ACTOR_SIZE});
  281. Game.inky.node = $("#inky");
  282. Game.inky.node.addClass( "actor" );
  283. Game.actors[ "inky" ] = Game.inky;
  284. Game.inky.loadBindings();
  285. Game.ghosts.push( Game.inky );
  286. } else {
  287. Game.inky.init();
  288. }
  289. Game.inky.initialSpeed = Game.levelData.ghost.speed;
  290. Game.inky.right();
  291. },
  292. addClyde : function() {
  293. if( $("#clyde").length == 0 ) {
  294. Game.clyde = new Ghost("clyde", 3, {x: 16 * TILE_SIZE, y: 16 * TILE_SIZE}, {x: 0, y: 34 }, function() {
  295. var prey = Game.actors[ "clyde" ].prey;
  296. return distance( {x: this.getTileX(), y: this.getTileY()} , {x: prey.getTileX(), y: prey.getTileY()}) < 8 ?
  297. this.scatterTarget : {x: prey.getTileX(), y: prey.getTileY()};
  298. }, [60,50,0], Game.GHOST_STATE_IN_JAIL);
  299. Game.clyde.center();
  300. $("#actors").addSprite("clyde", {animation: Game.clyde.animations["right"], posx:Game.clyde.x, posy: Game.clyde.y, width: ACTOR_SIZE, height: ACTOR_SIZE});
  301. Game.clyde.node = $("#clyde");
  302. Game.clyde.node.addClass( "actor" );
  303. Game.actors[ "clyde" ] = Game.clyde;
  304. Game.clyde.loadBindings();
  305. Game.ghosts.push( Game.clyde );
  306. } else {
  307. Game.clyde.init();
  308. }
  309. Game.clyde.initialSpeed = Game.levelData.ghost.speed;
  310. Game.clyde.left();
  311. },
  312. moveGhosts : function() {
  313. $.each(Game.ghosts, function(index, ghost ) {
  314. ghost.move();
  315. });
  316. },
  317. nearEndFright : function() {
  318. $.each(Game.ghosts, function(index, ghost ) {
  319. if( ghost.state != Game.GHOST_STATE_IN_JAIL && ghost.state != Game.GHOST_STATE_EATEN )
  320. ghost.state_to(Game.GHOST_STATE_FRIGHTENED_BLINK);
  321. });
  322. setTimeout( 'Game.endFright();', 160 * 4 * Game.levelData.frightFlashesCount);
  323. },
  324. endFright : function() {
  325. if( Game.timer )
  326. Game.timer.resume();
  327. Game.frightTimer = null;
  328. Game.eaten = 0;
  329. $('.actor').trigger( Game.mode == Game.GHOST_STATE_CHASE ? Game.GHOST_EVENT_CHASE : Game.GHOST_EVENT_SCATTER );
  330. }
  331. }
  332. function distance(currentTile, target) {
  333. return Math.sqrt( (target.x - currentTile.x) * (target.x - currentTile.x) + (target.y - currentTile.y)*(target.y - currentTile.y));
  334. };
  335. //Game objects:
  336. function Actor(){}
  337. Actor.prototype = {
  338. node : null,
  339. animations : null,
  340. x : null,
  341. y : null,
  342. speed : null,
  343. direction : null, // 1: up, 2: left, 3:down, 4: right
  344. directionX : 0,
  345. directionY : 0,
  346. getX : function() {
  347. return x;
  348. },
  349. getY : function() {
  350. return y;
  351. },
  352. getTileX : function() {
  353. return Math.floor(this.x / TILE_SIZE);
  354. },
  355. getTileY : function() {
  356. return Math.floor(this.y / TILE_SIZE);
  357. },
  358. getTile : function() {
  359. return this.getTileX() + this.getTileY() * WIDTH_TILE_COUNT;
  360. },
  361. getInsideTileX : function() {
  362. return this.x % TILE_SIZE;
  363. },
  364. getInsideTileY : function() {
  365. return this.y % TILE_SIZE;
  366. },
  367. move : function() {
  368. if( !Game.running )
  369. return;
  370. this.x += this.directionX * this.speed * ACTOR_SPEED;
  371. this.y += this.directionY * this.speed * ACTOR_SPEED;
  372. this.node.x(this.x );
  373. this.node.y(this.y );
  374. },
  375. up : function( force ) {
  376. if( force || this.direction != UP ) {
  377. this.directionX = 0;
  378. this.directionY = -1;
  379. this.direction = UP;
  380. this.node.setAnimation(this.animations["up"]);
  381. this.node.flipv(false);
  382. this.node.fliph(false);
  383. this.center();
  384. }
  385. },
  386. down : function( force ) {
  387. if( force || this.direction != DOWN ) {
  388. this.directionX = 0;
  389. this.directionY = 1;
  390. this.direction = DOWN;
  391. if( this.animations["down"] ) {
  392. this.node.setAnimation(this.animations["down"]);
  393. this.node.fliph( false );
  394. } else {
  395. this.node.setAnimation(this.animations["up"]);
  396. this.node.flipv( true );
  397. this.node.fliph( false );
  398. }
  399. this.center();
  400. }
  401. },
  402. left : function( force ) {
  403. if( force || this.direction != LEFT ) {
  404. this.directionX = -1;
  405. this.directionY = 0;
  406. this.direction = LEFT;
  407. this.node.flipv( false );
  408. if( this.animations["left"] ) {
  409. this.node.setAnimation(this.animations["left"]);
  410. } else {
  411. this.node.setAnimation(this.animations["right"]);
  412. this.node.fliph( true );
  413. }
  414. this.center();
  415. }
  416. },
  417. right : function( force ) {
  418. if( force || this.direction != RIGHT ) {
  419. this.directionX = 1;
  420. this.directionY = 0;
  421. this.direction = RIGHT;
  422. this.node.setAnimation(this.animations["right"]);
  423. this.node.fliph( false );
  424. this.node.flipv( false );
  425. this.center();
  426. }
  427. },
  428. canLeft : function() {
  429. return Game.maze.structure[this.getTileX() + this.getTileY() * WIDTH_TILE_COUNT - 1] <= 0;
  430. },
  431. canRight : function() {
  432. return Game.maze.structure[this.getTileX() + this.getTileY() * WIDTH_TILE_COUNT + 1] <= 0;
  433. },
  434. canUp : function() {
  435. return Game.maze.structure[this.getTileX() + (this.getTileY() - 1 ) * WIDTH_TILE_COUNT ] <= 0;
  436. },
  437. canDown : function() {
  438. return Game.maze.structure[this.getTileX() + (this.getTileY() + 1 ) * WIDTH_TILE_COUNT ] <= 0;
  439. },
  440. isNearMiddleTile : function() {
  441. return Math.abs( HALF_TILE_SIZE - this.getInsideTileX() ) < 4 && Math.abs( HALF_TILE_SIZE - this.getInsideTileY() ) < 4;
  442. },
  443. center : function() {
  444. this.x = this.getTileX() * TILE_SIZE + HALF_TILE_SIZE;
  445. this.y = this.getTileY() * TILE_SIZE + HALF_TILE_SIZE;
  446. },
  447. isInTunnel : function() {
  448. var tile = this.getTile();
  449. return $.inArray(tile, Game.maze.tunnel) > -1;
  450. }
  451. };
  452. /*********************************************/
  453. /****************** PACMAN *******************/
  454. /*********************************************/
  455. function Pacman() {
  456. this.animations = {
  457. "right": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 3, offsety: 16, delta: ACTOR_SIZE, rate: 120, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  458. "up": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 3, offsetx: 64, offsety: 16, delta: ACTOR_SIZE, rate: 120, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  459. "die": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 7, offsety: 208, delta: ACTOR_SIZE, rate: 120, type: $.gameQuery.ANIMATION_HORIZONTAL | $.gameQuery.ANIMATION_ONCE | $.gameQuery.ANIMATION_CALLBACK }),
  460. "die2": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 4, offsety: 240, delta: ACTOR_SIZE, rate: 120, type: $.gameQuery.ANIMATION_HORIZONTAL | $.gameQuery.ANIMATION_ONCE })
  461. }
  462. };
  463. Pacman.prototype = {
  464. x : Game.PACMAN_START_X,
  465. y : Game.PACMAN_START_Y,
  466. speed : null,
  467. directionX : 0,
  468. directionY : 0,
  469. lastEatenGhost : null,
  470. stop : false,
  471. previousTile : null,
  472. init : function() {
  473. this.x = Game.PACMAN_START_X;
  474. this.y = Game.PACMAN_START_Y;
  475. this.speed = Game.levelData.pacman.speed;
  476. this.right(true);
  477. this.left(true);
  478. this.node.x(this.x);
  479. this.node.y(this.y);
  480. },
  481. left : function() {
  482. if( this.direction != LEFT && this.canLeft() ) {
  483. this.stop = false;
  484. this._super("left", arguments);
  485. }
  486. },
  487. right : function() {
  488. if( this.direction != RIGHT && this.canRight() ) {
  489. this.stop = false;
  490. this._super("right", arguments);
  491. }
  492. },
  493. up : function() {
  494. if( this.direction != UP && this.canUp() ) {
  495. this.stop = false;
  496. this._super("up", arguments);
  497. }
  498. },
  499. down : function() {
  500. if( this.direction != DOWN && this.canDown() ) {
  501. this.stop = false;
  502. this._super("down", arguments);
  503. }
  504. },
  505. move : function() {
  506. if( !this.stop ) {
  507. this.previousTile = {x: this.getTileX(), y: this.getTileY()};
  508. this._super("move", arguments);
  509. var currentTile = {x: this.getTileX(), y: this.getTileY()};
  510. if( this.previousTile.x !== currentTile.x || this.previousTile.y !== currentTile.y ) {
  511. var id = this.getTile();
  512. if( Game.dots[ id ] )
  513. this.eatDot( id );
  514. if( id == Game.maze.bonus_target )
  515. this.eatBonus();
  516. this.eatGhosts();
  517. }
  518. var inTunnel = this.isInTunnel();
  519. if( this.x < 0 )
  520. this.x += PLAYGROUND_WIDTH;
  521. if( this.x > PLAYGROUND_WIDTH )
  522. this.x -= PLAYGROUND_WIDTH;
  523. switch( this.direction ) {
  524. case LEFT :
  525. if( !inTunnel && !this.canLeft() )
  526. this.stop = true;
  527. break;
  528. case RIGHT :
  529. if( !inTunnel && !this.canRight() )
  530. this.stop = true;
  531. break;
  532. case UP :
  533. if( !this.canUp() )
  534. this.stop = true;
  535. break;
  536. case DOWN :
  537. if( !this.canDown() )
  538. this.stop = true;
  539. break;
  540. }
  541. }
  542. },
  543. eatDot : function(id) {
  544. Game.eat(Game.dots[id]);
  545. $('.actor').trigger(Game.GHOST_EVENT_DOT_EATEN);
  546. if( Game.dots[id] === "bigDot" ) {
  547. $.each(Game.ghosts, function(index, ghost ) {
  548. if( ghost.state != Game.GHOST_STATE_IN_JAIL && ghost.state != Game.GHOST_STATE_EATEN )
  549. ghost.state_to(Game.GHOST_STATE_FRIGHTENED)
  550. });
  551. Game.timer.pause();
  552. if( Game.frightTimer )
  553. clearTimeout( Game.frightTimer );
  554. Game.frightTimer = setTimeout( 'Game.nearEndFright();', Game.levelData.frightTime * 1000 - 160 * 4 * Game.levelData.frightFlashesCount);
  555. }
  556. Game.dots[id] = null;
  557. $("#" + id ).addClass("hiddenDot");
  558. },
  559. eatGhosts : function() {
  560. var tile = this.getTile();
  561. $.each(Game.ghosts, function(index, ghost ) {
  562. if( tile == ghost.getTile() ) {
  563. Game.pacman.eatGhost( ghost );
  564. }
  565. });
  566. },
  567. eatGhost : function( ghost ) {
  568. if( ghost.state == Game.GHOST_STATE_EATEN ) {
  569. // console.log( ghost.id + " already eaten" );
  570. return;
  571. }
  572. if( ghost.state != Game.GHOST_STATE_FRIGHTENED && ghost.state != Game.GHOST_STATE_FRIGHTENED_BLINK ) {
  573. Game.die();
  574. } else if( Game.pacman.lastEatenGhost !== ghost.id ){
  575. ghost.state_to(Game.GHOST_STATE_EATEN);
  576. // console.log( "Eating " + ghost.id + " " + ghost.state );
  577. Game.eatGhost(ghost);
  578. }
  579. },
  580. eatBonus : function() {
  581. if( !$("#" + Game.maze.bonus_target).hasClass( Game.levelData.bonus.type) && Game.bonusTimer == null )
  582. return;
  583. Sound.play("fruit);
  584. eatenBonus.push(Game.levelData.bonus.type);
  585. Game.score += Game.levelData.bonus.points;
  586. // console.log( "Eating bonus " + Game.levelData.bonus.points + " " + Game.score );
  587. SCOREBOARD.add( Game.levelData.bonus.points );
  588. Game.hideBonus();
  589. },
  590. die : function() {
  591. Sound.play("dies");
  592. this.node.setAnimation(this.animations["die"], function(node) {
  593. Game.pacman.node.setAnimation(Game.pacman.animations["die2"]);
  594. });
  595. }
  596. };
  597. // Overriding Actor.methods() method
  598. heriter(Pacman.prototype, Actor.prototype);
  599. function Ghost(id, ghostIndex, start, scatterTarget, personnalTarget, dotsLimits, state ) {
  600. this.animations = {
  601. "normal_up": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 2, offsety: 48 + ghostIndex * 32, delta: ACTOR_SIZE, rate: 160, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  602. "normal_right": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 2, offsetx: 128, offsety: 48 + ghostIndex * 32, delta: ACTOR_SIZE, rate: 160, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  603. "normal_down": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 2, offsetx: 64, offsety: 48 + ghostIndex * 32, delta: ACTOR_SIZE, rate: 160, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  604. "frightened": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 2, offsetx: 0, offsety: 176, delta: ACTOR_SIZE, rate: 160, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  605. "frightened_blink": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 4, offsetx: 0, offsety: 176, delta: ACTOR_SIZE, rate: 160, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  606. "eaten_up": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 1, offsetx: 128, offsety: 176, delta: ACTOR_SIZE, rate: 160, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  607. "eaten_down": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 1, offsetx: 160, offsety: 176, delta: ACTOR_SIZE, rate: 160, type: $.gameQuery.ANIMATION_HORIZONTAL }),
  608. "eaten_right": new $.gameQuery.Animation({imageURL: "img/sprite.png", numberOfFrame: 1, offsetx: 192, offsety: 176, delta: ACTOR_SIZE, rate: 160, type: $.gameQuery.ANIMATION_HORIZONTAL })
  609. }
  610. this.animations["up"] = this.animations["normal_up"];
  611. this.animations["down"] = this.animations["normal_down"];
  612. this.animations["right"] = this.animations["normal_right"];
  613. this.id = id;
  614. this.scatterTarget = scatterTarget;
  615. this.personnalTarget = personnalTarget;
  616. this.x = start.x;
  617. this.y = start.y;
  618. this.startingTileX = start.x;
  619. this.startingTileY = start.y;
  620. this.state = state;
  621. this.dotsLimits = dotsLimits;
  622. this.prey = Game.pacman;
  623. };
  624. Ghost.prototype = {
  625. id : null,
  626. startingTileX : 0,
  627. startingTileY : 0,
  628. initialSpeed : 0,
  629. speed : 0,
  630. directionX : 0,
  631. directionY : 0,
  632. state: null,
  633. scatterTarget : null,
  634. lastDirectionTile : null,
  635. prey : null,
  636. dotsCounter : 0,
  637. dotsLimits : [],
  638. init : function() {
  639. this.dotsCounter = 0;
  640. this.speed = 0;
  641. this.x = this.startingTileX;
  642. this.y = this.startingTileY;
  643. this.right(true);
  644. this.left(true);
  645. this.state = Game.GHOST_STATE_IN_JAIL;
  646. this.node.x(this.x);
  647. this.node.y(this.y);
  648. },
  649. target : function() {
  650. switch( this.state ) {
  651. case Game.GHOST_STATE_CHASE :
  652. return this.personnalTarget();
  653. case Game.GHOST_STATE_SCATTER :
  654. return this.scatterTarget;
  655. case Game.GHOST_STATE_FRIGHTENED :
  656. var currentTile = {x: this.getTileX(), y: this.getTileY()};
  657. var targets = new Array();
  658. if( this.canUp() && this.direction != DOWN )
  659. targets.push( {x:currentTile.x, y:currentTile.y - 1} );
  660. if( this.canDown() && this.direction != UP )
  661. targets.push( {x:currentTile.x, y:currentTile.y + 1} );
  662. if( this.canLeft() && this.direction != RIGHT )
  663. targets.push( {x:currentTile.x - 1, y:currentTile.y} );
  664. if( this.canRight() && this.direction != LEFT )
  665. targets.push( {x:currentTile.x + 1, y:currentTile.y} );
  666. return targets[ parseInt(Math.random() * targets.length ) ];
  667. case Game.GHOST_STATE_IN_JAIL :
  668. case Game.GHOST_STATE_EATEN :
  669. return {x: 13, y: 14};
  670. }
  671. },
  672. loadBindings : function() {
  673. this.node.bind(Game.GHOST_EVENT_CHASE, {ghost: this}, function(evt) {
  674. var ghost = evt.data.ghost;
  675. if( ghost.state != Game.GHOST_STATE_IN_JAIL && ghost.state != Game.GHOST_STATE_EATEN )
  676. ghost.state_to(Game.GHOST_STATE_CHASE);
  677. });
  678. this.node.bind(Game.GHOST_EVENT_SCATTER, {ghost: this}, function(evt) {
  679. var ghost = evt.data.ghost;
  680. if( ghost.state != Game.GHOST_STATE_IN_JAIL && ghost.state != Game.GHOST_STATE_EATEN )
  681. ghost.state_to(Game.GHOST_STATE_SCATTER);
  682. });
  683. this.node.bind(Game.GHOST_EVENT_DOT_EATEN, {ghost: this}, function(evt) {
  684. var ghost = evt.data.ghost;
  685. if( ghost.state == Game.GHOST_STATE_IN_JAIL && ghost.dotsCounter++ >= ghost.dotsLimits[Math.min(Game.level, ghost.dotsLimits.length - 1)] ) {
  686. ghost.speed = ghost.initialSpeed;
  687. ghost.state_to(Game.mode);
  688. }
  689. });
  690. },
  691. personnalTarget : function() {
  692. },
  693. state_to : function( state ) {
  694. var up;
  695. var down;
  696. var right;
  697. var reverse = this.state != Game.GHOST_STATE_FRIGHTENED && this.state != Game.GHOST_STATE_IN_JAIL; // previous state
  698. this.state = state;
  699. switch( state ) {
  700. case Game.GHOST_STATE_CHASE :
  701. this.speed = Game.levelData.ghost.speed;
  702. case Game.GHOST_STATE_SCATTER :
  703. this.speed = Game.levelData.ghost.speed;
  704. case Game.GHOST_STATE_IN_JAIL :
  705. up = this.animations["normal_up"];
  706. down = this.animations["normal_down"];
  707. right = this.animations["normal_right"];
  708. break;
  709. case Game.GHOST_STATE_FRIGHTENED :
  710. up = down = right = this.animations["frightened"];
  711. this.speed = Game.levelData.ghost.frightSpeed;
  712. break;
  713. case Game.GHOST_STATE_FRIGHTENED_BLINK :
  714. up = down = right = this.animations["frightened_blink"];
  715. this.state = Game.GHOST_STATE_FRIGHTENED;
  716. break;
  717. case Game.GHOST_STATE_EATEN :
  718. up = this.animations["eaten_up"];
  719. down = this.animations["eaten_down"];
  720. right = this.animations["eaten_right"];
  721. this.speed = 1;
  722. break;
  723. }
  724. this.animations["up"] = up;
  725. this.animations["down"] = down;
  726. this.animations["right"] = right;
  727. if( reverse )
  728. switch( this.direction ) {
  729. case UP:
  730. this.direction = DOWN;
  731. break;
  732. case LEFT:
  733. this.direction = RIGHT;
  734. break;
  735. case DOWN:
  736. this.direction = UP;
  737. break;
  738. case RIGHT:
  739. this.direction = LEFT;
  740. break;
  741. }
  742. var inTunnel = this.isInTunnel();
  743. var distances = [
  744. {direction: UP, distance: this.canUp() && this.direction != DOWN ? 1 : INFINITY},
  745. {direction: LEFT, distance: (inTunnel && this.direction == LEFT ) || (this.canLeft() && this.direction != RIGHT) ? 1 : INFINITY},
  746. {direction: DOWN, distance: this.canDown() && this.direction != UP ? 1 : INFINITY},
  747. {direction: RIGHT, distance: (inTunnel && this.direction == RIGHT ) || (this.canRight() && this.direction != LEFT) ? 1 : INFINITY},
  748. ];
  749. distances.sort( function(a, b) {
  750. if( a.distance == b.distance )
  751. return a.direction - b.direction;
  752. return a.distance - b.distance;
  753. })
  754. var selected = distances[0];
  755. switch( selected.direction ) {
  756. case UP:
  757. this.up(true);
  758. break;
  759. case LEFT:
  760. this.left(true);
  761. break;
  762. case DOWN:
  763. this.down(true);
  764. break;
  765. case RIGHT:
  766. this.right(true);
  767. break;
  768. }
  769. },
  770. canUp : function() {
  771. switch( this.getTile() ) {
  772. case 404:
  773. case 407:
  774. case 684:
  775. case 687:
  776. return false;
  777. case 461:
  778. case 462:
  779. return true;
  780. default:
  781. return Game.maze.structure[ this.getTileX() + (this.getTileY() - 1 ) * WIDTH_TILE_COUNT ] <= 0;
  782. }
  783. },
  784. canDown : function() {
  785. switch( this.getTile() ) {
  786. case 405:
  787. case 406:
  788. return false;
  789. default:
  790. return Game.maze.structure[ this.getTileX() + (this.getTileY() + 1 ) * WIDTH_TILE_COUNT ] <= 0;
  791. }
  792. },
  793. move : function() {
  794. this._super("move", arguments);
  795. var currentTile = {x: this.getTileX(), y: this.getTileY()};
  796. var id = this.getTile();;
  797. if( this.lastDirectionTile != id && this.isNearMiddleTile()) {
  798. this.lastDirectionTile = id;
  799. this.eaten();
  800. var distances = null;
  801. var target = this.target();
  802. if( this.state == Game.GHOST_STATE_EATEN && id == Game.maze.ghost_frightened_target ) {
  803. this.state_to(Game.mode);
  804. }
  805. var inTunnel = this.isInTunnel();
  806. if( inTunnel )
  807. this.speed = Game.levelData.ghost.tunnelSpeed;
  808. else if( this.state != Game.GHOST_STATE_IN_JAIL )
  809. this.speed = this.state == Game.GHOST_STATE_FRIGHTENED ? Game.levelData.ghost.frightSpeed : Game.levelData.ghost.speed;
  810. if( this.x < 0 )
  811. this.x += PLAYGROUND_WIDTH;
  812. if( this.x > PLAYGROUND_WIDTH )
  813. this.x -= PLAYGROUND_WIDTH;
  814. if( Game.maze.choice_tiles.indexOf( id ) != -1 ) {
  815. distances = [
  816. {direction: UP, distance: this.canUp() && this.direction != DOWN ? distance({x:currentTile.x, y:currentTile.y - 1}, target ) : INFINITY},
  817. {direction: LEFT, distance: this.canLeft() && this.direction != RIGHT ? distance( {x:currentTile.x - 1, y:currentTile.y }, target ) : INFINITY},
  818. {direction: DOWN, distance: this.canDown() && this.direction != UP ? distance({x:currentTile.x, y:currentTile.y + 1}, target ) : INFINITY},
  819. {direction: RIGHT, distance: this.canRight() && this.direction != LEFT ? distance({x:currentTile.x + 1, y:currentTile.y}, target ) : INFINITY},
  820. ];
  821. } else {
  822. distances = [
  823. {direction: UP, distance: this.canUp() && this.direction != DOWN ? 1 : INFINITY},
  824. {direction: LEFT, distance: (inTunnel && this.direction == LEFT ) || (this.canLeft() && this.direction != RIGHT) ? 1 : INFINITY},
  825. {direction: DOWN, distance: this.canDown() && this.direction != UP ? 1 : INFINITY},
  826. {direction: RIGHT, distance: (inTunnel && this.direction == RIGHT ) || (this.canRight() && this.direction != LEFT) ? 1 : INFINITY},
  827. ];
  828. }
  829. distances.sort( function(a, b) {
  830. if( a.distance == b.distance )
  831. return a.direction - b.direction;
  832. return a.distance - b.distance;
  833. })
  834. var selected = distances[0];
  835. switch( selected.direction ) {
  836. case LEFT :
  837. if( this.direction != LEFT )
  838. this.left();
  839. break;
  840. case RIGHT :
  841. if( this.direction != RIGHT )
  842. this.right();
  843. break;
  844. case UP :
  845. if( this.direction != UP )
  846. this.up();
  847. break;
  848. case DOWN :
  849. if( this.direction != DOWN )
  850. this.down();
  851. break;
  852. }
  853. }
  854. var inTunnel = this.isInTunnel();
  855. if( this.x < 0 )
  856. this.x += PLAYGROUND_WIDTH;
  857. if( this.x > PLAYGROUND_WIDTH )
  858. this.x -= PLAYGROUND_WIDTH;
  859. },
  860. eaten : function(target) {
  861. if( typeof target === "undefined" )
  862. target = this;
  863. if( target.getTile() == Game.pacman.getTile() ) {
  864. // console.log(" Eaten from ghost" );
  865. Game.pacman.eatGhost(target);
  866. // if( target.state != Game.GHOST_STATE_FRIGHTENED && target.state != Game.GHOST_STATE_EATEN ) {
  867. // Game.die();
  868. // } else {
  869. // target.state_to(Game.GHOST_STATE_EATEN);
  870. // Game.eatGhost(this);
  871. // }
  872. }
  873. }
  874. };
  875. heriter(Ghost.prototype, Actor.prototype);