lot of changes
This commit is contained in:
		| @ -1,3 +1,11 @@ | |||||||
|  | @font-face { | ||||||
|  |     font-family: 'Share Tech Mono'; | ||||||
|  |     font-style: normal; | ||||||
|  |     font-weight: 400; | ||||||
|  |     src: local('Share Tech Mono'), local('ShareTechMono-Regular'), url(../fonts/ShareTechMono.woff2) format('woff2'); | ||||||
|  |     unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; | ||||||
|  | } | ||||||
|  |   | ||||||
|  * { |  * { | ||||||
|     padding: 0; |     padding: 0; | ||||||
|     margin: 0; |     margin: 0; | ||||||
| @ -12,7 +20,7 @@ body { | |||||||
| canvas { | canvas { | ||||||
|     display: block; |     display: block; | ||||||
|     flex-shrink: 0; |     flex-shrink: 0; | ||||||
|  } | } | ||||||
|  |  | ||||||
| .columns { | .columns { | ||||||
|     display: flex; |     display: flex; | ||||||
| @ -20,7 +28,7 @@ canvas { | |||||||
|     justify-content: center; |     justify-content: center; | ||||||
| } | } | ||||||
|  |  | ||||||
| .side { | .space { | ||||||
|     flex-grow: 2; |     flex-grow: 2; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -28,19 +36,27 @@ canvas { | |||||||
|     display: flex; |     display: flex; | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
|     margin: 5% 2%; |     margin: 5% 2%; | ||||||
|  |     height: 400px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .hold { | .hold { | ||||||
|     max-width: 120px; |     max-width: 120px; | ||||||
|     height: initial; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .stats { | .stats { | ||||||
|  |     display: flex; | ||||||
|  |     flex-direction: row; | ||||||
|     margin: 10% 0; |     margin: 10% 0; | ||||||
|  |     font-family: 'Share Tech Mono'; | ||||||
|  |     font-size: 1.2em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .stats-values { | ||||||
|  |     text-align: right; | ||||||
|  |     min-width: 80px; | ||||||
| } | } | ||||||
|  |  | ||||||
| .matrix { | .matrix { | ||||||
|     max-width: 200px; |  | ||||||
|     margin: 5% 2%; |     margin: 5% 2%; | ||||||
|     border: 0.5px solid grey; |     border: 0.5px solid grey; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								fonts/ShareTechMono.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								fonts/ShareTechMono.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										25
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								index.html
									
									
									
									
									
								
							| @ -9,18 +9,29 @@ | |||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|     <div class="columns"> |     <div class="columns"> | ||||||
|         <div class="side"></div> |         <div class="space"></div> | ||||||
|         <div class="rows"> |         <div class="rows"> | ||||||
|             <canvas id="hold" class="hold" width="120" height="120"></canvas> |             <div class="columns"> | ||||||
|             <div class="stats"> |                 <div class="space"></div> | ||||||
|                 SCORE: 123123123123<br/> |                 <canvas id="hold" class="hold" width="120" height="120"></canvas> | ||||||
|                 HIGH SCORE:<br/> |  | ||||||
|                 TIME:<br/> |  | ||||||
|             </div> |             </div> | ||||||
|  |             <div class="space"></div> | ||||||
|  |             <div class="stats"> | ||||||
|  |                 <div class="stats-names"> | ||||||
|  |                     SCORE<br/> | ||||||
|  |                     HIGH<br/> | ||||||
|  |                     LEVEL<br/> | ||||||
|  |                     GOAL<br/> | ||||||
|  |                     LINES<br/> | ||||||
|  |                     TIME<br/> | ||||||
|  |                 </div> | ||||||
|  |                 <div id="stats-values" class="stats-values"></div> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|         </div> |         </div> | ||||||
|         <canvas id="matrix" class="matrix" width="200" height="400">Votre navigateur ne supporte pas HTML5, veuillez le mettre à jour pour jouer.</canvas> |         <canvas id="matrix" class="matrix" width="200" height="400">Votre navigateur ne supporte pas HTML5, veuillez le mettre à jour pour jouer.</canvas> | ||||||
|         <canvas id="next" class="next" width="120" height="400"></canvas> |         <canvas id="next" class="next" width="120" height="400"></canvas> | ||||||
|         <div class="side"></div> |         <div class="space"></div> | ||||||
|     </div> |     </div> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
							
								
								
									
										364
									
								
								js/webtris.js
									
									
									
									
									
								
							
							
						
						
									
										364
									
								
								js/webtris.js
									
									
									
									
									
								
							| @ -1,6 +1,9 @@ | |||||||
| Array.prototype.add = function(other) { | Array.prototype.add = function(other) { | ||||||
|     return this.map((x, i) => x + other[i]) |     return this.map((x, i) => x + other[i]) | ||||||
| } | } | ||||||
|  | Array.prototype.translate = function(vector) { | ||||||
|  |     return this.map(pos => pos.add(vector)) | ||||||
|  | } | ||||||
| Array.prototype.rotate = function(spin) { | Array.prototype.rotate = function(spin) { | ||||||
|     return [-spin*this[1], spin*this[0]] |     return [-spin*this[1], spin*this[0]] | ||||||
| } | } | ||||||
| @ -11,8 +14,12 @@ Array.prototype.sample = function() { | |||||||
|  |  | ||||||
| const MINO_SIZE = 20 | const MINO_SIZE = 20 | ||||||
| const NEXT_PIECES = 5 | const NEXT_PIECES = 5 | ||||||
| const MATRIX_LINES = 20 | const HOLD_ROWS = 6 | ||||||
|  | const HOLD_COLUMNS = 6 | ||||||
|  | const MATRIX_ROWS = 20 | ||||||
| const MATRIX_COLUMNS = 10 | const MATRIX_COLUMNS = 10 | ||||||
|  | const NEXT_ROWS = 20 | ||||||
|  | const NEXT_COLUMNS = 6 | ||||||
| const HELD_PIECE_POSITION = [2, 2] | const HELD_PIECE_POSITION = [2, 2] | ||||||
| const FALLING_PIECE_POSITION = [4, 0] | const FALLING_PIECE_POSITION = [4, 0] | ||||||
| const NEXT_PIECES_POSITIONS = Array.from({length: NEXT_PIECES}, (v, k) => [2, k*4+2]) | const NEXT_PIECES_POSITIONS = Array.from({length: NEXT_PIECES}, (v, k) => [2, k*4+2]) | ||||||
| @ -21,12 +28,12 @@ const FALL_DELAY = 1000 | |||||||
| const AUTOREPEAT_DELAY = 300 | const AUTOREPEAT_DELAY = 300 | ||||||
| const AUTOREPEAT_PERIOD = 10 | const AUTOREPEAT_PERIOD = 10 | ||||||
| const MOVEMENT = { | const MOVEMENT = { | ||||||
|     LEFT: [-1, 0], |     LEFT:  [-1, 0], | ||||||
|     RIGHT: [1, 0], |     RIGHT: [ 1, 0], | ||||||
|     DOWN: [0, 1] |     DOWN:  [ 0, 1] | ||||||
| } | } | ||||||
| const SPIN = { | const SPIN = { | ||||||
|     CW: 1, |     CW:   1, | ||||||
|     CCW: -1 |     CCW: -1 | ||||||
| } | } | ||||||
| const T_SPIN = { | const T_SPIN = { | ||||||
| @ -47,16 +54,47 @@ const SCORES = [ | |||||||
|     {LINES_CLEAR_NAME: "TRIPLE", NO_T_SPIN: 5, T_SPIN: 16}, |     {LINES_CLEAR_NAME: "TRIPLE", NO_T_SPIN: 5, T_SPIN: 16}, | ||||||
|     {LINES_CLEAR_NAME: "TETRIS", NO_T_SPIN: 8}, |     {LINES_CLEAR_NAME: "TETRIS", NO_T_SPIN: 8}, | ||||||
| ] | ] | ||||||
|  | const REPEATABLE_ACTIONS = [moveLeft, moveRight, softDrop] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Scheduler { | ||||||
|  |     constructor() { | ||||||
|  |         this.intervalTasks = new Map() | ||||||
|  |         this.timeoutTasks = new Map() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     setInterval(func, delay) { | ||||||
|  |         this.intervalTasks.set(func, window.setInterval(func, delay)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     setTimeout(func, delay) { | ||||||
|  |         this.timeoutTasks.set(func, window.setTimeout(func, delay)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     clearInterval(func) { | ||||||
|  |         if (this.intervalTasks.has(func)) { | ||||||
|  |             window.clearInterval(this.intervalTasks.get(func)) | ||||||
|  |             this.intervalTasks.delete(func) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     clearTimeout(func) { | ||||||
|  |         if (this.timeoutTasks.has(func)) { | ||||||
|  |             window.clearTimeout(this.timeoutTasks.get(func)) | ||||||
|  |             this.timeoutTasks.delete(func) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| shapes = [] | shapes = [] | ||||||
| class Tetromino { | class Tetromino { | ||||||
|     constructor() { |     constructor(position=null, shape=null) { | ||||||
|         this.pos = FALLING_PIECE_POSITION |         this.pos = position | ||||||
|         this.orientation = 0 |         this.orientation = 0 | ||||||
|         this.rotated_last = false |         this.rotatedLast = false | ||||||
|         this.rotation_point_5_used = false |         this.rotationPoint5Used = false | ||||||
|         this.hold_enabled = true |         this.holdEnabled = true | ||||||
|         this.srs = { |         this.srs = { | ||||||
|             CW: [ |             CW: [ | ||||||
|                 [[0, 0], [-1, 0], [-1, -1], [0,  2], [-1,  2]], |                 [[0, 0], [-1, 0], [-1, -1], [0,  2], [-1,  2]], | ||||||
| @ -71,13 +109,18 @@ class Tetromino { | |||||||
|                 [[0, 0], [-1, 0], [-1,  1], [0,  2], [-1, -2]], |                 [[0, 0], [-1, 0], [-1,  1], [0,  2], [-1, -2]], | ||||||
|             ], |             ], | ||||||
|         } |         } | ||||||
|         if (!shapes.lenght) |         if (shape) | ||||||
|             shapes = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'] |             this.shape = shape | ||||||
|         this.shape = shapes.sample() |         else { | ||||||
|  |             if (!shapes.length) | ||||||
|  |                 shapes = ['I', 'J', 'L', 'O', 'S', 'T', 'Z'] | ||||||
|  |             this.shape = shapes.sample() | ||||||
|  |         } | ||||||
|         switch(this.shape) { |         switch(this.shape) { | ||||||
|             case 'I': |             case 'I': | ||||||
|                 this.color = "cyan" |                 this.color = "rgb(132, 225, 225)" | ||||||
|                 this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [2, 0]] |                 this.ghostColor = "rgba(40, 164, 164, 0.5)" | ||||||
|  |                 this.minoesPos = [[-1, 0], [0, 0], [1, 0], [2, 0]] | ||||||
|                 this.srs = { |                 this.srs = { | ||||||
|                     CW: [ |                     CW: [ | ||||||
|                         [[ 1,  0], [-1,  0], [ 2,  0], [-1,  1], [ 2, -2]], |                         [[ 1,  0], [-1,  0], [ 2,  0], [-1,  1], [ 2, -2]], | ||||||
| @ -94,47 +137,62 @@ class Tetromino { | |||||||
|                 } |                 } | ||||||
|                 break |                 break | ||||||
|             case 'J': |             case 'J': | ||||||
|                 this.color = "blue" |                 this.color = "rgb(102, 163, 255)" | ||||||
|                 this.minoes_pos = [[-1, -1], [-1, 0], [0, 0], [1, 0]] |                 this.ghostColor = "rgba(0, 82, 204, 0.5)" | ||||||
|  |                 this.minoesPos = [[-1, -1], [-1, 0], [0, 0], [1, 0]] | ||||||
|                 break |                 break | ||||||
|             case 'L': |             case 'L': | ||||||
|                 this.color = "orange" |                 this.color = "rgb(255, 148, 77)" | ||||||
|                 this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [1, -1]] |                 this.ghostColor = "rgba(204, 82, 0, 0.5)" | ||||||
|  |                 this.minoesPos = [[-1, 0], [0, 0], [1, 0], [1, -1]] | ||||||
|                 break |                 break | ||||||
|             case 'O': |             case 'O': | ||||||
|                 this.color = "yellow" |                 this.color = "rgb(255, 255, 102)" | ||||||
|                 this.minoes_pos = [[0, 0], [1, 0], [0, -1], [1, -1]] |                 this.ghostColor = "rgba(204, 204, 0, 0.5)" | ||||||
|  |                 this.minoesPos = [[0, 0], [1, 0], [0, -1], [1, -1]] | ||||||
|                 this.srs = { |                 this.srs = { | ||||||
|                     CW: [[]], |                     CW: [[]], | ||||||
|                     CCW: [[]] |                     CCW: [[]] | ||||||
|                 } |                 } | ||||||
|                 break |                 break | ||||||
|             case 'S': |             case 'S': | ||||||
|                 this.color = "green" |                 this.color = "rgb(159, 255, 128)" | ||||||
|                 this.minoes_pos = [[-1, -1], [0, -1], [0, 0], [1, 0]] |                 this.ghostColor = "rgb(38, 153, 0, 0.5)" | ||||||
|  |                 this.minoesPos = [[-1, -1], [0, -1], [0, 0], [1, 0]] | ||||||
|                 break |                 break | ||||||
|             case 'T': |             case 'T': | ||||||
|                 this.color = "magenta" |                 this.color = "rgb(179, 102, 255)" | ||||||
|                 this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [0, -1]] |                 this.ghostColor = "rgba(102, 0, 204, 0.5)" | ||||||
|  |                 this.minoesPos = [[-1, 0], [0, 0], [1, 0], [0, -1]] | ||||||
|                 break |                 break | ||||||
|             case 'Z': |             case 'Z': | ||||||
|                 this.color = "red" |                 this.color = "rgb(255, 51, 51)" | ||||||
|                 this.minoes_pos = [[-1, -1], [0, -1], [0, 0], [1, 0]] |                 this.ghostColor = "rgba(204, 0, 0, 0.5)" | ||||||
|  |                 this.minoesPos = [[-1, -1], [0, -1], [0, 0], [1, 0]] | ||||||
|                 break |                 break | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|          |          | ||||||
|     get abs_minoes_pos() { |     get absMinoesPos() { | ||||||
|         return this.minoes_pos.map(pos => pos.add(this.pos)) |         return this.minoesPos.translate(this.pos) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     draw(context) { |     draw(context, ghostYOffset=0) { | ||||||
|         this.abs_minoes_pos.map(pos => draw_mino(context, ...pos, this.color)) |         if (ghostYOffset) { | ||||||
|  |             context.save() | ||||||
|  |             context.shadowColor = this.ghostColor | ||||||
|  |             context.shadowOffsetX = 0 | ||||||
|  |             context.shadowOffsetY  = ghostYOffset * MINO_SIZE | ||||||
|  |             context.shadowBlur = 3 | ||||||
|  |         } | ||||||
|  |         this.absMinoesPos.map(pos => draw_mino(context, ...pos, this.color)) | ||||||
|  |         if (ghostYOffset) | ||||||
|  |             context.restore() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| function draw_mino(context, x, y, color) { | function draw_mino(context, x, y, color, ghostYOffset) { | ||||||
|     context.fillStyle = color |     context.fillStyle = color | ||||||
|     context.fillRect(x*MINO_SIZE, y*MINO_SIZE, MINO_SIZE, MINO_SIZE) |     context.fillRect(x*MINO_SIZE, y*MINO_SIZE, MINO_SIZE, MINO_SIZE) | ||||||
|     context.lineWidth = 0.5 |     context.lineWidth = 0.5 | ||||||
| @ -143,50 +201,132 @@ function draw_mino(context, x, y, color) { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class HoldQueue { | ||||||
|  |     constructor(context) { | ||||||
|  |         this.context = context | ||||||
|  |         this.piece = null | ||||||
|  |         this.width = HOLD_COLUMNS*MINO_SIZE | ||||||
|  |         this.height = HOLD_ROWS*MINO_SIZE | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     draw() { | ||||||
|  |         this.context.clearRect(0, 0, this.width, this.height) | ||||||
|  |         if (this.piece) | ||||||
|  |             this.piece.draw(this.context) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | timeFormat = new Intl.DateTimeFormat("en-US", { | ||||||
|  |     hour: "numeric", minute: "2-digit", second: "2-digit", hourCycle: "h24", timeZone: "UTC" | ||||||
|  | }).format | ||||||
|  |  | ||||||
|  | class Stats { | ||||||
|  |     constructor (div, start_level=1) { | ||||||
|  |         this.div = div | ||||||
|  |         this._score = 0 | ||||||
|  |         this.highScore = 0 | ||||||
|  |         this.level = start_level - 1 | ||||||
|  |         this.goal = 0 | ||||||
|  |         this.linesCleared = 0 | ||||||
|  |         this.startTime = Date.now() | ||||||
|  |         this.combo = -1 | ||||||
|  |         this.lockDelay = LOCK_DELAY | ||||||
|  |         this.fallDelay = FALL_DELAY | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     get score() { | ||||||
|  |         return this._score | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     set score(score) { | ||||||
|  |         this._score = score | ||||||
|  |         if (score > this.highScore) | ||||||
|  |             this.highScore = score | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     new_level() { | ||||||
|  |         this.level++ | ||||||
|  |         this.goal += 5 * this.level | ||||||
|  |         if (this.level <= 20) | ||||||
|  |             this.fallDelay = 1000 * Math.pow(0.8 - ((this.level - 1) * 0.007), this.level - 1) | ||||||
|  |         if (this.level > 15) | ||||||
|  |             this.lockDelay = 500 * Math.pow(0.9, this.level - 15) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     print() { | ||||||
|  |         this.div.innerHTML  = this.score | ||||||
|  |         this.div.innerHTML  += "<br/>" + this.highScore | ||||||
|  |         this.div.innerHTML  += "<br/>" + this.level | ||||||
|  |         this.div.innerHTML  += "<br/>" + this.goal | ||||||
|  |         this.div.innerHTML  += "<br/>" + this.linesCleared | ||||||
|  |         this.div.innerHTML  += "<br/>" + timeFormat(Date.now() - this.startTime) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| class Matrix { | class Matrix { | ||||||
|     constructor(context) { |     constructor(context) { | ||||||
|         this.context = context |         this.context = context | ||||||
|         this.cells = Array.from({length: MATRIX_COLUMNS}, (v, k) => Array(MATRIX_LINES)) |         this.cells = Array.from(Array(MATRIX_COLUMNS), x => Array(MATRIX_ROWS)) | ||||||
|  |         this.width = MATRIX_COLUMNS*MINO_SIZE | ||||||
|  |         this.height = MATRIX_ROWS*MINO_SIZE | ||||||
|  |         this.piece = null | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     cell_is_occupied(x, y) { |     cellIsOccupied(x, y) { | ||||||
|         return 0 <= x && x < MATRIX_COLUMNS && y < MATRIX_LINES ? this.cells[x][y] : true |         return 0 <= x && x < MATRIX_COLUMNS && y < MATRIX_ROWS ? this.cells[x][y] : true | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     space_to_move(piece_pos, minoes_pos) { |     spaceToMove(absMinoesPos) { | ||||||
|         for (const pos of minoes_pos) { |         for (const pos of absMinoesPos) { | ||||||
|             if (this.cell_is_occupied(...pos.add(piece_pos))) |             if (this.cellIsOccupied(...pos)) | ||||||
|                 return false |                 return false | ||||||
|         } |         } | ||||||
|         return true |         return true | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     draw() { |     draw() { | ||||||
|  |         this.context.clearRect(0, 0, this.width, this.height) | ||||||
|         // grid |         // grid | ||||||
|         const width = MATRIX_COLUMNS*MINO_SIZE |  | ||||||
|         const height = MATRIX_LINES*MINO_SIZE |  | ||||||
|         this.context.clearRect(0, 0, width, height) |  | ||||||
|         this.context.strokeStyle = "rgba(128, 128, 128, 128)" |         this.context.strokeStyle = "rgba(128, 128, 128, 128)" | ||||||
|         this.context.lineWidth = 0.5 |         this.context.lineWidth = 0.5 | ||||||
|         this.context.beginPath() |         this.context.beginPath() | ||||||
|         for (var x = 0; x <= width; x += MINO_SIZE) { |         for (var x = 0; x <= this.width; x += MINO_SIZE) { | ||||||
|             this.context.moveTo(x, 0); |             this.context.moveTo(x, 0); | ||||||
|             this.context.lineTo(x, height); |             this.context.lineTo(x, this.height); | ||||||
|         } |         } | ||||||
|         for (var y = 0; y <= height; y += MINO_SIZE) { |         for (var y = 0; y <= this.height; y += MINO_SIZE) { | ||||||
|             this.context.moveTo(0, y); |             this.context.moveTo(0, y); | ||||||
|             this.context.lineTo(width, y); |             this.context.lineTo(this.width, y); | ||||||
|         } |         } | ||||||
|         this.context.stroke() |         this.context.stroke() | ||||||
|         // falling piece |         // falling piece | ||||||
|         falling_piece.draw(this.context) |         if (this.piece) | ||||||
|  |             for (var ghostYOffset = 0; this.spaceToMove(this.piece.minoesPos.translate([this.piece.pos[0], this.piece.pos[1]+ghostYOffset])); ghostYOffset++) {} | ||||||
|  |             this.piece.draw(this.context, --ghostYOffset) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class NextQueue { | ||||||
|  |     constructor(context) { | ||||||
|  |         this.context = context | ||||||
|  |         this.pieces = Array.from({length: NEXT_PIECES}, (v, k) => new Tetromino(NEXT_PIECES_POSITIONS[k])) | ||||||
|  |         this.width = NEXT_COLUMNS*MINO_SIZE | ||||||
|  |         this.height = NEXT_ROWS*MINO_SIZE | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     draw() { | ||||||
|  |         this.context.clearRect(0, 0, this.width, this.height) | ||||||
|  |         this.pieces.map(piece => piece.draw(this.context)) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| function move(movement) { | function move(movement) { | ||||||
|     const test_pos = falling_piece.pos.add(movement) |     const test_pos = matrix.piece.pos.add(movement) | ||||||
|     if (matrix.space_to_move(test_pos, falling_piece.minoes_pos)) { |     if (matrix.spaceToMove(matrix.piece.minoesPos.translate(test_pos))) { | ||||||
|         falling_piece.pos = test_pos |         matrix.piece.pos = test_pos | ||||||
|         return true |         return true | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
| @ -195,14 +335,14 @@ function move(movement) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function rotate(spin) { | function rotate(spin) { | ||||||
|     const test_minoes_pos = falling_piece.minoes_pos.map(pos => pos.rotate(spin)) |     const test_minoes_pos = matrix.piece.minoesPos.map(pos => pos.rotate(spin)) | ||||||
|     rotation_point = 0 |     rotation_point = 0 | ||||||
|     for (const movement of falling_piece.srs[spin==SPIN.CW?"CW":"CCW"][falling_piece.orientation]) { |     for (const movement of matrix.piece.srs[spin==SPIN.CW?"CW":"CCW"][matrix.piece.orientation]) { | ||||||
|         const test_pos = falling_piece.pos.add(movement) |         const test_pos = matrix.piece.pos.add(movement) | ||||||
|         if (matrix.space_to_move(test_pos, test_minoes_pos)) { |         if (matrix.spaceToMove(test_minoes_pos.translate(test_pos))) { | ||||||
|             falling_piece.pos = test_pos |             matrix.piece.pos = test_pos | ||||||
|             falling_piece.minoes_pos = test_minoes_pos |             matrix.piece.minoesPos = test_minoes_pos | ||||||
|             falling_piece.orientation = (falling_piece.orientation + spin + 4) % 4 |             matrix.piece.orientation = (matrix.piece.orientation + spin + 4) % 4 | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         rotation_point++ |         rotation_point++ | ||||||
| @ -239,34 +379,47 @@ function rotateCCW() { | |||||||
|     rotate(SPIN.CCW) |     rotate(SPIN.CCW) | ||||||
| } | } | ||||||
|  |  | ||||||
| actions = { | function hold() { | ||||||
|     "ArrowLeft":    moveLeft, |     if (this.matrix.piece.holdEnabled) { | ||||||
|     "ArrowRight":   moveRight, |         this.matrix.piece.holdEnabled = false | ||||||
|     "ArrowDown":    softDrop, |         clearInterval(lockPhaseIntervalID) | ||||||
|     " ":            hardDrop, |         var shape = this.matrix.piece.shape | ||||||
|     "ArrowUp":      rotateCW, |         this.matrix.piece = this.holdQueue.piece | ||||||
|     "z":            rotateCCW |         this.holdQueue.piece = new Tetromino(HELD_PIECE_POSITION, shape) | ||||||
|  |         this.generationPhase(this.matrix.piece) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pressedKeys = new Set() | function new_level() { | ||||||
| repeatableActions = [moveLeft, moveRight, softDrop] |     stats.new_level() | ||||||
| actionsToRepeat = [] |     fallIntervalID = setInterval(fall, stats.fallDelay) | ||||||
| autorepeatTimeoutID = null |     generationPhase() | ||||||
| autorepeatIntervalID = null | } | ||||||
|  |  | ||||||
|  | function generationPhase(held_piece=null) { | ||||||
|  |     if (!held_piece) { | ||||||
|  |         this.matrix.piece = this.nextQueue.pieces.shift() | ||||||
|  |         this.nextQueue.pieces.push(new Tetromino()) | ||||||
|  |         this.nextQueue.pieces.map((piece, i, pieces) => piece.pos = NEXT_PIECES_POSITIONS[i]) | ||||||
|  |     } | ||||||
|  |     this.matrix.piece.pos = FALLING_PIECE_POSITION | ||||||
|  |     /*if (this.matrix.spaceToMove(this.matrix.piece.minoesPos.translate(this.matrix.piece.pos))) | ||||||
|  |         fallingPhase() | ||||||
|  |     else | ||||||
|  |         gameOver()*/ | ||||||
|  | } | ||||||
|  |  | ||||||
| function autorepeat() { | function autorepeat() { | ||||||
|     if (actionsToRepeat.length) { |     if (actionsToRepeat.length) { | ||||||
|         actionsToRepeat[0]() |         actionsToRepeat[0]() | ||||||
|         if (autorepeatTimeoutID) { |         if (scheduler.timeoutTasks.has(autorepeat)) { | ||||||
|             autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID) |             scheduler.clearTimeout(autorepeat) | ||||||
|             autorepeatIntervalID = setInterval(autorepeat, AUTOREPEAT_PERIOD) |             scheduler.setInterval(autorepeat, AUTOREPEAT_PERIOD) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     else { |     else { | ||||||
|         if (autorepeatTimeoutID) |         scheduler.clearTimeout(autorepeat) | ||||||
|             autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID) |         scheduler.clearInterval(autorepeat) | ||||||
|         if (autorepeatIntervalID) |  | ||||||
|             autorepeatIntervalID = clearInterval(autorepeatIntervalID) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -276,18 +429,14 @@ function keyDownHandler(e) { | |||||||
|             pressedKeys.add(e.key) |             pressedKeys.add(e.key) | ||||||
|             action = actions[e.key] |             action = actions[e.key] | ||||||
|             action() |             action() | ||||||
|             if (repeatableActions.includes(action)) { |             if (REPEATABLE_ACTIONS.includes(action)) { | ||||||
|                 actionsToRepeat.unshift(action) |                 actionsToRepeat.unshift(action) | ||||||
|                 if (autorepeatTimeoutID) { |                 scheduler.clearTimeout(autorepeat) | ||||||
|                     autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID) |                 scheduler.clearInterval(autorepeat) | ||||||
|                 } |  | ||||||
|                 if (autorepeatIntervalID) { |  | ||||||
|                     autorepeatIntervalID = clearInterval(autorepeatIntervalID) |  | ||||||
|                 } |  | ||||||
|                 if (actionsToRepeat == softDrop) |                 if (actionsToRepeat == softDrop) | ||||||
|                     autorepeatIntervalID = setInterval(autorepeat, FALL_DELAY / 20) |                     scheduler.setInterval(autorepeat, FALL_DELAY / 20) | ||||||
|                 else |                 else | ||||||
|                     autorepeatTimeoutID = setTimeout(autorepeat, AUTOREPEAT_DELAY) |                     scheduler.setTimeout(autorepeat, AUTOREPEAT_DELAY) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -300,39 +449,42 @@ function keyUpHandler(e) { | |||||||
|         if (actionsToRepeat.includes(action)) { |         if (actionsToRepeat.includes(action)) { | ||||||
|             actionsToRepeat.splice(actionsToRepeat.indexOf(action), 1) |             actionsToRepeat.splice(actionsToRepeat.indexOf(action), 1) | ||||||
|             if (!actionsToRepeat.length) { |             if (!actionsToRepeat.length) { | ||||||
|                 if (autorepeatTimeoutID) { |                 scheduler.clearTimeout(autorepeat) | ||||||
|                     autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID) |                 scheduler.clearInterval(autorepeat) | ||||||
|                 } |  | ||||||
|                 if (autorepeatIntervalID) { |  | ||||||
|                     autorepeatIntervalID = clearInterval(autorepeatIntervalID) |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| function draw() { | function draw() { | ||||||
|     held_piece.draw(holdContext) |     holdQueue.draw() | ||||||
|  |     stats.print() | ||||||
|     matrix.draw() |     matrix.draw() | ||||||
|     next_pieces.map(piece => piece.draw(nextContext)) |     nextQueue.draw() | ||||||
|     requestAnimationFrame(draw) |     requestAnimationFrame(draw) | ||||||
| } | } | ||||||
|  |  | ||||||
| window.onload = function() { | window.onload = function() { | ||||||
|     holdContext = document.getElementById("hold").getContext("2d") |     holdQueue = new HoldQueue(document.getElementById("hold").getContext("2d")) | ||||||
|     matrixContext = document.getElementById("matrix").getContext("2d") |     stats = new Stats(document.getElementById("stats-values")) | ||||||
|     nextContext = document.getElementById("next").getContext("2d") |     matrix = new Matrix(document.getElementById("matrix").getContext("2d")) | ||||||
|  |     nextQueue = new NextQueue(document.getElementById("next").getContext("2d")) | ||||||
|  |     scheduler = new Scheduler() | ||||||
|  |  | ||||||
|     matrix = new Matrix(matrixContext) |     actions = { | ||||||
|     held_piece = new Tetromino() |         "ArrowLeft":    moveLeft, | ||||||
|     held_piece.pos = HELD_PIECE_POSITION |         "ArrowRight":   moveRight, | ||||||
|     falling_piece = new Tetromino() |         "ArrowDown":    softDrop, | ||||||
|     falling_piece.pos = FALLING_PIECE_POSITION |         " ":            hardDrop, | ||||||
|     next_pieces = Array.from({length: NEXT_PIECES}, (v, k) => new Tetromino()) |         "ArrowUp":      rotateCW, | ||||||
|     next_pieces.map((piece, i, array) => piece.pos = NEXT_PIECES_POSITIONS[i]) |         "z":            rotateCCW, | ||||||
|  |         "c":            hold | ||||||
|     document.addEventListener("keydown", keyDownHandler, false); |     } | ||||||
|     document.addEventListener("keyup", keyUpHandler, false); |     pressedKeys = new Set() | ||||||
|     setInterval(fall, FALL_DELAY); |     actionsToRepeat = [] | ||||||
|  |     addEventListener("keydown", keyDownHandler, false) | ||||||
|  |     addEventListener("keyup", keyUpHandler, false) | ||||||
|     requestAnimationFrame(draw) |     requestAnimationFrame(draw) | ||||||
|  |  | ||||||
|  |     this.new_level() | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user