Index: src/viewport_func.h =================================================================== --- src/viewport_func.h (Revision 20854) +++ src/viewport_func.h (Arbeitskopie) @@ -73,4 +73,6 @@ extern Point _tile_fract_coords; +int GetRowAtTile(int viewport_y, Point tile); + #endif /* VIEWPORT_FUNC_H */ Index: src/functions.h =================================================================== --- src/functions.h (Revision 20854) +++ src/functions.h (Arbeitskopie) @@ -34,6 +34,7 @@ * @ingroup dirty */ void MarkTileDirtyByTile(TileIndex tile); +void MarkTileDirtyByTileOutsideMap(int x, int y); void ShowCostOrIncomeAnimation(int x, int y, int z, Money cost); void ShowFeederIncomeAnimation(int x, int y, int z, Money cost); Index: src/terraform_cmd.cpp =================================================================== --- src/terraform_cmd.cpp (Revision 20854) +++ src/terraform_cmd.cpp (Arbeitskopie) @@ -348,6 +348,84 @@ TileIndex *ti = ts.tile_table; for (count = ts.tile_table_count; count != 0; count--, ti++) { MarkTileDirtyByTile(*ti); + int height = TerraformGetHeightOfTile(&ts, *ti); + + /* Now, if we alter the height of the map edge, we need to take care + * about repainting the affected areas outside map as well. + * Remember: + * Outside map, we assume that our landscape descends to + * height zero as fast as possible. + * Those simulated tiles (they don't exist as datastructure, + * only as concept in code) need to be repainted properly, + * otherwise we will get ugly glitches. + * + * Furthermore, note that we have to take care about the possibility, + * that landscape was higher before the change, + * so also tiles a bit outside need to be repainted. */ + int x = TileX(*ti); + int y = TileY(*ti); + + if (x == 0) { + if (y == 0) { + /* Height of the northern corner is altered. */ + for (int cx = 0; cx >= -height - 1; cx--) { + for (int cy = 0; cy >= -height - 1; cy--) { + /* This means, tiles in the sector north of that + * corner need to be repainted. */ + if (cx + cy >= -height - 2) { + /* But only tiles that actually might have changed. */ + MarkTileDirtyByTileOutsideMap(cx, cy); + } + } + } + } else if (y < (int)MapMaxY()) { + for (int cx = 0; cx >= -height - 1; cx--) { + MarkTileDirtyByTileOutsideMap(cx, y); + } + } else { + for (int cx = 0; cx >= -height - 1; cx--) { + for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) { + if (cx + ((int)MapMaxY() - cy) >= -height - 2) { + MarkTileDirtyByTileOutsideMap(cx, cy); + } + } + } + } + } else if (x < (int)MapMaxX()) { + if (y == 0) { + for (int cy = 0; cy >= -height - 1; cy--) { + MarkTileDirtyByTileOutsideMap(x, cy); + } + } else if (y < (int)MapMaxY()) { + /* Nothing to be done here, we are inside the map. */ + } else { + for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) { + MarkTileDirtyByTileOutsideMap(x, cy); + } + } + } else { + if (y == 0) { + for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) { + for (int cy = 0; cy >= -height - 1; cy--) { + if (((int)MapMaxX() - cx) + cy >= -height - 2) { + MarkTileDirtyByTileOutsideMap(cx, cy); + } + } + } + } else if (y < (int)MapMaxY()) { + for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) { + MarkTileDirtyByTileOutsideMap(cx, y); + } + } else { + for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) { + for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) { + if (((int)MapMaxX() - cx) + ((int)MapMaxY() - cy) >= -height - 2) { + MarkTileDirtyByTileOutsideMap(cx, cy); + } + } + } + } + } } } } Index: src/viewport.cpp =================================================================== --- src/viewport.cpp (Revision 20854) +++ src/viewport.cpp (Arbeitskopie) @@ -48,6 +48,8 @@ #include "table/strings.h" +#include + PlaceProc *_place_proc; Point _tile_fract_coords; @@ -995,91 +997,498 @@ } } -static void ViewportAddLandscape() +/** + * Returns the upper left edge of the region defined by the given (x,y)-pair + * converted to tile coordinates (i.e. as returned by TileX / TileY). We assume + * that the upper left edge is at height zero here.

