OpenTTD Source 20250612-master-gb012d9e3dc
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
64{
65private:
66 WaterRegionData &data;
67 const OrthogonalTileArea tile_area;
68
75 inline int GetLocalIndex(TileIndex tile) const
76 {
77 assert(this->tile_area.Contains(tile));
78 return (TileX(tile) - TileX(this->tile_area.tile)) + WATER_REGION_EDGE_LENGTH * (TileY(tile) - TileY(this->tile_area.tile));
79 }
80
81public:
82 WaterRegion(int region_x, int region_y, WaterRegionData &water_region_data)
83 : data(water_region_data)
84 , tile_area(TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH)
85 {}
86
87 OrthogonalTileIterator begin() const { return this->tile_area.begin(); }
88 OrthogonalTileIterator end() const { return this->tile_area.end(); }
89
97 WaterRegionTraversabilityBits GetEdgeTraversabilityBits(DiagDirection side) const { return this->data.edge_traversability_bits[side]; }
98
103 int NumberOfPatches() const { return static_cast<int>(this->data.number_of_patches); }
104
108 bool HasCrossRegionAqueducts() const { return this->data.has_cross_region_aqueducts; }
109
116 {
117 assert(this->tile_area.Contains(tile));
118 if (this->data.tile_patch_labels == nullptr) {
119 return this->NumberOfPatches() == 0 ? INVALID_WATER_REGION_PATCH : FIRST_REGION_LABEL;
120 }
121 return (*this->data.tile_patch_labels)[this->GetLocalIndex(tile)];
122 }
123
129 {
130 Debug(map, 3, "Updating water region ({},{})", GetWaterRegionX(this->tile_area.tile), GetWaterRegionY(this->tile_area.tile));
131 this->data.has_cross_region_aqueducts = false;
132
133 /* Acquire a tile patch label array if this region does not already have one */
134 if (this->data.tile_patch_labels == nullptr) {
135 this->data.tile_patch_labels = std::make_unique<WaterRegionPatchLabelArray>();
136 }
137
138 this->data.tile_patch_labels->fill(INVALID_WATER_REGION_PATCH);
139 this->data.edge_traversability_bits.fill(0);
140
141 WaterRegionPatchLabel current_label = FIRST_REGION_LABEL;
142 WaterRegionPatchLabel highest_assigned_label = INVALID_WATER_REGION_PATCH;
143
144 /* Perform connected component labeling. This uses a flooding algorithm that expands until no
145 * additional tiles can be added. Only tiles inside the water region are considered. */
146 for (const TileIndex start_tile : this->tile_area) {
147 static std::vector<TileIndex> tiles_to_check;
148 tiles_to_check.clear();
149 tiles_to_check.push_back(start_tile);
150
151 bool increase_label = false;
152 while (!tiles_to_check.empty()) {
153 const TileIndex tile = tiles_to_check.back();
154 tiles_to_check.pop_back();
155
156 const TrackdirBits valid_dirs = TrackBitsToTrackdirBits(GetWaterTracks(tile));
157 if (valid_dirs == TRACKDIR_BIT_NONE) continue;
158
159 WaterRegionPatchLabel &tile_patch = (*this->data.tile_patch_labels)[this->GetLocalIndex(tile)];
160 if (tile_patch != INVALID_WATER_REGION_PATCH) continue;
161
162 tile_patch = current_label;
163 highest_assigned_label = current_label;
164 increase_label = true;
165
166 for (const Trackdir dir : SetTrackdirBitIterator(valid_dirs)) {
167 /* By using a TrackFollower we "play by the same rules" as the actual ship pathfinder */
169 if (ft.Follow(tile, dir)) {
170 if (this->tile_area.Contains(ft.new_tile)) {
171 tiles_to_check.push_back(ft.new_tile);
172 } else if (!ft.is_bridge) {
173 assert(DistanceManhattan(ft.new_tile, tile) == 1);
174 const auto side = DiagdirBetweenTiles(tile, ft.new_tile);
175 const int local_x_or_y = DiagDirToAxis(side) == AXIS_X ? TileY(tile) - TileY(this->tile_area.tile) : TileX(tile) - TileX(this->tile_area.tile);
176 SetBit(this->data.edge_traversability_bits[side], local_x_or_y);
177 } else {
178 this->data.has_cross_region_aqueducts = true;
179 }
180 }
181 }
182 }
183
184 if (increase_label) current_label++;
185 }
186
187 this->data.number_of_patches = highest_assigned_label.base();
188
189 if (this->NumberOfPatches() == 0 || (this->NumberOfPatches() == 1 &&
190 std::all_of(this->data.tile_patch_labels->begin(), this->data.tile_patch_labels->end(), [](WaterRegionPatchLabel label) { return label == FIRST_REGION_LABEL; }))) {
191 /* No need for patch storage: trivial cases */
192 this->data.tile_patch_labels.reset();
193 }
194 }
195
196 void PrintDebugInfo()
197 {
198 Debug(map, 9, "Water region {},{} labels and edge traversability = ...", GetWaterRegionX(this->tile_area.tile), GetWaterRegionY(this->tile_area.tile));
199
200 const size_t max_element_width = fmt::format("{}", this->NumberOfPatches()).size();
201
202 std::string traversability = fmt::format("{:0{}b}", this->GetEdgeTraversabilityBits(DIAGDIR_NW), WATER_REGION_EDGE_LENGTH);
203 Debug(map, 9, " {:{}}", fmt::join(traversability, " "), max_element_width);
204 Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
205
206 for (int y = 0; y < WATER_REGION_EDGE_LENGTH; ++y) {
207 std::string line{};
208 for (int x = 0; x < WATER_REGION_EDGE_LENGTH; ++x) {
209 const auto label = this->GetLabel(TileAddXY(this->tile_area.tile, x, y));
210 if (label == INVALID_WATER_REGION_PATCH) {
211 line = fmt::format("{:{}} {}", ".", max_element_width, line);
212 } else {
213 line = fmt::format("{:{}} {}", label, max_element_width, line);
214 }
215 }
216 Debug(map, 9, "{} | {}| {}", GB(this->GetEdgeTraversabilityBits(DIAGDIR_SW), y, 1), line, GB(this->GetEdgeTraversabilityBits(DIAGDIR_NE), y, 1));
217 }
218
219 Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
220 traversability = fmt::format("{:0{}b}", this->GetEdgeTraversabilityBits(DIAGDIR_SE), WATER_REGION_EDGE_LENGTH);
221 Debug(map, 9, " {:{}}", fmt::join(traversability, " "), max_element_width);
222 }
223};
224
227
228static TileIndex GetTileIndexFromLocalCoordinate(int region_x, int region_y, int local_x, int local_y)
229{
230 assert(local_x >= 0 && local_x < WATER_REGION_EDGE_LENGTH);
231 assert(local_y >= 0 && local_y < WATER_REGION_EDGE_LENGTH);
232 return TileXY(WATER_REGION_EDGE_LENGTH * region_x + local_x, WATER_REGION_EDGE_LENGTH * region_y + local_y);
233}
234
235static TileIndex GetEdgeTileCoordinate(int region_x, int region_y, DiagDirection side, int x_or_y)
236{
237 assert(x_or_y >= 0 && x_or_y < WATER_REGION_EDGE_LENGTH);
238 switch (side) {
239 case DIAGDIR_NE: return GetTileIndexFromLocalCoordinate(region_x, region_y, 0, x_or_y);
240 case DIAGDIR_SW: return GetTileIndexFromLocalCoordinate(region_x, region_y, WATER_REGION_EDGE_LENGTH - 1, x_or_y);
241 case DIAGDIR_NW: return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, 0);
242 case DIAGDIR_SE: return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, WATER_REGION_EDGE_LENGTH - 1);
243 default: NOT_REACHED();
244 }
245}
246
247static WaterRegion GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
248{
249 const WaterRegionIndex index = GetWaterRegionIndex(region_x, region_y);
250 WaterRegion water_region(region_x, region_y, _water_region_data[index]);
251 if (!_is_water_region_valid[index]) {
252 water_region.ForceUpdate();
253 _is_water_region_valid[index] = true;
254 }
255 return water_region;
256}
257
258static WaterRegion GetUpdatedWaterRegion(TileIndex tile)
259{
260 return GetUpdatedWaterRegion(GetWaterRegionX(tile), GetWaterRegionY(tile));
261}
262
267static WaterRegionIndex GetWaterRegionIndex(const WaterRegionDesc &water_region)
268{
269 return GetWaterRegionIndex(water_region.x, water_region.y);
270}
271
277{
278 return water_region_patch.label.base() | GetWaterRegionIndex(water_region_patch).base() << 8;
279}
280
287{
288 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));
289}
290
296{
297 return WaterRegionDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile) };
298}
299
305{
306 const WaterRegion region = GetUpdatedWaterRegion(tile);
307 return WaterRegionPatchDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile), region.GetLabel(tile) };
308}
309
315{
316 if (!IsValidTile(tile)) return;
317
318 auto invalidate_region = [](TileIndex tile) {
319 const WaterRegionIndex water_region_index = GetWaterRegionIndex(tile);
320 if (!_is_water_region_valid[water_region_index]) Debug(map, 3, "Invalidated water region ({},{})", GetWaterRegionX(tile), GetWaterRegionY(tile));
321 _is_water_region_valid[water_region_index] = false;
322 };
323
324 invalidate_region(tile);
325
326 /* When updating the water region we look into the first tile of adjacent water regions to determine edge
327 * traversability. This means that if we invalidate any region edge tiles we might also change the traversability
328 * of the adjacent region. This code ensures the adjacent regions also get invalidated in such a case. */
329 for (DiagDirection side = DIAGDIR_BEGIN; side < DIAGDIR_END; side++) {
330 const TileIndex adjacent_tile = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDiagDir(side));
331 if (adjacent_tile == INVALID_TILE) continue;
332 if (GetWaterRegionIndex(adjacent_tile) != GetWaterRegionIndex(tile)) invalidate_region(adjacent_tile);
333 }
334}
335
343static inline void VisitAdjacentWaterRegionPatchNeighbours(const WaterRegionPatchDesc &water_region_patch, DiagDirection side, VisitWaterRegionPatchCallback &func)
344{
345 if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
346
347 const WaterRegion current_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
348
349 const TileIndexDiffC offset = TileIndexDiffCByDiagDir(side);
350 const int nx = water_region_patch.x + offset.x;
351 const int ny = water_region_patch.y + offset.y;
352
353 if (nx < 0 || ny < 0 || nx >= GetWaterRegionMapSizeX() || ny >= GetWaterRegionMapSizeY()) return;
354
355 const WaterRegion neighbouring_region = GetUpdatedWaterRegion(nx, ny);
356 const DiagDirection opposite_side = ReverseDiagDir(side);
357
358 /* Indicates via which local x or y coordinates (depends on the "side" parameter) we can cross over into the adjacent region. */
359 const WaterRegionTraversabilityBits traversability_bits = current_region.GetEdgeTraversabilityBits(side)
360 & neighbouring_region.GetEdgeTraversabilityBits(opposite_side);
361 if (traversability_bits == 0) return;
362
363 if (current_region.NumberOfPatches() == 1 && neighbouring_region.NumberOfPatches() == 1) {
364 func(WaterRegionPatchDesc{ nx, ny, FIRST_REGION_LABEL }); // No further checks needed because we know there is just one patch for both adjacent regions
365 return;
366 }
367
368 /* Multiple water patches can be reached from the current patch. Check each edge tile individually. */
369 static std::vector<WaterRegionPatchLabel> unique_labels; // static and vector-instead-of-map for performance reasons
370 unique_labels.clear();
371 for (int x_or_y = 0; x_or_y < WATER_REGION_EDGE_LENGTH; ++x_or_y) {
372 if (!HasBit(traversability_bits, x_or_y)) continue;
373
374 const TileIndex current_edge_tile = GetEdgeTileCoordinate(water_region_patch.x, water_region_patch.y, side, x_or_y);
375 const WaterRegionPatchLabel current_label = current_region.GetLabel(current_edge_tile);
376 if (current_label != water_region_patch.label) continue;
377
378 const TileIndex neighbour_edge_tile = GetEdgeTileCoordinate(nx, ny, opposite_side, x_or_y);
379 const WaterRegionPatchLabel neighbour_label = neighbouring_region.GetLabel(neighbour_edge_tile);
380 assert(neighbour_label != INVALID_WATER_REGION_PATCH);
381 if (std::ranges::find(unique_labels, neighbour_label) == unique_labels.end()) unique_labels.push_back(neighbour_label);
382 }
383 for (WaterRegionPatchLabel unique_label : unique_labels) func(WaterRegionPatchDesc{ nx, ny, unique_label });
384}
385
392void VisitWaterRegionPatchNeighbours(const WaterRegionPatchDesc &water_region_patch, VisitWaterRegionPatchCallback &callback)
393{
394 if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
395
396 const WaterRegion current_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
397
398 /* Visit adjacent water region patches in each cardinal direction */
399 for (DiagDirection side = DIAGDIR_BEGIN; side < DIAGDIR_END; side++) VisitAdjacentWaterRegionPatchNeighbours(water_region_patch, side, callback);
400
401 /* Visit neighbouring water patches accessible via cross-region aqueducts */
402 if (current_region.HasCrossRegionAqueducts()) {
403 for (const TileIndex tile : current_region) {
404 if (GetWaterRegionPatchInfo(tile) == water_region_patch && IsAqueductTile(tile)) {
405 const TileIndex other_end_tile = GetOtherBridgeEnd(tile);
406 if (GetWaterRegionIndex(tile) != GetWaterRegionIndex(other_end_tile)) callback(GetWaterRegionPatchInfo(other_end_tile));
407 }
408 }
409 }
410}
411
416{
417 const int number_of_regions = GetWaterRegionMapSizeX() * GetWaterRegionMapSizeY();
418
419 _water_region_data.clear();
420 _water_region_data.resize(number_of_regions);
421
422 _is_water_region_valid.clear();
423 _is_water_region_valid.resize(number_of_regions, false);
424
425 Debug(map, 2, "Allocating {} x {} water regions", GetWaterRegionMapSizeX(), GetWaterRegionMapSizeY());
426 assert(_is_water_region_valid.size() == _water_region_data.size());
427}
428
429void PrintWaterRegionDebugInfo(TileIndex tile)
430{
431 GetUpdatedWaterRegion(tile).PrintDebugInfo();
432}
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 adds 'at(pos)' and 'operator[](pos)' implementations for 'ConvertibleThroughBase...
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.