Source

browser/lib/Entity/Player.js

  1. import { Collision } from '../Collision';
  2. import { Renderer } from '../Renderer';
  3. import { AudioFX } from '../Audio/AudioFX';
  4. import { config } from '../../config';
  5. /**
  6. * Player entity
  7. * @class
  8. * @category Game Entities
  9. */
  10. export class Player
  11. {
  12. /**
  13. * Create a new player entity.
  14. *
  15. * @constructor
  16. * @param {Object} spawn - the player spawn coordinates
  17. * @param {number} spawn.x - the player spawn x-coordinate
  18. * @param {number} spawn.y - the player spawn y-coordinate
  19. */
  20. constructor (spawn) {
  21. /**
  22. * type - the type of entity.
  23. * @type {string}
  24. */
  25. this.type = 'player';
  26. /**
  27. * bounding - the entity's bounding behavior.
  28. * @type {string}
  29. */
  30. this.bounding = 'arc';
  31. /**
  32. * x - the entity's x coordinate.
  33. * @type {number}
  34. */
  35. this.x = spawn.x * config.cell.size;
  36. /**
  37. * y - the entity's y coordinate.
  38. * @type {number}
  39. */
  40. this.y = spawn.y * config.cell.size;
  41. /**
  42. * angle - the entity's angle.
  43. * @type {number}
  44. */
  45. this.angle = 0;
  46. /**
  47. * position - the entity's position.
  48. * @type {number}
  49. */
  50. this.position = 0;
  51. /**
  52. * incrementer - the entity's speed incrementer.
  53. * @type {number}
  54. */
  55. this.incrementer = 0;
  56. /**
  57. * speed - the entity's speed.
  58. * @type {number}
  59. */
  60. this.speed = 5;
  61. /**
  62. * stamina - entity stamina boost enabled.
  63. * @type {boolean}
  64. */
  65. this.stamina = false;
  66. /**
  67. * staminaAmount - the amount of speed to boost by.
  68. * @type {number}
  69. */
  70. this.staminaAmount = 0;
  71. /**
  72. * sleep - the entity's render state.
  73. * @type {boolean}
  74. */
  75. this.sleep = true;
  76. /**
  77. * invincible - the entity's damage state.
  78. * @type {boolean}
  79. */
  80. this.invincible = false;
  81. /**
  82. * health - the entity's health.
  83. * @type {number}
  84. */
  85. this.health = 100;
  86. this.pickups = {
  87. health: 0,
  88. stamina: 0
  89. };
  90. this.damage = {
  91. x: 0,
  92. y: 0
  93. };
  94. this.dead = false;
  95. }
  96. /**
  97. * Render the player entity on the canvas.
  98. *
  99. * This is called every frame/repaint to render the entity. Note that this is
  100. * an animated entity, therefore the x,y coordinates will change on
  101. * update.
  102. *
  103. * @param {CanvasRenderingContext2D} context - the canvas rendering context
  104. *
  105. * @returns {void}
  106. */
  107. render (context) {
  108. Renderer.render(this, context);
  109. }
  110. /**
  111. * Update the player entity for rendering, collision and behaviour.
  112. *
  113. * Checks the render state, sets the player entity's speed, angle and direction, detects
  114. * collision between the player and walls, checks damage and updates the player entity's
  115. * position. This is called every frame/repaint.
  116. *
  117. * @param {Game} game - the managed game instance
  118. *
  119. * @returns {void}
  120. */
  121. update (game) {
  122. if (this.sleep || this.dead) {
  123. return;
  124. }
  125. let count = 0;
  126. count += game.keyboard.up ? 1 : 0;
  127. count += game.keyboard.down ? 1 : 0;
  128. count += game.keyboard.left ? 1 : 0;
  129. count += game.keyboard.right ? 1 : 0;
  130. let currentSpeed = this.speed;
  131. if (count > 1) {
  132. currentSpeed /= Math.sqrt(2);
  133. }
  134. if (Math.abs(this.damage.x) != 0 < Math.abs(this.damage.y) != 0) {
  135. this.damage.x *= 0.9;
  136. this.damage.y *= 0.9;
  137. this.x += this.damage.x;
  138. this.y += this.damage.y;
  139. if (Math.abs(this.damage.x) < 0.5 && Math.abs(this.damage.y) < 0.5) {
  140. this.damage = {
  141. x: 0,
  142. y: 0
  143. };
  144. this.invincible = false;
  145. }
  146. } else {
  147. if (game.keyboard.up) this.y -= currentSpeed;
  148. if (game.keyboard.down) this.y += currentSpeed;
  149. if (game.keyboard.left) this.x -= currentSpeed;
  150. if (game.keyboard.right) this.x += currentSpeed;
  151. }
  152. const collisionVector = Collision.entityToWalls(this, game.walls);
  153. this.x += collisionVector.x * currentSpeed;
  154. this.y += collisionVector.y * currentSpeed;
  155. let vectorX = game.camera.offsetX + game.context.canvas.width / 2 - game.mouse.x;
  156. let vectorY = game.camera.offsetY + game.context.canvas.height / 2 - game.mouse.y;
  157. const distance = Collision.distance(vectorX, vectorY);
  158. if (distance > 0) {
  159. vectorX /= distance;
  160. vectorY /= distance;
  161. this.angle = Math.atan2(vectorY, vectorX) + 90 * Math.PI / 180;
  162. }
  163. if (game.keyboard.up || game.keyboard.down || game.keyboard.left || game.keyboard.right) {
  164. this.incrementer += this.speed;
  165. }
  166. this.position = Math.sin(this.incrementer * Math.PI / 180);
  167. }
  168. /**
  169. * Handle damage from colliding enemies.
  170. *
  171. * @param {Enemy} enemy the enemy entity
  172. *
  173. * @returns {void}
  174. */
  175. takeDamage (enemy) {
  176. if (! this.invincible) {
  177. const vectorX = this.x - enemy.x;
  178. const vectorY = this.y - enemy.y;
  179. const distance = Collision.distance(vectorX, vectorY);
  180. if (distance > 0) {
  181. this.damage.x = vectorX / distance * 20;
  182. this.damage.y = vectorY / distance * 20;
  183. this.invincible = true;
  184. this.health -= 25;
  185. this.health = this.health < 0 ? 0 : this.health;
  186. if (this.health == 0) {
  187. AudioFX.snippet({ name: 'defib' });
  188. this.dead = true;
  189. }
  190. }
  191. }
  192. }
  193. /**
  194. * Boost player speed when stamina entity is picked up.
  195. *
  196. * @param {number} amount - the amount of speed to boost by
  197. *
  198. * @returns {void}
  199. */
  200. boostSpeed (amount) {
  201. this.speed = amount;
  202. setTimeout(() => {
  203. this.speed = 5;
  204. }, 3000);
  205. }
  206. /**
  207. * Refill player health when health entity is picked up or used.
  208. *
  209. * @param {number} amount - the amount of health to restore
  210. * @param {boolean} pickup - if false, then player is using a stored item
  211. *
  212. * @returns {void}
  213. */
  214. refillHealth (amount, pickup = false) {
  215. const refill = (health) => {
  216. const increase = this.health + health;
  217. if (increase <= 100) {
  218. this.health = increase;
  219. }
  220. };
  221. if (this.health < 100) {
  222. if (! pickup) {
  223. if (this.pickups.health <= 0) {
  224. this.pickups.health = 0;
  225. } else {
  226. refill(amount);
  227. --this.pickups.health;
  228. }
  229. } else {
  230. refill(amount);
  231. }
  232. } else if (pickup) {
  233. ++this.pickups.health;
  234. }
  235. document.querySelector('#medkits-available').innerHTML = this.pickups.health;
  236. }
  237. }