Merge branch 'master' into jgrpp
Bump savegame for water regions for ship pathfinder Use ring_buffer for ShipPathCachepull/642/head
commit
914b92b21f
@ -0,0 +1,379 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file water_regions.cpp Handles dividing the water in the map into square regions to assist pathfinding. */
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "map_func.h"
|
||||||
|
#include "water_regions.h"
|
||||||
|
#include "map_func.h"
|
||||||
|
#include "tilearea_type.h"
|
||||||
|
#include "track_func.h"
|
||||||
|
#include "transport_type.h"
|
||||||
|
#include "landscape.h"
|
||||||
|
#include "tunnelbridge_map.h"
|
||||||
|
#include "follow_track.hpp"
|
||||||
|
#include "ship.h"
|
||||||
|
|
||||||
|
using TWaterRegionTraversabilityBits = uint16_t;
|
||||||
|
constexpr TWaterRegionPatchLabel FIRST_REGION_LABEL = 1;
|
||||||
|
constexpr TWaterRegionPatchLabel INVALID_WATER_REGION_PATCH = 0;
|
||||||
|
|
||||||
|
static_assert(sizeof(TWaterRegionTraversabilityBits) * 8 == WATER_REGION_EDGE_LENGTH);
|
||||||
|
|
||||||
|
static inline TrackBits GetWaterTracks(TileIndex tile) { return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)); }
|
||||||
|
static inline bool IsAqueductTile(TileIndex tile) { return IsBridgeTile(tile) && GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER; }
|
||||||
|
|
||||||
|
static inline int GetWaterRegionX(TileIndex tile) { return TileX(tile) / WATER_REGION_EDGE_LENGTH; }
|
||||||
|
static inline int GetWaterRegionY(TileIndex tile) { return TileY(tile) / WATER_REGION_EDGE_LENGTH; }
|
||||||
|
|
||||||
|
static inline int GetWaterRegionMapSizeX() { return MapSizeX() / WATER_REGION_EDGE_LENGTH; }
|
||||||
|
static inline int GetWaterRegionMapSizeY() { return MapSizeY() / WATER_REGION_EDGE_LENGTH; }
|
||||||
|
|
||||||
|
static inline TWaterRegionIndex GetWaterRegionIndex(int region_x, int region_y) { return GetWaterRegionMapSizeX() * region_y + region_x; }
|
||||||
|
static inline TWaterRegionIndex GetWaterRegionIndex(TileIndex tile) { return GetWaterRegionIndex(GetWaterRegionX(tile), GetWaterRegionY(tile)); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a square section of the map of a fixed size. Within this square individual unconnected patches of water are
|
||||||
|
* identified using a Connected Component Labeling (CCL) algorithm. Note that all information stored in this class applies
|
||||||
|
* only to tiles within the square section, there is no knowledge about the rest of the map. This makes it easy to invalidate
|
||||||
|
* and update a water region if any changes are made to it, such as construction or terraforming.
|
||||||
|
*/
|
||||||
|
class WaterRegion
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::array<TWaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{};
|
||||||
|
bool has_cross_region_aqueducts = false;
|
||||||
|
TWaterRegionPatchLabel number_of_patches = 0; // 0 = no water, 1 = one single patch of water, etc...
|
||||||
|
const OrthogonalTileArea tile_area;
|
||||||
|
std::array<TWaterRegionPatchLabel, WATER_REGION_NUMBER_OF_TILES> tile_patch_labels{};
|
||||||
|
bool initialized = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the local index of the tile within the region. The N corner represents 0,
|
||||||
|
* the x direction is positive in the SW direction, and Y is positive in the SE direction.
|
||||||
|
* @param tile Tile within the water region.
|
||||||
|
* @returns The local index.
|
||||||
|
*/
|
||||||
|
inline int GetLocalIndex(TileIndex tile) const
|
||||||
|
{
|
||||||
|
assert(this->tile_area.Contains(tile));
|
||||||
|
return (TileX(tile) - TileX(this->tile_area.tile)) + WATER_REGION_EDGE_LENGTH * (TileY(tile) - TileY(this->tile_area.tile));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
WaterRegion(int region_x, int region_y)
|
||||||
|
: tile_area(TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH)
|
||||||
|
{}
|
||||||
|
|
||||||
|
OrthogonalTileIterator begin() const { return this->tile_area.begin(); }
|
||||||
|
OrthogonalTileIterator end() const { return this->tile_area.end(); }
|
||||||
|
|
||||||
|
bool IsInitialized() const { return this->initialized; }
|
||||||
|
|
||||||
|
void Invalidate() { this->initialized = false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a set of bits indicating whether an edge tile on a particular side is traversable or not. These
|
||||||
|
* values can be used to determine whether a ship can enter/leave the region through a particular edge tile.
|
||||||
|
* @see GetLocalIndex() for a description of the coordinate system used.
|
||||||
|
* @param side Which side of the region we want to know the edge traversability of.
|
||||||
|
* @returns A value holding the edge traversability bits.
|
||||||
|
*/
|
||||||
|
TWaterRegionTraversabilityBits GetEdgeTraversabilityBits(DiagDirection side) const { return edge_traversability_bits[side]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The amount of individual water patches present within the water region. A value of
|
||||||
|
* 0 means there is no water present in the water region at all.
|
||||||
|
*/
|
||||||
|
int NumberOfPatches() const { return this->number_of_patches; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Whether the water region contains aqueducts that cross the region boundaries.
|
||||||
|
*/
|
||||||
|
bool HasCrossRegionAqueducts() const { return this->has_cross_region_aqueducts; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the patch label that was assigned to the tile.
|
||||||
|
* @param tile The tile of which we want to retrieve the label.
|
||||||
|
* @returns The label assigned to the tile.
|
||||||
|
*/
|
||||||
|
TWaterRegionPatchLabel GetLabel(TileIndex tile) const
|
||||||
|
{
|
||||||
|
assert(this->tile_area.Contains(tile));
|
||||||
|
return this->tile_patch_labels[GetLocalIndex(tile)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the connected component labeling and other data gathering.
|
||||||
|
* @see WaterRegion
|
||||||
|
*/
|
||||||
|
void ForceUpdate()
|
||||||
|
{
|
||||||
|
this->has_cross_region_aqueducts = false;
|
||||||
|
|
||||||
|
this->tile_patch_labels.fill(INVALID_WATER_REGION_PATCH);
|
||||||
|
|
||||||
|
for (const TileIndex tile : this->tile_area) {
|
||||||
|
if (IsAqueductTile(tile)) {
|
||||||
|
const TileIndex other_aqueduct_end = GetOtherBridgeEnd(tile);
|
||||||
|
if (!tile_area.Contains(other_aqueduct_end)) {
|
||||||
|
this->has_cross_region_aqueducts = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TWaterRegionPatchLabel current_label = 1;
|
||||||
|
TWaterRegionPatchLabel highest_assigned_label = 0;
|
||||||
|
|
||||||
|
/* Perform connected component labeling. This uses a flooding algorithm that expands until no
|
||||||
|
* additional tiles can be added. Only tiles inside the water region are considered. */
|
||||||
|
for (const TileIndex start_tile : tile_area) {
|
||||||
|
static std::vector<TileIndex> tiles_to_check;
|
||||||
|
tiles_to_check.clear();
|
||||||
|
tiles_to_check.push_back(start_tile);
|
||||||
|
|
||||||
|
bool increase_label = false;
|
||||||
|
while (!tiles_to_check.empty()) {
|
||||||
|
const TileIndex tile = tiles_to_check.back();
|
||||||
|
tiles_to_check.pop_back();
|
||||||
|
|
||||||
|
const TrackdirBits valid_dirs = TrackBitsToTrackdirBits(GetWaterTracks(tile));
|
||||||
|
if (valid_dirs == TRACKDIR_BIT_NONE) continue;
|
||||||
|
|
||||||
|
if (this->tile_patch_labels[GetLocalIndex(tile)] != INVALID_WATER_REGION_PATCH) continue;
|
||||||
|
|
||||||
|
this->tile_patch_labels[GetLocalIndex(tile)] = current_label;
|
||||||
|
highest_assigned_label = current_label;
|
||||||
|
increase_label = true;
|
||||||
|
|
||||||
|
for (const Trackdir dir : SetTrackdirBitIterator(valid_dirs)) {
|
||||||
|
/* By using a TrackFollower we "play by the same rules" as the actual ship pathfinder */
|
||||||
|
CFollowTrackWater ft;
|
||||||
|
if (ft.Follow(tile, dir) && this->tile_area.Contains(ft.m_new_tile)) tiles_to_check.push_back(ft.m_new_tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (increase_label) current_label++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->number_of_patches = highest_assigned_label;
|
||||||
|
this->initialized = true;
|
||||||
|
|
||||||
|
/* Calculate the traversability (whether the tile can be entered / exited) for all edges. Note that
|
||||||
|
* we always follow the same X and Y scanning direction, this is important for comparisons later on! */
|
||||||
|
this->edge_traversability_bits.fill(0);
|
||||||
|
const int top_x = TileX(tile_area.tile);
|
||||||
|
const int top_y = TileY(tile_area.tile);
|
||||||
|
for (int i = 0; i < WATER_REGION_EDGE_LENGTH; ++i) {
|
||||||
|
if (GetWaterTracks(TileXY(top_x + i, top_y)) & TRACK_BIT_3WAY_NW) SetBit(this->edge_traversability_bits[DIAGDIR_NW], i); // NW edge
|
||||||
|
if (GetWaterTracks(TileXY(top_x + i, top_y + WATER_REGION_EDGE_LENGTH - 1)) & TRACK_BIT_3WAY_SE) SetBit(this->edge_traversability_bits[DIAGDIR_SE], i); // SE edge
|
||||||
|
if (GetWaterTracks(TileXY(top_x, top_y + i)) & TRACK_BIT_3WAY_NE) SetBit(this->edge_traversability_bits[DIAGDIR_NE], i); // NE edge
|
||||||
|
if (GetWaterTracks(TileXY(top_x + WATER_REGION_EDGE_LENGTH - 1, top_y + i)) & TRACK_BIT_3WAY_SW) SetBit(this->edge_traversability_bits[DIAGDIR_SW], i); // SW edge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the patch labels and other data, but only if the region is not yet initialized.
|
||||||
|
*/
|
||||||
|
inline void UpdateIfNotInitialized()
|
||||||
|
{
|
||||||
|
if (!this->initialized) ForceUpdate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<WaterRegion> _water_regions;
|
||||||
|
|
||||||
|
TileIndex GetTileIndexFromLocalCoordinate(int region_x, int region_y, int local_x, int local_y)
|
||||||
|
{
|
||||||
|
assert(local_x >= 0 && local_y < WATER_REGION_EDGE_LENGTH);
|
||||||
|
assert(local_y >= 0 && local_y < WATER_REGION_EDGE_LENGTH);
|
||||||
|
return TileXY(WATER_REGION_EDGE_LENGTH * region_x + local_x, WATER_REGION_EDGE_LENGTH * region_y + local_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
TileIndex GetEdgeTileCoordinate(int region_x, int region_y, DiagDirection side, int x_or_y)
|
||||||
|
{
|
||||||
|
assert(x_or_y >= 0 && x_or_y < WATER_REGION_EDGE_LENGTH);
|
||||||
|
switch (side) {
|
||||||
|
case DIAGDIR_NE: return GetTileIndexFromLocalCoordinate(region_x, region_y, 0, x_or_y);
|
||||||
|
case DIAGDIR_SW: return GetTileIndexFromLocalCoordinate(region_x, region_y, WATER_REGION_EDGE_LENGTH - 1, x_or_y);
|
||||||
|
case DIAGDIR_NW: return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, 0);
|
||||||
|
case DIAGDIR_SE: return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, WATER_REGION_EDGE_LENGTH - 1);
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WaterRegion &GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
|
||||||
|
{
|
||||||
|
WaterRegion &result = _water_regions[GetWaterRegionIndex(region_x, region_y)];
|
||||||
|
result.UpdateIfNotInitialized();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaterRegion &GetUpdatedWaterRegion(TileIndex tile)
|
||||||
|
{
|
||||||
|
WaterRegion &result = _water_regions[GetWaterRegionIndex(tile)];
|
||||||
|
result.UpdateIfNotInitialized();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the water region
|
||||||
|
* @param water_region The Water region to return the index for
|
||||||
|
*/
|
||||||
|
TWaterRegionIndex GetWaterRegionIndex(const WaterRegionDesc &water_region)
|
||||||
|
{
|
||||||
|
return GetWaterRegionIndex(water_region.x, water_region.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the center tile of a particular water region.
|
||||||
|
* @param water_region The water region to find the center tile for.
|
||||||
|
* @returns The center tile of the water region.
|
||||||
|
*/
|
||||||
|
TileIndex GetWaterRegionCenterTile(const WaterRegionDesc &water_region)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns basic water region information for the provided tile.
|
||||||
|
* @param tile The tile for which the information will be calculated.
|
||||||
|
*/
|
||||||
|
WaterRegionDesc GetWaterRegionInfo(TileIndex tile)
|
||||||
|
{
|
||||||
|
return WaterRegionDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns basic water region patch information for the provided tile.
|
||||||
|
* @param tile The tile for which the information will be calculated.
|
||||||
|
*/
|
||||||
|
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile)
|
||||||
|
{
|
||||||
|
WaterRegion ®ion = GetUpdatedWaterRegion(tile);
|
||||||
|
return WaterRegionPatchDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile), region.GetLabel(tile)};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the water region that tile is part of as invalid.
|
||||||
|
* @param tile Tile within the water region that we wish to invalidate.
|
||||||
|
*/
|
||||||
|
void InvalidateWaterRegion(TileIndex tile)
|
||||||
|
{
|
||||||
|
const int index = GetWaterRegionIndex(tile);
|
||||||
|
if (index > static_cast<int>(_water_regions.size())) return;
|
||||||
|
_water_regions[index].Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the provided callback function for all water region patches
|
||||||
|
* accessible from one particular side of the starting patch.
|
||||||
|
* @param water_region_patch Water patch within the water region to start searching from
|
||||||
|
* @param side Side of the water region to look for neigboring patches of water
|
||||||
|
* @param callback The function that will be called for each neighbor that is found
|
||||||
|
*/
|
||||||
|
static inline void VisitAdjacentWaterRegionPatchNeighbors(const WaterRegionPatchDesc &water_region_patch, DiagDirection side, TVisitWaterRegionPatchCallBack &func)
|
||||||
|
{
|
||||||
|
const WaterRegion ¤t_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
||||||
|
|
||||||
|
const TileIndexDiffC offset = TileIndexDiffCByDiagDir(side);
|
||||||
|
const int nx = water_region_patch.x + offset.x;
|
||||||
|
const int ny = water_region_patch.y + offset.y;
|
||||||
|
|
||||||
|
if (nx < 0 || ny < 0 || nx >= GetWaterRegionMapSizeX() || ny >= GetWaterRegionMapSizeY()) return;
|
||||||
|
|
||||||
|
const WaterRegion &neighboring_region = GetUpdatedWaterRegion(nx, ny);
|
||||||
|
const DiagDirection opposite_side = ReverseDiagDir(side);
|
||||||
|
|
||||||
|
/* Indicates via which local x or y coordinates (depends on the "side" parameter) we can cross over into the adjacent region. */
|
||||||
|
const TWaterRegionTraversabilityBits traversability_bits = current_region.GetEdgeTraversabilityBits(side)
|
||||||
|
& neighboring_region.GetEdgeTraversabilityBits(opposite_side);
|
||||||
|
if (traversability_bits == 0) return;
|
||||||
|
|
||||||
|
if (current_region.NumberOfPatches() == 1 && neighboring_region.NumberOfPatches() == 1) {
|
||||||
|
func(WaterRegionPatchDesc{ nx, ny, FIRST_REGION_LABEL }); // No further checks needed because we know there is just one patch for both adjacent regions
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Multiple water patches can be reached from the current patch. Check each edge tile individually. */
|
||||||
|
static std::vector<TWaterRegionPatchLabel> unique_labels; // static and vector-instead-of-map for performance reasons
|
||||||
|
unique_labels.clear();
|
||||||
|
for (int x_or_y = 0; x_or_y < WATER_REGION_EDGE_LENGTH; ++x_or_y) {
|
||||||
|
if (!HasBit(traversability_bits, x_or_y)) continue;
|
||||||
|
|
||||||
|
const TileIndex current_edge_tile = GetEdgeTileCoordinate(water_region_patch.x, water_region_patch.y, side, x_or_y);
|
||||||
|
const TWaterRegionPatchLabel current_label = current_region.GetLabel(current_edge_tile);
|
||||||
|
if (current_label != water_region_patch.label) continue;
|
||||||
|
|
||||||
|
const TileIndex neighbor_edge_tile = GetEdgeTileCoordinate(nx, ny, opposite_side, x_or_y);
|
||||||
|
const TWaterRegionPatchLabel neighbor_label = neighboring_region.GetLabel(neighbor_edge_tile);
|
||||||
|
if (std::find(unique_labels.begin(), unique_labels.end(), neighbor_label) == unique_labels.end()) unique_labels.push_back(neighbor_label);
|
||||||
|
}
|
||||||
|
for (TWaterRegionPatchLabel unique_label : unique_labels) func(WaterRegionPatchDesc{ nx, ny, unique_label });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the provided callback function on all accessible water region patches in
|
||||||
|
* each cardinal direction, plus any others that are reachable via aqueducts.
|
||||||
|
* @param water_region_patch Water patch within the water region to start searching from
|
||||||
|
* @param callback The function that will be called for each accessible water patch that is found
|
||||||
|
*/
|
||||||
|
void VisitWaterRegionPatchNeighbors(const WaterRegionPatchDesc &water_region_patch, TVisitWaterRegionPatchCallBack &callback)
|
||||||
|
{
|
||||||
|
const WaterRegion ¤t_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
||||||
|
|
||||||
|
/* Visit adjacent water region patches in each cardinal direction */
|
||||||
|
for (DiagDirection side = DIAGDIR_BEGIN; side < DIAGDIR_END; side++) VisitAdjacentWaterRegionPatchNeighbors(water_region_patch, side, callback);
|
||||||
|
|
||||||
|
/* Visit neigboring water patches accessible via cross-region aqueducts */
|
||||||
|
if (current_region.HasCrossRegionAqueducts()) {
|
||||||
|
for (const TileIndex tile : current_region) {
|
||||||
|
if (GetWaterRegionPatchInfo(tile) == water_region_patch && IsAqueductTile(tile)) {
|
||||||
|
const TileIndex other_end_tile = GetOtherBridgeEnd(tile);
|
||||||
|
if (GetWaterRegionIndex(tile) != GetWaterRegionIndex(other_end_tile)) callback(GetWaterRegionPatchInfo(other_end_tile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WaterRegionSaveLoadInfo> GetWaterRegionSaveLoadInfo()
|
||||||
|
{
|
||||||
|
std::vector<WaterRegionSaveLoadInfo> result;
|
||||||
|
for (WaterRegion ®ion : _water_regions) result.push_back({ region.IsInitialized() });
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadWaterRegions(const std::vector<WaterRegionSaveLoadInfo> &save_load_info)
|
||||||
|
{
|
||||||
|
_water_regions.clear();
|
||||||
|
_water_regions.reserve(save_load_info.size());
|
||||||
|
TWaterRegionIndex index = 0;
|
||||||
|
for (const auto &loaded_region_info : save_load_info) {
|
||||||
|
const int region_x = index % GetWaterRegionMapSizeX();
|
||||||
|
const int region_y = index / GetWaterRegionMapSizeX();
|
||||||
|
WaterRegion ®ion = _water_regions.emplace_back(region_x, region_y);
|
||||||
|
if (loaded_region_info.initialized) region.ForceUpdate();
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes all water regions. All water tiles will be scanned and interconnected water patches within regions will be identified.
|
||||||
|
*/
|
||||||
|
void InitializeWaterRegions()
|
||||||
|
{
|
||||||
|
_water_regions.clear();
|
||||||
|
_water_regions.reserve(static_cast<size_t>(GetWaterRegionMapSizeX()) * GetWaterRegionMapSizeY());
|
||||||
|
|
||||||
|
for (int region_y = 0; region_y < GetWaterRegionMapSizeY(); region_y++) {
|
||||||
|
for (int region_x = 0; region_x < GetWaterRegionMapSizeX(); region_x++) {
|
||||||
|
_water_regions.emplace_back(region_x, region_y).ForceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file water_regions.h Handles dividing the water in the map into regions to assist pathfinding. */
|
||||||
|
|
||||||
|
#ifndef WATER_REGIONS_H
|
||||||
|
#define WATER_REGIONS_H
|
||||||
|
|
||||||
|
#include "tile_type.h"
|
||||||
|
#include "map_func.h"
|
||||||
|
|
||||||
|
using TWaterRegionPatchLabel = uint8_t;
|
||||||
|
using TWaterRegionIndex = uint;
|
||||||
|
|
||||||
|
constexpr int WATER_REGION_EDGE_LENGTH = 16;
|
||||||
|
constexpr int WATER_REGION_NUMBER_OF_TILES = WATER_REGION_EDGE_LENGTH * WATER_REGION_EDGE_LENGTH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a single interconnected patch of water within a particular water region.
|
||||||
|
*/
|
||||||
|
struct WaterRegionPatchDesc
|
||||||
|
{
|
||||||
|
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
|
||||||
|
TWaterRegionPatchLabel label; ///< Unique label identifying the patch within the region
|
||||||
|
|
||||||
|
bool operator==(const WaterRegionPatchDesc &other) const { return x == other.x && y == other.y && label == other.label; }
|
||||||
|
bool operator!=(const WaterRegionPatchDesc &other) const { return !(*this == other); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a single square water region.
|
||||||
|
*/
|
||||||
|
struct WaterRegionDesc
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
WaterRegionDesc(const int x, const int y) : x(x), y(y) {}
|
||||||
|
WaterRegionDesc(const WaterRegionPatchDesc &water_region_patch) : x(water_region_patch.x), y(water_region_patch.y) {}
|
||||||
|
|
||||||
|
bool operator==(const WaterRegionDesc &other) const { return x == other.x && y == other.y; }
|
||||||
|
bool operator!=(const WaterRegionDesc &other) const { return !(*this == other); }
|
||||||
|
};
|
||||||
|
|
||||||
|
TWaterRegionIndex GetWaterRegionIndex(const WaterRegionDesc &water_region);
|
||||||
|
|
||||||
|
TileIndex GetWaterRegionCenterTile(const WaterRegionDesc &water_region);
|
||||||
|
|
||||||
|
WaterRegionDesc GetWaterRegionInfo(TileIndex tile);
|
||||||
|
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile);
|
||||||
|
|
||||||
|
void InvalidateWaterRegion(TileIndex tile);
|
||||||
|
|
||||||
|
using TVisitWaterRegionPatchCallBack = std::function<void(const WaterRegionPatchDesc &)>;
|
||||||
|
void VisitWaterRegionPatchNeighbors(const WaterRegionPatchDesc &water_region_patch, TVisitWaterRegionPatchCallBack &callback);
|
||||||
|
|
||||||
|
void InitializeWaterRegions();
|
||||||
|
|
||||||
|
struct WaterRegionSaveLoadInfo
|
||||||
|
{
|
||||||
|
bool initialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<WaterRegionSaveLoadInfo> GetWaterRegionSaveLoadInfo();
|
||||||
|
void LoadWaterRegions(const std::vector<WaterRegionSaveLoadInfo> &save_load_info);
|
||||||
|
|
||||||
|
#endif /* WATER_REGIONS_H */
|
@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file yapf_ship_regions.cpp Implementation of YAPF for water regions, which are used for finding intermediate ship destinations. */
|
||||||
|
|
||||||
|
#include "../../stdafx.h"
|
||||||
|
#include "../../ship.h"
|
||||||
|
|
||||||
|
#include "yapf.hpp"
|
||||||
|
#include "yapf_ship_regions.h"
|
||||||
|
#include "../water_regions.h"
|
||||||
|
|
||||||
|
#include "../../safeguards.h"
|
||||||
|
|
||||||
|
constexpr int DIRECT_NEIGHBOR_COST = 100;
|
||||||
|
constexpr int NODES_PER_REGION = 4;
|
||||||
|
constexpr int MAX_NUMBER_OF_NODES = 65536;
|
||||||
|
|
||||||
|
/** Yapf Node Key that represents a single patch of interconnected water within a water region. */
|
||||||
|
struct CYapfRegionPatchNodeKey {
|
||||||
|
WaterRegionPatchDesc m_water_region_patch;
|
||||||
|
|
||||||
|
static_assert(sizeof(TWaterRegionPatchLabel) == sizeof(byte)); // Important for the hash calculation.
|
||||||
|
|
||||||
|
inline void Set(const WaterRegionPatchDesc &water_region_patch)
|
||||||
|
{
|
||||||
|
m_water_region_patch = water_region_patch;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int CalcHash() const { return m_water_region_patch.label | GetWaterRegionIndex(m_water_region_patch) << 8; }
|
||||||
|
inline bool operator==(const CYapfRegionPatchNodeKey &other) const { return CalcHash() == other.CalcHash(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint ManhattanDistance(const CYapfRegionPatchNodeKey &a, const CYapfRegionPatchNodeKey &b)
|
||||||
|
{
|
||||||
|
return (std::abs(a.m_water_region_patch.x - b.m_water_region_patch.x) + std::abs(a.m_water_region_patch.y - b.m_water_region_patch.y)) * DIRECT_NEIGHBOR_COST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Yapf Node for water regions. */
|
||||||
|
template <class Tkey_>
|
||||||
|
struct CYapfRegionNodeT {
|
||||||
|
typedef Tkey_ Key;
|
||||||
|
typedef CYapfRegionNodeT<Tkey_> Node;
|
||||||
|
|
||||||
|
Tkey_ m_key;
|
||||||
|
Node *m_hash_next;
|
||||||
|
Node *m_parent;
|
||||||
|
int m_cost;
|
||||||
|
int m_estimate;
|
||||||
|
|
||||||
|
inline void Set(Node *parent, const WaterRegionPatchDesc &water_region_patch)
|
||||||
|
{
|
||||||
|
m_key.Set(water_region_patch);
|
||||||
|
m_hash_next = nullptr;
|
||||||
|
m_parent = parent;
|
||||||
|
m_cost = 0;
|
||||||
|
m_estimate = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Set(Node *parent, const Key &key)
|
||||||
|
{
|
||||||
|
Set(parent, key.m_water_region_patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
DiagDirection GetDiagDirFromParent() const
|
||||||
|
{
|
||||||
|
if (!m_parent) return INVALID_DIAGDIR;
|
||||||
|
const int dx = m_key.m_water_region_patch.x - m_parent->m_key.m_water_region_patch.x;
|
||||||
|
const int dy = m_key.m_water_region_patch.y - m_parent->m_key.m_water_region_patch.y;
|
||||||
|
if (dx > 0 && dy == 0) return DIAGDIR_SW;
|
||||||
|
if (dx < 0 && dy == 0) return DIAGDIR_NE;
|
||||||
|
if (dx == 0 && dy > 0) return DIAGDIR_SE;
|
||||||
|
if (dx == 0 && dy < 0) return DIAGDIR_NW;
|
||||||
|
return INVALID_DIAGDIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Node *GetHashNext() { return m_hash_next; }
|
||||||
|
inline void SetHashNext(Node *pNext) { m_hash_next = pNext; }
|
||||||
|
inline const Tkey_ &GetKey() const { return m_key; }
|
||||||
|
inline int GetCost() { return m_cost; }
|
||||||
|
inline int GetCostEstimate() { return m_estimate; }
|
||||||
|
inline bool operator<(const Node &other) const { return m_estimate < other.m_estimate; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** YAPF origin for water regions. */
|
||||||
|
template <class Types>
|
||||||
|
class CYapfOriginRegionT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename Types::Tpf Tpf; ///< The pathfinder class (derived from THIS class).
|
||||||
|
typedef typename Types::NodeList::Titem Node; ///< This will be our node type.
|
||||||
|
typedef typename Node::Key Key; ///< Key to hash tables.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
inline Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<CYapfRegionPatchNodeKey> m_origin_keys;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void AddOrigin(const WaterRegionPatchDesc &water_region_patch)
|
||||||
|
{
|
||||||
|
if (!HasOrigin(water_region_patch)) m_origin_keys.push_back(CYapfRegionPatchNodeKey{ water_region_patch });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasOrigin(const WaterRegionPatchDesc &water_region_patch)
|
||||||
|
{
|
||||||
|
return std::find(m_origin_keys.begin(), m_origin_keys.end(), CYapfRegionPatchNodeKey{ water_region_patch }) != m_origin_keys.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PfSetStartupNodes()
|
||||||
|
{
|
||||||
|
for (const CYapfRegionPatchNodeKey &origin_key : m_origin_keys) {
|
||||||
|
Node &node = Yapf().CreateNewNode();
|
||||||
|
node.Set(nullptr, origin_key);
|
||||||
|
Yapf().AddStartupNode(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** YAPF destination provider for water regions. */
|
||||||
|
template <class Types>
|
||||||
|
class CYapfDestinationRegionT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename Types::Tpf Tpf; ///< The pathfinder class (derived from THIS class).
|
||||||
|
typedef typename Types::NodeList::Titem Node; ///< This will be our node type.
|
||||||
|
typedef typename Node::Key Key; ///< Key to hash tables.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Key m_dest;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void SetDestination(const WaterRegionPatchDesc &water_region_patch)
|
||||||
|
{
|
||||||
|
m_dest.Set(water_region_patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline bool PfDetectDestination(Node &n) const
|
||||||
|
{
|
||||||
|
return n.m_key == m_dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool PfCalcEstimate(Node &n)
|
||||||
|
{
|
||||||
|
if (PfDetectDestination(n)) {
|
||||||
|
n.m_estimate = n.m_cost;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
n.m_estimate = n.m_cost + ManhattanDistance(n.m_key, m_dest);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** YAPF node following for water region pathfinding. */
|
||||||
|
template <class Types>
|
||||||
|
class CYapfFollowRegionT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename Types::Tpf Tpf; ///< The pathfinder class (derived from THIS class).
|
||||||
|
typedef typename Types::TrackFollower TrackFollower;
|
||||||
|
typedef typename Types::NodeList::Titem Node; ///< This will be our node type.
|
||||||
|
typedef typename Node::Key Key; ///< Key to hash tables.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
inline Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline void PfFollowNode(Node &old_node)
|
||||||
|
{
|
||||||
|
TVisitWaterRegionPatchCallBack visitFunc = [&](const WaterRegionPatchDesc &water_region_patch)
|
||||||
|
{
|
||||||
|
Node &node = Yapf().CreateNewNode();
|
||||||
|
node.Set(&old_node, water_region_patch);
|
||||||
|
Yapf().AddNewNode(node, TrackFollower{});
|
||||||
|
};
|
||||||
|
VisitWaterRegionPatchNeighbors(old_node.m_key.m_water_region_patch, visitFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline char TransportTypeChar() const { return '^'; }
|
||||||
|
|
||||||
|
static std::vector<WaterRegionPatchDesc> FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
|
||||||
|
{
|
||||||
|
const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile);
|
||||||
|
|
||||||
|
/* We reserve 4 nodes (patches) per water region. The vast majority of water regions have 1 or 2 regions so this should be a pretty
|
||||||
|
* safe limit. We cap the limit at 65536 which is at a region size of 16x16 is equivalent to one node per region for a 4096x4096 map. */
|
||||||
|
Tpf pf(std::min(static_cast<int>(MapSize() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES));
|
||||||
|
pf.SetDestination(start_water_region_patch);
|
||||||
|
|
||||||
|
if (v->current_order.IsType(OT_GOTO_STATION)) {
|
||||||
|
DestinationID station_id = v->current_order.GetDestination();
|
||||||
|
const BaseStation *station = BaseStation::Get(station_id);
|
||||||
|
TileArea tile_area;
|
||||||
|
station->GetTileArea(&tile_area, STATION_DOCK);
|
||||||
|
for (const auto &tile : tile_area) {
|
||||||
|
if (IsDockingTile(tile) && IsShipDestinationTile(tile, station_id)) {
|
||||||
|
pf.AddOrigin(GetWaterRegionPatchInfo(tile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TileIndex tile = v->dest_tile;
|
||||||
|
pf.AddOrigin(GetWaterRegionPatchInfo(tile));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If origin and destination are the same we simply return that water patch. */
|
||||||
|
std::vector<WaterRegionPatchDesc> path = { start_water_region_patch };
|
||||||
|
path.reserve(max_returned_path_length);
|
||||||
|
if (pf.HasOrigin(start_water_region_patch)) return path;
|
||||||
|
|
||||||
|
/* Find best path. */
|
||||||
|
if (!pf.FindPath(v)) return {}; // Path not found.
|
||||||
|
|
||||||
|
Node *node = pf.GetBestNode();
|
||||||
|
for (int i = 0; i < max_returned_path_length - 1; ++i) {
|
||||||
|
if (node != nullptr) {
|
||||||
|
node = node->m_parent;
|
||||||
|
if (node != nullptr) path.push_back(node->m_key.m_water_region_patch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!path.empty());
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Cost Provider of YAPF for water regions. */
|
||||||
|
template <class Types>
|
||||||
|
class CYapfCostRegionT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename Types::Tpf Tpf; ///< The pathfinder class (derived from THIS class).
|
||||||
|
typedef typename Types::TrackFollower TrackFollower;
|
||||||
|
typedef typename Types::NodeList::Titem Node; ///< This will be our node type.
|
||||||
|
typedef typename Node::Key Key; ///< Key to hash tables.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** To access inherited path finder. */
|
||||||
|
Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Called by YAPF to calculate the cost from the origin to the given node.
|
||||||
|
* Calculates only the cost of given node, adds it to the parent node cost
|
||||||
|
* and stores the result into Node::m_cost member.
|
||||||
|
*/
|
||||||
|
inline bool PfCalcCost(Node &n, const TrackFollower *)
|
||||||
|
{
|
||||||
|
n.m_cost = n.m_parent->m_cost + ManhattanDistance(n.m_key, n.m_parent->m_key);
|
||||||
|
|
||||||
|
/* Incentivise zigzagging by adding a slight penalty when the search continues in the same direction. */
|
||||||
|
Node *grandparent = n.m_parent->m_parent;
|
||||||
|
if (grandparent != nullptr) {
|
||||||
|
const DiagDirDiff dir_diff = DiagDirDifference(n.m_parent->GetDiagDirFromParent(), n.GetDiagDirFromParent());
|
||||||
|
if (dir_diff != DIAGDIRDIFF_90LEFT && dir_diff != DIAGDIRDIFF_90RIGHT) n.m_cost += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* We don't need a follower but YAPF requires one. */
|
||||||
|
struct DummyFollower : public CFollowTrackWater {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config struct of YAPF for route planning.
|
||||||
|
* Defines all 6 base YAPF modules as classes providing services for CYapfBaseT.
|
||||||
|
*/
|
||||||
|
template <class Tpf_, class Tnode_list>
|
||||||
|
struct CYapfRegion_TypesT
|
||||||
|
{
|
||||||
|
typedef CYapfRegion_TypesT<Tpf_, Tnode_list> Types; ///< Shortcut for this struct type.
|
||||||
|
typedef Tpf_ Tpf; ///< Pathfinder type.
|
||||||
|
typedef DummyFollower TrackFollower; ///< Track follower helper class
|
||||||
|
typedef Tnode_list NodeList;
|
||||||
|
typedef Ship VehicleType;
|
||||||
|
|
||||||
|
/** Pathfinder components (modules). */
|
||||||
|
typedef CYapfBaseT<Types> PfBase; ///< Base pathfinder class.
|
||||||
|
typedef CYapfFollowRegionT<Types> PfFollow; ///< Node follower.
|
||||||
|
typedef CYapfOriginRegionT<Types> PfOrigin; ///< Origin provider.
|
||||||
|
typedef CYapfDestinationRegionT<Types> PfDestination; ///< Destination/distance provider.
|
||||||
|
typedef CYapfSegmentCostCacheNoneT<Types> PfCache; ///< Segment cost cache provider.
|
||||||
|
typedef CYapfCostRegionT<Types> PfCost; ///< Cost provider.
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef CNodeList_HashTableT<CYapfRegionNodeT<CYapfRegionPatchNodeKey>, 12, 12> CRegionNodeListWater;
|
||||||
|
|
||||||
|
struct CYapfRegionWater : CYapfT<CYapfRegion_TypesT<CYapfRegionWater, CRegionNodeListWater>>
|
||||||
|
{
|
||||||
|
explicit CYapfRegionWater(int max_nodes) { m_max_search_nodes = max_nodes; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a path at the water region level. Note that the starting region is always included if the path was found.
|
||||||
|
* @param v The ship to find a path for.
|
||||||
|
* @param start_tile The tile to start searching from.
|
||||||
|
* @param max_returned_path_length The maximum length of the path that will be returned.
|
||||||
|
* @returns A path of water region patches, or an empty vector if no path was found.
|
||||||
|
*/
|
||||||
|
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
|
||||||
|
{
|
||||||
|
return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length);
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file yapf_ship_regions.h Implementation of YAPF for water regions, which are used for finding intermediate ship destinations. */
|
||||||
|
|
||||||
|
#ifndef YAPF_SHIP_REGIONS_H
|
||||||
|
#define YAPF_SHIP_REGIONS_H
|
||||||
|
|
||||||
|
#include "../../stdafx.h"
|
||||||
|
#include "../../tile_type.h"
|
||||||
|
#include "../water_regions.h"
|
||||||
|
|
||||||
|
struct Ship;
|
||||||
|
|
||||||
|
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length);
|
||||||
|
|
||||||
|
#endif /* YAPF_SHIP_REGIONS_H */
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file water_regions_sl.cpp Handles saving and loading of water region data */
|
||||||
|
|
||||||
|
#include "../stdafx.h"
|
||||||
|
|
||||||
|
#include "saveload.h"
|
||||||
|
#include "../pathfinder/water_regions.h"
|
||||||
|
|
||||||
|
#include "../safeguards.h"
|
||||||
|
|
||||||
|
namespace upstream_sl {
|
||||||
|
|
||||||
|
static const SaveLoad _water_region_desc[] = {
|
||||||
|
SLE_VAR(WaterRegionSaveLoadInfo, initialized, SLE_BOOL),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WRGNChunkHandler : ChunkHandler {
|
||||||
|
WRGNChunkHandler() : ChunkHandler('WRGN', CH_TABLE) {}
|
||||||
|
|
||||||
|
void Save() const override
|
||||||
|
{
|
||||||
|
SlTableHeader(_water_region_desc);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (WaterRegionSaveLoadInfo ®ion : GetWaterRegionSaveLoadInfo()) {
|
||||||
|
SlSetArrayIndex(index++);
|
||||||
|
SlObject(®ion, _water_region_desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Load() const override
|
||||||
|
{
|
||||||
|
const std::vector<SaveLoad> slt = SlTableHeader(_water_region_desc);
|
||||||
|
|
||||||
|
int index;
|
||||||
|
|
||||||
|
std::vector<WaterRegionSaveLoadInfo> loaded_info;
|
||||||
|
while ((index = SlIterateArray()) != -1) {
|
||||||
|
WaterRegionSaveLoadInfo region_info;
|
||||||
|
SlObject(®ion_info, slt);
|
||||||
|
loaded_info.push_back(std::move(region_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadWaterRegions(loaded_info);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const WRGNChunkHandler WRGN;
|
||||||
|
static const ChunkHandlerRef water_region_chunk_handlers[] = { WRGN };
|
||||||
|
extern const ChunkHandlerTable _water_region_chunk_handlers(water_region_chunk_handlers);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* 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.
|
||||||
|
* 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.
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file water_regions_sl.cpp Handles saving and loading of water region data */
|
||||||
|
#include "../stdafx.h"
|
||||||
|
|
||||||
|
#include "saveload.h"
|
||||||
|
|
||||||
|
extern SaveLoadVersion _sl_xv_upstream_version;
|
||||||
|
|
||||||
|
struct GetWaterRegionsLoadInfo
|
||||||
|
{
|
||||||
|
static SaveLoadVersion GetLoadVersion()
|
||||||
|
{
|
||||||
|
return _sl_xv_upstream_version != SL_MIN_VERSION ? _sl_xv_upstream_version : SLV_WATER_REGIONS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const ChunkHandler water_region_chunk_handlers[] = {
|
||||||
|
MakeUpstreamChunkHandler<'WRGN', GetWaterRegionsLoadInfo>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const ChunkHandlerTable _water_region_chunk_handlers(water_region_chunk_handlers);
|
Loading…
Reference in New Issue