diff --git a/src/clear_cmd.cpp b/src/clear_cmd.cpp index 181a6882a8..2ad67cd18e 100644 --- a/src/clear_cmd.cpp +++ b/src/clear_cmd.cpp @@ -15,6 +15,7 @@ #include "viewport_func.h" #include "core/random_func.hpp" #include "newgrf_generic.h" +#include "town.h" #include "landscape_cmd.h" #include "table/strings.h" @@ -369,8 +370,14 @@ static void ChangeTileOwner_Clear(TileIndex, Owner, Owner) return; } -static CommandCost TerraformTile_Clear(TileIndex tile, DoCommandFlag flags, int, Slope) +static CommandCost TerraformTile_Clear(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new) { + if (flags & DC_NO_TERRAFORM_FLOOD) { + if (!CanTownTerraformTileWithoutFlooding(tile, z_new, tileh_new)) { + return CMD_ERROR; + } + } + return Command::Do(flags, tile); } diff --git a/src/command_type.h b/src/command_type.h index dae768104b..bd55c542c8 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -372,7 +372,7 @@ enum DoCommandFlag { DC_AUTO = 0x002, ///< don't allow building on structures DC_QUERY_COST = 0x004, ///< query cost only, don't build. DC_NO_WATER = 0x008, ///< don't allow building on water - // 0x010 is unused + DC_NO_TERRAFORM_FLOOD = 0x010, ///< don't allow terraforming on tiles with a flooding behaviour (used for town growth) DC_NO_TEST_TOWN_RATING = 0x020, ///< town rating does not disallow you from building DC_BANKRUPT = 0x040, ///< company bankrupts, skip money check, skip vehicle on tile check in some cases DC_AUTOREPLACE = 0x080, ///< autoreplace/autorenew is in progress, this shall disable vehicle limits when building, and ignore certain restrictions when undoing things (like vehicle attach callback) diff --git a/src/town.h b/src/town.h index 5034041136..9b0bafb948 100644 --- a/src/town.h +++ b/src/town.h @@ -317,7 +317,7 @@ inline uint16_t TownTicksToGameTicks(uint16_t ticks) return (std::min(ticks, MAX_TOWN_GROWTH_TICKS) + 1) * Ticks::TOWN_GROWTH_TICKS - 1; } - +bool CanTownTerraformTileWithoutFlooding(TileIndex tile, int z_new, Slope tileh_new); RoadType GetTownRoadType(); bool CheckTownRoadTypes(); diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 7a5d9221a1..bf67f0baa5 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -48,6 +48,7 @@ #include "object_base.h" #include "ai/ai.hpp" #include "game/game.hpp" +#include "water.h" #include "town_cmd.h" #include "landscape_cmd.h" #include "road_cmd.h" @@ -1054,7 +1055,7 @@ static bool IsRoadAllowedHere(Town *t, TileIndex tile, DiagDirection dir) CommandCost res = CMD_ERROR; if (!_generating_world && Chance16(1, 10)) { /* Note: Do not replace "^ SLOPE_ELEVATED" with ComplementSlope(). The slope might be steep. */ - res = std::get<0>(Command::Do(DC_EXEC | DC_AUTO | DC_NO_WATER, + res = std::get<0>(Command::Do(DC_EXEC | DC_AUTO | DC_NO_WATER | DC_NO_TERRAFORM_FLOOD, tile, Chance16(1, 16) ? cur_slope : cur_slope ^ SLOPE_ELEVATED, false)); } if (res.Failed() && Chance16(1, 3)) { @@ -1071,9 +1072,9 @@ static bool TerraformTownTile(TileIndex tile, Slope edges, bool dir) { assert(tile < Map::Size()); - CommandCost r = std::get<0>(Command::Do(DC_AUTO | DC_NO_WATER, tile, edges, dir)); + CommandCost r = std::get<0>(Command::Do(DC_AUTO | DC_NO_WATER | DC_NO_TERRAFORM_FLOOD, tile, edges, dir)); if (r.Failed() || r.GetCost() >= (_price[PR_TERRAFORM] + 2) * 8) return false; - Command::Do(DC_AUTO | DC_NO_WATER | DC_EXEC, tile, edges, dir); + Command::Do(DC_AUTO | DC_NO_WATER | DC_NO_TERRAFORM_FLOOD | DC_EXEC, tile, edges, dir); return true; } @@ -1092,6 +1093,31 @@ static void LevelTownLand(TileIndex tile) } } +/** + * Check if a town can safely terraform a tile without it being flooded by the ocean. + * + * @param tile Tile to check. + * @param z_new The new height the tile would get after being terraformed. + * @param tileh_new The new slope type the tile would get after being terraformed. + * @return true iff a tile with or without a flooding behaviour can be terraformed. + * @note This function is intended to be used for the town growth algorithm using the terraform command. + */ +bool CanTownTerraformTileWithoutFlooding(TileIndex tile, int z_new, Slope tileh_new) +{ + /* This function is intended to be used for the town growth algorithm using the terraform command. */ + assert(_current_company == OWNER_TOWN); + + if (GetFloodingBehaviour(tile) != FLOOD_NONE) { + return z_new != 0 || (!IsSlopeWithOneCornerRaised(tileh_new) && tileh_new != SLOPE_FLAT); + } + + if (_settings_game.construction.freeform_edges && DistanceFromEdge(tile) == 1) { + return tileh_new != SLOPE_FLAT; + } + + return true; +} + /** * Generate the RoadBits of a grid tile. * @@ -1417,8 +1443,8 @@ static bool GrowTownWithTunnel(const Town *t, const TileIndex tile, const DiagDi /* Attempt to build the tunnel. Return false if it fails to let the town build a road instead. */ RoadType rt = GetTownRoadType(); - if (Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), tile, TRANSPORT_ROAD, rt).Succeeded()) { - Command::Do(DC_EXEC | CommandFlagsToDCFlags(GetCommandFlags()), tile, TRANSPORT_ROAD, rt); + if (Command::Do(DC_NO_TERRAFORM_FLOOD | CommandFlagsToDCFlags(GetCommandFlags()), tile, TRANSPORT_ROAD, rt).Succeeded()) { + Command::Do(DC_EXEC | DC_NO_TERRAFORM_FLOOD | CommandFlagsToDCFlags(GetCommandFlags()), tile, TRANSPORT_ROAD, rt); _grow_town_result = GROWTH_SUCCEED; return true; } diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index 1a58c6c2e1..3caa22f183 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -878,8 +878,14 @@ void InitializeTrees() _trees_tick_ctr = 0; } -static CommandCost TerraformTile_Trees(TileIndex tile, DoCommandFlag flags, int, Slope) +static CommandCost TerraformTile_Trees(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new) { + if (flags & DC_NO_TERRAFORM_FLOOD) { + if (!CanTownTerraformTileWithoutFlooding(tile, z_new, tileh_new)) { + return CMD_ERROR; + } + } + return Command::Do(flags, tile); } diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 5a0a94d172..b493ca17b2 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -37,6 +37,7 @@ #include "company_gui.h" #include "newgrf_generic.h" #include "industry.h" +#include "town.h" #include "water_cmd.h" #include "landscape_cmd.h" #include "pathfinder/water_regions.h" @@ -1381,11 +1382,17 @@ static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *, TileIndex, int, int) return VETSB_CONTINUE; } -static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int, Slope) +static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new) { /* Canals can't be terraformed */ if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST); + if (flags & DC_NO_TERRAFORM_FLOOD) { + if (!CanTownTerraformTileWithoutFlooding(tile, z_new, tileh_new)) { + return CMD_ERROR; + } + } + return Command::Do(flags, tile); }