Board.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import { Ball } from "./Ball.js";
  2. import { Slot } from "./Slot.js";
  3. import { Point } from "../libraries/spatial/Point.js";
  4. import { Rectangle } from "../libraries/spatial/Rectangle.js";
  5. export class Board {
  6. constructor() {
  7. this.slots = [];
  8. this.balls = [];
  9. /*
  10. Design and color pallet by @kateradeg
  11. */
  12. this.heldBall = null;
  13. this.removedFromSlot = null;
  14. this.game = null;
  15. this.levelData = "";
  16. this.canvasBounds = null;
  17. }
  18. init(game) {
  19. this.game = game;
  20. this.reset();
  21. }
  22. reset() {
  23. this.loadLevel();
  24. // this.generateRandomLevel();
  25. }
  26. generateRandomLevel() {
  27. this.balls = [];
  28. this.slots = [];
  29. let slotCount = 2;
  30. let level = this.game.playerData.current;
  31. switch(level) {
  32. case 1:
  33. case 2:
  34. slotCount = 2;
  35. break;
  36. case 3:
  37. case 4:
  38. case 5:
  39. case 6:
  40. slotCount = 4;
  41. break;
  42. case 7:
  43. case 8:
  44. case 9:
  45. case 10:
  46. case 11:
  47. slotCount = 6;
  48. break;
  49. case 12:
  50. case 13:
  51. case 14:
  52. case 15:
  53. case 16:
  54. slotCount = 8;
  55. break;
  56. case 17:
  57. case 18:
  58. case 19:
  59. case 20:
  60. case 21:
  61. case 22:
  62. slotCount = 10;
  63. break;
  64. default:
  65. slotCount = 12;
  66. break;
  67. }
  68. for(let i = 0; i < slotCount; i++){
  69. for(let j = 0; j < 4; j++) {
  70. this.balls.push(new Ball(i));
  71. }
  72. }
  73. this.balls = this.shuffle(this.balls);
  74. for(let i = 0; i < slotCount; i++){
  75. let slot = new Slot(i);
  76. for(let j = 0; j < 4; j++) {
  77. slot.addBall(this.balls[4*i+j]);
  78. }
  79. this.slots.push(slot);
  80. }
  81. this.slots.push(new Slot(slotCount)); //two empties
  82. this.slots.push(new Slot(slotCount+1));
  83. //this.levelData += "\t" + JSON.stringify({slots: this.slots.length, balls:this.balls.map(b => b.colorId)}) +",\n";
  84. }
  85. shuffle(a) {
  86. var j, x, i;
  87. for (i = a.length - 1; i > 0; i--) {
  88. j = Math.floor(Math.random() * (i + 1));
  89. x = a[i];
  90. a[i] = a[j];
  91. a[j] = x;
  92. }
  93. return a;
  94. }
  95. loadLevel() {
  96. this.balls = [];
  97. this.slots = [];
  98. let level = this.game.playerData.current;
  99. let levelData = this.game.getLevel(level);
  100. if(!levelData) {
  101. this.generateRandomLevel();
  102. return;
  103. }
  104. this.balls = levelData.balls.map(colorId => new Ball(colorId));
  105. for(let i = 0; i < levelData.slots-2; i++){
  106. let slot = new Slot(i);
  107. for(let j = 0; j < 4; j++) {
  108. slot.addBall(this.balls[4*i+j]);
  109. }
  110. this.slots.push(slot);
  111. }
  112. this.slots.push(new Slot(levelData.slots-2));
  113. this.slots.push(new Slot(levelData.slots-1));
  114. }
  115. draw(ctx, scaledCanvas) {
  116. this.canvasBounds = scaledCanvas.bounds;
  117. ctx.strokeStyle = "rgba(255,255,255,0.5)";
  118. ctx.fillStyle = "rgba(255,255,255,0.3)";
  119. ctx.lineWidth = 3;
  120. for(let i = 0; i < this.slots.length; i++) {
  121. let slotBounds = this.getSlotBounds(this.slots[i]);
  122. ctx.save();
  123. ctx.translate(slotBounds.x, slotBounds.y);
  124. this.slots[i].draw(ctx, scaledCanvas);
  125. ctx.restore();
  126. }
  127. if(this.heldBall) {
  128. let bounds = this.getSlotBounds(this.removedFromSlot);
  129. ctx.save();
  130. let radiusPadding = Ball.radius + Ball.radiusPadding;
  131. ctx.translate(bounds.x + Slot.width / 2, bounds.y - radiusPadding);
  132. this.heldBall.draw(ctx, scaledCanvas);
  133. ctx.restore();
  134. }
  135. }
  136. update(delta) {
  137. }
  138. mouseDown(event) {
  139. let mousePoint = new Point(event.clientX, event.clientY);
  140. for(let i = 0; i < this.slots.length; i++) {
  141. let slot = this.slots[i];
  142. if(this.getSlotBounds(slot).pointWithin(mousePoint)) {
  143. if(!this.heldBall) {
  144. this.heldBall = slot.removeBall();
  145. this.removedFromSlot = slot;
  146. } else {
  147. if(slot.canAddBall(this.heldBall) || slot == this.removedFromSlot) {
  148. this.game.trackMove(this.removedFromSlot, slot);
  149. this.placeHeldBallInSlot(slot);
  150. this.checkForWin();
  151. }
  152. }
  153. }
  154. }
  155. }
  156. mouseMove(event) {
  157. let mousePoint = new Point(event.clientX, event.clientY);
  158. for(let i = 0; i < this.slots.length; i++) {
  159. let slot = this.slots[i];
  160. if(this.getSlotBounds(slot).pointWithin(mousePoint)) {
  161. document.body.style.cursor = "pointer";
  162. }
  163. }
  164. }
  165. placeHeldBallInSlot(slot) {
  166. slot.addBall(this.heldBall);
  167. this.heldBall = null;
  168. this.removedFromSlot = null;
  169. }
  170. getSlotBounds(slot) {
  171. let widthPadding = Slot.width + Slot.widthPadding;
  172. let heightPadding = Slot.height + Slot.heightPadding;
  173. let radiusPadding = Ball.radius + Ball.radiusPadding;
  174. let maxSlotsPerRow = Math.floor((this.canvasBounds.width - 40) / widthPadding);
  175. let slotsInFirstRow = Math.min(this.slots.length, maxSlotsPerRow);
  176. if(slotsInFirstRow < this.slots.length) {
  177. slotsInFirstRow = this.slots.length / 2;
  178. }
  179. let slotsTotalWidth = slotsInFirstRow * widthPadding;
  180. let offsetX = (this.canvasBounds.width - slotsTotalWidth - 40 + widthPadding) / 2
  181. return new Rectangle(
  182. offsetX + (slot.id % slotsInFirstRow) * widthPadding,
  183. Math.floor(slot.id / slotsInFirstRow) * heightPadding + Math.max((2 * radiusPadding), this.canvasBounds.height < 660 ? 10 : this.canvasBounds.height / 4),
  184. Slot.width,
  185. Slot.height + radiusPadding,
  186. );
  187. }
  188. checkForWin() {
  189. if(this.hasWon()) {
  190. this.game.win();
  191. }
  192. }
  193. hasWon() {
  194. for(let i = 0; i < this.slots.length; i++) {
  195. let slot = this.slots[i];
  196. if(!slot.colorsAllMatch()) {
  197. return false;
  198. }
  199. }
  200. return true;
  201. }
  202. initDemo() {
  203. //this.levelData = "[\n";
  204. //for(let i = 1; i< 256; i++) {
  205. this.game = {playerData: {current: 15}};
  206. this.generateRandomLevel();
  207. //}
  208. //this.levelData += "]\n";
  209. //console.log(this.levelData);
  210. }
  211. drawDemo(ctx, scaledCanvas) {
  212. this.draw(ctx,scaledCanvas);
  213. }
  214. getBounds() {
  215. let firstSlotBounds = this.getSlotBounds(this.slots[0]);
  216. let lastSlotBounds = this.getSlotBounds(this.slots[this.slots.length - 1]);
  217. return new Rectangle(
  218. firstSlotBounds.x,
  219. firstSlotBounds.y,
  220. lastSlotBounds.x - firstSlotBounds.x + lastSlotBounds.width,
  221. lastSlotBounds.y - firstSlotBounds.y + lastSlotBounds.height,
  222. )
  223. }
  224. }