OpenTTD Source 20260218-master-g2123fca5ea
heightmap.cpp
Go to the documentation of this file.
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "heightmap.h"
12#include "landscape.h"
13#include "clear_map.h"
14#include "strings_func.h"
15#include "void_map.h"
16#include "error.h"
17#include "saveload/saveload.h"
18#include "bmp.h"
19#include "gfx_func.h"
20#include "fios.h"
21#include "fileio_func.h"
22
23#include "table/strings.h"
24
25#ifdef WITH_PNG
26#include <png.h>
27#endif /* WITH_PNG */
28
29#include "safeguards.h"
30
37
38/*
39 * Maximum size in pixels of the heightmap image.
40 */
41static const uint MAX_HEIGHTMAP_SIZE_PIXELS = 256 << 20; // ~256 million
42/*
43 * When loading a PNG or BMP the 24 bpp variant requires at least 4 bytes per pixel
44 * of memory to load the data. Make sure the "reasonable" limit is well within the
45 * maximum amount of memory allocatable on 32 bit platforms.
46 */
47static_assert(MAX_HEIGHTMAP_SIZE_PIXELS < UINT32_MAX / 8);
48
58static inline bool IsValidHeightmapDimension(size_t width, size_t height)
59{
60 return (uint64_t)width * height <= MAX_HEIGHTMAP_SIZE_PIXELS &&
61 width > 0 && width <= MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS &&
62 height > 0 && height <= MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS;
63}
64
73static inline uint8_t RGBToGreyscale(uint8_t red, uint8_t green, uint8_t blue)
74{
75 /* To avoid doubles and stuff, multiply it with a total of 65536 (16bits), then
76 * divide by it to normalize the value to a byte again. */
77 return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
78}
79
80
81#ifdef WITH_PNG
82
89static void ReadHeightmapPNGImageData(std::span<uint8_t> map, png_structp png_ptr, png_infop info_ptr)
90{
91 uint x, y;
92 uint8_t gray_palette[256];
93 png_bytep *row_pointers = nullptr;
94 bool has_palette = png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE;
95 uint channels = png_get_channels(png_ptr, info_ptr);
96
97 /* Get palette and convert it to greyscale */
98 if (has_palette) {
99 int i;
100 int palette_size;
101 png_color *palette;
102 bool all_gray = true;
103
104 png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
105 for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
106 all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
107 gray_palette[i] = RGBToGreyscale(palette[i].red, palette[i].green, palette[i].blue);
108 }
109
116 if (palette_size == 16 && !all_gray) {
117 for (i = 0; i < palette_size; i++) {
118 gray_palette[i] = 256 * i / palette_size;
119 }
120 }
121 }
122
123 row_pointers = png_get_rows(png_ptr, info_ptr);
124
125 /* Read the raw image data and convert in 8-bit greyscale */
126 for (x = 0; x < png_get_image_width(png_ptr, info_ptr); x++) {
127 for (y = 0; y < png_get_image_height(png_ptr, info_ptr); y++) {
128 uint8_t *pixel = &map[y * png_get_image_width(png_ptr, info_ptr) + x];
129 uint x_offset = x * channels;
130
131 if (has_palette) {
132 *pixel = gray_palette[row_pointers[y][x_offset]];
133 } else if (channels == 3) {
134 *pixel = RGBToGreyscale(row_pointers[y][x_offset + 0],
135 row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
136 } else {
137 *pixel = row_pointers[y][x_offset];
138 }
139 }
140 }
141}
142
153static bool ReadHeightmapPNG(std::string_view filename, uint *x, uint *y, std::vector<uint8_t> *map)
154{
155 png_structp png_ptr = nullptr;
156 png_infop info_ptr = nullptr;
157
158 auto fp = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR);
159 if (!fp.has_value()) {
160 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_FILE_NOT_FOUND), WL_ERROR);
161 return false;
162 }
163
164 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
165 if (png_ptr == nullptr) {
166 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_MISC), WL_ERROR);
167 return false;
168 }
169
170 info_ptr = png_create_info_struct(png_ptr);
171 if (info_ptr == nullptr || setjmp(png_jmpbuf(png_ptr))) {
172 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_MISC), WL_ERROR);
173 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
174 return false;
175 }
176
177 png_init_io(png_ptr, *fp);
178
179 /* Allocate memory and read image, without alpha or 16-bit samples
180 * (result is either 8-bit indexed/greyscale or 24-bit RGB) */
181 png_set_packing(png_ptr);
182 png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, nullptr);
183
184 /* Maps of wrong colour-depth are not used.
185 * (this should have been taken care of by stripping alpha and 16-bit samples on load) */
186 if ((png_get_channels(png_ptr, info_ptr) != 1) && (png_get_channels(png_ptr, info_ptr) != 3) && (png_get_bit_depth(png_ptr, info_ptr) != 8)) {
187 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_IMAGE_TYPE), WL_ERROR);
188 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
189 return false;
190 }
191
192 uint width = png_get_image_width(png_ptr, info_ptr);
193 uint height = png_get_image_height(png_ptr, info_ptr);
194
195 if (!IsValidHeightmapDimension(width, height)) {
196 ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_HEIGHTMAP_TOO_LARGE), WL_ERROR);
197 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
198 return false;
199 }
200
201 if (map != nullptr) {
202 map->resize(static_cast<size_t>(width) * height);
203 ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
204 }
205
206 *x = width;
207 *y = height;
208
209 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
210 return true;
211}
212
213#endif /* WITH_PNG */
214
215
222static void ReadHeightmapBMPImageData(std::span<uint8_t> map, const BmpInfo &info, const BmpData &data)
223{
224 uint8_t gray_palette[256];
225
226 if (!data.palette.empty()) {
227 bool all_gray = true;
228
229 if (info.palette_size != 2) {
230 for (uint i = 0; i < info.palette_size && (info.palette_size != 16 || all_gray); i++) {
231 all_gray &= data.palette[i].r == data.palette[i].g && data.palette[i].r == data.palette[i].b;
232 gray_palette[i] = RGBToGreyscale(data.palette[i].r, data.palette[i].g, data.palette[i].b);
233 }
234
241 if (info.palette_size == 16 && !all_gray) {
242 for (uint i = 0; i < info.palette_size; i++) {
243 gray_palette[i] = 256 * i / info.palette_size;
244 }
245 }
246 } else {
251 gray_palette[0] = 0;
252 gray_palette[1] = 16;
253 }
254 }
255
256 /* Read the raw image data and convert in 8-bit greyscale */
257 for (uint y = 0; y < info.height; y++) {
258 uint8_t *pixel = &map[y * static_cast<size_t>(info.width)];
259 const uint8_t *bitmap = &data.bitmap[y * static_cast<size_t>(info.width) * (info.bpp == 24 ? 3 : 1)];
260
261 for (uint x = 0; x < info.width; x++) {
262 if (info.bpp != 24) {
263 *pixel++ = gray_palette[*bitmap++];
264 } else {
265 *pixel++ = RGBToGreyscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
266 bitmap += 3;
267 }
268 }
269 }
270}
271
282static bool ReadHeightmapBMP(std::string_view filename, uint *x, uint *y, std::vector<uint8_t> *map)
283{
284 auto f = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR);
285 if (!f.has_value()) {
286 ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_PNGMAP_FILE_NOT_FOUND), WL_ERROR);
287 return false;
288 }
289
290 RandomAccessFile file(filename, HEIGHTMAP_DIR);
291 BmpInfo info{};
292 BmpData data{};
293
294 if (!BmpReadHeader(file, info, data)) {
295 ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_BMPMAP_IMAGE_TYPE), WL_ERROR);
296 return false;
297 }
298
299 if (!IsValidHeightmapDimension(info.width, info.height)) {
300 ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_HEIGHTMAP_TOO_LARGE), WL_ERROR);
301 return false;
302 }
303
304 if (map != nullptr) {
305 if (!BmpReadBitmap(file, info, data)) {
306 ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_BMPMAP_IMAGE_TYPE), WL_ERROR);
307 return false;
308 }
309
310 map->resize(static_cast<size_t>(info.width) * info.height);
311 ReadHeightmapBMPImageData(*map, info, data);
312 }
313
314 *x = info.width;
315 *y = info.height;
316
317 return true;
318}
319
327static void GreyscaleToMapHeights(uint img_width, uint img_height, std::span<const uint8_t> map)
328{
329 /* Defines the detail of the aspect ratio (to avoid doubles) */
330 const uint num_div = 16384;
331 /* Ensure multiplication with num_div does not cause overflows. */
332 static_assert(num_div <= std::numeric_limits<uint>::max() / MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS);
333
334 uint width, height;
335 uint row, col;
336 uint row_pad = 0, col_pad = 0;
337 uint img_scale;
338 uint img_row, img_col;
339 TileIndex tile;
340
341 /* Get map size and calculate scale and padding values */
342 switch (_settings_game.game_creation.heightmap_rotation) {
343 default: NOT_REACHED();
345 width = Map::SizeX();
346 height = Map::SizeY();
347 break;
348 case HM_CLOCKWISE:
349 width = Map::SizeY();
350 height = Map::SizeX();
351 break;
352 }
353
354 if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
355 /* Image is wider than map - center vertically */
356 img_scale = (width * num_div) / img_width;
357 row_pad = (1 + height - ((img_height * img_scale) / num_div)) / 2;
358 } else {
359 /* Image is taller than map - center horizontally */
360 img_scale = (height * num_div) / img_height;
361 col_pad = (1 + width - ((img_width * img_scale) / num_div)) / 2;
362 }
363
364 if (_settings_game.construction.freeform_edges) {
365 for (uint x = 0; x < Map::SizeX(); x++) MakeVoid(TileXY(x, 0));
366 for (uint y = 0; y < Map::SizeY(); y++) MakeVoid(TileXY(0, y));
367 }
368
369 /* Form the landscape */
370 for (row = 0; row < height; row++) {
371 for (col = 0; col < width; col++) {
372 switch (_settings_game.game_creation.heightmap_rotation) {
373 default: NOT_REACHED();
374 case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break;
375 case HM_CLOCKWISE: tile = TileXY(row, col); break;
376 }
377
378 /* Check if current tile is within the 1-pixel map edge or padding regions */
379 if ((!_settings_game.construction.freeform_edges && DistanceFromEdge(tile) <= 1) ||
380 (row < row_pad) || (row >= (height - row_pad - (_settings_game.construction.freeform_edges ? 0 : 1))) ||
381 (col < col_pad) || (col >= (width - col_pad - (_settings_game.construction.freeform_edges ? 0 : 1)))) {
382 SetTileHeight(tile, 0);
383 } else {
384 /* Use nearest neighbour resizing to scale map data.
385 * We rotate the map 45 degrees (counter)clockwise */
386 img_row = (((row - row_pad) * num_div) / img_scale);
387 switch (_settings_game.game_creation.heightmap_rotation) {
388 default: NOT_REACHED();
390 img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
391 break;
392 case HM_CLOCKWISE:
393 img_col = (((col - col_pad) * num_div) / img_scale);
394 break;
395 }
396
397 assert(img_row < img_height);
398 assert(img_col < img_width);
399
400 uint heightmap_height = map[img_row * img_width + img_col];
401
402 if (heightmap_height > 0) {
403 /* 0 is sea level.
404 * Other grey scales are scaled evenly to the available height levels > 0.
405 * (The coastline is independent from the number of height levels) */
406 heightmap_height = 1 + (heightmap_height - 1) * _settings_game.game_creation.heightmap_height / 255;
407 }
408
409 SetTileHeight(tile, heightmap_height);
410 }
411 /* Only clear the tiles within the map area. */
412 if (IsInnerTile(tile)) {
414 }
415 }
416 }
417}
418
424{
425 uint width, height;
426 int row, col;
427 uint8_t current_height;
428 uint8_t max_height = _settings_game.construction.map_height_limit;
429
430 /* Adjust height difference to maximum one horizontal/vertical change. */
431 width = Map::SizeX();
432 height = Map::SizeY();
433
434 /* Top and left edge */
435 for (row = 0; (uint)row < height; row++) {
436 for (col = 0; (uint)col < width; col++) {
437 current_height = MAX_TILE_HEIGHT;
438 if (col != 0) {
439 /* Find lowest tile; either the top or left one */
440 current_height = TileHeight(TileXY(col - 1, row)); // top edge
441 }
442 if (row != 0) {
443 if (TileHeight(TileXY(col, row - 1)) < current_height) {
444 current_height = TileHeight(TileXY(col, row - 1)); // left edge
445 }
446 }
447
448 /* Does the height differ more than one? */
449 TileIndex tile = TileXY(col, row);
450 if (TileHeight(tile) >= (uint)current_height + 2) {
451 /* Then change the height to be no more than one */
452 SetTileHeight(tile, current_height + 1);
453 /* Height was changed so now there's a chance, more likely at higher altitude, of the
454 * tile turning into rock. */
455 if (IsInnerTile(tile) && RandomRange(max_height) <= current_height) {
457 }
458 }
459 }
460 }
461
462 /* Bottom and right edge */
463 for (row = height - 1; row >= 0; row--) {
464 for (col = width - 1; col >= 0; col--) {
465 current_height = MAX_TILE_HEIGHT;
466 if ((uint)col != width - 1) {
467 /* Find lowest tile; either the bottom and right one */
468 current_height = TileHeight(TileXY(col + 1, row)); // bottom edge
469 }
470
471 if ((uint)row != height - 1) {
472 if (TileHeight(TileXY(col, row + 1)) < current_height) {
473 current_height = TileHeight(TileXY(col, row + 1)); // right edge
474 }
475 }
476
477 /* Does the height differ more than one? */
478 TileIndex tile = TileXY(col, row);
479 if (TileHeight(tile) >= (uint)current_height + 2) {
480 /* Then change the height to be no more than one */
481 SetTileHeight(tile, current_height + 1);
482 /* Height was changed so now there's a chance, more likely at higher altitude, of the
483 * tile turning into rock. */
484 if (IsInnerTile(tile) && RandomRange(max_height) <= current_height) {
486 }
487 }
488 }
489 }
490}
491
501static bool ReadHeightMap(DetailedFileType dft, std::string_view filename, uint *x, uint *y, std::vector<uint8_t> *map)
502{
503 switch (dft) {
504 default:
505 NOT_REACHED();
506
507#ifdef WITH_PNG
509 return ReadHeightmapPNG(filename, x, y, map);
510#endif /* WITH_PNG */
511
513 return ReadHeightmapBMP(filename, x, y, map);
514 }
515}
516
525bool GetHeightmapDimensions(DetailedFileType dft, std::string_view filename, uint *x, uint *y)
526{
527 return ReadHeightMap(dft, filename, x, y, nullptr);
528}
529
538bool LoadHeightmap(DetailedFileType dft, std::string_view filename)
539{
540 uint x, y;
541 std::vector<uint8_t> map;
542
543 if (!ReadHeightMap(dft, filename, &x, &y, &map)) {
544 return false;
545 }
546
547 GreyscaleToMapHeights(x, y, map);
548
549 FixSlopes();
550
551 /* If all map borders are water, we will draw infinite water. */
552 _settings_game.construction.freeform_edges = !IsMapSurroundedByWater();
553
555
556 return true;
557}
558
563void FlatEmptyWorld(uint8_t tile_height)
564{
565 int edge_distance = _settings_game.construction.freeform_edges ? 0 : 2;
566 for (uint row = edge_distance; row < Map::SizeY() - edge_distance; row++) {
567 for (uint col = edge_distance; col < Map::SizeX() - edge_distance; col++) {
568 SetTileHeight(TileXY(col, row), tile_height);
569 }
570 }
571
572 FixSlopes();
574}
bool BmpReadBitmap(RandomAccessFile &file, BmpInfo &info, BmpData &data)
Reads the bitmap 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps.
Definition bmp.cpp:313
bool BmpReadHeader(RandomAccessFile &file, BmpInfo &info, BmpData &data)
Reads bitmap headers, and palette (if any).
Definition bmp.cpp:236
Read and write support for bmps.
A file from which bytes, words and double words are read in (potentially) a random order.
Map accessors for 'clear' tiles.
@ Rocks
Rocks with snow transition (0-3).
Definition clear_map.h:24
@ Grass
Plain grass with dirt transition (0-3).
Definition clear_map.h:22
void MakeClear(Tile t, ClearGround g, uint density)
Make a clear tile.
Definition clear_map.h:253
Functions related to errors.
@ WL_ERROR
Errors (eg. saving/loading failed).
Definition error.h:26
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
std::optional< FileHandle > FioFOpenFile(std::string_view filename, std::string_view mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition fileio.cpp:244
Functions for standard in/out file operations.
DetailedFileType
Kinds of files in each AbstractFileType.
Definition fileio_type.h:28
@ DFT_HEIGHTMAP_BMP
BMP file.
Definition fileio_type.h:34
@ DFT_HEIGHTMAP_PNG
PNG file.
Definition fileio_type.h:35
@ HEIGHTMAP_DIR
Subdirectory of scenario for heightmaps.
Definition fileio_type.h:93
Declarations for savegames operations.
Functions related to the gfx engine.
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1554
static const uint MAX_HEIGHTMAP_SIDE_LENGTH_IN_PIXELS
Maximum number of pixels for one dimension of a heightmap image.
Definition heightmap.cpp:36
static bool ReadHeightmapBMP(std::string_view filename, uint *x, uint *y, std::vector< uint8_t > *map)
Reads the heightmap and/or size of the heightmap from a BMP file.
bool GetHeightmapDimensions(DetailedFileType dft, std::string_view filename, uint *x, uint *y)
Get the dimensions of a heightmap.
static bool ReadHeightmapPNG(std::string_view filename, uint *x, uint *y, std::vector< uint8_t > *map)
Reads the heightmap and/or size of the heightmap from a PNG file.
void FlatEmptyWorld(uint8_t tile_height)
Make an empty world where all tiles are of height 'tile_height'.
static void GreyscaleToMapHeights(uint img_width, uint img_height, std::span< const uint8_t > map)
Converts a given greyscale map to something that fits in OTTD map system and create a map of that dat...
static void ReadHeightmapPNGImageData(std::span< uint8_t > map, png_structp png_ptr, png_infop info_ptr)
The PNG Heightmap loader.
Definition heightmap.cpp:89
static bool ReadHeightMap(DetailedFileType dft, std::string_view filename, uint *x, uint *y, std::vector< uint8_t > *map)
Reads the heightmap with the correct file reader.
bool LoadHeightmap(DetailedFileType dft, std::string_view filename)
Load a heightmap from file and change the map in its current dimensions to a landscape representing t...
static uint8_t RGBToGreyscale(uint8_t red, uint8_t green, uint8_t blue)
Convert RGB colours to Greyscale using 29.9% Red, 58.7% Green, 11.4% Blue (average luminosity formula...
Definition heightmap.cpp:73
static bool IsValidHeightmapDimension(size_t width, size_t height)
Check whether the loaded dimension of the heightmap image are considered valid enough to attempt to l...
Definition heightmap.cpp:58
void FixSlopes()
This function takes care of the fact that land in OpenTTD can never differ more than 1 in height.
static void ReadHeightmapBMPImageData(std::span< uint8_t > map, const BmpInfo &info, const BmpData &data)
The BMP Heightmap loader.
Functions related to creating heightmaps from files.
@ HM_CLOCKWISE
Rotate the map clockwise 45 degrees.
Definition heightmap.h:21
@ HM_COUNTER_CLOCKWISE
Rotate the map counter clockwise 45 degrees.
Definition heightmap.h:20
bool IsMapSurroundedByWater()
Check if all tiles on the map edge should be considered water borders.
Functions related to OTTD's landscape.
uint DistanceFromEdge(TileIndex tile)
Param the minimum distance to an edge.
Definition map.cpp:218
static TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:378
static const uint MAX_MAP_SIZE
Maximal map size = 4096.
Definition map_type.h:40
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
A number of safeguards to prevent using unsafe methods.
Functions/types related to saving and loading games.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
Definition of base types and functions in a cross-platform compatible way.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
Definition bmp.h:26
Definition bmp.h:16
uint32_t height
bitmap height
Definition bmp.h:19
uint32_t width
bitmap width
Definition bmp.h:18
uint32_t palette_size
number of colours in palette
Definition bmp.h:23
uint16_t bpp
bits per pixel
Definition bmp.h:21
static uint SizeX()
Get the size of the map along the X.
Definition map_func.h:264
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:273
static uint TileHeight(Tile tile)
Returns the height of a tile.
Definition tile_map.h:29
bool IsInnerTile(Tile tile)
Check if a tile is within the map (not a border).
Definition tile_map.h:109
void SetTileHeight(Tile tile, uint height)
Sets the height of a tile.
Definition tile_map.h:57
static constexpr uint MAX_TILE_HEIGHT
Maximum allowed tile height.
Definition tile_type.h:24
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
Map accessors for void tiles.
void MakeVoid(Tile t)
Make a nice void tile ;).
Definition void_map.h:19