diff --git a/src/landscape.cpp b/src/landscape.cpp index 79b24baf72..991a445ad8 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -131,9 +131,15 @@ Point InverseRemapCoords2(int x, int y, bool clamp_to_map, bool *clamped) * (FOUNDATION_HALFTILE_LOWER on SLOPE_STEEP_S hides north halftile completely) * So give it a z-malus of 4 in the first iterations. */ int z = 0; - for (int i = 0; i < 5; i++) z = GetSlopePixelZ(Clamp(pt.x + max(z, 4) - 4, min_coord, max_x), Clamp(pt.y + max(z, 4) - 4, min_coord, max_y)) / 2; - for (int m = 3; m > 0; m--) z = GetSlopePixelZ(Clamp(pt.x + max(z, m) - m, min_coord, max_x), Clamp(pt.y + max(z, m) - m, min_coord, max_y)) / 2; - for (int i = 0; i < 5; i++) z = GetSlopePixelZ(Clamp(pt.x + z, min_coord, max_x), Clamp(pt.y + z, min_coord, max_y)) / 2; + if (clamp_to_map) { + for (int i = 0; i < 5; i++) z = GetSlopePixelZ(Clamp(pt.x + max(z, 4) - 4, min_coord, max_x), Clamp(pt.y + max(z, 4) - 4, min_coord, max_y)) / 2; + for (int m = 3; m > 0; m--) z = GetSlopePixelZ(Clamp(pt.x + max(z, m) - m, min_coord, max_x), Clamp(pt.y + max(z, m) - m, min_coord, max_y)) / 2; + for (int i = 0; i < 5; i++) z = GetSlopePixelZ(Clamp(pt.x + z, min_coord, max_x), Clamp(pt.y + z, min_coord, max_y)) / 2; + } else { + for (int i = 0; i < 5; i++) z = GetSlopePixelZOutsideMap(pt.x + max(z, 4) - 4, pt.y + max(z, 4) - 4) / 2; + for (int m = 3; m > 0; m--) z = GetSlopePixelZOutsideMap(pt.x + max(z, m) - m, pt.y + max(z, m) - m) / 2; + for (int i = 0; i < 5; i++) z = GetSlopePixelZOutsideMap(pt.x + z, pt.y + z ) / 2; + } pt.x += z; pt.y += z; @@ -342,6 +348,23 @@ int GetSlopePixelZ(int x, int y) return _tile_type_procs[GetTileType(tile)]->get_slope_z_proc(tile, x, y); } +/** + * Return world \c z coordinate of a given point of a tile, + * also for tiles outside the map (virtual "black" tiles). + * + * @param x World X coordinate in tile "units", may be ouside the map. + * @param y World Y coordinate in tile "units", may be ouside the map. + * @return World Z coordinate at tile ground level, including slopes and foundations. + */ +int GetSlopePixelZOutsideMap(int x, int y) +{ + if (IsInsideBS(x, 0, MapSizeX() * TILE_SIZE) && IsInsideBS(y, 0, MapSizeY() * TILE_SIZE)) { + return GetSlopePixelZ(x, y); + } else { + return _tile_type_procs[MP_VOID]->get_slope_z_proc(INVALID_TILE, x, y); + } +} + /** * Determine the Z height of a corner relative to TileZ. * diff --git a/src/landscape.h b/src/landscape.h index d24d0d190d..43d9e5f2e6 100644 --- a/src/landscape.h +++ b/src/landscape.h @@ -40,6 +40,7 @@ Slope GetFoundationSlope(TileIndex tile, int *z = NULL); uint GetPartialPixelZ(int x, int y, Slope corners); int GetSlopePixelZ(int x, int y); +int GetSlopePixelZOutsideMap(int x, int y); void GetSlopePixelZOnEdge(Slope tileh, DiagDirection edge, int *z1, int *z2); /** diff --git a/src/tile_map.cpp b/src/tile_map.cpp index 05b0ea1942..200a203089 100644 --- a/src/tile_map.cpp +++ b/src/tile_map.cpp @@ -14,60 +14,6 @@ #include "safeguards.h" -/** - * Returns the tile height for a coordinate outside map. Such a height is - * needed for painting the area outside map using completely black tiles. - * The idea is descending to heightlevel 0 as fast as possible. - * @param x The X-coordinate (same unit as TileX). - * @param y The Y-coordinate (same unit as TileY). - * @return The height in the same unit as TileHeight. - */ -uint TileHeightOutsideMap(int x, int y) -{ - /* In all cases: Descend to heightlevel 0 as fast as possible. - * So: If we are at the 0-side of the map (x<0 or y<0), we must - * subtract the distance to coordinate 0 from the heightlevel at - * coordinate 0. - * In other words: Subtract e.g. -x. If we are at the MapMax - * side of the map, we also need to subtract the distance to - * the edge of map, e.g. MapMaxX - x. - * - * NOTE: Assuming constant heightlevel outside map would be - * simpler here. However, then we run into painting problems, - * since whenever a heightlevel change at the map border occurs, - * we would need to repaint anything outside map. - * In contrast, by doing it this way, we can localize this change, - * which means we may assume constant heightlevel for all tiles - * at more than distance from the - * map border. - */ - if (x < 0) { - if (y < 0) { - return max((int)TileHeight(TileXY(0, 0)) - (-x) - (-y), 0); - } else if (y < (int)MapMaxY()) { - return max((int)TileHeight(TileXY(0, y)) - (-x), 0); - } else { - return max((int)TileHeight(TileXY(0, (int)MapMaxY())) - (-x) - (y - (int)MapMaxY()), 0); - } - } else if (x < (int)MapMaxX()) { - if (y < 0) { - return max((int)TileHeight(TileXY(x, 0)) - (-y), 0); - } else if (y < (int)MapMaxY()) { - return TileHeight(TileXY(x, y)); - } else { - return max((int)TileHeight(TileXY(x, (int)MapMaxY())) - (y - (int)MapMaxY()), 0); - } - } else { - if (y < 0) { - return max((int)TileHeight(TileXY((int)MapMaxX(), 0)) - (x - (int)MapMaxX()) - (-y), 0); - } else if (y < (int)MapMaxY()) { - return max((int)TileHeight(TileXY((int)MapMaxX(), y)) - (x - (int)MapMaxX()), 0); - } else { - return max((int)TileHeight(TileXY((int)MapMaxX(), (int)MapMaxY())) - (x - (int)MapMaxX()) - (y - (int)MapMaxY()), 0); - } - } -} - /** * Get a tile's slope given the heigh of its four corners. * @param hnorth The height at the northern corner in the same unit as TileHeight. @@ -114,30 +60,26 @@ static Slope GetTileSlopeGivenHeight(int hnorth, int hwest, int heast, int hsout */ Slope GetTileSlope(TileIndex tile, int *h) { - assert(tile < MapSize()); + uint x1 = TileX(tile); + uint y1 = TileY(tile); + uint x2 = min(x1 + 1, MapMaxX()); + uint y2 = min(y1 + 1, MapMaxY()); - uint x = TileX(tile); - uint y = TileY(tile); - if (x == MapMaxX() || y == MapMaxY()) { - if (h != NULL) *h = TileHeight(tile); - return SLOPE_FLAT; - } - - int hnorth = TileHeight(tile); // Height of the North corner. - int hwest = TileHeight(tile + TileDiffXY(1, 0)); // Height of the West corner. - int heast = TileHeight(tile + TileDiffXY(0, 1)); // Height of the East corner. - int hsouth = TileHeight(tile + TileDiffXY(1, 1)); // Height of the South corner. + int hnorth = TileHeight(tile); // Height of the North corner. + int hwest = TileHeight(TileXY(x2, y1)); // Height of the West corner. + int heast = TileHeight(TileXY(x1, y2)); // Height of the East corner. + int hsouth = TileHeight(TileXY(x2, y2)); // Height of the South corner. return GetTileSlopeGivenHeight(hnorth, hwest, heast, hsouth, h); } /** - * Return the slope of a given tile outside the map. + * Return the slope of a given tile, also for tiles outside the map (virtual "black" tiles). * - * @param x X-coordinate of the tile outside to compute height of. - * @param y Y-coordinate of the tile outside to compute height of. - * @param h If not \c NULL, pointer to storage of z height. - * @return Slope of the tile outside map, except for the HALFTILE part. + * @param x X coordinate of the tile to compute slope of, may be ouside the map. + * @param y Y coordinate of the tile to compute slope of, may be ouside the map. + * @param h If not \c NULL, pointer to storage of z height. + * @return Slope of the tile, except for the HALFTILE part. */ Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h) { @@ -159,17 +101,15 @@ Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h) */ bool IsTileFlat(TileIndex tile, int *h) { - assert(tile < MapSize()); - - if (!IsInnerTile(tile)) { - if (h != NULL) *h = TileHeight(tile); - return true; - } + uint x1 = TileX(tile); + uint y1 = TileY(tile); + uint x2 = min(x1 + 1, MapMaxX()); + uint y2 = min(y1 + 1, MapMaxY()); uint z = TileHeight(tile); - if (TileHeight(tile + TileDiffXY(1, 0)) != z) return false; - if (TileHeight(tile + TileDiffXY(0, 1)) != z) return false; - if (TileHeight(tile + TileDiffXY(1, 1)) != z) return false; + if (TileHeight(TileXY(x2, y1)) != z) return false; + if (TileHeight(TileXY(x1, y2)) != z) return false; + if (TileHeight(TileXY(x2, y2)) != z) return false; if (h != NULL) *h = z; return true; @@ -182,12 +122,15 @@ bool IsTileFlat(TileIndex tile, int *h) */ int GetTileZ(TileIndex tile) { - if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return 0; + uint x1 = TileX(tile); + uint y1 = TileY(tile); + uint x2 = min(x1 + 1, MapMaxX()); + uint y2 = min(y1 + 1, MapMaxY()); - int h = TileHeight(tile); // N corner - h = min(h, TileHeight(tile + TileDiffXY(1, 0))); // W corner - h = min(h, TileHeight(tile + TileDiffXY(0, 1))); // E corner - h = min(h, TileHeight(tile + TileDiffXY(1, 1))); // S corner + int h = TileHeight(tile); // N corner + h = min(h, TileHeight(TileXY(x2, y1))); // W corner + h = min(h, TileHeight(TileXY(x1, y2))); // E corner + h = min(h, TileHeight(TileXY(x2, y2))); // S corner return h; } @@ -199,12 +142,15 @@ int GetTileZ(TileIndex tile) */ int GetTileMaxZ(TileIndex t) { - if (TileX(t) == MapMaxX() || TileY(t) == MapMaxY()) return TileHeightOutsideMap(TileX(t), TileY(t)); + uint x1 = TileX(t); + uint y1 = TileY(t); + uint x2 = min(x1 + 1, MapMaxX()); + uint y2 = min(y1 + 1, MapMaxY()); - int h = TileHeight(t); // N corner - h = max(h, TileHeight(t + TileDiffXY(1, 0))); // W corner - h = max(h, TileHeight(t + TileDiffXY(0, 1))); // E corner - h = max(h, TileHeight(t + TileDiffXY(1, 1))); // S corner + int h = TileHeight(t); // N corner + h = max(h, TileHeight(TileXY(x2, y1))); // W corner + h = max(h, TileHeight(TileXY(x1, y2))); // E corner + h = max(h, TileHeight(TileXY(x2, y2))); // S corner return h; } diff --git a/src/tile_map.h b/src/tile_map.h index 3fad368cd7..7a76f2b974 100644 --- a/src/tile_map.h +++ b/src/tile_map.h @@ -34,7 +34,17 @@ static inline uint TileHeight(TileIndex tile) return _m[tile].height; } -uint TileHeightOutsideMap(int x, int y); +/** + * Returns the height of a tile, also for tiles outside the map (virtual "black" tiles). + * + * @param x X coordinate of the tile, may be ouside the map. + * @param y Y coordinate of the tile, may be ouside the map. + * @return The height in the same unit as TileHeight. + */ +static inline uint TileHeightOutsideMap(int x, int y) +{ + return TileHeight(TileXY(Clamp(x, 0, MapMaxX()), Clamp(y, 0, MapMaxY()))); +} /** * Sets the height of a tile. @@ -67,11 +77,10 @@ static inline uint TilePixelHeight(TileIndex tile) } /** - * Returns the tile height for a coordinate outside map. Such a height is - * needed for painting the area outside map using completely black tiles. - * The idea is descending to heightlevel 0 as fast as possible. - * @param x The X-coordinate (same unit as TileX). - * @param y The Y-coordinate (same unit as TileY). + * Returns the height of a tile in pixels, also for tiles outside the map (virtual "black" tiles). + * + * @param x X coordinate of the tile, may be ouside the map. + * @param y Y coordinate of the tile, may be ouside the map. * @return The height in pixels in the same unit as TilePixelHeight. */ static inline uint TilePixelHeightOutsideMap(int x, int y) diff --git a/src/void_cmd.cpp b/src/void_cmd.cpp index ffe54df992..a2a45e7227 100644 --- a/src/void_cmd.cpp +++ b/src/void_cmd.cpp @@ -10,7 +10,7 @@ /** @file void_cmd.cpp Handling of void tiles. */ #include "stdafx.h" -#include "tile_cmd.h" +#include "landscape.h" #include "command_func.h" #include "viewport_func.h" #include "slope_func.h" @@ -28,7 +28,12 @@ static void DrawTile_Void(TileInfo *ti) static int GetSlopePixelZ_Void(TileIndex tile, uint x, uint y) { - return TilePixelHeight(tile); + /* This function may be called on tiles outside the map, don't asssume + * that 'tile' is a valid tile index. See GetSlopePixelZOutsideMap. */ + int z; + Slope tileh = GetTilePixelSlopeOutsideMap(x >> 4, y >> 4, &z); + + return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh); } static Foundation GetFoundation_Void(TileIndex tile, Slope tileh)