diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index f7269fd7c7..8ca01caac6 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -328,9 +328,6 @@ CommandCost CmdBuildBridge(DoCommandFlag flags, TileIndex tile_end, TileIndex ti auto [tileh_end, z_end] = GetTileSlopeZ(tile_end); bool pbs_reservation = false; - CommandCost terraform_cost_north = CheckBridgeSlope(BRIDGE_PIECE_NORTH, direction, tileh_start, z_start); - CommandCost terraform_cost_south = CheckBridgeSlope(BRIDGE_PIECE_SOUTH, direction, tileh_end, z_end); - /* Aqueducts can't be built of flat land. */ if (transport_type == TRANSPORT_WATER && (tileh_start == SLOPE_FLAT || tileh_end == SLOPE_FLAT)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); if (z_start != z_end) return CommandCost(STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT); @@ -408,24 +405,71 @@ CommandCost CmdBuildBridge(DoCommandFlag flags, TileIndex tile_end, TileIndex ti } else { /* Build a new bridge. */ - bool allow_on_slopes = (_settings_game.construction.build_on_slopes && transport_type != TRANSPORT_WATER); - /* Try and clear the start landscape */ CommandCost ret = Command::Do(flags, tile_start); if (ret.Failed()) return ret; cost = ret; - if (terraform_cost_north.Failed() || (terraform_cost_north.GetCost() != 0 && !allow_on_slopes)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); - cost.AddCost(terraform_cost_north); - /* Try and clear the end landscape */ ret = Command::Do(flags, tile_end); if (ret.Failed()) return ret; cost.AddCost(ret); - /* false - end tile slope check */ - if (terraform_cost_south.Failed() || (terraform_cost_south.GetCost() != 0 && !allow_on_slopes)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); - cost.AddCost(terraform_cost_south); + /* for aqueducts, slope of end tile must be complementary to the slope of the start tile */ + if (transport_type == TRANSPORT_WATER && tileh_end != ComplementSlope(tileh_start)) { + /* Check if the tile to be terraformed is the start or end tile */ + TileIndex terraformTile; + Slope terraformTileh; + if (tileh_start == SLOPE_N || tileh_start == SLOPE_S || tileh_start == SLOPE_W || tileh_start == SLOPE_E) { + terraformTile = tile_start; + terraformTileh = tileh_start ^ ComplementSlope(tileh_end); + } else { + terraformTile = tile_end; + terraformTileh = tileh_end ^ ComplementSlope(tileh_start); + } + + /* Mark the tile as already cleared for the terraform command. + * Do this for all tiles (like trees), not only objects. */ + ClearedObjectArea *coa = FindClearedObject(terraformTile); + if (coa == nullptr) { + coa = &_cleared_object_areas.emplace_back(ClearedObjectArea{ terraformTile, TileArea(terraformTile, 1, 1) }); + } + + /* Hide the tile from the terraforming command */ + TileIndex old_first_tile = coa->first_tile; + coa->first_tile = INVALID_TILE; + + /* CMD_TERRAFORM_LAND may append further items to _cleared_object_areas, + * however it will never erase or re-order existing items. + * _cleared_object_areas is a value-type self-resizing vector, therefore appending items + * may result in a backing-store re-allocation, which would invalidate the coa pointer. + * The index of the coa pointer into the _cleared_object_areas vector remains valid, + * and can be used safely after the CMD_TERRAFORM_LAND operation. + * Deliberately clear the coa pointer to avoid leaving dangling pointers which could + * inadvertently be dereferenced. + */ + ClearedObjectArea *begin = _cleared_object_areas.data(); + assert(coa >= begin && coa < begin + _cleared_object_areas.size()); + size_t coa_index = coa - begin; + assert(coa_index < UINT_MAX); // more than 2**32 cleared areas would be a bug in itself + coa = nullptr; + + ret = std::get<0>(Command::Do(DoCommandFlag(flags & ~DC_NO_WATER), terraformTile, terraformTileh, true)); + _cleared_object_areas[(uint)coa_index].first_tile = old_first_tile; + if (ret.Failed()) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); + cost.AddCost(ret); + } + + if (transport_type != TRANSPORT_WATER){ + CommandCost terraform_cost_north = CheckBridgeSlope(BRIDGE_PIECE_NORTH, direction, tileh_start, z_start); + if (terraform_cost_north.Failed() || (terraform_cost_north.GetCost() != 0 && !_settings_game.construction.build_on_slopes)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); + cost.AddCost(terraform_cost_north); + + CommandCost terraform_cost_south = CheckBridgeSlope(BRIDGE_PIECE_SOUTH, direction, tileh_end, z_end); + /* false - end tile slope check */ + if (terraform_cost_south.Failed() || (terraform_cost_south.GetCost() != 0 && !_settings_game.construction.build_on_slopes)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); + cost.AddCost(terraform_cost_south); + } const TileIndex heads[] = {tile_start, tile_end}; for (int i = 0; i < 2; i++) {