Source: RouteEngineA1.js

  1. if (!RouteEngine) {
  2. /**
  3. * @namespace RouteEngine
  4. * @prop {Array} games Holds all of the {@link Game} Objects that are in the document
  5. * @prop {Array} images Holds all of the images that have been loaded
  6. * @prop {Array} ImagesLoaders Holds all of the {@link ImageLoadStack} Objects that are in the document.
  7. * @prop {Image} noImage This is a placeholder image that is used when errors occur during image loading.
  8. */
  9. var RouteEngine = {};
  10. RouteEngine.games = [];
  11. RouteEngine.images = [];
  12. RouteEngine.ImageLoaders = [];
  13. RouteEngine.noImage = loadImage("none.png", 20, 20);
  14. RouteEngine.GameClicked = function(event) {
  15. RouteEngine.games[event.target.dataset.gameId].clicked(event);
  16. };
  17. RouteEngine.GameDown = function(event) {
  18. RouteEngine.games[event.target.dataset.gameId].down(event);
  19. };
  20. RouteEngine.GameUp = function(event) {
  21. RouteEngine.games[event.target.dataset.gameId].up(event);
  22. };
  23. RouteEngine.GameMouseMove = function(event) {
  24. RouteEngine.games[event.target.dataset.gameId].mousemove(event);
  25. };
  26. RouteEngine.GameMouseOut = function(event) {
  27. RouteEngine.games[event.target.dataset.gameId].mouseout(event);
  28. };
  29. RouteEngine.IdImageLoaded = function(e) {
  30. //All image loaders assign their image objects with "ImageId" + loader's id + ":" + image id.
  31. //Redirect the image load event to the loader event via id.
  32. var id = e.target.id.substring(7);
  33. RouteEngine.ImageLoaders[id.split(":")[0]].imageLoaded(id.split(":")[1]);
  34. };
  35. }
  36. /**
  37. * @class Game
  38. * @constructor
  39. * @param {Object} element An HTML5 canvas (use document.getElementById)
  40. * @prop {Integer} id
  41. * @prop {Object} c - The HTML5 canvas element that this game is being drawn onto
  42. * @prop {Object} can - The 2D rendering context the game is using
  43. * @prop {Integer} cW - The width of the canvas element
  44. * @prop {Integer} cH - The height of the canvas element
  45. * @prop {Array} images An array of images that are being used by the game
  46. * @prop {Array} sprites The array of sprites that are in the game
  47. * @prop {Boolean} mouseDown Is set to true if the user's left mouse click is currently down
  48. * @prop {Background} background The current {@link Background} that the game is using
  49. */
  50. function Game(element) {
  51. this.id = RouteEngine.games.length;
  52. RouteEngine.games.push(this);
  53. this.c = element;
  54. this.can = this.c.getContext("2d");
  55. this.cW = this.c.width;
  56. this.cH = this.c.height;
  57. this.self = this;
  58. this.images = [];
  59. this.sprites = [];
  60. this.mouseDown = false;
  61. this.background = new Background("clear");
  62. /**
  63. * @memberof Game
  64. * @abstract
  65. * @method onclick
  66. * @param {Object} e - An Event Object of the triggering event
  67. * @description Meant to be overridden, called when the user clicks within the game canvas.
  68. */
  69. this.onclick = function(e) {};
  70. /**
  71. * @memberof Game
  72. * @abstract
  73. * @method onmouseup
  74. * @param {Object} e - An Event Object of the triggering event
  75. * @description Meant to be overridden, called when the user lets go of the left mouse click.
  76. */
  77. this.onmouseup = function(e) {};
  78. /**
  79. * @memberof Game
  80. * @abstract
  81. * @method onmousedown
  82. * @param {Object} e - An Event Object of the triggering event
  83. * @description Meant to be overridden, called when the user presses down the left mouse button.
  84. */
  85. this.onmousedown = function(e) {};
  86. /**
  87. * @memberof Game
  88. * @abstract
  89. * @method onmouseup
  90. * @param {Object} e - An Event Object of the triggering event
  91. * @description Meant to be overridden, called when the user moves the mouse across the game canvas
  92. */
  93. this.onmousemove = function(e) {};
  94. /**
  95. * @memberof Game
  96. * @abstract
  97. * @method onstart
  98. * @description Meant to be overridden, called the game loop is started.
  99. */
  100. this.onstart = function() {};
  101. /**
  102. * @memberof Game
  103. * @abstract
  104. * @method onstart
  105. * @description Meant to be overridden, called the game is loaded. CURRENTLY NOT WORKING
  106. */
  107. this.onload = function() {};
  108. /**
  109. * @memberof Game
  110. * @abstract
  111. * @method onstart
  112. * @description Meant to be overridden, called when the user moves the mouse out of the game canvas.
  113. */
  114. this.onmouseout = function(e) {};
  115. this.setSize = function (w, h) {
  116. this.c.setAttribute("width", w + "px");
  117. this.c.setAttribute("height", h + "px");
  118. this.cW = this.c.width;
  119. this.cH = this.c.height;
  120. };
  121. this.clicked = function(event) {
  122. this.onclick();
  123. };
  124. this.mousemove = function(event) {
  125. this.getPosition(event);
  126. this.onmousemove(event);
  127. };
  128. this.down = function(event) {
  129. this.mouseDown = true;
  130. this.onmousedown(event);
  131. };
  132. this.up = function(event) {
  133. this.mouseDown = false;
  134. this.onmouseup(event);
  135. };
  136. this.mouseout = function(event) {
  137. this.self.mouseDown = false;
  138. this.self.mouseX = -1;
  139. this.self.mouseY = -1;
  140. this.self.onmouseout();
  141. };
  142. this.c.dataset.gameId = this.id;
  143. this.c.addEventListener("click", function(event) {
  144. RouteEngine.GameClicked(event);
  145. });
  146. this.c.addEventListener("mousedown", function(event) {
  147. RouteEngine.GameDown(event);
  148. });
  149. this.c.addEventListener("mouseup", function(event) {
  150. RouteEngine.GameUp(event);
  151. });
  152. this.c.addEventListener("mousemove", function(event) {
  153. RouteEngine.GameMouseMove(event);
  154. });
  155. this.c.addEventListener("mouseout", function(event) {
  156. RouteEngine.GameMouseOut(event);
  157. });
  158. /**
  159. * @method render
  160. * @memberof Game
  161. * @description - Renders the background and all sprites onto the game's canvas
  162. */
  163. this.render = function() {
  164. this.background.draw(this.self);
  165. for (var s = 0; s < this.sprites.length; s++) {
  166. this.sprites[s].draw(this.self);
  167. }
  168. };
  169. /**
  170. * @method clear
  171. * @memberof Game
  172. * @description - Clears the entire game's canvas
  173. */
  174. this.clear = function() {
  175. this.can.clearRect(0, 0, this.cW, this.cH);
  176. };
  177. /**
  178. * @method getPosition
  179. * @memberof Game
  180. * @callback
  181. * @description - Used by event handlers to update the location of the mouse pointer. You really should have no need to ever touch this.
  182. */
  183. this.getPosition = function(event) {
  184. var totalOffsetX = 0;
  185. var totalOffsetY = 0;
  186. var canvasX = 0;
  187. var canvasY = 0;
  188. var currentElement = event.target;
  189. do {
  190. totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
  191. totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
  192. }
  193. while (currentElement = currentElement.offsetParent)
  194. canvasX = event.pageX - totalOffsetX;
  195. canvasY = event.pageY - totalOffsetY;
  196. this.mouseX = canvasX;
  197. this.mouseY = canvasY;
  198. };
  199. }
  200. /**
  201. * @class Background
  202. * @constructor
  203. * @param {String} type - The type of background that this Background object will be. Acceptable values: "color", "image" or "img", "clear"
  204. * @param {Object} value - This parameter is either a HTML color ("#00F" or "#0000FF") or an image object. Depends upon the type you specified. If the type is "clear", then this parameter is ignored
  205. * @prop {String} color - The current color of this Background object if it is the "color" type
  206. * @prop {Object} img - The current background image if this object is the "image" or "img" type.
  207. * @prop {String} type - The type of background this Background object currently is. Acceptable values: "color", "image" or "img", "clear"
  208. * @description This Background class is used as a simple way to set backgrounds for games. Background Objects can have three different types: "color", "image" or "img", "clear" If the background type is "color", then the background will render as the solid color as specified by the value parameter you pass in. If the background type is "img" or "image" (there is no difference), then the background will render as the image that you pass in via the value parameter. If the background type is "clear", then the background will not render at all.
  209. */
  210. function Background(type, value) {
  211. this.color = "#FFF";
  212. this.img = RouteEngine.noImage;
  213. this.type = type;
  214. switch (this.type) {
  215. case "color":
  216. this.color = value;
  217. break;
  218. case "image":
  219. case "img":
  220. this.img = value;
  221. break;
  222. case "clear":
  223. //Nothing
  224. break;
  225. default:
  226. //No Type! Default to clear
  227. this.type = "clear";
  228. break;
  229. }
  230. /**
  231. * @method draw
  232. * @memberof Background
  233. * @param {Game} game - The {@link Game} that we are rendering this background onto
  234. * @description - Renders the background onto the game canvas according to the type of background this is.
  235. */
  236. this.draw = function(game) {
  237. switch (this.type) {
  238. case "color":
  239. game.can.fillStyle = this.color;
  240. game.can.fillRect(0, 0, game.cW, game.cH);
  241. break;
  242. case "image":
  243. case "img":
  244. game.can.drawImage(this.img, 0, 0);
  245. break;
  246. case "clear":
  247. game.clear();
  248. break;
  249. default:
  250. console.log("Impossible really, how can this happen?");
  251. break;
  252. }
  253. };
  254. }
  255. /**
  256. * @class Sound
  257. * @description Basically a wrapper object for loading and playing sounds.
  258. * @constructor
  259. * @param {String} src The uri for the audiofile to be loaded
  260. * @prop {String} src The uri/filepath for the sound
  261. * @prop {Audio} sound
  262. */
  263. function Sound(src) {
  264. this.src = src;
  265. this.sound = new Audio();
  266. this.sound.src = this.src;
  267. /**
  268. * @method onload
  269. * @callback
  270. * @abstract
  271. * @memberof Sound
  272. * @description This function is meant to be extended, it is called once the sound loads.
  273. */
  274. this.onload = function() {};
  275. /**
  276. * @method onplay
  277. * @callback
  278. * @abstract
  279. * @memberof Sound
  280. * @description This function is meant to be extended, it is called whenever the sound plays.
  281. */
  282. this.onplay = function() {};
  283. /**
  284. * @memberof Sound
  285. * @method load
  286. * @description Loads the sound.
  287. */
  288. this.load = function() {
  289. this.sound.load();
  290. };
  291. this.sound.onload = function() {
  292. this.onload();
  293. };
  294. /**
  295. * @method play
  296. * @public
  297. * @memberof Sound
  298. * @description Will load and play the sound.
  299. */
  300. this.play = function() {
  301. this.sound.load();
  302. this.sound.play();
  303. this.onplay();
  304. };
  305. }
  306. /**
  307. * @class TextSprite
  308. * @description A sprite that is a string of text. It can change position, visibility, color, font, alignment, and the actual text. Also includes hitbox detection.
  309. * @param {String} text - The String that will be rendered
  310. * @param {Number} x - The x position of this sprite
  311. * @param {Number} y - The y position of this sprite
  312. * @prop {String} align - Which direction this text is aligned to. Acceptable values: "left", "start", "center", "right", "end". Default: "left"
  313. * @prop {String} text - The String that will be rendered
  314. * @prop {Number} x - The x position of this sprite
  315. * @prop {Number} y - The y position of this sprite
  316. * @prop {Boolean} visible - A boolean value of whether of not this sprite is visible/will be rendered. True means it will be rendered
  317. * @prop {String} color - The color of the text
  318. * @prop {String} font - The size and font of the text. Default value: "30px Arial"
  319. */
  320. function TextSprite(text, x, y) {
  321. this.align = "left"; //left, start, center, right, end
  322. this.text = text;
  323. this.font = "30px Arial";
  324. this.x = x;
  325. this.y = y;
  326. this.visible = true;
  327. this.color = "#222";
  328. /**
  329. * @memberof TextSprite
  330. * @method draw
  331. * @param {Game} game - The game this sprite will be rendered onto
  332. * @description Renders the sprite onto the give game canvas.
  333. */
  334. this.draw = function(game) {
  335. if (this.visible) {
  336. game.can.font = this.font;
  337. game.can.fillStyle = this.color;
  338. game.can.textAlign = this.align;
  339. game.can.fillText(this.text, this.x, this.y);
  340. }
  341. };
  342. /**
  343. * @memberof TextSprite
  344. * @method hide
  345. * @description Makes this sprite not visible
  346. */
  347. this.hide = function() {
  348. this.visible = false;
  349. };
  350. /**
  351. * @memberof TextSprite
  352. * @method hide
  353. * @description Makes this sprite visible
  354. */
  355. this.show = function() {
  356. this.visible = true;
  357. };
  358. this.toggle = function() {
  359. if (this.visible) {
  360. this.visible = false;
  361. } else {
  362. this.visible = true;
  363. }
  364. };
  365. /**
  366. * @memberof TextSprite
  367. * @method inHit
  368. * @param {Number} cx - The x coordinate
  369. * @param {Number} cy - The y coordinate
  370. * @description Checks to see if the given coordinates are within the sprite's "hit box"
  371. * @returns {Boolean} True is the coordinates are within the "hit box", False otherwise.
  372. */
  373. this.inHit = function(cx, cy) {
  374. var c = document.createElement("canvas").getContext("2d");
  375. c.font = this.font;
  376. var metrics = c.measureText(this.text);
  377. switch (this.align) {
  378. case "left":
  379. case "start":
  380. if (this.x < cx && this.x + metrics.width > cx && cy < this.y && this.y - parseInt(this.font.split("px")[0]) < cy) {
  381. return true;
  382. } else {
  383. return false;
  384. }
  385. break;
  386. case "center":
  387. if (this.x < cx && this.x + (metrics.width / 2) > cx && cy < this.y && this.y - (parseInt(this.font.split("px")[0]) / 2) < cy) {
  388. return true;
  389. } else {
  390. return false;
  391. }
  392. break;
  393. case "right":
  394. case "end":
  395. if (this.x > cx && this.x - metrics.width < cx && cy < this.y && this.y - parseInt(this.font.split("px")[0]) < cy) {
  396. return true;
  397. } else {
  398. return false;
  399. }
  400. break;
  401. }
  402. };
  403. }
  404. /**
  405. * @class ImageSprite
  406. * @constructor
  407. * @description A sprite that is simply an image. The sprite can change between images, visibility, position, and rotation. Also includes basic hitbox detection.
  408. * @param {Image} img The starting images that this sprite will render as. Will be in index 0 of the images array.
  409. * @param {Number} x The starting x-position of this sprite
  410. * @param {Number} y The starting y-position of this sprite
  411. * @prop {Number} x The current x-position of this sprite
  412. * @prop {Number} y The current y-position of this sprite
  413. * @prop {Number} rotation - The number of degrees this sprite is currently rotated. Default: 0
  414. * @prop {Boolean} visible - Whether or not this sprite is visible
  415. * @prop {Array} images - An Array of Images that this sprite can switch between
  416. * @prop {Image} img - The current image the sprite will render as
  417. *
  418. *
  419. */
  420. function ImageSprite(img, x, y) {
  421. this.img = img;
  422. this.x = x;
  423. this.y = y;
  424. this.images = [];
  425. this.visible = true;
  426. this.rotation = 0;
  427. this.images[0] = img;
  428. /**
  429. * @memberof ImageSprite
  430. * @method draw
  431. * @param {Game} game - The game this sprite will be rendered onto
  432. * @description Renders the sprite onto the give game canvas.
  433. */
  434. this.draw = function(game) {
  435. if (this.visible) {
  436. game.can.save();
  437. game.can.translate(this.img.width + this.x, this.img.height + this.y);
  438. game.can.rotate((Math.PI / 180) * this.rotation);
  439. game.can.drawImage(this.img, -1 * this.img.width / 2, -1 * this.img.height / 2);
  440. game.can.restore();
  441. }
  442. };
  443. /**
  444. * @memberof ImageSprite
  445. * @method hide
  446. * @description Makes this sprite not visible
  447. */
  448. this.hide = function() {
  449. this.visible = false;
  450. };
  451. /**
  452. * @memberof ImageSprite
  453. * @method hide
  454. * @description Makes this sprite visible
  455. */
  456. this.show = function() {
  457. this.visible = true;
  458. };
  459. this.toggle = function() {
  460. if (this.visible) {
  461. this.visible = false;
  462. } else {
  463. this.visible = true;
  464. }
  465. };
  466. /**
  467. * @memberof ImageSprite
  468. * @method inHit
  469. * @param {Number} cx - The x coordinate
  470. * @param {Number} cy - The y coordinate
  471. * @description Checks to see if the given coordinates are within the sprite's "hit box"
  472. * @returns {Boolean} True is the coordinates are within the "hit box", False otherwise.
  473. */
  474. this.inHit = function(cx, cy) {
  475. if (this.x < cx && this.x + this.img.width > cx && cy > this.y && this.y + this.img.height > cy) {
  476. return true;
  477. } else {
  478. return false;
  479. }
  480. };
  481. }
  482. /**
  483. * @class ImageLoadStack
  484. * @description An object used to load a series/collection of images all at once and give progress statistics and has an onload function once all images are loaded. Specify all images to be loaded with the addImage function and then initiate loading with the start function
  485. * @construtor
  486. * @param {Array} images - An array of all the images that this ImageLoadStack loaded
  487. * @param {Boolean} finished - Tells if all of the images are loaded or not.
  488. * @param {Boolean} started - Tells if the ImageLoadStack has started loading images yet or not. (Start loading by calling the start() function)
  489. * @param {Integer} loaded - The number of images that are loaded
  490. * @param {Integer} total - The number of images that are loaded or not loaded.
  491. * @param {Integer} id - The id of this ImageLoadStack
  492. */
  493. function ImageLoadStack() {
  494. this.images = [];
  495. this.finished = false; //Are all of the images loaded
  496. this.loaded = 0; //The number of images that are loaded
  497. this.total = 0; //The total number of images, finished or not
  498. this.started = false;
  499. this.progress = 0; //Percentage of loading done
  500. this.id = RouteEngine.ImageLoaders.length;
  501. RouteEngine.ImageLoaders.push(this);
  502. /**
  503. * @memberof ImageLoadStack
  504. * @method onload
  505. * @abstract
  506. * @callback
  507. * @description Meant to be overrided, called once all of the images have loaded.
  508. */
  509. this.onload = function() {};
  510. /**
  511. * @method start
  512. * @memberof ImageLoadStack
  513. * @description Call this function once all images are in the que to start loading them. Will set this.started to True.
  514. *
  515. */
  516. this.start = function() {
  517. this.started = true;
  518. if (this.total == this.loaded) {
  519. this.finished = true;
  520. this.onload();
  521. }
  522. };
  523. /**
  524. * @method addImage
  525. * @memberof ImageLoadStack
  526. * @param {String} src - The URI of the image to be loaded
  527. * @param {Integer} width - The width in pixels of the image
  528. * @param {Integer} height - The height in pixels of the image
  529. * @description Use this function to add images to the loading que before starting loading.
  530. * @returns {Integer} The index of the image in the images array
  531. */
  532. this.addImage = function(src, width, height) {
  533. var img = loadIdImage(src, width, height, "ImageId" + this.id + ":" + this.images.length);
  534. img.addEventListener("load", RouteEngine.IdImageLoaded);
  535. this.images.push(img);
  536. this.total++;
  537. return this.images.length - 1;
  538. };
  539. /**
  540. * @memberof ImageLoadStack
  541. * @protected
  542. * @param {Integer} imgId
  543. * @callback
  544. * @function imageLoaded
  545. * @description An event handler that is called when an image is done loading. Really no reason for developers to touch this function.
  546. */
  547. this.imageLoaded = function(imgId) {
  548. this.loaded++;
  549. this.progress = Math.round((this.loaded / this.total) * 100);
  550. if (this.total == this.loaded) {
  551. this.finished = true;
  552. this.onload();
  553. }
  554. };
  555. }
  556. /**
  557. * @global
  558. * @function loadIdImage
  559. * @param {String} src - The source URI for the image to be loaded
  560. * @param {Integer} width - The width in pixels of the image
  561. * @param {Integer} height - The height in pixels of the image
  562. * @param {Integer} id - The ID of the image. Used by {@link ImageLoadStack}s.
  563. * @returns {Image} An Image object, regardless of whether it has loaded yet or not.
  564. */
  565. function loadIdImage(src, width, height, id) {
  566. var i = new Image(width, height);
  567. i.src = src;
  568. i.id = id;
  569. return i;
  570. }
  571. /**
  572. * @global
  573. * @function loadImage
  574. * @param {String} src - The source URI for the image to be loaded
  575. * @param {Integer} width - The width in pixels of the image
  576. * @param {Integer} height - The height in pixels of the image
  577. * @returns {Image} An Image object, regardless of whether it has loaded yet or not.
  578. */
  579. function loadImage(src, width, height) {
  580. var i = new Image(width, height);
  581. i.src = src;
  582. return i;
  583. }