diff --git a/src/tile_map.cpp b/src/tile_map.cpp index 9855484dff..17d7d6a392 100644 --- a/src/tile_map.cpp +++ b/src/tile_map.cpp @@ -15,7 +15,99 @@ #include "safeguards.h" /** - * Return the slope of a given 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). + * @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. + * @param hwest The height at the western corner in the same unit as TileHeight. + * @param heast The height at the eastern corner in the same unit as TileHeight. + * @param hsouth The height at the southern corner in the same unit as TileHeight. + * @param [out] h The lowest height of the four corners. + * @return The slope. + */ +static Slope GetTileSlopeGivenHeight(int hnorth, int hwest, int heast, int hsouth, int *h) +{ + /* Due to the fact that tiles must connect with each other without leaving gaps, the + * biggest difference in height between any corner and 'min' is between 0, 1, or 2. + * + * Also, there is at most 1 corner with height difference of 2. + */ + int hminnw = min(hnorth, hwest); + int hmines = min(heast, hsouth); + int hmin = min(hminnw, hmines); + + if (h != NULL) *h = hmin; + + int hmaxnw = max(hnorth, hwest); + int hmaxes = max(heast, hsouth); + int hmax = max(hmaxnw, hmaxes); + + Slope r = SLOPE_FLAT; + + if (hnorth != hmin) r |= SLOPE_N; + if (hwest != hmin) r |= SLOPE_W; + if (heast != hmin) r |= SLOPE_E; + if (hsouth != hmin) r |= SLOPE_S; + + if (hmax - hmin == 2) r |= SLOPE_STEEP; + + return r; +} + +/** + * Return the slope of a given tile inside the map. * @param tile Tile to compute slope of * @param h If not \c NULL, pointer to storage of z height * @return Slope of the tile, except for the HALFTILE part @@ -29,35 +121,31 @@ Slope GetTileSlope(TileIndex tile, int *h) return SLOPE_FLAT; } - int a = TileHeight(tile); // Height of the N corner - int min = a; // Minimal height of all corners examined so far - int b = TileHeight(tile + TileDiffXY(1, 0)); // Height of the W corner - if (min > b) min = b; - int c = TileHeight(tile + TileDiffXY(0, 1)); // Height of the E corner - if (min > c) min = c; - int d = TileHeight(tile + TileDiffXY(1, 1)); // Height of the S corner - if (min > d) min = d; + 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. - /* Due to the fact that tiles must connect with each other without leaving gaps, the - * biggest difference in height between any corner and 'min' is between 0, 1, or 2. - * - * Also, there is at most 1 corner with height difference of 2. - */ + return GetTileSlopeGivenHeight(hnorth, hwest, heast, hsouth, h); +} - uint r = SLOPE_FLAT; // Computed slope of the tile +/** + * Return the slope of a given tile outside the map. + * + * @param tile Tile outside the map to compute slope 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. + */ +Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h) +{ + int hnorth = TileHeightOutsideMap(x, y); // N corner. + int hwest = TileHeightOutsideMap(x + 1, y); // W corner. + int heast = TileHeightOutsideMap(x, y + 1); // E corner. + int hsouth = TileHeightOutsideMap(x + 1, y + 1); // S corner. - /* For each corner if not equal to minimum height: - * - set the SLOPE_STEEP flag if the difference is 2 - * - add the corresponding SLOPE_X constant to the computed slope - */ - if ((a -= min) != 0) r += (--a << 4) + SLOPE_N; - if ((c -= min) != 0) r += (--c << 4) + SLOPE_E; - if ((d -= min) != 0) r += (--d << 4) + SLOPE_S; - if ((b -= min) != 0) r += (--b << 4) + SLOPE_W; - - if (h != NULL) *h = min; - - return (Slope)r; + Slope s = GetTileSlopeGivenHeight(hnorth, hwest, heast, hsouth, h); + if (h != NULL) *h *= TILE_HEIGHT; + return s; } /** @@ -93,7 +181,7 @@ int GetTileZ(TileIndex tile) { if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return 0; - int h = TileHeight(tile); // N corner + 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 @@ -102,18 +190,52 @@ int GetTileZ(TileIndex tile) } /** - * Get top height of the tile + * Get bottom height of the tile outside map. + * + * @param tile Tile outside the map to compute height of. + * @return Minimum height of the tile outside the map. + */ +int GetTilePixelZOutsideMap(int x, int y) +{ + uint h = TileHeightOutsideMap(x, y); // N corner. + h = min(h, TileHeightOutsideMap(x + 1, y)); // W corner. + h = min(h, TileHeightOutsideMap(x, y + 1)); // E corner. + h = min(h, TileHeightOutsideMap(x + 1, y + 1)); // S corner + + return h * TILE_HEIGHT; +} + +/** + * Get top height of the tile inside the map. * @param t Tile to compute height of * @return Maximum height of the tile */ int GetTileMaxZ(TileIndex t) { - if (TileX(t) == MapMaxX() || TileY(t) == MapMaxY()) return 0; + if (TileX(t) == MapMaxX() || TileY(t) == MapMaxY()) return TileHeightOutsideMap(TileX(t), TileY(t)); - int h = TileHeight(t); // N corner + 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 return h; } + +/** + * Get top height of the tile outside the map. + * + * @see Detailed description in header. + * + * @param tile Tile outside to compute height of. + * @return Maximum height of the tile. + */ +int GetTileMaxPixelZOutsideMap(int x, int y) +{ + uint h = TileHeightOutsideMap(x, y); + h = max(h, TileHeightOutsideMap(x + 1, y)); + h = max(h, TileHeightOutsideMap(x, y + 1)); + h = max(h, TileHeightOutsideMap(x + 1, y + 1)); + + return h * TILE_HEIGHT; +} diff --git a/src/tile_map.h b/src/tile_map.h index 98863eb599..af1cc32dc2 100644 --- a/src/tile_map.h +++ b/src/tile_map.h @@ -34,6 +34,8 @@ static inline uint TileHeight(TileIndex tile) return GB(_m[tile].type_height, 0, 4); } +uint TileHeightOutsideMap(int x, int y); + /** * Sets the height of a tile. * @@ -262,6 +264,8 @@ static inline Slope GetTilePixelSlope(TileIndex tile, int *h) return s; } +Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h); + /** * Get bottom height of the tile * @param tile Tile to compute height of @@ -272,6 +276,8 @@ static inline int GetTilePixelZ(TileIndex tile) return GetTileZ(tile) * TILE_HEIGHT; } +int GetTilePixelZOutsideMap(int x, int y); + /** * Get top height of the tile * @param t Tile to compute height of @@ -282,6 +288,8 @@ static inline int GetTileMaxPixelZ(TileIndex tile) return GetTileMaxZ(tile) * TILE_HEIGHT; } +int GetTileMaxPixelZOutsideMap(int x, int y); + /** * Calculate a hash value from a tile position