diff --git a/src/landscape.cpp b/src/landscape.cpp index 02a61ddfc0..6599427fa0 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -1207,103 +1207,92 @@ static bool FlowsDown(TileIndex begin, TileIndex end) return slope_end == SLOPE_FLAT || slope_begin == SLOPE_FLAT; } -/** Parameters for river generation to pass as AyStar user data. */ -struct River_UserData { - TileIndex spring; ///< The current spring during river generation. - bool main_river; ///< Whether the current river is a big river that others flow into. -}; - -/* AyStar callback for checking whether we reached our destination. */ -static AyStarStatus River_EndNodeCheck(const AyStar *aystar, const PathNode *current) -{ - return current->GetTile() == *static_cast(aystar->user_target) ? AyStarStatus::FoundEndNode : AyStarStatus::Done; -} - -/* AyStar callback for getting the cost of the current node. */ -static int32_t River_CalculateG(AyStar *, AyStarNode *, PathNode *) -{ - return 1 + RandomRange(_settings_game.game_creation.river_route_random); -} - -/* AyStar callback for getting the estimated cost to the destination. */ -static int32_t River_CalculateH(AyStar *aystar, AyStarNode *current, PathNode *) -{ - return DistanceManhattan(*static_cast(aystar->user_target), current->tile); -} - -/* AyStar callback for getting the neighbouring nodes of the given node. */ -static void River_GetNeighbours(AyStar *aystar, PathNode *current) -{ - TileIndex tile = current->GetTile(); - - aystar->neighbours.clear(); - for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) { - TileIndex t = tile + TileOffsByDiagDir(d); - if (IsValidTile(t) && FlowsDown(tile, t)) { - auto &neighbour = aystar->neighbours.emplace_back(); - neighbour.tile = t; - neighbour.td = INVALID_TRACKDIR; - } +/** Search path and build river */ +class RiverBuilder : public AyStar { +protected: + AyStarStatus EndNodeCheck(const PathNode ¤t) const override + { + return current.GetTile() == this->end ? AyStarStatus::FoundEndNode : AyStarStatus::Done; } -} -/* AyStar callback when an route has been found. */ -static void River_FoundEndNode(AyStar *aystar, PathNode *current) -{ - River_UserData *data = static_cast(aystar->user_data); + int32_t CalculateG(const AyStarNode &, const PathNode &) const override + { + return 1 + RandomRange(_settings_game.game_creation.river_route_random); + } - /* First, build the river without worrying about its width. */ - for (PathNode *path = current->parent; path != nullptr; path = path->parent) { - TileIndex tile = path->GetTile(); - if (!IsWaterTile(tile)) { - MakeRiverAndModifyDesertZoneAround(tile); + int32_t CalculateH(const AyStarNode ¤t, const PathNode &) const override + { + return DistanceManhattan(this->end, current.tile); + } + + void GetNeighbours(const PathNode ¤t, std::vector &neighbours) const override + { + TileIndex tile = current.GetTile(); + + neighbours.clear(); + for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) { + TileIndex t = tile + TileOffsByDiagDir(d); + if (IsValidTile(t) && FlowsDown(tile, t)) { + auto &neighbour = neighbours.emplace_back(); + neighbour.tile = t; + neighbour.td = INVALID_TRACKDIR; + } } } - /* If the river is a main river, go back along the path to widen it. - * Don't make wide rivers if we're using the original landscape generator. - */ - if (_settings_game.game_creation.land_generator != LG_ORIGINAL && data->main_river) { - const uint long_river_length = _settings_game.game_creation.min_river_length * 4; - - for (PathNode *path = current->parent; path != nullptr; path = path->parent) { + void FoundEndNode(const PathNode ¤t) override + { + /* First, build the river without worrying about its width. */ + for (PathNode *path = current.parent; path != nullptr; path = path->parent) { TileIndex tile = path->GetTile(); + if (!IsWaterTile(tile)) { + MakeRiverAndModifyDesertZoneAround(tile); + } + } - /* Check if we should widen river depending on how far we are away from the source. */ - uint current_river_length = DistanceManhattan(data->spring, tile); - uint radius = std::min(3u, (current_river_length / (long_river_length / 3u)) + 1u); + /* If the river is a main river, go back along the path to widen it. + * Don't make wide rivers if we're using the original landscape generator. + */ + if (_settings_game.game_creation.land_generator != LG_ORIGINAL && this->main_river) { + const uint long_river_length = _settings_game.game_creation.min_river_length * 4; - if (radius > 1) CircularTileSearch(&tile, radius, RiverMakeWider, &path->key.tile); + for (PathNode *path = current.parent; path != nullptr; path = path->parent) { + TileIndex tile = path->GetTile(); + + /* Check if we should widen river depending on how far we are away from the source. */ + uint current_river_length = DistanceManhattan(this->spring, tile); + uint radius = std::min(3u, (current_river_length / (long_river_length / 3u)) + 1u); + + if (radius > 1) CircularTileSearch(&tile, radius, RiverMakeWider, &path->key.tile); + } } } -} -/** - * Actually build the river between the begin and end tiles using AyStar. - * @param begin The begin of the river. - * @param end The end of the river. - * @param spring The springing point of the river. - * @param main_river Whether the current river is a big river that others flow into. - */ -static void BuildRiver(TileIndex begin, TileIndex end, TileIndex spring, bool main_river) -{ - River_UserData user_data = { spring, main_river }; + RiverBuilder(TileIndex end, TileIndex spring, bool main_river) : end(end), spring(spring), main_river(main_river) {} - AyStar finder = {}; - finder.CalculateG = River_CalculateG; - finder.CalculateH = River_CalculateH; - finder.GetNeighbours = River_GetNeighbours; - finder.EndNodeCheck = River_EndNodeCheck; - finder.FoundEndNode = River_FoundEndNode; - finder.user_target = &end; - finder.user_data = &user_data; +private: + TileIndex end; ///< Destination for the river. + TileIndex spring; ///< The current spring during river generation. + bool main_river; ///< Whether the current river is a big river that others flow into. - AyStarNode start; - start.tile = begin; - start.td = INVALID_TRACKDIR; - finder.AddStartNode(&start, 0); - finder.Main(); -} +public: + /** + * Actually build the river between the begin and end tiles using AyStar. + * @param begin The begin of the river. + * @param end The end of the river. + * @param spring The springing point of the river. + * @param main_river Whether the current river is a big river that others flow into. + */ + static void Exec(TileIndex begin, TileIndex end, TileIndex spring, bool main_river) + { + RiverBuilder builder(end, spring, main_river); + AyStarNode start; + start.tile = begin; + start.td = INVALID_TRACKDIR; + builder.AddStartNode(&start, 0); + builder.Main(); + } +}; /** * Try to flow the river down from a given begin. @@ -1383,7 +1372,7 @@ static std::tuple FlowRiver(TileIndex spring, TileIndex begin, uint } marks.clear(); - if (found) BuildRiver(begin, end, spring, main_river); + if (found) RiverBuilder::Exec(begin, end, spring, main_river); return { found, main_river }; } diff --git a/src/pathfinder/aystar.cpp b/src/pathfinder/aystar.cpp index 01781a9116..b885c76c43 100644 --- a/src/pathfinder/aystar.cpp +++ b/src/pathfinder/aystar.cpp @@ -41,7 +41,7 @@ void AyStar::CheckTile(AyStarNode *current, PathNode *parent) if (this->nodes.FindClosedNode(*current) != nullptr) return; /* Calculate the G-value for this node */ - int new_g = this->CalculateG(this, current, parent); + int new_g = this->CalculateG(*current, *parent); /* If the value was INVALID_NODE, we don't do anything with this node */ if (new_g == AYSTAR_INVALID_NODE) return; @@ -51,7 +51,7 @@ void AyStar::CheckTile(AyStarNode *current, PathNode *parent) new_g += parent->cost; /* Calculate the h-value */ - int new_h = this->CalculateH(this, current, parent); + int new_h = this->CalculateH(*current, *parent); /* There should not be given any error-code.. */ assert(new_h >= 0); @@ -96,10 +96,8 @@ AyStarStatus AyStar::Loop() if (current == nullptr) return AyStarStatus::EmptyOpenList; /* Check for end node and if found, return that code */ - if (this->EndNodeCheck(this, current) == AyStarStatus::FoundEndNode && current->parent != nullptr) { - if (this->FoundEndNode != nullptr) { - this->FoundEndNode(this, current); - } + if (this->EndNodeCheck(*current) == AyStarStatus::FoundEndNode && current->parent != nullptr) { + this->FoundEndNode(*current); return AyStarStatus::FoundEndNode; } @@ -107,7 +105,7 @@ AyStarStatus AyStar::Loop() this->nodes.InsertClosedNode(*current); /* Load the neighbours */ - this->GetNeighbours(this, current); + this->GetNeighbours(*current, this->neighbours); /* Go through all neighbours */ for (auto &neighbour : this->neighbours) { diff --git a/src/pathfinder/aystar.h b/src/pathfinder/aystar.h index ff939ad1e6..e74d1f3476 100644 --- a/src/pathfinder/aystar.h +++ b/src/pathfinder/aystar.h @@ -38,95 +38,61 @@ using AyStarNode = CYapfNodeKeyTrackDir; struct PathNode : CYapfNodeT { }; -struct AyStar; - -/** - * Check whether the end-tile is found. - * @param aystar %AyStar search algorithm data. - * @param current Node to exam one. - * @note The 2nd parameter should be #OpenListNode, and \em not #AyStarNode. #AyStarNode is - * part of #OpenListNode and so it could be accessed without any problems. - * The good part about #OpenListNode is, and how AIs use it, that you can - * access the parent of the current node, and so check if you, for example - * don't try to enter the file tile with a 90-degree curve. So please, leave - * this an #OpenListNode, it works just fine. - * @return Status of the node: - * - #AyStarStatus::FoundEndNode : indicates this is the end tile - * - #AyStarStatus::Done : indicates this is not the end tile (or direction was wrong) - */ -typedef AyStarStatus AyStar_EndNodeCheck(const AyStar *aystar, const PathNode *current); - -/** - * Calculate the G-value for the %AyStar algorithm. - * @return G value of the node: - * - #AYSTAR_INVALID_NODE : indicates an item is not valid (e.g.: unwalkable) - * - Any value >= 0 : the g-value for this tile - */ -typedef int32_t AyStar_CalculateG(AyStar *aystar, AyStarNode *current, PathNode *parent); - -/** - * Calculate the H-value for the %AyStar algorithm. - * Mostly, this must return the distance (Manhattan way) between the current point and the end point. - * @return The h-value for this tile (any value >= 0) - */ -typedef int32_t AyStar_CalculateH(AyStar *aystar, AyStarNode *current, PathNode *parent); - -/** - * This function requests the tiles around the current tile and put them in #neighbours. - * #neighbours is never reset, so if you are not using directions, just leave it alone. - * @warning Never add more #neighbours than memory allocated for it. - */ -typedef void AyStar_GetNeighbours(AyStar *aystar, PathNode *current); - -/** - * If the End Node is found, this function is called. - * It can do, for example, calculate the route and put that in an array. - */ -typedef void AyStar_FoundEndNode(AyStar *aystar, PathNode *current); - /** * %AyStar search algorithm struct. - * Before calling #Init(), fill #CalculateG, #CalculateH, #GetNeighbours, #EndNodeCheck, and #FoundEndNode. - * If you want to change them after calling #Init(), first call #Free() ! - * - * The #user_path, #user_target, and #user_data[10] are intended to be used by the user routines. The data not accessed by the #AyStar code itself. - * The user routines can change any moment they like. */ -struct AyStar { -/* These fields should be filled before initing the AyStar, but not changed - * afterwards (except for user_data)! (free and init again to change them) */ - - /* These should point to the application specific routines that do the - * actual work */ - AyStar_CalculateG *CalculateG; - AyStar_CalculateH *CalculateH; - AyStar_GetNeighbours *GetNeighbours; - AyStar_EndNodeCheck *EndNodeCheck; - AyStar_FoundEndNode *FoundEndNode; - - /* These are completely untouched by AyStar, they can be accessed by - * the application specific routines to input and output data. - * user_path should typically contain data about the resulting path - * afterwards, user_target should typically contain information about - * what you where looking for, and user_data can contain just about - * everything */ - void *user_target; - void *user_data; - - /* These should be filled with the neighbours of a tile by GetNeighbours */ - std::vector neighbours; - - /* These will contain the methods for manipulating the AyStar. Only - * Main() should be called externally */ - void AddStartNode(AyStarNode *start_node, int g); - AyStarStatus Main(); - AyStarStatus Loop(); - void CheckTile(AyStarNode *current, PathNode *parent); - +class AyStar { protected: - NodeList nodes; + /** + * Calculate the G-value for the %AyStar algorithm. + * @return G value of the node: + * - #AYSTAR_INVALID_NODE : indicates an item is not valid (e.g.: unwalkable) + * - Any value >= 0 : the g-value for this tile + */ + virtual int32_t CalculateG(const AyStarNode ¤t, const PathNode &parent) const = 0; + /** + * Calculate the H-value for the %AyStar algorithm. + * Mostly, this must return the distance (Manhattan way) between the current point and the end point. + * @return The h-value for this tile (any value >= 0) + */ + virtual int32_t CalculateH(const AyStarNode ¤t, const PathNode &parent) const = 0; + + /** + * This function requests the tiles around the current tile. + * #neighbours is never reset, so if you are not using directions, just leave it alone. + */ + virtual void GetNeighbours(const PathNode ¤t, std::vector &neighours) const = 0; + + /** + * Check whether the end-tile is found. + * @param current Node to exam. + * @return Status of the node: + * - #AyStarStatus::FoundEndNode : indicates this is the end tile + * - #AyStarStatus::Done : indicates this is not the end tile (or direction was wrong) + */ + virtual AyStarStatus EndNodeCheck(const PathNode ¤t) const = 0; + + /** + * If the End Node is found, this function is called. + * It can do, for example, calculate the route and put that in an array. + */ + virtual void FoundEndNode(const PathNode ¤t) = 0; + + void AddStartNode(AyStarNode *start_node, int g); + + AyStarStatus Main(); + +public: + virtual ~AyStar() = default; + +private: + NodeList nodes; + mutable std::vector neighbours; + + AyStarStatus Loop(); void OpenListAdd(PathNode *parent, const AyStarNode *node, int f, int g); + void CheckTile(AyStarNode *current, PathNode *parent); }; #endif /* AYSTAR_H */