text: /* eslint-disable no-underscore-dangle */
  /* eslint-disable no-undef */
  /* eslint-disable no-param-reassign */
  /* eslint-disable no-shadow */
  const map = {
    cols: 12,
    rows: 12,
    tsize: 64,
  
    /*
    
      layers: [[
      3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3,
      3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3,
      3, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2, 3,
      3, 1, 2, 1, 1, 2, 6, 6, 6, 2, 1, 3,
      3, 1, 2, 8, 8, 2, 7, 7, 7, 2, 1, 3,
      3, 1, 2, 8, 8, 2, 6, 6, 6, 2, 1, 3,
      3, 1, 2, 9, 8, 2, 2, 2, 2, 2, 1, 3,
      3, 1, 2, 8, 8, 8, 8, 9, 8, 2, 1, 3,
      3, 1, 2, 8, 9, 8, 8, 8, 8, 2, 1, 3,
      3, 1, 2, 8, 8, 8, 8, 8, 8, 2, 1, 3,
      3, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 3,
      3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3
    ], [
      4, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4,
      4, 0, 0, 5, 5, 0, 0, 0, 0, 5, 0, 4,
      4, 6, 0, 5, 5, 0, 0, 0, 0, 0, 0, 4,
      4, 6, 0, 5, 5, 0, 6, 6, 6, 0, 0, 4,
      4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
      4, 6, 0, 0, 0, 0, 6, 6, 6, 0, 0, 4,
      4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
      4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
      4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
      4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
      4, 6, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
      4, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4
    ]],
  
    */
  
    layers: [
      [
        3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3,
        1, 2, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 1, 2, 1, 1, 2, 6, 6, 6, 2, 1, 3, 3, 1,
        2, 8, 8, 2, 7, 7, 7, 2, 1, 3, 3, 1, 2, 8, 8, 2, 6, 6, 6, 2, 1, 3, 3, 1, 2,
        9, 8, 2, 2, 2, 2, 2, 1, 3, 3, 1, 2, 8, 8, 8, 8, 9, 8, 2, 1, 3, 3, 1, 2, 8,
        9, 8, 8, 8, 8, 2, 1, 3, 3, 1, 2, 8, 8, 8, 8, 8, 8, 2, 1, 3, 3, 1, 2, 2, 2,
        2, 2, 2, 2, 2, 1, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3,
      ],
      [
        4, 0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 4, 4, 0, 0, 5, 5, 0, 0, 0, 0, 5, 0, 4, 4,
        6, 0, 5, 5, 0, 0, 0, 0, 0, 0, 4, 4, 6, 0, 5, 5, 0, 6, 6, 6, 0, 0, 4, 4, 6,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 0, 0, 0, 0, 6, 6, 6, 0, 0, 4, 4, 6, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 0, 4, 4,
        4, 4, 4, 4, 4, 4, 4, 4, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4,
      ],
    ],
    getTile(layer, col, row) {
      return this.layers[layer][row * map.cols + col];
    },
    isSolidTileAtXY(x, y) {
      const col = Math.floor(x / this.tsize);
      const row = Math.floor(y / this.tsize);
  
      // tiles 3 and 5 are solid -- the rest are walkable
      // loop through all layers and return TRUE if any tile is solid
      return this.layers.reduce((res, layer, index) => {
        const tile = this.getTile(index, col, row);
        const isSolid = tile === 3 || tile === 5 || tile === 6;
        return res || isSolid;
      }, false);
    },
    ispokemon(x, y) {
      const col = Math.floor(x / this.tsize);
      const row = Math.floor(y / this.tsize);
  
      // tiles 3 and 5 are solid -- the rest are walkable
      // loop through all layers and return TRUE if any tile is solid
      return this.layers.reduce((res, layer, index) => {
        const tile = this.getTile(index, col, row);
        const isSolid = tile === 9;
        return res || isSolid;
      }, false);
    },
    getCol(x) {
      return Math.floor(x / this.tsize);
    },
    getRow(y) {
      return Math.floor(y / this.tsize);
    },
    getX(col) {
      return col * this.tsize;
    },
    getY(row) {
      return row * this.tsize;
    },
  };
  
  function Camera(map, width, height) {
    this.x = 0;
    this.y = 0;
    this.width = width;
    this.height = height;
    this.maxX = map.cols * map.tsize - width;
    this.maxY = map.rows * map.tsize - height;
  }
  
  Camera.prototype.follow = function defSprite (sprite) {
    this.following = sprite;
    sprite.screenX = 0;
    sprite.screenY = 0;
  };
  
  Camera.prototype.update = function heroSpawn () {
    // assume followed sprite should be placed at the center of the screen
    // whenever possible
    this.following.screenX = this.width / 2;
    this.following.screenY = this.height / 2;
  
    // make the camera follow the sprite
    this.x = this.following.x - this.width / 2;
    this.y = this.following.y - this.height / 2;
    // clamp values
    this.x = Math.max(0, Math.min(this.x, this.maxX));
    this.y = Math.max(0, Math.min(this.y, this.maxY));
  
    // in map corners, the sprite cannot be placed in the center of the screen
    // and we have to change its screen coordinates
  
    // left and right sides
    if (
      this.following.x < this.width / 2 ||
      this.following.x > this.maxX + this.width / 2
    ) {
      this.following.screenX = this.following.x - this.x;
    }
    // top and bottom sides
    if (
      this.following.y < this.height / 2 ||
      this.following.y > this.maxY + this.height / 2
    ) {
      this.following.screenY = this.following.y - this.y;
    }
  };
  
  function Hero(map, x, y) {
    this.map = map;
    this.x = x;
    this.y = y;
    this.width = map.tsize;
    this.height = map.tsize;
  
    this.image = Loader.getImage('hero');
  }
  
  Hero.SPEED = 256; // pixels per second
  
  Hero.prototype.move = function heroMove (delta, dirx, diry) {
    // move hero
    this.x += dirx * Hero.SPEED * delta;
    this.y += diry * Hero.SPEED * delta;
  
    // check if we walked into a non-walkable tile
    this._collide(dirx, diry);
  
    // clamp values
    const maxX = this.map.cols * this.map.tsize;
    const maxY = this.map.rows * this.map.tsize;
    this.x = Math.max(0, Math.min(this.x, maxX));
    this.y = Math.max(0, Math.min(this.y, maxY));
  };
  
  Hero.prototype._collide = function heroCollide (dirx, diry) {
    let row;
    let col;
    // -1 in right and bottom is because image ranges from 0..63
    // and not up to 64
    const left = this.x - this.width / 2;
    const right = this.x + this.width / 2 - 1;
    const top = this.y - this.height / 2;
    const bottom = this.y + this.height / 2 - 1;
  
    // check for collisions on sprite sides
    const collision =
      this.map.isSolidTileAtXY(left, top) ||
      this.map.isSolidTileAtXY(right, top) ||
      this.map.isSolidTileAtXY(right, bottom) ||
      this.map.isSolidTileAtXY(left, bottom) ||
      this.map.ispokemon(right, bottom) ||
      this.map.ispokemon(left, bottom) ||
      this.map.ispokemon(right, top) ||
      this.map.ispokemon(left, top);
    if (!collision) {
      return;
    }
  
    const pok =
      this.map.ispokemon(right, bottom) ||
      this.map.ispokemon(left, bottom) ||
      this.map.ispokemon(right, top) ||
      this.map.ispokemon(left, top);
  
    if (pok) {
      document.getElementById('game').hidden = true;
      document.getElementById('war').hidden = false;
    }
  
    if (diry > 0) {
      row = this.map.getRow(bottom);
      this.y = -this.height / 2 + this.map.getY(row);
    } else if (diry < 0) {
      row = this.map.getRow(top);
      this.y = this.height / 2 + this.map.getY(row + 1);
    } else if (dirx > 0) {
      col = this.map.getCol(right);
      this.x = -this.width / 2 + this.map.getX(col);
    } else if (dirx < 0) {
      col = this.map.getCol(left);
      this.x = this.width / 2 + this.map.getX(col + 1);
    }
  };
  
  Game.load = function gameLoad () {
    const modo = localStorage.getItem('modo');
    document.getElementById('gmode').innerHTML = `Gamemode: ${modo}`;
    localStorage.setItem('pokemon', '0');
    return [
      Loader.loadImage('tiles', '../images/tilespeedrun.png'),
      Loader.loadImage('hero', '../images/game-character.png'),
    ];
  };
  
  Game.init = function gameInit () {
    Keyboard.listenForEvents([
      Keyboard.LEFT,
      Keyboard.RIGHT,
      Keyboard.UP,
      Keyboard.DOWN,
    ]);
    this.tileAtlas = Loader.getImage('tiles');
  
    this.hero = new Hero(map, 160, 160);
    this.camera = new Camera(map, 512, 512);
    this.camera.follow(this.hero);
  };
  
  Game.update = function gameUpd (delta) {
    // handle hero movement with arrow keys
    let dirx = 0;
    let diry = 0;
  
    if (Keyboard.isDown(Keyboard.LEFT)) {
      dirx = -1;
    } else if (Keyboard.isDown(Keyboard.RIGHT)) {
      dirx = 1;
    } else if (Keyboard.isDown(Keyboard.UP)) {
      diry = -1;
    } else if (Keyboard.isDown(Keyboard.DOWN)) {
      diry = 1;
    }
  
    this.hero.move(delta, dirx, diry);
    this.camera.update();
  };
  
  Game._drawLayer = function gameLayer (layer) {
    const startCol = Math.floor(this.camera.x / map.tsize);
    const endCol = startCol + this.camera.width / map.tsize;
    const startRow = Math.floor(this.camera.y / map.tsize);
    const endRow = startRow + this.camera.height / map.tsize;
    const offsetX = -this.camera.x + startCol * map.tsize;
    const offsetY = -this.camera.y + startRow * map.tsize;
  
    for (let c = startCol; c <= endCol; c += 1) {
      for (let r = startRow; r <= endRow; r += 1) {
        const tile = map.getTile(layer, c, r);
        const x = (c - startCol) * map.tsize + offsetX;
        const y = (r - startRow) * map.tsize + offsetY;
        if (tile !== 0) {
          // 0 => empty tile
          this.ctx.drawImage(
            this.tileAtlas, // image
            (tile - 1) * map.tsize, // source x
            0, // source y
            map.tsize, // source width
            map.tsize, // source height
            Math.round(x), // target x
            Math.round(y), // target y
            map.tsize, // target width
            map.tsize // target height
          );
        }
      }
    }
  };
  
  Game._drawGrid = function gameGrid () {
    const width = map.cols * map.tsize;
    const height = map.rows * map.tsize;
    let x;
    let y;
    for (let r = 0; r < map.rows; r += 1) {
      x = -this.camera.x;
      y = r * map.tsize - this.camera.y;
      this.ctx.beginPath();
      this.ctx.moveTo(x, y);
      this.ctx.lineTo(width, y);
      this.ctx.stroke();
    }
    for (let c = 0; c < map.cols; c += 1) {
      x = c * map.tsize - this.camera.x;
      y = -this.camera.y;
      this.ctx.beginPath();
      this.ctx.moveTo(x, y);
      this.ctx.lineTo(x, height);
      this.ctx.stroke();
    }
  };
  
  Game.render = function gameRender () {
    // draw map background layer
    this._drawLayer(0);
  
    // draw main character
    this.ctx.drawImage(
      this.hero.image,
      this.hero.screenX - this.hero.width / 2,
      this.hero.screenY - this.hero.height / 2
    );
  
    // draw map top layer
    this._drawLayer(1);
  };
  