1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
8 /** @file tile_map.cpp Global tile accessors. */
9 
10 #include "stdafx.h"
11 #include "tile_map.h"
12 
13 #include "safeguards.h"
14 
15 /**
16  * Get a tile's slope given the heigh of its four corners.
17  * @param hnorth The height at the northern corner in the same unit as TileHeight.
18  * @param hwest  The height at the western corner in the same unit as TileHeight.
19  * @param heast  The height at the eastern corner in the same unit as TileHeight.
20  * @param hsouth The height at the southern corner in the same unit as TileHeight.
21  * @param[out] h The lowest height of the four corners.
22  * @return The slope.
23  */
GetTileSlopeGivenHeight(int hnorth,int hwest,int heast,int hsouth,int * h)24 static Slope GetTileSlopeGivenHeight(int hnorth, int hwest, int heast, int hsouth, int *h)
25 {
26 	/* Due to the fact that tiles must connect with each other without leaving gaps, the
27 	 * biggest difference in height between any corner and 'min' is between 0, 1, or 2.
28 	 *
29 	 * Also, there is at most 1 corner with height difference of 2.
30 	 */
31 	int hminnw = std::min(hnorth, hwest);
32 	int hmines = std::min(heast, hsouth);
33 	int hmin = std::min(hminnw, hmines);
34 
35 	if (h != nullptr) *h = hmin;
36 
37 	int hmaxnw = std::max(hnorth, hwest);
38 	int hmaxes = std::max(heast, hsouth);
39 	int hmax = std::max(hmaxnw, hmaxes);
40 
41 	Slope r = SLOPE_FLAT;
42 
43 	if (hnorth != hmin) r |= SLOPE_N;
44 	if (hwest  != hmin) r |= SLOPE_W;
45 	if (heast  != hmin) r |= SLOPE_E;
46 	if (hsouth != hmin) r |= SLOPE_S;
47 
48 	if (hmax - hmin == 2) r |= SLOPE_STEEP;
49 
50 	return r;
51 }
52 
53 /**
54  * Return the slope of a given tile inside the map.
55  * @param tile Tile to compute slope of
56  * @param h    If not \c nullptr, pointer to storage of z height
57  * @return Slope of the tile, except for the HALFTILE part
58  */
GetTileSlope(TileIndex tile,int * h)59 Slope GetTileSlope(TileIndex tile, int *h)
60 {
61 	uint x1 = TileX(tile);
62 	uint y1 = TileY(tile);
63 	uint x2 = std::min(x1 + 1, MapMaxX());
64 	uint y2 = std::min(y1 + 1, MapMaxY());
65 
66 	int hnorth = TileHeight(tile);           // Height of the North corner.
67 	int hwest  = TileHeight(TileXY(x2, y1)); // Height of the West corner.
68 	int heast  = TileHeight(TileXY(x1, y2)); // Height of the East corner.
69 	int hsouth = TileHeight(TileXY(x2, y2)); // Height of the South corner.
70 
71 	return GetTileSlopeGivenHeight(hnorth, hwest, heast, hsouth, h);
72 }
73 
74 /**
75  * Return the slope of a given tile, also for tiles outside the map (virtual "black" tiles).
76  *
77  * @param x X coordinate of the tile to compute slope of, may be outside the map.
78  * @param y Y coordinate of the tile to compute slope of, may be outside the map.
79  * @param h If not \c nullptr, pointer to storage of z height.
80  * @return Slope of the tile, except for the HALFTILE part.
81  */
GetTilePixelSlopeOutsideMap(int x,int y,int * h)82 Slope GetTilePixelSlopeOutsideMap(int x, int y, int *h)
83 {
84 	int hnorth = TileHeightOutsideMap(x,     y);     // N corner.
85 	int hwest  = TileHeightOutsideMap(x + 1, y);     // W corner.
86 	int heast  = TileHeightOutsideMap(x,     y + 1); // E corner.
87 	int hsouth = TileHeightOutsideMap(x + 1, y + 1); // S corner.
88 
89 	Slope s = GetTileSlopeGivenHeight(hnorth, hwest, heast, hsouth, h);
90 	if (h != nullptr) *h *= TILE_HEIGHT;
91 	return s;
92 }
93 
94 /**
95  * Check if a given tile is flat
96  * @param tile Tile to check
97  * @param h If not \c nullptr, pointer to storage of z height (only if tile is flat)
98  * @return Whether the tile is flat
99  */
IsTileFlat(TileIndex tile,int * h)100 bool IsTileFlat(TileIndex tile, int *h)
101 {
102 	uint x1 = TileX(tile);
103 	uint y1 = TileY(tile);
104 	uint x2 = std::min(x1 + 1, MapMaxX());
105 	uint y2 = std::min(y1 + 1, MapMaxY());
106 
107 	uint z = TileHeight(tile);
108 	if (TileHeight(TileXY(x2, y1)) != z) return false;
109 	if (TileHeight(TileXY(x1, y2)) != z) return false;
110 	if (TileHeight(TileXY(x2, y2)) != z) return false;
111 
112 	if (h != nullptr) *h = z;
113 	return true;
114 }
115 
116 /**
117  * Get bottom height of the tile
118  * @param tile Tile to compute height of
119  * @return Minimum height of the tile
120  */
GetTileZ(TileIndex tile)121 int GetTileZ(TileIndex tile)
122 {
123 	uint x1 = TileX(tile);
124 	uint y1 = TileY(tile);
125 	uint x2 = std::min(x1 + 1, MapMaxX());
126 	uint y2 = std::min(y1 + 1, MapMaxY());
127 
128 	return std::min({
129 		TileHeight(tile),           // N corner
130 		TileHeight(TileXY(x2, y1)), // W corner
131 		TileHeight(TileXY(x1, y2)), // E corner
132 		TileHeight(TileXY(x2, y2)), // S corner
133 	});
134 }
135 
136 /**
137  * Get top height of the tile inside the map.
138  * @param t Tile to compute height of
139  * @return Maximum height of the tile
140  */
GetTileMaxZ(TileIndex t)141 int GetTileMaxZ(TileIndex t)
142 {
143 	uint x1 = TileX(t);
144 	uint y1 = TileY(t);
145 	uint x2 = std::min(x1 + 1, MapMaxX());
146 	uint y2 = std::min(y1 + 1, MapMaxY());
147 
148 	return std::max({
149 		TileHeight(t),              // N corner
150 		TileHeight(TileXY(x2, y1)), // W corner
151 		TileHeight(TileXY(x1, y2)), // E corner
152 		TileHeight(TileXY(x2, y2)), // S corner
153 	});
154 }
155