OpenTTD Source 20250816-master-g94ba9b1454
water_regions.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 <http://www.gnu.org/licenses/>.
6 */
7
10#include "../stdafx.h"
11#include "../map_func.h"
12#include "water_regions.h"
13#include "../tilearea_type.h"
14#include "../track_func.h"
15#include "../transport_type.h"
16#include "../landscape.h"
17#include "../tunnelbridge_map.h"
18#include "follow_track.hpp"
19#include "../ship.h"
20#include "../debug.h"
21#include "../3rdparty/fmt/ranges.h"
22#include "../core/convertible_through_base.hpp"
23#include "../safeguards.h"
24
25using WaterRegionTraversabilityBits = uint16_t;
26constexpr WaterRegionPatchLabel FIRST_REGION_LABEL{1};
27
28static_assert(sizeof(WaterRegionTraversabilityBits) * 8 == WATER_REGION_EDGE_LENGTH);
29static_assert(sizeof(WaterRegionPatchLabel) == sizeof(uint8_t)); // Important for the hash calculation.
30
31static inline TrackBits GetWaterTracks(TileIndex tile) { return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)); }
32static inline bool IsAqueductTile(TileIndex tile) { return IsBridgeTile(tile) && GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER; }
33
34static inline int GetWaterRegionX(TileIndex tile) { return TileX(tile) / WATER_REGION_EDGE_LENGTH; }
35static inline int GetWaterRegionY(TileIndex tile) { return TileY(tile) / WATER_REGION_EDGE_LENGTH; }
36
37static inline int GetWaterRegionMapSizeX() { return Map::SizeX() / WATER_REGION_EDGE_LENGTH; }
38static inline int GetWaterRegionMapSizeY() { return Map::SizeY() / WATER_REGION_EDGE_LENGTH; }
39
40static inline WaterRegionIndex GetWaterRegionIndex(int region_x, int region_y) { return WaterRegionIndex(GetWaterRegionMapSizeX() * region_y + region_x); }
41static inline WaterRegionIndex GetWaterRegionIndex(TileIndex tile) { return GetWaterRegionIndex(GetWaterRegionX(tile), GetWaterRegionY(tile)); }
42
43using WaterRegionPatchLabelArray = std::array<WaterRegionPatchLabel, WATER_REGION_NUMBER_OF_TILES>;
44
49 friend class WaterRegion;
50
51 std::array<WaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{};
52 std::unique_ptr<WaterRegionPatchLabelArray> tile_patch_labels; // Tile patch labels, this may be nullptr in the following trivial cases: region is invalid, region is only land (0 patches), region is only water (1 patch)
53 bool has_cross_region_aqueducts = false;
54 WaterRegionPatchLabel::BaseType number_of_patches{0}; // 0 = no water, 1 = one single patch of water, etc...
55};
56
64private:
65 WaterRegionData &data;
66 const OrthogonalTileArea tile_area;
67
74 inline int GetLocalIndex(TileIndex tile) const
75 {
76 assert(this->tile_area.Contains(tile));
77 return (TileX(tile) - TileX(this->tile_area.tile)) + WATER_REGION_EDGE_LENGTH * (TileY(tile) - TileY(this->tile_area.tile));
78 }
79
80public:
81 WaterRegion(int region_x, int region_y, WaterRegionData &water_region_data)
82 : data(water_region_data)
83 , tile_area(TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH)
84 {}
85
86 OrthogonalTileIterator begin() const { return this->tile_area.begin(); }
87 OrthogonalTileIterator end() const { return this->tile_area.end(); }
88
96 WaterRegionTraversabilityBits GetEdgeTraversabilityBits(DiagDirection side) const { return this->data.edge_traversability_bits[side]; }
97
102 int NumberOfPatches() const { return static_cast<int>(this->data.number_of_patches); }
103
107 bool HasCrossRegionAqueducts() const { return this->data.has_cross_region_aqueducts; }
108
115 {
116 assert(this->tile_area.Contains(tile));
117 if (this->data.tile_patch_labels == nullptr) {
118 return this->NumberOfPatches() == 0 ? INVALID_WATER_REGION_PATCH : FIRST_REGION_LABEL;
119 }
120 return (*this->data.tile_patch_labels)[this->GetLocalIndex(tile)];
121 }
122
128 {
129 Debug(map, 3, "Updating water region ({},{})", GetWaterRegionX(this->tile_area.tile), GetWaterRegionY(this->tile_area.tile));
130 this->data.has_cross_region_aqueducts = false;
131
132 /* Acquire a tile patch label array if this region does not already have one */
133 if (this->data.tile_patch_labels == nullptr) {
134 this->data.tile_patch_labels = std::make_unique<WaterRegionPatchLabelArray>();
135 }
136
137 this->data.tile_patch_labels->fill(INVALID_WATER_REGION_PATCH);
138 this->data.edge_traversability_bits.fill(0);
139
140 WaterRegionPatchLabel current_label = FIRST_REGION_LABEL;
141 WaterRegionPatchLabel highest_assigned_label = INVALID_WATER_REGION_PATCH;
142
143 /* Perform connected component labeling. This uses a flooding algorithm that expands until no
144 * additional tiles can be added. Only tiles inside the water region are considered. */
145 for (const TileIndex start_tile : this->tile_area) {
146 static std::vector<TileIndex> tiles_to_check;
147 tiles_to_check.clear();
148 tiles_to_check.push_back(start_tile);
149
150 bool increase_label = false;
151 while (!tiles_to_check.empty()) {
152 const TileIndex tile = tiles_to_check.back();
153 tiles_to_check.pop_back();
154
155 const TrackdirBits valid_dirs = TrackBitsToTrackdirBits(GetWaterTracks(tile));
156 if (valid_dirs == TRACKDIR_BIT_NONE) continue;
157
158 WaterRegionPatchLabel &tile_patch = (*this->data.tile_patch_labels)[this->GetLocalIndex(tile)];
159 if (tile_patch != INVALID_WATER_REGION_PATCH) continue;
160
161 tile_patch = current_label;
162 highest_assigned_label = current_label;
163 increase_label = true;
164
165 for (const Trackdir dir : SetTrackdirBitIterator(valid_dirs)) {
166 /* By using a TrackFollower we "play by the same rules" as the actual ship pathfinder */
168 if (ft.Follow(tile, dir)) {
169 if (this->tile_area.Contains(ft.new_tile)) {
170 tiles_to_check.push_back(ft.new_tile);
171 } else if (!ft.is_bridge) {
172 assert(DistanceManhattan(ft.new_tile, tile) == 1);
173 const auto side = DiagdirBetweenTiles(tile, ft.new_tile);
174 const int local_x_or_y = DiagDirToAxis(side) == AXIS_X ? TileY(tile) - TileY(this->tile_area.tile) : TileX(tile) - TileX(this->tile_area.tile);
175 SetBit(this->data.edge_traversability_bits[side], local_x_or_y);
176 } else {
177 this->data.has_cross_region_aqueducts = true;
178 }
179 }
180 }
181 }
182
183 if (increase_label) current_label++;
184 }
185
186 this->data.number_of_patches = highest_assigned_label.base();
187
188 if (this->NumberOfPatches() == 0 || (this->NumberOfPatches() == 1 &&
189 std::all_of(this->data.tile_patch_labels->begin(), this->data.tile_patch_labels->end(), [](WaterRegionPatchLabel label) { return label == FIRST_REGION_LABEL; }))) {
190 /* No need for patch storage: trivial cases */
191 this->data.tile_patch_labels.reset();
192 }
193 }
194
195 void PrintDebugInfo()
196 {
197 Debug(map, 9, "Water region {},{} labels and edge traversability = ...", GetWaterRegionX(this->tile_area.tile), GetWaterRegionY(this->tile_area.tile));
198
199 const size_t max_element_width = fmt::format("{}", this->NumberOfPatches()).size();
200
201 std::string traversability = fmt::format("{:0{}b}", this->GetEdgeTraversabilityBits(DIAGDIR_NW), WATER_REGION_EDGE_LENGTH);
202 Debug(map, 9, " {:{}}", fmt::join(traversability, " "), max_element_width);
203 Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
204
205 for (int y = 0; y < WATER_REGION_EDGE_LENGTH; ++y) {
206 std::string line{};
207 for (int x = 0; x < WATER_REGION_EDGE_LENGTH; ++x) {
208 const auto label = this->GetLabel(TileAddXY(this->tile_area.tile, x, y));
209 if (label == INVALID_WATER_REGION_PATCH) {
210 line = fmt::format("{:{}} {}", ".", max_element_width, line);
211 } else {
212 line = fmt::format("{:{}} {}", label, max_element_width, line);
213 }
214 }
215 Debug(map, 9, "{} | {}| {}", GB(this->GetEdgeTraversabilityBits(DIAGDIR_SW), y, 1), line, GB(this->GetEdgeTraversabilityBits(DIAGDIR_NE), y, 1));
216 }
217
218 Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
219 traversability = fmt::format("{:0{}b}", this->GetEdgeTraversabilityBits(DIAGDIR_SE), WATER_REGION_EDGE_LENGTH);
220 Debug(map, 9, " {:{}}", fmt::join(traversability, " "), max_element_width);
221 }
222};
223
226
227static TileIndex GetTileIndexFromLocalCoordinate(int region_x, int region_y, int local_x, int local_y)
228{
229 assert(local_x >= 0 && local_x < WATER_REGION_EDGE_LENGTH);
230 assert(local_y >= 0 && local_y < WATER_REGION_EDGE_LENGTH);
231 return TileXY(WATER_REGION_EDGE_LENGTH * region_x + local_x, WATER_REGION_EDGE_LENGTH * region_y + local_y);
232}
233
234static TileIndex GetEdgeTileCoordinate(int region_x, int region_y, DiagDirection side, int x_or_y)
235{
236 assert(x_or_y >= 0 && x_or_y < WATER_REGION_EDGE_LENGTH);
237 switch (side) {
238 case DIAGDIR_NE: return GetTileIndexFromLocalCoordinate(region_x, region_y, 0, x_or_y);
239 case DIAGDIR_SW: return GetTileIndexFromLocalCoordinate(region_x, region_y, WATER_REGION_EDGE_LENGTH - 1, x_or_y);
240 case DIAGDIR_NW: return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, 0);
241 case DIAGDIR_SE: return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, WATER_REGION_EDGE_LENGTH - 1);
242 default: NOT_REACHED();
243 }
244}
245
246static WaterRegion GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
247{
248 const WaterRegionIndex index = GetWaterRegionIndex(region_x, region_y);
249 WaterRegion water_region(region_x, region_y, _water_region_data[index]);
250 if (!_is_water_region_valid[index]) {
251 water_region.ForceUpdate();
252 _is_water_region_valid[index] = true;
253 }
254 return water_region;
255}
256
257static WaterRegion GetUpdatedWaterRegion(TileIndex tile)
258{
259 return GetUpdatedWaterRegion(GetWaterRegionX(tile), GetWaterRegionY(tile));
260}
261
266static WaterRegionIndex GetWaterRegionIndex(const WaterRegionDesc &water_region)
267{
268 return GetWaterRegionIndex(water_region.x, water_region.y);
269}
270
276{
277 return water_region_patch.label.base() | GetWaterRegionIndex(water_region_patch).base() << 8;
278}
279
286{
287 return TileXY(water_region.x * WATER_REGION_EDGE_LENGTH + (WATER_REGION_EDGE_LENGTH / 2), water_region.y * WATER_REGION_EDGE_LENGTH + (WATER_REGION_EDGE_LENGTH / 2));
288}
289
295{
296 return WaterRegionDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile) };
297}
298
304{
305 const WaterRegion region = GetUpdatedWaterRegion(tile);
306 return WaterRegionPatchDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile), region.GetLabel(tile) };
307}
308
314{
315 if (!IsValidTile(tile)) return;
316
317 auto invalidate_region = [](TileIndex tile) {
318 const WaterRegionIndex water_region_index = GetWaterRegionIndex(tile);
319 if (!_is_water_region_valid[water_region_index]) Debug(map, 3, "Invalidated water region ({},{})", GetWaterRegionX(tile), GetWaterRegionY(tile));
320 _is_water_region_valid[water_region_index] = false;
321 };
322
323 invalidate_region(tile);
324
325 /* When updating the water region we look into the first tile of adjacent water regions to determine edge
326 * traversability. This means that if we invalidate any region edge tiles we might also change the traversability
327 * of the adjacent region. This code ensures the adjacent regions also get invalidated in such a case. */
328 for (DiagDirection side = DIAGDIR_BEGIN; side < DIAGDIR_END; side++) {
329 const TileIndex adjacent_tile = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDiagDir(side));
330 if (adjacent_tile == INVALID_TILE) continue;
331 if (GetWaterRegionIndex(adjacent_tile) != GetWaterRegionIndex(tile)) invalidate_region(adjacent_tile);
332 }
333}
334
342static inline void VisitAdjacentWaterRegionPatchNeighbours(const WaterRegionPatchDesc &water_region_patch, DiagDirection side, VisitWaterRegionPatchCallback &func)
343{
344 if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
345
346 const WaterRegion current_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
347
348 const TileIndexDiffC offset = TileIndexDiffCByDiagDir(side);
349 const int nx = water_region_patch.x + offset.x;
350 const int ny = water_region_patch.y + offset.y;
351
352 if (nx < 0 || ny < 0 || nx >= GetWaterRegionMapSizeX() || ny >= GetWaterRegionMapSizeY()) return;
353
354 const WaterRegion neighbouring_region = GetUpdatedWaterRegion(nx, ny);
355 const DiagDirection opposite_side = ReverseDiagDir(side);
356
357 /* Indicates via which local x or y coordinates (depends on the "side" parameter) we can cross over into the adjacent region. */
358 const WaterRegionTraversabilityBits traversability_bits = current_region.GetEdgeTraversabilityBits(side)
359 & neighbouring_region.GetEdgeTraversabilityBits(opposite_side);
360 if (traversability_bits == 0) return;
361
362 if (current_region.NumberOfPatches() == 1 && neighbouring_region.NumberOfPatches() == 1) {
363 func(WaterRegionPatchDesc{ nx, ny, FIRST_REGION_LABEL }); // No further checks needed because we know there is just one patch for both adjacent regions
364 return;
365 }
366
367 /* Multiple water patches can be reached from the current patch. Check each edge tile individually. */
368 static std::vector<WaterRegionPatchLabel> unique_labels; // static and vector-instead-of-map for performance reasons
369 unique_labels.clear();
370 for (int x_or_y = 0; x_or_y < WATER_REGION_EDGE_LENGTH; ++x_or_y) {
371 if (!HasBit(traversability_bits, x_or_y)) continue;
372
373 const TileIndex current_edge_tile = GetEdgeTileCoordinate(water_region_patch.x, water_region_patch.y, side, x_or_y);
374 const WaterRegionPatchLabel current_label = current_region.GetLabel(current_edge_tile);
375 if (current_label != water_region_patch.label) continue;
376
377 const TileIndex neighbour_edge_tile = GetEdgeTileCoordinate(nx, ny, opposite_side, x_or_y);
378 const WaterRegionPatchLabel neighbour_label = neighbouring_region.GetLabel(neighbour_edge_tile);
379 assert(neighbour_label != INVALID_WATER_REGION_PATCH);
380 if (std::ranges::find(unique_labels, neighbour_label) == unique_labels.end()) unique_labels.push_back(neighbour_label);
381 }
382 for (WaterRegionPatchLabel unique_label : unique_labels) func(WaterRegionPatchDesc{ nx, ny, unique_label });
383}
384
391void VisitWaterRegionPatchNeighbours(const WaterRegionPatchDesc &water_region_patch, VisitWaterRegionPatchCallback &callback)
392{
393 if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
394
395 const WaterRegion current_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
396
397 /* Visit adjacent water region patches in each cardinal direction */
398 for (DiagDirection side = DIAGDIR_BEGIN; side < DIAGDIR_END; side++) VisitAdjacentWaterRegionPatchNeighbours(water_region_patch, side, callback);
399
400 /* Visit neighbouring water patches accessible via cross-region aqueducts */
401 if (current_region.HasCrossRegionAqueducts()) {
402 for (const TileIndex tile : current_region) {
403 if (GetWaterRegionPatchInfo(tile) == water_region_patch && IsAqueductTile(tile)) {
404 const TileIndex other_end_tile = GetOtherBridgeEnd(tile);
405 if (GetWaterRegionIndex(tile) != GetWaterRegionIndex(other_end_tile)) callback(GetWaterRegionPatchInfo(other_end_tile));
406 }
407 }
408 }
409}
410
415{
416 const int number_of_regions = GetWaterRegionMapSizeX() * GetWaterRegionMapSizeY();
417
418 _water_region_data.clear();
419 _water_region_data.resize(number_of_regions);
420
421 _is_water_region_valid.clear();
422 _is_water_region_valid.resize(number_of_regions, false);
423
424 Debug(map, 2, "Allocating {} x {} water regions", GetWaterRegionMapSizeX(), GetWaterRegionMapSizeY());
425 assert(_is_water_region_valid.size() == _water_region_data.size());
426}
427
428void PrintWaterRegionDebugInfo(TileIndex tile)
429{
430 GetUpdatedWaterRegion(tile).PrintDebugInfo();
431}
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
TileIndex GetOtherBridgeEnd(TileIndex tile)
Starting at one bridge end finds the other bridge end.
bool IsBridgeTile(Tile t)
checks if there is a bridge on this tile
Definition bridge_map.h:35
Iterator to iterate over a tile area (rectangle) of the map.
A sort-of mixin that implements 'at(pos)' and 'operator[](pos)' only for a specific type.
The data stored for each water region.
Represents a square section of the map of a fixed size.
WaterRegionPatchLabel GetLabel(TileIndex tile) const
Returns the patch label that was assigned to the tile.
int NumberOfPatches() const
bool HasCrossRegionAqueducts() const
int GetLocalIndex(TileIndex tile) const
Returns the local index of the tile within the region.
void ForceUpdate()
Performs the connected component labeling and other data gathering.
WaterRegionTraversabilityBits GetEdgeTraversabilityBits(DiagDirection side) const
Returns a set of bits indicating whether an edge tile on a particular side is traversable or not.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
@ AXIS_X
The X axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_END
Used for iterations.
@ DIAGDIR_BEGIN
Used for iterations.
@ DIAGDIR_SW
Southwest.
Template function for track followers.
TrackStatus GetTileTrackStatus(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
Returns information about trackdirs and signal states.
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:142
DiagDirection DiagdirBetweenTiles(TileIndex tile_from, TileIndex tile_to)
Determines the DiagDirection to get from one tile to another.
Definition map_func.h:622
TileIndex TileAddXY(TileIndex tile, int x, int y)
Adds a given offset to a tile.
Definition map_func.h:469
TileIndex AddTileIndexDiffCWrap(TileIndex tile, TileIndexDiffC diff)
Add a TileIndexDiffC to a TileIndex and returns the new one.
Definition map_func.h:514
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:372
TileIndexDiffC TileIndexDiffCByDiagDir(DiagDirection dir)
Returns the TileIndexDiffC offset from a DiagDirection.
Definition map_func.h:482
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:424
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:414
Track follower helper template class (can serve pathfinders and vehicle controllers).
bool Follow(TileIndex old_tile, Trackdir old_td)
main follower routine.
bool is_bridge
last turn passed bridge ramp
TileIndex new_tile
the new tile (the vehicle has entered)
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:278
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:269
Represents the covered area of e.g.
OrthogonalTileIterator end() const
Returns an iterator to the end of the tile area.
Definition tilearea.cpp:162
bool Contains(TileIndex tile) const
Does this tile area contain a tile?
Definition tilearea.cpp:104
TileIndex tile
The base tile of the area.
OrthogonalTileIterator begin() const
Returns an iterator to the beginning of the tile area.
Definition tilearea.cpp:153
Iterable ensemble of each set bit in a value.
A pair-construct of a TileIndexDiff.
Definition map_type.h:31
int16_t x
The x value of the coordinate.
Definition map_type.h:32
int16_t y
The y value of the coordinate.
Definition map_type.h:33
Describes a single square water region.
int x
The X coordinate of the water region, i.e. X=2 is the 3rd water region along the X-axis.
int y
The Y coordinate of the water region, i.e. Y=2 is the 3rd water region along the Y-axis.
Describes a single interconnected patch of water within a particular water region.
int y
The Y coordinate of the water region, i.e. Y=2 is the 3rd water region along the Y-axis.
int x
The X coordinate of the water region, i.e. X=2 is the 3rd water region along the X-axis.
WaterRegionPatchLabel label
Unique label identifying the patch within the region.
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
TrackBits TrackStatusToTrackBits(TrackStatus ts)
Returns the present-track-information of a TrackStatus.
Definition track_func.h:363
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
TrackdirBits
Allow incrementing of Trackdir variables.
Definition track_type.h:97
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
@ TRANSPORT_WATER
Transport over water.
TransportType GetTunnelBridgeTransportType(Tile t)
Tunnel: Get the transport type of the tunnel (road or rail) Bridge: Get the transport type of the bri...
void AllocateWaterRegions()
Allocates the appropriate amount of water regions for the current map size.
WaterRegionDesc GetWaterRegionInfo(TileIndex tile)
Returns basic water region information for the provided tile.
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile)
Returns basic water region patch information for the provided tile.
static void VisitAdjacentWaterRegionPatchNeighbours(const WaterRegionPatchDesc &water_region_patch, DiagDirection side, VisitWaterRegionPatchCallback &func)
Calls the provided callback function for all water region patches accessible from one particular side...
TileIndex GetWaterRegionCenterTile(const WaterRegionDesc &water_region)
Returns the center tile of a particular water region.
void InvalidateWaterRegion(TileIndex tile)
Marks the water region that tile is part of as invalid.
int CalculateWaterRegionPatchHash(const WaterRegionPatchDesc &water_region_patch)
Calculates a number that uniquely identifies the provided water region patch.
void VisitWaterRegionPatchNeighbours(const WaterRegionPatchDesc &water_region_patch, VisitWaterRegionPatchCallback &callback)
Calls the provided callback function on all accessible water region patches in each cardinal directio...
Handles dividing the water in the map into regions to assist pathfinding.