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 slope_func.h Functions related to slopes. */
9 
10 #ifndef SLOPE_FUNC_H
11 #define SLOPE_FUNC_H
12 
13 #include "core/math_func.hpp"
14 #include "slope_type.h"
15 #include "direction_type.h"
16 #include "tile_type.h"
17 
18 /**
19  * Rangecheck for Corner enumeration.
20  *
21  * @param corner A #Corner.
22  * @return true iff corner is in a valid range.
23  */
IsValidCorner(Corner corner)24 static inline bool IsValidCorner(Corner corner)
25 {
26 	return IsInsideMM(corner, 0, CORNER_END);
27 }
28 
29 
30 /**
31  * Checks if a slope is steep.
32  *
33  * @param s The given #Slope.
34  * @return True if the slope is steep, else false.
35  */
IsSteepSlope(Slope s)36 static inline bool IsSteepSlope(Slope s)
37 {
38 	return (s & SLOPE_STEEP) != 0;
39 }
40 
41 /**
42  * Checks for non-continuous slope on halftile foundations.
43  *
44  * @param s The given #Slope.
45  * @return True if the slope is non-continuous, else false.
46  */
IsHalftileSlope(Slope s)47 static inline bool IsHalftileSlope(Slope s)
48 {
49 	return (s & SLOPE_HALFTILE) != 0;
50 }
51 
52 /**
53  * Removes a halftile slope from a slope
54  *
55  * Non-halftile slopes remain unmodified.
56  *
57  * @param s A #Slope.
58  * @return The slope s without its halftile slope.
59  */
RemoveHalftileSlope(Slope s)60 static inline Slope RemoveHalftileSlope(Slope s)
61 {
62 	return s & ~SLOPE_HALFTILE_MASK;
63 }
64 
65 /**
66  * Return the complement of a slope.
67  *
68  * This method returns the complement of a slope. The complement of a
69  * slope is a slope with raised corner which aren't raised in the given
70  * slope.
71  *
72  * @pre The slope must neither be steep nor a halftile slope.
73  * @param s The #Slope to get the complement.
74  * @return a complement Slope of the given slope.
75  */
ComplementSlope(Slope s)76 static inline Slope ComplementSlope(Slope s)
77 {
78 	assert(!IsSteepSlope(s) && !IsHalftileSlope(s));
79 	return s ^ SLOPE_ELEVATED;
80 }
81 
82 /**
83  * Tests if a specific slope has exactly one corner raised.
84  *
85  * @param s The #Slope
86  * @return true iff exactly one corner is raised
87  */
IsSlopeWithOneCornerRaised(Slope s)88 static inline bool IsSlopeWithOneCornerRaised(Slope s)
89 {
90 	return (s == SLOPE_W) || (s == SLOPE_S) || (s == SLOPE_E) || (s == SLOPE_N);
91 }
92 
93 /**
94  * Returns the slope with a specific corner raised.
95  *
96  * @param corner The #Corner.
97  * @return The #Slope with corner "corner" raised.
98  */
SlopeWithOneCornerRaised(Corner corner)99 static inline Slope SlopeWithOneCornerRaised(Corner corner)
100 {
101 	assert(IsValidCorner(corner));
102 	return (Slope)(1 << corner);
103 }
104 
105 /**
106  * Tests if a slope has a highest corner (i.e. one corner raised or a steep slope).
107  *
108  * Note: A halftile slope is ignored.
109  *
110  * @param s The #Slope.
111  * @return  true iff the slope has a highest corner.
112  */
HasSlopeHighestCorner(Slope s)113 static inline bool HasSlopeHighestCorner(Slope s)
114 {
115 	s = RemoveHalftileSlope(s);
116 	return IsSteepSlope(s) || IsSlopeWithOneCornerRaised(s);
117 }
118 
119 /**
120  * Returns the highest corner of a slope (one corner raised or a steep slope).
121  *
122  * @pre      The slope must be a slope with one corner raised or a steep slope. A halftile slope is ignored.
123  * @param s  The #Slope.
124  * @return   Highest corner.
125  */
GetHighestSlopeCorner(Slope s)126 static inline Corner GetHighestSlopeCorner(Slope s)
127 {
128 	switch (RemoveHalftileSlope(s)) {
129 		case SLOPE_W:
130 		case SLOPE_STEEP_W: return CORNER_W;
131 		case SLOPE_S:
132 		case SLOPE_STEEP_S: return CORNER_S;
133 		case SLOPE_E:
134 		case SLOPE_STEEP_E: return CORNER_E;
135 		case SLOPE_N:
136 		case SLOPE_STEEP_N: return CORNER_N;
137 		default: NOT_REACHED();
138 	}
139 }
140 
141 /**
142  * Returns the leveled halftile of a halftile slope.
143  *
144  * @pre     The slope must be a halftile slope.
145  * @param s The #Slope.
146  * @return  The corner of the leveled halftile.
147  */
GetHalftileSlopeCorner(Slope s)148 static inline Corner GetHalftileSlopeCorner(Slope s)
149 {
150 	assert(IsHalftileSlope(s));
151 	return (Corner)((s >> 6) & 3);
152 }
153 
154 /**
155  * Returns the height of the highest corner of a slope relative to TileZ (= minimal height)
156  *
157  * @param s The #Slope.
158  * @return Relative height of highest corner.
159  */
GetSlopeMaxZ(Slope s)160 static inline int GetSlopeMaxZ(Slope s)
161 {
162 	if (s == SLOPE_FLAT) return 0;
163 	if (IsSteepSlope(s)) return 2;
164 	return 1;
165 }
166 
167 /**
168  * Returns the height of the highest corner of a slope relative to TileZ (= minimal height)
169  *
170  * @param s The #Slope.
171  * @return Relative height of highest corner.
172  */
GetSlopeMaxPixelZ(Slope s)173 static inline int GetSlopeMaxPixelZ(Slope s)
174 {
175 	return GetSlopeMaxZ(s) * TILE_HEIGHT;
176 }
177 
178 /**
179  * Returns the opposite corner.
180  *
181  * @param corner A #Corner.
182  * @return The opposite corner to "corner".
183  */
OppositeCorner(Corner corner)184 static inline Corner OppositeCorner(Corner corner)
185 {
186 	return (Corner)(corner ^ 2);
187 }
188 
189 /**
190  * Tests if a specific slope has exactly three corners raised.
191  *
192  * @param s The #Slope
193  * @return true iff exactly three corners are raised
194  */
IsSlopeWithThreeCornersRaised(Slope s)195 static inline bool IsSlopeWithThreeCornersRaised(Slope s)
196 {
197 	return !IsHalftileSlope(s) && !IsSteepSlope(s) && IsSlopeWithOneCornerRaised(ComplementSlope(s));
198 }
199 
200 /**
201  * Returns the slope with all except one corner raised.
202  *
203  * @param corner The #Corner.
204  * @return The #Slope with all corners but "corner" raised.
205  */
SlopeWithThreeCornersRaised(Corner corner)206 static inline Slope SlopeWithThreeCornersRaised(Corner corner)
207 {
208 	return ComplementSlope(SlopeWithOneCornerRaised(corner));
209 }
210 
211 /**
212  * Returns a specific steep slope
213  *
214  * @param corner A #Corner.
215  * @return The steep #Slope with "corner" as highest corner.
216  */
SteepSlope(Corner corner)217 static inline Slope SteepSlope(Corner corner)
218 {
219 	return SLOPE_STEEP | SlopeWithThreeCornersRaised(OppositeCorner(corner));
220 }
221 
222 /**
223  * Tests if a specific slope is an inclined slope.
224  *
225  * @param s The #Slope
226  * @return true iff the slope is inclined.
227  */
IsInclinedSlope(Slope s)228 static inline bool IsInclinedSlope(Slope s)
229 {
230 	return (s == SLOPE_NW) || (s == SLOPE_SW) || (s == SLOPE_SE) || (s == SLOPE_NE);
231 }
232 
233 /**
234  * Returns the direction of an inclined slope.
235  *
236  * @param s A #Slope
237  * @return The direction the slope goes up in. Or INVALID_DIAGDIR if the slope is not an inclined slope.
238  */
GetInclinedSlopeDirection(Slope s)239 static inline DiagDirection GetInclinedSlopeDirection(Slope s)
240 {
241 	switch (s) {
242 		case SLOPE_NE: return DIAGDIR_NE;
243 		case SLOPE_SE: return DIAGDIR_SE;
244 		case SLOPE_SW: return DIAGDIR_SW;
245 		case SLOPE_NW: return DIAGDIR_NW;
246 		default: return INVALID_DIAGDIR;
247 	}
248 }
249 
250 /**
251  * Returns the slope that is inclined in a specific direction.
252  *
253  * @param dir A #DiagDirection
254  * @return The #Slope that goes up in direction dir.
255  */
InclinedSlope(DiagDirection dir)256 static inline Slope InclinedSlope(DiagDirection dir)
257 {
258 	switch (dir) {
259 		case DIAGDIR_NE: return SLOPE_NE;
260 		case DIAGDIR_SE: return SLOPE_SE;
261 		case DIAGDIR_SW: return SLOPE_SW;
262 		case DIAGDIR_NW: return SLOPE_NW;
263 		default: NOT_REACHED();
264 	}
265 }
266 
267 /**
268  * Adds a halftile slope to a slope.
269  *
270  * @param s #Slope without a halftile slope.
271  * @param corner The #Corner of the halftile.
272  * @return The #Slope s with the halftile slope added.
273  */
HalftileSlope(Slope s,Corner corner)274 static inline Slope HalftileSlope(Slope s, Corner corner)
275 {
276 	assert(IsValidCorner(corner));
277 	return (Slope)(s | SLOPE_HALFTILE | (corner << 6));
278 }
279 
280 
281 /**
282  * Tests for FOUNDATION_NONE.
283  *
284  * @param f  Maybe a #Foundation.
285  * @return   true iff f is a foundation.
286  */
IsFoundation(Foundation f)287 static inline bool IsFoundation(Foundation f)
288 {
289 	return f != FOUNDATION_NONE;
290 }
291 
292 /**
293  * Tests if the foundation is a leveled foundation.
294  *
295  * @param f  The #Foundation.
296  * @return   true iff f is a leveled foundation.
297  */
IsLeveledFoundation(Foundation f)298 static inline bool IsLeveledFoundation(Foundation f)
299 {
300 	return f == FOUNDATION_LEVELED;
301 }
302 
303 /**
304  * Tests if the foundation is an inclined foundation.
305  *
306  * @param f  The #Foundation.
307  * @return   true iff f is an inclined foundation.
308  */
IsInclinedFoundation(Foundation f)309 static inline bool IsInclinedFoundation(Foundation f)
310 {
311 	return (f == FOUNDATION_INCLINED_X) || (f == FOUNDATION_INCLINED_Y);
312 }
313 
314 /**
315  * Tests if a foundation is a non-continuous foundation, i.e. halftile-foundation or FOUNDATION_STEEP_BOTH.
316  *
317  * @param f  The #Foundation.
318  * @return   true iff f is a non-continuous foundation
319  */
IsNonContinuousFoundation(Foundation f)320 static inline bool IsNonContinuousFoundation(Foundation f)
321 {
322 	return IsInsideMM(f, FOUNDATION_STEEP_BOTH, FOUNDATION_HALFTILE_N + 1);
323 }
324 
325 /**
326  * Returns the halftile corner of a halftile-foundation
327  *
328  * @pre f != FOUNDATION_STEEP_BOTH
329  *
330  * @param f  The #Foundation.
331  * @return   The #Corner with track.
332  */
GetHalftileFoundationCorner(Foundation f)333 static inline Corner GetHalftileFoundationCorner(Foundation f)
334 {
335 	assert(IsInsideMM(f, FOUNDATION_HALFTILE_W, FOUNDATION_HALFTILE_N + 1));
336 	return (Corner)(f - FOUNDATION_HALFTILE_W);
337 }
338 
339 /**
340  * Tests if a foundation is a special rail foundation for single horizontal/vertical track.
341  *
342  * @param f  The #Foundation.
343  * @return   true iff f is a special rail foundation for single horizontal/vertical track.
344  */
IsSpecialRailFoundation(Foundation f)345 static inline bool IsSpecialRailFoundation(Foundation f)
346 {
347 	return IsInsideMM(f, FOUNDATION_RAIL_W, FOUNDATION_RAIL_N + 1);
348 }
349 
350 /**
351  * Returns the track corner of a special rail foundation
352  *
353  * @param f  The #Foundation.
354  * @return   The #Corner with track.
355  */
GetRailFoundationCorner(Foundation f)356 static inline Corner GetRailFoundationCorner(Foundation f)
357 {
358 	assert(IsSpecialRailFoundation(f));
359 	return (Corner)(f - FOUNDATION_RAIL_W);
360 }
361 
362 /**
363  * Returns the foundation needed to flatten a slope.
364  * The returned foundation is either FOUNDATION_NONE if the tile was already flat, or FOUNDATION_LEVELED.
365  *
366  * @param s  The current #Slope.
367  * @return   The needed #Foundation.
368  */
FlatteningFoundation(Slope s)369 static inline Foundation FlatteningFoundation(Slope s)
370 {
371 	return (s == SLOPE_FLAT ? FOUNDATION_NONE : FOUNDATION_LEVELED);
372 }
373 
374 /**
375  * Returns the along a specific axis inclined foundation.
376  *
377  * @param axis  The #Axis.
378  * @return      The needed #Foundation.
379  */
InclinedFoundation(Axis axis)380 static inline Foundation InclinedFoundation(Axis axis)
381 {
382 	return (axis == AXIS_X ? FOUNDATION_INCLINED_X : FOUNDATION_INCLINED_Y);
383 }
384 
385 /**
386  * Returns the halftile foundation for single horizontal/vertical track.
387  *
388  * @param corner The #Corner with the track.
389  * @return       The wanted #Foundation.
390  */
HalftileFoundation(Corner corner)391 static inline Foundation HalftileFoundation(Corner corner)
392 {
393 	assert(IsValidCorner(corner));
394 	return (Foundation)(FOUNDATION_HALFTILE_W + corner);
395 }
396 
397 /**
398  * Returns the special rail foundation for single horizontal/vertical track.
399  *
400  * @param corner The #Corner with the track.
401  * @return       The wanted #Foundation.
402  */
SpecialRailFoundation(Corner corner)403 static inline Foundation SpecialRailFoundation(Corner corner)
404 {
405 	assert(IsValidCorner(corner));
406 	return (Foundation)(FOUNDATION_RAIL_W + corner);
407 }
408 
409 /**
410  * Returns the #Sprite offset for a given #Slope.
411  *
412  * @param s The #Slope to get the offset for.
413  * @return The sprite offset for this #Slope.
414  */
SlopeToSpriteOffset(Slope s)415 static inline uint SlopeToSpriteOffset(Slope s)
416 {
417 	extern const byte _slope_to_sprite_offset[32];
418 	return _slope_to_sprite_offset[s];
419 }
420 
421 #endif /* SLOPE_FUNC_H */
422