@Override
  public void mouseWheelMoved(MouseWheelEvent e) {
    double z = zoom;

    zoom *=
        Math.pow(
            1.1, (Client.instance.preferences.game.invertZoom ? 1 : -1) * e.getWheelRotation());
    if (zoom < 0.2) zoom = 0.2;
    if (zoom > 5) zoom = 5;
    double d = zoom - z;
    scrollX += d * e.getX() * 2;
    scrollY += d * e.getY() * 2;
    if (scrollX < 0) scrollX = 0;
    if (scrollY < 0) scrollY = 0;
    int mapWidth = mapCache.length;
    int mapHeight = mapCache[0].length;
    if (scrollX > (int) (mapWidth * tileSize * zoom - width))
      scrollX = (int) (mapWidth * tileSize * zoom - width);
    if (scrollY > (int) (mapHeight * tileSize * zoom - height))
      scrollY = (int) (mapHeight * tileSize * zoom - height);
  }
  @Override
  public void render(Graphics2D g2do, int width, int height) {
    this.width = width;
    this.height = height;
    cache = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = cache.createGraphics();
    for (GuiControl c : controlList) c.render(g2d, width, height);

    Tile[][] map = Client.instance.map;
    int mapWidth = map.length;
    int mapHeight = map[0].length;
    double w = (mapWidth * tileSize * zoom);
    if (w < width) zoom /= w / width;
    double h = (mapHeight * tileSize * zoom);
    if (h < height) zoom /= h / height;
    AffineTransform tx = g2d.getTransform();
    g2d.translate(-scrollX, -scrollY);
    g2d.scale(zoom, zoom);

    // Rendering Map
    if (this.map == null || !map.equals(mapCache)) {
      this.map =
          new BufferedImage(
              map.length * tileSize, map[0].length * tileSize, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g = this.map.createGraphics();
      for (int x = 0; x < map.length; x++) {
        for (int y = 0; y < map[x].length; y++) {
          Tile tile = map[x][y];
          Texture tex = TileRenderMap.getTexture(tile);
          if (tex == null) continue;
          g.drawImage(tex.getImage(), x * tileSize, y * tileSize, tileSize, tileSize, tex);
        }
      }
      g.dispose();
      mapCache = map;
    }
    g2d.drawImage(this.map, 0, 0, null);

    // Rendering Buildings
    Building[][] buildings = Client.instance.buildings;
    for (int x = 0; x < buildings.length; x++) {
      for (int y = 0; y < buildings[x].length; y++) {
        if (buildings[x][y] != null) {
          Texture tex = buildings[x][y].getTexture();
          if (tex != null)
            g2d.drawImage(
                tex.getImage(),
                x * tileSize,
                y * tileSize,
                tex.getWidth() / (TILE_SIZE_NORMAL / tileSize),
                tex.getHeight() / (TILE_SIZE_NORMAL / tileSize),
                tex);
        }
      }
    }

    if (fieldHover == null || fieldHover.length != mapWidth || fieldHover[0].length != mapHeight)
      fieldHover = new boolean[mapWidth][mapHeight];
    if (field == null || field.length != mapWidth || field[0].length != mapHeight)
      field = new boolean[mapWidth][mapHeight];

    // Rendering Click and Hover
    for (int x = 0; x < fieldHover.length; x++) {
      for (int y = 0; y < fieldHover[x].length; y++) {
        if (fieldHover[x][y]) {
          Texture tex = StandardTexture.get("hover");
          Building b = buildings[x][y];
          g2d.drawImage(
              tex.getImage(),
              x * tileSize,
              y * tileSize,
              tileSize * b.getSizeX(),
              tileSize * b.getSizeY(),
              tex);
        }

        if (field[x][y]) {
          Texture tex = StandardTexture.get("hover_clicked");
          Building b = buildings[x][y];
          g2d.drawImage(
              tex.getImage(),
              x * tileSize,
              y * tileSize,
              tileSize * b.getSizeX(),
              tileSize * b.getSizeY(),
              tex);
        }
      }
    }

    g2d.setTransform(tx);

    int[] x = new int[] {width / 2, width - tileSize / 2, width / 2, tileSize / 2};
    int[] y = new int[] {tileSize / 2, height / 2, height - tileSize / 2, height / 2};
    Texture tex = StandardTexture.get("arrow_" + Client.instance.preferences.game.arrow);
    for (int i = 0; i < 4; i++) {
      if (i == 0 && scrollY <= 0) continue;
      if (i == 1 && scrollX >= (int) (mapWidth * tileSize * zoom - width)) continue;
      if (i == 2 && scrollY >= (int) (mapHeight * tileSize * zoom - height)) continue;
      if (i == 3 && scrollX <= 0) continue;
      g2d.translate(x[i], y[i]);
      g2d.rotate(Math.toRadians(90 * i));
      g2d.drawImage(tex.getImage(), -tileSize / 2, -tileSize / 2, tex);
      g2d.setTransform(tx);
    }

    g2d.dispose();
    g2do.drawImage(cache, 0, 0, null);
  }