public int countImagesInSector(Sector sector, int levelNumber) {
    if (sector == null) {
      String msg = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    Level targetLevel = this.levels.getLastLevel();
    if (levelNumber >= 0) {
      for (int i = levelNumber; i < this.getLevels().getLastLevel().getLevelNumber(); i++) {
        if (this.levels.isLevelEmpty(i)) continue;

        targetLevel = this.levels.getLevel(i);
        break;
      }
    }

    // Collect all the tiles intersecting the input sector.
    LatLon delta = targetLevel.getTileDelta();
    Angle latOrigin = this.levels.getTileOrigin().getLatitude();
    Angle lonOrigin = this.levels.getTileOrigin().getLongitude();
    final int nwRow = Tile.computeRow(delta.getLatitude(), sector.getMaxLatitude(), latOrigin);
    final int nwCol = Tile.computeColumn(delta.getLongitude(), sector.getMinLongitude(), lonOrigin);
    final int seRow = Tile.computeRow(delta.getLatitude(), sector.getMinLatitude(), latOrigin);
    final int seCol = Tile.computeColumn(delta.getLongitude(), sector.getMaxLongitude(), lonOrigin);

    int numRows = nwRow - seRow + 1;
    int numCols = seCol - nwCol + 1;

    return numRows * numCols;
  }
コード例 #2
0
  /**
   * Base tick method for the level. Updates all entities, tiles, and player. Has slow repopulation
   * algorithm
   */
  public void tick() {
    player.tick();
    List<Entity> toTick = new ArrayList<Entity>();
    int lr = 7;
    for (int x = -lr; x < lr; x++) {
      for (int y = -lr; y < lr; y++) {
        Tile tile = getTile(player.x + x, player.z + y);
        tile.addToTick(toTick);
        tile.tick();
      }
    }

    for (int i = 0; i < toTick.size(); i++) {
      Entity e = toTick.get(i);
      e.tick(this);
    }
    tickcount++;
    if (tickcount % 1800 == 0) {
      System.out.println("Adding entity to world");
      if (r != null) {
        int rx = r.nextInt(w);
        int ry = r.nextInt(h);

        if (skillNoise.noise(rx / 80d, ry / 80d) > 0.0) {
          SmallMob mob = new SmallMob();
          if (!tiles[rx + ry * w].blocks(mob)) {
            tiles[rx + ry * w].addEntity(r.nextInt(4), mob);
          }
        }
      }
    }
  }
コード例 #3
0
ファイル: Chunk.java プロジェクト: WebFreak001/CubeShaft
  public final void update() {
    chunkUpdates++;
    this.setAllDirty();

    Tesselator t = Tesselator.instance;
    for (int i = 0; i < 3; i++) {
      glNewList(this.glRenderList + i, GL_COMPILE);
      t.begin();

      for (int x = x0; x < x1; x++) {
        for (int y = y0; y < y1; y++) {
          for (int z = z0; z < z1; z++) {
            int type = this.level.getTile(x, y, z);
            if (type > 0) {
              Tile tile = Tile.tiles[type];
              tile.render(i, x, y, z);
            }
          }
        }
      }

      t.end();
      glEndList();
      this.isDirty = false;
    }
  }
コード例 #4
0
 @Override
 public void tilesRemoved(Dimension dimension, Set<Tile> tiles) {
   for (Tile tile : tiles) {
     tile.removeListener(this);
   }
   fireTilesChangedIncludeBorder(tiles);
 }
コード例 #5
0
ファイル: TileSet.java プロジェクト: dosage/nenya
  /**
   * Reports statistics detailing the image manager cache performance and the current size of the
   * cached images.
   */
  protected void reportCachePerformance() {
    if (
    /* Log.getLevel() != Log.log.DEBUG || */
    _improv == null || _cacheStatThrottle.throttleOp()) {
      return;
    }

    // compute our estimated memory usage
    long amem = 0;
    int asize = 0;
    synchronized (_atiles) {
      // first total up the active tiles
      Iterator<SoftReference<Tile>> iter = _atiles.values().iterator();
      while (iter.hasNext()) {
        SoftReference<Tile> sref = iter.next();
        Tile tile = sref.get();
        if (tile != null) {
          asize++;
          amem += tile.getEstimatedMemoryUsage();
        }
      }
    }
    log.info(
        "Tile caches",
        "amem",
        (amem / 1024) + "k",
        "tmem",
        (Tile._totalTileMemory / 1024) + "k",
        "seen",
        _atiles.size(),
        "asize",
        +asize);
  }
コード例 #6
0
ファイル: WorldMap.java プロジェクト: Nomworthy/PenguinStrike
 public void constructTile(int xTile, int yTile) {
   tileIntegrity[xTile][yTile] = ((STONEPHASE - 1) * STONEPHASESTR) - 1.0;
   Tile t = new Tile(xTile, yTile);
   t.id = 4002;
   setTileId(t.x, t.y, wallLayerIndex, t.id);
   dirtyTiles.add(t);
 }
コード例 #7
0
ファイル: Game.java プロジェクト: jermin2/minesweeper
 public void right_click(int x, int y) {
   if (this.state == State.Running) {
     Tile clicked_tile = tiles[x][y];
     clicked_tile.updateVisible(Tile.FLAG);
     mines_remaining--;
   }
 }
コード例 #8
0
 /** {@inheritDoc} */
 @Override
 public boolean traverse(final EnumSet<TraversalOption> options) {
   final Tile next = getNext();
   if (next == null) {
     return false;
   }
   if (next.equals(getEnd())) {
     if (Calculations.distanceTo(next) <= 1
         || end && (!Players.getLocal().isMoving() || Calculations.distanceTo(next) < 3)) {
       return false;
     }
     end = true;
   } else {
     end = false;
   }
   if (options != null
       && options.contains(TraversalOption.HANDLE_RUN)
       && !Walking.isRunEnabled()
       && Walking.getEnergy() > 50) {
     Walking.setRun(true);
     Task.sleep(300);
   }
   if (options != null && options.contains(TraversalOption.SPACE_ACTIONS)) {
     final Tile dest = Walking.getDestination();
     if (dest != null
         && Players.getLocal().isMoving()
         && Calculations.distanceTo(dest) > 5
         && Calculations.distanceBetween(next, dest) < 7) {
       return true;
     }
   }
   return Walking.walkTileMM(next, 0, 0);
 }
コード例 #9
0
  public void renderLevel(Graphics g) {
    // Tile loops

    for (Tile t : tiles) {
      if (t.isVisible && t != null) t.render(g);
    }
  } // End render
コード例 #10
0
  private int calculateWord(Word word) {

    int wordScore = 0;
    int tileTimes = 1;
    int wordTimes = 1;
    int[] property;
    LinkedList<Tile> tiles = word.getTiles();
    for (int i = 0; i < tiles.size(); i++) {
      Tile tile = tiles.get(i);
      Location loc = tile.getLocation();

      property = board.getProperty(loc);

      tileTimes = property[0];
      if (1 < property[1]) wordTimes *= property[1];
      System.out.println(
          tile.getCharacter() + ":" + tiles.get(i).getValue() + " " + "tileTimes:" + tileTimes);
      System.out.println(
          tile.getCharacter() + ":" + tiles.get(i).getValue() + " " + "wordTimes:" + property[1]);
      wordScore = wordScore + tiles.get(i).getValue() * tileTimes;
      System.out.println("wordScore update: " + wordScore);
    }
    System.out.println(" one wordTimes: " + wordTimes);

    wordScore *= wordTimes;
    System.out.println(" one word score: " + wordScore);

    return wordScore;
  }
コード例 #11
0
ファイル: Map.java プロジェクト: thyme4soup/adventure_game
  public void genTiles() {
    tiles = new Tile[15][15];
    int tileWidth = getWidth() / tiles.length;
    int tileHeight = getHeight() / tiles[0].length;

    int ponds = 0;
    for (int i = 0; i < 15; i++) {
      for (int j = 0; j < 15; j++) {
        tiles[i][j] = new Tile(tileWidth, tileHeight, (i == 7 && j == 7));
        c.gridx = i;
        c.gridy = j;
        if (tiles[i][j].contains("pond")) ponds++;
        add(tiles[i][j], c);
      }
    }
    // Color background = new Color(167, 175, 60);
    // Color dest = new Color(22, 192, 16);
    ponds -= 20;
    if (ponds < 0) ponds = 0;
    if (ponds > 10) ponds = 10;
    int r = (int) (167 - 14.5 * ponds);
    int g = (int) (175 + 1.7 * ponds);
    int b = (int) (60 - 4.4 * ponds);

    for (Tile[] tileRow : tiles) {
      for (Tile tile : tileRow) {
        tile.background = new Color(r, g, b);
      }
    }

    p.x = (int) Math.ceil(tiles.length / 2);
    p.y = (int) Math.ceil(tiles[p.x].length / 2);
    // tiles[p.x][p.y].setPlayer(true);
    balance();
  }
コード例 #12
0
 public void mouseClicked(MouseEvent e) {
   Tile t = (Tile) e.getSource();
   ImageIcon temp = (ImageIcon) t.getIcon();
   currTileImg = new ImageIcon(scaleImage(temp.getImage(), DISPLAY_SCALE));
   currTileDisplay.setIcon(new ImageIcon(scaleImage(temp.getImage(), DISPLAY_SCALE * 2)));
   currTileLoc = t.getSource();
 }
コード例 #13
0
 /**
  * Returns the move of a blank field that will swap it with this tile according to the rules.
  * Returns <code>null</code> if this tile is not a neighbor of the blank tile, or if it denotes
  * the blank field.
  *
  * @throws IllegalArgumentException if the tile does not belong to this board's tile set
  */
 public Move permittedMoveFor(final Tile tile) {
   final int number = tile.getNumber();
   if (0 > number || tiles.length <= number || tile != tiles[number])
     throw new IllegalArgumentException(tile.toString());
   final int yoffset = tile.getRow() - blank.getRow();
   final int xoffset = tile.getColumn() - blank.getColumn();
   Move move = null;
   switch (xoffset) {
     case 0: // same column
       switch (yoffset) {
         case -1: // tile is above the blank
           move = Move.UP;
           break;
         case 1: // tile is below the blank
           move = Move.DOWN;
           break;
       }
       break;
     case 1:
     case -1:
       // horizontal moves are allowed within a single row
       if (0 == yoffset) move = 0 > xoffset ? Move.LEFT : Move.RIGHT;
       break;
   }
   return move;
 }
コード例 #14
0
 protected void establishTarget(Tile tile) {
   int number = tile.getNumber();
   if (0 == number) number = getTileCount();
   number--;
   final int edgeSize = getSize();
   tile.target(number / edgeSize, number % edgeSize);
 }
コード例 #15
0
ファイル: Platform.java プロジェクト: museng/Runner
  public Platform(GameAssets assets, int level) {

    textureIds = new String[kindsCount * partsCount];
    for (int i = 0, k = 0; i < textureIds.length; i += partsCount, k++) {
      textureIds[i] = "platform." + String.valueOf(k) + ".top";
      textureIds[i + 1] = "platform." + String.valueOf(k) + ".top_left";
      textureIds[i + 2] = "platform." + String.valueOf(k) + ".top_right";
      textureIds[i + 3] = "platform." + String.valueOf(k) + ".bottom";
      textureIds[i + 4] = "platform." + String.valueOf(k) + ".bottom_left";
      textureIds[i + 5] = "platform." + String.valueOf(k) + ".bottom_right";
    }

    this.assets = assets;

    topLeft = new Sprite();
    topLeft.boundingBox.width = blockWidth;
    topLeft.boundingBox.height = blockHeight;

    top = new Tile(blockWidth, blockHeight);
    top.boundingBox.height = blockHeight;
    top.boundingBox.x = topLeft.boundingBox.width;

    topRight = new Sprite();
    topRight.boundingBox.width = blockWidth;
    topRight.boundingBox.height = blockHeight;

    if (level >= 0) {
      float bottomTileHeight = 32;
      int v = 6 - level;
      float by = -2 * top.boundingBox.height;
      bottomLeft = new Tile(blockWidth, bottomTileHeight);
      bottomLeft.setCountV(v);
      bottomLeft.boundingBox.y = by;

      bottom = new Tile(blockWidth, bottomTileHeight);
      bottom.setCountV(v);
      bottom.boundingBox.y = by;
      bottom.boundingBox.x = bottomLeft.boundingBox.width;

      bottomRight = new Tile(blockWidth, bottomTileHeight);
      bottomRight.setCountV(v);
      bottomRight.boundingBox.y = by;

      figures.add(bottomLeft);
      figures.add(bottom);
      figures.add(bottomRight);
    }

    figures.add(topLeft);
    figures.add(topRight);
    figures.add(top);

    setBlocksCount(minBlocksCount);
    boundingBox.height = top.boundingBox.height;
    setKind(0);

    for (int i = 0; i < figures.size(); i++) {
      ((Sprite) figures.get(i)).brightness = 1 - (3 - level) * 0.15f;
    }
  }
コード例 #16
0
ファイル: TileLayerList.java プロジェクト: Grokmoo/hale
  /**
   * saves this grid to JSON format for creating area files
   *
   * @return this grid JSON data
   */
  public JSONOrderedObject writeToJSON() {
    JSONOrderedObject data = new JSONOrderedObject();

    Map<String, List<int[]>> out = new LinkedHashMap<String, List<int[]>>();

    for (int x = 0; x < tiles.length; x++) {
      for (int y = 0; y < tiles[x].length; y++) {
        // important to create the array here each time so it isn't overwritten later
        int[] coords = new int[2];
        coords[0] = x;
        coords[1] = y;

        TileList list = tiles[x][y];

        for (Tile tile : list) {
          String id = tile.getTileID();

          if (!out.containsKey(id)) {
            out.put(id, new ArrayList<int[]>());
          }

          out.get(id).add(coords);
        }
      }
    }

    for (String tileID : out.keySet()) {
      data.put(tileID, out.get(tileID).toArray());
    }

    return data;
  }
コード例 #17
0
ファイル: Map.java プロジェクト: scarboot/Base-Attack
  @Override
  public void update(double t) {

    { // UPDATE TOWERS
      for (Tile[] tiles : getTiles())
        for (Tile tile : tiles) if (tile.hasTower()) tile.getTower().update(t);
    }

    { // UPDATE BULLETS
      final Iterator<Bullet> it = getBullets().iterator();

      while (it.hasNext()) {

        final Bullet b = it.next();

        if (b.update(t)) it.remove();
      }
    }

    { // UPDATE MOBS
      final Iterator<Mob> it = getMobs().iterator();

      while (it.hasNext()) {

        final Mob m = it.next();

        m.update(t);

        if (m.shouldBeRemoved()) {
          m.onRemove(getGame());
          it.remove();
        }
      }
    }
  }
コード例 #18
0
ファイル: Board.java プロジェクト: hellsant/wd-chess
 public void clearGameBoard() {
   for (final Tile tile : this.gameBoard) {
     if (tile.isTileOccupied()) {
       tile.removePiece();
     }
   }
 }
コード例 #19
0
 /**
  * Serialize the List
  *
  * @return The list as a String
  */
 String serialize() {
   JSONArray array = new JSONArray();
   for (Tile t : this) {
     array.put(t.json());
   }
   return array.toString();
 }
コード例 #20
0
ファイル: RoleControl.java プロジェクト: darragh-murphy/LGame
	public final void AddPosition(Vector2f nextDestPosition) {
		Vector2f vector;
		nextDestPosition.y = MathUtils.clamp(nextDestPosition.y, 0f,
				LSystem.screenRect.height - 1f);
		nextDestPosition.x = MathUtils.clamp(nextDestPosition.x, 0f,
				LSystem.screenRect.width - 1f);

		vector = nextDestPosition;
		Vector2f position = this.position.cpy();
		if (this.destination.size() != 0) {
			position = this.destination.get(this.destination.size() - 1).cpy();
		}
		float num = MathUtils.atan2((nextDestPosition.y - position.y),
				(nextDestPosition.x - position.x));

		if (Vector2f.dst(position, nextDestPosition) > Tile.size) {
			vector = position.add(new Vector2f(MathUtils.cos(num) * Tile.size,
					MathUtils.sin(num) * Tile.size));
		}

		Vector2f item = new Vector2f(Tile.getBounds(vector).getCenterX(), Tile
				.getBounds(vector).getCenterY());

		if (!this.destination.contains(item)) {
			this.destination.add(item);
			AddPosition(vector);
		}
		int index = this.destination.indexOf(item);
		this.destination.subList(index + 1,
				(this.destination.size() - index) - 1 + index + 1).clear();
	}
コード例 #21
0
ファイル: Game.java プロジェクト: jermin2/minesweeper
  /**
   * Add a mine to the board, and increment the surrounding neighbours
   *
   * @param x
   * @param y
   * @return true if added mine
   */
  private boolean addMine(int x, int y) {

    if (checkBounds(x, y) == false) return false;

    Tile t = tiles[x][y];

    if (t.value == -1) return false;

    t.updateValue(-1);

    // Add 1 to the value of the neighbours
    for (int f = -1; f <= 1; f++)
      for (int g = -1; g <= 1; g++) {

        // Check the neighbours are within the bounds
        if (checkBounds(x + f, y + g)) {
          Tile neighbour = tiles[x + f][y + g];

          // Check neighbour is not a mine
          if (neighbour.value != -1) neighbour.updateValue(neighbour.value + 1);
        }
      }

    return true;
  }
コード例 #22
0
ファイル: Field.java プロジェクト: TimotheusG/ColorGame
 public static void evalTile(Tile tile, GlobalValues.Colors color) {
   if (tile.color == color) {
     tile.selected = true;
     tile.changeColor(GlobalValues.Colors.selected);
     evalNeighbors(tile, color);
   }
 }
コード例 #23
0
ファイル: TileSet.java プロジェクト: dosage/nenya
  /**
   * Creates a {@link Tile} object from this tileset corresponding to the specified tile id and
   * returns that tile. A null tile will never be returned, but one with an error image may be
   * returned if a problem occurs loading the underlying tileset image.
   *
   * @param tileIndex the index of the tile in the tileset. Tile indexes start with zero as the
   *     upper left tile and increase by one as the tiles move left to right and top to bottom over
   *     the source image.
   * @param zations colorizations to be applied to the tile image prior to returning it. These may
   *     be null for uncolorized images.
   * @return the tile object.
   */
  public Tile getTile(int tileIndex, Colorization[] zations) {
    Tile tile = null;

    // first look in the active set; if it's in use by anyone or in the cache, it will be in
    // the active set
    synchronized (_atiles) {
      _key.tileSet = this;
      _key.tileIndex = tileIndex;
      _key.zations = zations;
      SoftReference<Tile> sref = _atiles.get(_key);
      if (sref != null) {
        tile = sref.get();
      }
    }

    // if it's not in the active set, it's not in memory; so load it
    if (tile == null) {
      tile = createTile();
      tile.key = new Tile.Key(this, tileIndex, zations);
      initTile(tile, tileIndex, zations);
      synchronized (_atiles) {
        _atiles.put(tile.key, new SoftReference<Tile>(tile));
      }
    }

    // periodically report our image cache performance
    reportCachePerformance();

    return tile;
  }
コード例 #24
0
  /**
   * Visszaad egy tile-t adott pozícióra adott szám alapján.
   *
   * @param tileNumber
   * @param posX
   * @param posY
   * @return
   * @throws ENoSuchTileException
   * @throws EInitException
   */
  public static Tile getTile(int tileNumber, int posX, int posY)
      throws ENoSuchTileException, EInitException {
    Tile tile;

    switch (tileNumber) {
      case 0:
        return null;
      case 1:
        tile = new WallTile(posX, posY);
        break;
      case 2:
        tile = new ConcreteWallTile(posX, posY);
        break;
      case 3:
        tile = new GrassTile(posX, posY);
        break;
      case 9:
        tile = new EggTile(posX, posY);
        break;
      case 4:
        tile = new WaterTile(posX, posY);
        break;
      default:
        throw new ENoSuchTileException();
    }

    tile.init();

    return tile;
  }
コード例 #25
0
 /**
  * Calculates distance between two locations on the game map.
  *
  * @param t1 one location on the game map
  * @param t2 another location on the game map
  * @return distance between <code>t1</code> and <code>t2</code>
  */
 public int getDistance(Tile t1, Tile t2) {
   int rowDelta = Math.abs(t1.getRow() - t2.getRow());
   int colDelta = Math.abs(t1.getCol() - t2.getCol());
   rowDelta = Math.min(rowDelta, rows - rowDelta);
   colDelta = Math.min(colDelta, cols - colDelta);
   return rowDelta * rowDelta + colDelta * colDelta;
 }
コード例 #26
0
ファイル: Map.java プロジェクト: bloodrizer/Nordland
  public static synchronized void build_tile(int x, int y, int z) {
    Vector3 origin;
    float height = 0.0f;

    if (USE_HASHMAP) {
      origin = new Vector3(x, y, z);
    } else {
      origin = util_v3.set(x, y, z);
    }

    height = Heightmap.get_height(x, z);
    Tile tmp_tile = get_tile(x, y, z); // careful, it will switch util_v3 AND origin to local view

    if (tmp_tile != null) {
      tmp_tile.v_reset();
    }

    if (y < height) {
      if (tmp_tile == null) {

        tmp_tile = new Tile(origin);
        set_tile(origin, tmp_tile);

      } else {
        tiles[origin.x][origin.y][origin.z].tile_type = 1;
      }

      tile_count++;

    } else {
      if (tmp_tile != null) {
        tmp_tile.drop();
      }
    }
  }
コード例 #27
0
 private void fireTilesChangedIncludeBorder(Set<Tile> tiles) {
   if (showBorder
       && (tileProvider instanceof Dimension)
       && (((Dimension) tileProvider).getDim() == DIM_NORMAL)
       && (((Dimension) tileProvider).getBorder() != null)) {
     final Set<Point> coordSet = new HashSet<>();
     for (Tile tile : tiles) {
       final int tileX = tile.getX(),
           tileY = tile.getY(),
           borderSize = ((Dimension) tileProvider).getBorderSize();
       for (int dx = -borderSize; dx <= borderSize; dx++) {
         for (int dy = -borderSize; dy <= borderSize; dy++) {
           coordSet.add(getTileCoordinates(tileX + dx, tileY + dy));
         }
       }
     }
     for (TileListener listener : listeners) {
       listener.tilesChanged(this, coordSet);
     }
   } else {
     Set<Point> coords = tiles.stream().map(this::getTileCoordinates).collect(Collectors.toSet());
     for (TileListener listener : listeners) {
       listener.tilesChanged(this, coords);
     }
   }
 }
コード例 #28
0
  protected static boolean isTileVisible(
      DrawContext dc, Tile tile, double minDistanceSquared, double maxDistanceSquared) {
    if (!tile.getSector().intersects(dc.getVisibleSector())) return false;

    View view = dc.getView();
    Position eyePos = view.getEyePosition();
    if (eyePos == null) return false;

    Angle lat =
        clampAngle(
            eyePos.getLatitude(),
            tile.getSector().getMinLatitude(),
            tile.getSector().getMaxLatitude());
    Angle lon =
        clampAngle(
            eyePos.getLongitude(),
            tile.getSector().getMinLongitude(),
            tile.getSector().getMaxLongitude());
    Vec4 p = dc.getGlobe().computePointFromPosition(lat, lon, 0d);
    double distSquared = dc.getView().getEyePoint().distanceToSquared3(p);
    //noinspection RedundantIfStatement
    if (minDistanceSquared > distSquared || maxDistanceSquared < distSquared) return false;

    return true;
  }
コード例 #29
0
  protected boolean loadTile(Tile tile, java.net.URL url) {
    if (WWIO.isFileOutOfDate(url, this.placeNameServiceSet.getExpiryTime())) {
      // The file has expired. Delete it then request download of newer.
      this.getDataFileStore().removeFile(url);
      String message = Logging.getMessage("generic.DataFileExpired", url);
      Logging.logger().fine(message);
      return false;
    }

    PlaceNameChunk tileData;
    synchronized (this.fileLock) {
      tileData = readTileData(tile, url);
    }

    if (tileData == null) {
      // Assume that something's wrong with the file and delete it.
      this.getDataFileStore().removeFile(url);
      tile.getPlaceNameService()
          .markResourceAbsent(tile.getPlaceNameService().getTileNumber(tile.row, tile.column));
      String message = Logging.getMessage("generic.DeletedCorruptDataFile", url);
      Logging.logger().fine(message);
      return false;
    }

    tile.setDataChunk(tileData);
    WorldWind.getMemoryCache(Tile.class.getName()).add(tile.getFileCachePath(), tile);
    return true;
  }
コード例 #30
0
 @Override
 public void drawTo(Canvas drawCanvas, float left, float top, Paint paint, boolean onlyDirty) {
   final Rect src = new Rect(0, 0, mTileSize, mTileSize);
   final Rect dst = new Rect(0, 0, mTileSize, mTileSize);
   drawCanvas.save();
   drawCanvas.translate(-left, -top);
   drawCanvas.clipRect(0, 0, mWidth, mHeight);
   for (int j = 0; j < mTilesY; j++) {
     for (int i = 0; i < mTilesX; i++) {
       dst.offsetTo(i * mTileSize, j * mTileSize);
       final int p = j * mTilesX + i;
       final Tile tile = mTiles[p];
       if (!onlyDirty || tile.dirty) {
         drawCanvas.drawBitmap(tile.getBitmap(), src, dst, paint);
         tile.dirty = false;
         if (mDebug) {
           mDrawCount++;
           dbgPaint.setColor(DEBUG_COLORS[tile.top % DEBUG_COLORS.length]);
           // drawCanvas.drawRect(dst, (mDrawCount % 2 == 0) ? dbgPaint1 : dbgPaint2);
           drawCanvas.drawRect(dst, dbgPaint);
           // drawCanvas.drawRect(dst, dbgStroke);
           drawCanvas.drawText(
               String.format("%d,%d v%d", tile.x, tile.y, tile.top),
               dst.left + 4,
               dst.bottom - 4,
               dbgTextPaint);
         }
       }
     }
   }
   drawCanvas.restore();
 }