src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

game.js (3836B)


      1 import * as direction from '/static/js/minotaur/direction.js';
      2 
      3 // Game represents a maze game with player interaction.
      4 export default class Game {
      5   constructor({ document, maze }) {
      6     this.document = document;
      7     this.escaped = false;
      8     this.initialized = false;
      9     this.intervals = { score: null, minotaur: null };
     10     this.killed = false;
     11     this.maze = maze;
     12     this.pressed = {
     13       down: false,
     14       left: false,
     15       right: false,
     16       space: false,
     17       up: false,
     18     };
     19     this.score = 0;
     20 
     21     // Add the event listeners.
     22     document.addEventListener('keydown', (e) => this.keyDownHandler(e), false);
     23     document.addEventListener('keyup', (e) => this.keyUpHandler(e), false);
     24   }
     25 
     26   // End handles the game over condition.
     27   end() {
     28     // Clear the intervals.
     29     clearInterval(this.intervals.score);
     30     clearInterval(this.intervals.minotaur);
     31 
     32     // Disable the event handlers.
     33     this.document.removeEventListener('keydown', this.keyDownHandler, false);
     34     this.document.removeEventListener('keyup', this.keyUpHandler, false);
     35 
     36     // Display the game over text.
     37     if (this.escaped) {
     38       this.document.getElementById('game-over').innerHTML = `You escaped the labyrinth!\nScore: ${this.score}.`;
     39     }
     40     if (this.killed) {
     41       this.document.getElementById('game-over').innerHTML = `You were slain by the Minotaur. Score: ${this.score}.`;
     42     }
     43 
     44     // Render the last frame.
     45     return this.maze.render();
     46   }
     47 
     48   // init starts the game score and the Minotaur's movement.
     49   init() {
     50     // Start measuring the player's score.
     51     this.intervals.score = setInterval(() => { this.score += 1; }, 1000);
     52 
     53     // Move the minotaur.
     54     this.intervals.minotaur = setInterval(() => this.maze.moveMinotaur(), 250);
     55 
     56     this.initialized = true;
     57   }
     58 
     59   // keyDownHandler handles keydown events.
     60   keyDownHandler(e) {
     61     if (e.code === 'ArrowUp') {
     62       this.pressed.up = true;
     63       return e.preventDefault();
     64     }
     65     if (e.code === 'ArrowRight') {
     66       this.pressed.right = true;
     67       return e.preventDefault();
     68     }
     69     if (e.code === 'ArrowDown') {
     70       this.pressed.down = true;
     71       return e.preventDefault();
     72     }
     73     if (e.code === 'ArrowLeft') {
     74       this.pressed.left = true;
     75       return e.preventDefault();
     76     }
     77     if (e.code === 'Space') {
     78       this.pressed.space = true;
     79       return e.preventDefault();
     80     }
     81 
     82     return null;
     83   }
     84 
     85   // keyUpHandler handles keyup events.
     86   keyUpHandler(e) {
     87     if (e.code === 'ArrowUp') this.pressed.up = false;
     88     if (e.code === 'ArrowRight') this.pressed.right = false;
     89     if (e.code === 'ArrowDown') this.pressed.down = false;
     90     if (e.code === 'ArrowLeft') this.pressed.left = false;
     91     if (e.code === 'Space') this.pressed.space = false;
     92   }
     93 
     94   // run executes the game loop.
     95   run() {
     96     if (!this.initialized) this.init();
     97 
     98     // Render the solution.
     99     if (this.pressed.space) {
    100       this.maze.setEscapePath();
    101     } else {
    102       this.maze.clearEscapePath();
    103     }
    104 
    105     // Move Theseus.
    106     let dir = null;
    107     if (this.pressed.up) dir = direction.up;
    108     if (this.pressed.right) dir = direction.right;
    109     if (this.pressed.down) dir = direction.down;
    110     if (this.pressed.left) dir = direction.left;
    111     if (dir) {
    112       this.maze.moveTheseus({ d: dir });
    113 
    114       // Prevent the user from moving more than one cell per keypress.
    115       this.pressed.up = false;
    116       this.pressed.right = false;
    117       this.pressed.down = false;
    118       this.pressed.left = false;
    119       this.pressed.space = false;
    120     }
    121 
    122     // Render the maze.
    123     this.maze.render();
    124 
    125     // Check for game over conditions.
    126     if (this.maze.escaped()) {
    127       this.escaped = true;
    128       return this.end();
    129     }
    130     if (this.maze.killed()) {
    131       this.killed = true;
    132       return this.end();
    133     }
    134 
    135     return requestAnimationFrame(() => this.run());
    136   }
    137 }