+ * + * In detail: + * We return a tile where the upper left edge of the mentioned region + * is either in its lower right section or in a neighbor tile below / right of + * that section. By doing so, we want to enforce that we can travel to east or + * south from that point without leaving the region again. (of course we + * would leave it if we would pass the opposite end of the region). + * + * @param x some x coordinate. + * @param y some y coordinate. + * @return upper left edge of the region as tile coordinates. + */ +static Point GetMinTileCoordsIgnoringHeight(int x, int y) { - int x, y, width, height; - TileInfo ti; - bool direction; + Point tile_coord; + /* First convert from the screen coordinate system (where the width of tiles + * is twice their height) to the tile coordinate system. That means, turn + * around by 45 degrees and make the tiles quadratic. */ + tile_coord.x = (y / 2) - (x / 4); + tile_coord.y = (y / 2) + (x / 4); - _cur_ti = &ti; + /* Scale from a 16x16-grid to a 1x1-grid as returned by TileX/TileY. */ + tile_coord.x /= (int)TILE_SIZE; + tile_coord.y /= (int)TILE_SIZE; - /* Transform into tile coordinates and round to closest full tile */ - x = ((_vd.dpi.top >> 1) - (_vd.dpi.left >> 2)) & ~TILE_UNIT_MASK; - y = ((_vd.dpi.top >> 1) + (_vd.dpi.left >> 2) - TILE_SIZE) & ~TILE_UNIT_MASK; + /* Expand area to be painted in order to avoid situations + * where south or east of the to be painted point in dpi are tiles + * which will not be painted. */ + tile_coord.y--; - /* determine size of area */ + return tile_coord; +} + +static Point GetMaxTileCoordsIgnoringHeight(int x, int y) +{ + Point tile_coord; + /* First convert from the screen coordinate system (where the width of tiles + * is twice their height) to the tile coordinate system. That means, turn + * around by 45 degrees and make the tiles quadratic. */ + tile_coord.x = (y / 2) - (x / 4); + tile_coord.y = (y / 2) + (x / 4); + + /* Scale from a 16x16-grid to a 1x1-grid as returned by + * TileX/TileY. */ + tile_coord.x /= (int)TILE_SIZE; + tile_coord.y /= (int)TILE_SIZE; + + /* Expand area to be painted to southeast in order to avoid situations + * where north or east of the given to be painted point in dpi are + * tiles which will not be repainted. */ + tile_coord.y++; + + return tile_coord; +} + +/** + * Given a tile coordinate as returned by TileX / TileY, this returns its column. + * + * Examples: + * GetTileColumnFromTileCoord(0,0) = 0. + * GetTileColumnFromTileCoord(1,1) = 0. + * GetTileColumnFromTileCoord(1,0) = -1. + * GetTileColumnFromTileCoord(0,1) = 1. + */ +static int GetTileColumnFromTileCoord(Point tile_coord) +{ + return tile_coord.y - tile_coord.x; +} + +/* Not needed. But maybe useful in the future? + static int GetTileRowFromTileCoord(Point tile_coord) { - Point pt = RemapCoords(x, y, 241); - width = (_vd.dpi.left + _vd.dpi.width - pt.x + 95) >> 6; - height = (_vd.dpi.top + _vd.dpi.height - pt.y) >> 5 << 1; + return tile_coord.y + tile_coord.x; } +*/ - assert(width > 0); - assert(height > 0); +/** + * Returns the y coordinate in the viewport coordinate system where the given + * tile is painted. + * @param tile Any tile. + * @return The viewport y coordinate where the tile is painted. + */ +static int GetViewportY(Point tile) +{ + return tile.y * TILE_SIZE + tile.x * TILE_SIZE - GetTileMaxZOutsideMap(tile.x, tile.y); +} - direction = false; +/** + * Returns the position of the tile at the northern end of the column of the + * given tile. + * @param tile Any tile. + * @return northern_end Position of the tile at the northern end of the column as described. + */ +static Point GetNorthernEndOfColumn(Point tile) +{ + Point distance_to_end; + distance_to_end.x = (int)MapMaxX() - tile.x; + distance_to_end.y = (int)MapMaxY() - tile.y; + Point northern_end; - do { - int width_cur = width; - uint x_cur = x; - uint y_cur = y; + if (tile.x < tile.y) { + northern_end.x = 0; + northern_end.y = tile.y - tile.x; + } else { + northern_end.x = tile.x - tile.y; + northern_end.y = 0; + } - do { - TileType tt = MP_VOID; + return northern_end; +} - ti.x = x_cur; - ti.y = y_cur; +/** + * Returns the position of the tile at the southern end of the column of the + * given tile. + * @param tile any tile. + * @return southern_end position of the tile at the soutern end of the column as described. + */ +static Point GetSouthernEndOfColumn(Point tile) +{ + Point distance_to_end; + distance_to_end.x = (int)MapMaxX() - tile.x; + distance_to_end.y = (int)MapMaxY() - tile.y; + Point southern_end; - ti.z = 0; + if (distance_to_end.x < distance_to_end.y) { + southern_end.x = tile.x + distance_to_end.x; + southern_end.y = tile.y + distance_to_end.x; + } else { + southern_end.x = tile.x + distance_to_end.y; + southern_end.y = tile.y + distance_to_end.y; + } - ti.tileh = SLOPE_FLAT; - ti.tile = INVALID_TILE; + return southern_end; +} - if (x_cur < MapMaxX() * TILE_SIZE && - y_cur < MapMaxY() * TILE_SIZE) { - TileIndex tile = TileVirtXY(x_cur, y_cur); +/** + * Returns the tile exactly in the middle between two given tiles. + * + * @param tile Point upper_tile, any tile. + * @param tile Point lower_tile, any tile. + * @return middle_tile The tile in the middle of Point upper_tile and Point lower_tile. + */ +static Point GetMiddleTile(Point upper_tile, Point lower_tile) { +// Handling is better than asserting (in this case) ... maybe a debug function? +// assert(upper_tile.x <= lower_tile.x); +// assert(upper_tile.y <= lower_tile.y); - if (!_settings_game.construction.freeform_edges || (TileX(tile) != 0 && TileY(tile) != 0)) { - if (x_cur == ((int)MapMaxX() - 1) * TILE_SIZE || y_cur == ((int)MapMaxY() - 1) * TILE_SIZE) { - uint maxh = max(TileHeight(tile), 1); - for (uint h = 0; h < maxh; h++) { - AddTileSpriteToDraw(SPR_SHADOW_CELL, PAL_NONE, ti.x, ti.y, h * TILE_HEIGHT); - } - } + /* If upper_tile is lower than lower_tile swap tiles. + * Since we are only interested in the middle tile here, it is safe to do so. */ + if (upper_tile.x <= lower_tile.x) Swap(upper_tile.x, lower_tile.x); + if (upper_tile.y <= lower_tile.y) Swap(upper_tile.y, lower_tile.y); + + Point middle_tile; - ti.tile = tile; - ti.tileh = GetTileSlope(tile, &ti.z); - tt = GetTileType(tile); - } - } + middle_tile.x = upper_tile.x + (lower_tile.x - upper_tile.x) / 2; + middle_tile.y = upper_tile.y + (lower_tile.y - upper_tile.y) / 2; + return middle_tile; +} - _vd.foundation_part = FOUNDATION_PART_NONE; - _vd.foundation[0] = -1; - _vd.foundation[1] = -1; - _vd.last_foundation_child[0] = NULL; - _vd.last_foundation_child[1] = NULL; +/** + * Given a tile coordinate ignoring height, this returns the row actually + * painted at this tile coordinate if one recognizes height. + * + * The problem concerning this calculation is that we have not enough + * information to calculate this in one closed formula. Which row we + * search rather depends on the height distribution on the map. So + * we have to search. + * + * First, the searched tile may be located outside map. Then, we know + * that we are not too far outside map, so we can step tile by tile, + * starting at the given tile, until we have passed the searched tile. + * + * If the searched tile is inside map, searching is more difficult. A + * linear search on some thousand tiles would be not that efficient. But, + * we can solve the problem by interval intersection. We know for sure, + * that the searched tile is south of the given tile, simply because + * mountains of height > 0 (and we have only such mountains) are always + * painted north of their tile. So we choose a tile half way between the + * given tile and the southern end of the map, have a look wether it is + * north or south of the given position, and intersect again. Until + * our interval has length 1, then we take the upper one. + * + * @param tile some tile coordinate + * @param viewport_y the viewport y corresponding to tile, if one assumes height zero for that tile + * @return the row which is painted at this coordinate, according to the discussion above. + */ +int GetRowAtTile(int viewport_y, Point tile) +{ + Point northern_tile = GetNorthernEndOfColumn(tile); + Point southern_tile = GetSouthernEndOfColumn(tile); - _tile_type_procs[tt]->draw_tile_proc(&ti); + int northern_tile_viewport_y = GetViewportY(northern_tile); + int southern_tile_viewport_y = GetViewportY(southern_tile); - if ((x_cur == (int)MapMaxX() * TILE_SIZE && IsInsideMM(y_cur, 0, MapMaxY() * TILE_SIZE + 1)) || - (y_cur == (int)MapMaxY() * TILE_SIZE && IsInsideMM(x_cur, 0, MapMaxX() * TILE_SIZE + 1))) { - TileIndex tile = TileVirtXY(x_cur, y_cur); - ti.tile = tile; - ti.tileh = GetTileSlope(tile, &ti.z); - tt = GetTileType(tile); + DEBUG(driver, 9, "==> GetRowAtTile: tile=(%i,%i); n/s_tile=(%i,%i),(%i,%i); n/s_tile_vp_y=(%i,%i); viewport_y = %i", + tile.x, tile.y, northern_tile.x, northern_tile.y, southern_tile.x, southern_tile.y, + northern_tile_viewport_y, southern_tile_viewport_y, viewport_y); + + if (northern_tile_viewport_y >= viewport_y) { + /* We are north of the map, search tile by tile with direction north. */ + while (northern_tile_viewport_y >= viewport_y) { + northern_tile.x--; + northern_tile.y--; + northern_tile_viewport_y = GetViewportY(northern_tile); + } + + DEBUG(driver, 9, "==> We are north of the map => Returning row for (%i,%i)", + northern_tile.x, northern_tile.y); + + return northern_tile.x + northern_tile.y; + } else if (southern_tile_viewport_y <= viewport_y) { + /* We are south of the map, search tile by tile with direction south. */ + while (southern_tile_viewport_y <= viewport_y) { + southern_tile.x++; + southern_tile.y++; + southern_tile_viewport_y = GetViewportY(southern_tile); + } + + DEBUG(driver, 9, "==> We are south of the map => Returning row for (%i,%i)", + southern_tile.x, southern_tile.y); + + return southern_tile.x + southern_tile.y; + } else { + /* We are inside the map. */ + + /* Invariant in the code below: The searched tile shown at viewport_y + * always is between upper_tile and lower_tile. */ + Point upper_tile = tile; + Point lower_tile = GetSouthernEndOfColumn(upper_tile); + int middle_bound; + + DEBUG(driver, 9, "==> GetRowAtTile for vp_y = %i, (upper_)tile (%i,%i), lower_tile (%i,%i)", + viewport_y, upper_tile.x, upper_tile.y, lower_tile.x, lower_tile.y); + + do { + Point middle_tile = GetMiddleTile(upper_tile, lower_tile); + middle_bound = GetViewportY(middle_tile); + + DEBUG(driver, 9, "====> upper: (%i,%i), middle (%i,%i), lower (%i,%i); m_bound = %i", + upper_tile.x, upper_tile.y, middle_tile.x, middle_tile.y, + lower_tile.x, lower_tile.y, middle_bound); + + if (middle_bound >= viewport_y) { + /* The tile shown at viewport_y is somewhere in the upper half of + * the currently observed section. */ + lower_tile = middle_tile; + } else { + /* The tile shown at viewport_y is somewhere in the lower half of + * the currently observed section. */ + upper_tile = middle_tile; } - if (ti.tile != INVALID_TILE) DrawTileSelection(&ti); + } + while (lower_tile.y - upper_tile.y > 1); - y_cur += 0x10; - x_cur -= 0x10; - } while (--width_cur); + /* Now our interval has length 1, so it only contains two tiles, and we take + * the upper one, calculating and returning its row. */ + return upper_tile.x + upper_tile.y; + } +} - if ((direction ^= 1) != 0) { - y += 0x10; +/* Not needed. But maybe useful in the future? + * + * TODO: + * coding style is to be aplied when/if integrated into the patch. */ +/** + * Returns the coordinates of the northest possible tile south of top_tile_of_column + * in the column of top_tile_of_column. Sense of this function: Having a look on + * tiles outside the map does not make sense. Here, we map all tiles possibly + * calculated outside the map on the northwest / northeast map border. In other + * words, such tiles are mapped southwards. If south of them is no map tile, + * the tile (-1,-1) is returned. + */ +/* +static Point GetTopExistingTileOfColumn(Point top_tile_of_column) +{ + Point top_existing_tile_of_column; + top_existing_tile_of_column.x = top_tile_of_column.x; + top_existing_tile_of_column.y = top_tile_of_column.y; + // We are northeast of the northeast map border. Go south until we + // are at that border. + if (top_existing_tile_of_column.x < 0) { + // E.g. map (-5,4) to (0,9) at the northeast map border + top_existing_tile_of_column.y += (-top_existing_tile_of_column.x); + top_existing_tile_of_column.x = 0; + } + // We are northwest of the northwest map border. Go south until we + // are at the border + if (top_existing_tile_of_column.y < 0) { + // E.g. map (3,-7) to (10,0) + top_existing_tile_of_column.x += (-top_existing_tile_of_column.y); + top_existing_tile_of_column.y = 0; + } + // If our starting point is too far in the west or east, we missed the map. + // Then, at least one of the two coordinates is bigger than allowed. + if (top_existing_tile_of_column.x > (int)MapMaxX() + || top_existing_tile_of_column.y > (int)MapMaxY()) { + top_existing_tile_of_column.x = -1; + top_existing_tile_of_column.y = -1; + } + + return top_existing_tile_of_column; +} +*/ + +/** + * Returns the bottom tile of the column of upper_tile shown on the viewport, + * given upper_tile and the lower right tile shown on the viewport. + * + * @param upper_tile any tile inside the map + * @param lower_right_tile the tile shown at the southeast edge of the viewport + * (ignoring height). Note that this tile may be located + * northeast of the upper_tile, because upper_tile is usually + * calculated by shifting a tile southwards until we reach + * the northern map border. + * @return the lowest existing tile located in the column defined by upper_tile, + * which is in the same row as lower_right_tile or above that row + * If lower_right_tile was northeast of upper_tile, (-1,-1) is returned. + */ +static Point GetBottomTileOfColumn(Point upper_tile, Point lower_right_tile) +{ + int upper_row = upper_tile.x + upper_tile.y; + int lower_row = lower_right_tile.x + lower_right_tile.y; + + assert(upper_row <= lower_row); + + int number_of_rows = lower_row - upper_row; + + if (number_of_rows % 2 != 0) { + /* Avoid 0.5 being rounded down to zero; painting too much is better than + * painting to less. */ + number_of_rows++; + } + + Point bottom_tile; + bottom_tile.x = upper_tile.x + number_of_rows / 2; + bottom_tile.y = upper_tile.y + number_of_rows / 2; + + int bottom_row = bottom_tile.x + bottom_tile.y; + + assert(bottom_row >= lower_row); + + return bottom_tile; +} + +static void ViewportAddLandscape() +{ + assert(_vd.dpi.top <= _vd.dpi.top + _vd.dpi.height); + assert(_vd.dpi.left <= _vd.dpi.left + _vd.dpi.width); + + /* The upper and lower edge of the viewport part to paint. Add some number + * of pixels to the lower end in order to ensure that we also take tiles + * south of the given area, but with high buildings intersecting the area. + * Subtract some pixels from the upper end in order to avoid glitches at the + * upper end of the top be painted area. */ + int viewport_top = _vd.dpi.top - 16; + int viewport_bottom = _vd.dpi.top + _vd.dpi.height + 116; + + DEBUG(driver, 9, "viewport: x = %i, y = %i, w = %i, h = %i", + _vd.dpi.left, _vd.dpi.top, _vd.dpi.width, _vd.dpi.height); + + DEBUG(driver, 9, "viewport_top = %i, viewport_bottom = %i", + viewport_top, viewport_bottom); + + /* First get the position of the tile at the upper left / lower right edge, + * for now ignoring the height. (i.e. assuming height zero.) */ + Point upper_left_tile = GetMinTileCoordsIgnoringHeight(_vd.dpi.left, viewport_top); + Point lower_right_tile = GetMaxTileCoordsIgnoringHeight(_vd.dpi.left + _vd.dpi.width, viewport_bottom); + + DEBUG(driver, 9, "upper_left_tile = (%i,%i); lower_right_tile = (%i,%i)", + upper_left_tile.x, upper_left_tile.y, lower_right_tile.x, lower_right_tile.y); + + /* Calculate the bounding columns. We won't need to draw anything + * left / right of them. */ + int left_column = GetTileColumnFromTileCoord(upper_left_tile); + /* Correction to avoid glitches when approaching the left edge of the map. + * for this purpose, left_column-- would suffice. left_column-- is + * necessary because otherwise we get glitches when moving a window + * to the left at the very right edge of the map. */ + left_column--; + int right_column = GetTileColumnFromTileCoord(lower_right_tile); + right_column++; + + DEBUG(driver, 9, "left_column = %i, right_column = %i", + left_column, right_column); + + /* For each column, calculate the top and the bottom row. These are the + * bounding rows for that specific column. */ + int *top_row = AllocaM(int, right_column - left_column + 1); // Pre-allocate memory for visual studio/express to be able to compile. + int *bottom_row = AllocaM(int, right_column - left_column + 1); // Pre-allocate memory for visual studio/express to be able to compile. + int min_top_row = MapMaxX() + MapMaxY(); + int max_bottom_row = 0; + Point top_tile_of_column = upper_left_tile; + + /* And now for each column, determine the top and the bottom row we must paint. */ + bool south_east_direction = false; + for (int x = left_column; x <= right_column; x++) { + Point bottom_tile_of_column = GetBottomTileOfColumn(top_tile_of_column, lower_right_tile); + + DEBUG(driver, 9, "Inspecting column %i; top_tile=(%i,%i); bottom_tile=(%i, %i)", + x, top_tile_of_column.x, top_tile_of_column.y, + bottom_tile_of_column.x, bottom_tile_of_column.y); + + /* And then actually find out the top and the bottom row. Note that + * top_tile_of_column and bottom_tile_of_column may be outside the map here. + * This possibility is needed, otherwise we couldn't paint the black area + * outside the map (and in particular the edge of map) properly. + * Subtract / add one to avoid glitches because our calculation seems to + * be not absolutely exactly. */ + top_row[x - left_column] = GetRowAtTile(viewport_top, top_tile_of_column); + + DEBUG(driver, 9, "GetRowAtTile for viewport_top = %i and top_tile_of_column = (%i,%i) is %i", + viewport_top, top_tile_of_column.x, top_tile_of_column.y, top_row[x - left_column]); + + top_row[x - left_column] -= 2; + bottom_row[x - left_column] = GetRowAtTile(viewport_bottom, bottom_tile_of_column); + bottom_row[x - left_column]++; + + /* We never paint things in rows < min_top_row or > max_bottom_row. */ + min_top_row = min(min_top_row, top_row[x - left_column]); + max_bottom_row = max(max_bottom_row, bottom_row[x - left_column]); + + /* Go to next column in the east. */ + if (south_east_direction) { + top_tile_of_column.y++; } else { - x += 0x10; + top_tile_of_column.x--; } - } while (--height); + + /* Switch between directions southeast and northeast. */ + south_east_direction = !south_east_direction; + } + + for (int n = 0; n < right_column - left_column + 1; n++) { + + DEBUG(driver, 9, "==> top/bottom of column %i is %i .. %i", + left_column + n, top_row[n], bottom_row[n]); + } + + for (int row = min_top_row; row <= max_bottom_row; row++) { + for (int column = left_column + (row % 2); column <= right_column; column += 2) { + /* For each column, we only paint the interval top_row .. bottom_row. */ + if ( (top_row[column - left_column] <= row) + && (row <= bottom_row[column - left_column])) { + + TileType tile_type; + TileInfo tile_info; + /* Copied from the old code, I know that the treatment of that + * global variable is not very nice - By modifying tile_info + * below, we also modify _cur_ti! */ + _cur_ti = &tile_info; + + /* column = y - x; row = x + y; now solve the equation system + * for x and y. */ + int x = (row - column) / 2; + int y = (row + column) / 2; + tile_info.x = x; + tile_info.y = y; + + /* For some strange reason, those fields inside tile_info are uints. However, + * in the old code their copies in an int variable where compared against zero. */ + if (0 < x && x < (int)MapMaxX() + && 0 < y && y < (int)MapMaxY()) { + /* We are inside the map => paint landscape. */ + tile_info.tile = TileXY(tile_info.x, tile_info.y); + tile_info.tileh = GetTileSlope(tile_info.tile, &tile_info.z); + tile_type = GetTileType(tile_info.tile); + } else { + /* We are outside the map => paint black. */ + tile_info.tile = 0; + tile_info.tileh = GetTileSlopeOutsideMap(tile_info.x, tile_info.y, &tile_info.z); + tile_type = MP_VOID; + } + + /* Scale to 16x16 tiles, needed for the drawing procedures called below. */ + tile_info.x *= TILE_SIZE; + tile_info.y *= TILE_SIZE; + + _vd.foundation_part = FOUNDATION_PART_NONE; + _vd.foundation[0] = -1; + _vd.foundation[1] = -1; + _vd.last_foundation_child[0] = NULL; + _vd.last_foundation_child[1] = NULL; + + _tile_type_procs[tile_type]->draw_tile_proc(&tile_info); + DrawTileSelection(&tile_info); + } + } + } } /** @@ -1612,6 +2023,20 @@ ); } +void MarkTileDirtyByTileOutsideMap(int x, int y) +{ + Point pt = RemapCoords(x * TILE_SIZE, y * TILE_SIZE, GetTileZOutsideMap(x, y)); + /* Since tiles painted outside the map don't contain buildings, trees, etc., + * this reduced area for repainting should suffice. If not, adjust the offsets + * used below. */ + MarkAllViewportsDirty( + pt.x - TILE_SIZE + 1, + pt.y, + pt.x + TILE_SIZE - 1, + pt.y + TILE_SIZE + TILE_HEIGHT - 1 + ); +} + /** * Marks the selected tiles as dirty. *