diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index ef5f24315a..41ee3ac06e 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -1601,7 +1601,7 @@ static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, DoCommandFlag flags, } /* This is not 100% correct check, but the best we can do without modifying the map. * What is missing, is if the difference in height is more than 1.. */ - if (Command::Do(flags & ~DC_EXEC, tile_walk, SLOPE_N, curh <= h).Failed()) { + if (std::get<0>(Command::Do(flags & ~DC_EXEC, tile_walk, SLOPE_N, curh <= h)).Failed()) { cur_company.Restore(); return false; } diff --git a/src/object_gui.cpp b/src/object_gui.cpp index fa90eb43b6..9726aae667 100644 --- a/src/object_gui.cpp +++ b/src/object_gui.cpp @@ -26,6 +26,7 @@ #include "zoom_func.h" #include "terraform_cmd.h" #include "object_cmd.h" +#include "road_cmd.h" #include "widgets/object_widget.h" @@ -543,7 +544,7 @@ public: void OnPlaceObject(Point pt, TileIndex tile) override { ObjectClass *objclass = ObjectClass::Get(_selected_object_class); - Command::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcTerraform, + Command::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, tile, objclass->GetSpec(_selected_object_index)->Index(), _selected_object_view); } diff --git a/src/terraform_cmd.cpp b/src/terraform_cmd.cpp index e1f7944124..21df5ccbe3 100644 --- a/src/terraform_cmd.cpp +++ b/src/terraform_cmd.cpp @@ -38,8 +38,6 @@ struct TerraformerState { TileIndexToHeightMap tile_to_new_height; ///< The tiles for which the height has changed. }; -TileIndex _terraform_err_tile; ///< first tile we couldn't terraform - /** * Gets the TileHeight (height of north corner) of a tile as of current terraforming progress. * @@ -101,20 +99,20 @@ static void TerraformAddDirtyTileAround(TerraformerState *ts, TileIndex tile) * @param height Aimed height. * @return Error code or cost. */ -static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height) +static std::tuple TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height) { assert(tile < MapSize()); /* Check range of destination height */ - if (height < 0) return_cmd_error(STR_ERROR_ALREADY_AT_SEA_LEVEL); - if (height > _settings_game.construction.map_height_limit) return_cmd_error(STR_ERROR_TOO_HIGH); + if (height < 0) return { CommandCost(STR_ERROR_ALREADY_AT_SEA_LEVEL), INVALID_TILE }; + if (height > _settings_game.construction.map_height_limit) return { CommandCost(STR_ERROR_TOO_HIGH), INVALID_TILE }; /* * Check if the terraforming has any effect. * This can only be true, if multiple corners of the start-tile are terraformed (i.e. the terraforming is done by towns/industries etc.). * In this case the terraforming should fail. (Don't know why.) */ - if (height == TerraformGetHeightOfTile(ts, tile)) return CMD_ERROR; + if (height == TerraformGetHeightOfTile(ts, tile)) return { CMD_ERROR, INVALID_TILE }; /* Check "too close to edge of map". Only possible when freeform-edges is off. */ uint x = TileX(tile); @@ -125,8 +123,7 @@ static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int */ if (x == 1) x = 0; if (y == 1) y = 0; - _terraform_err_tile = TileXY(x, y); - return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP); + return { CommandCost(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP), TileXY(x, y) }; } /* Mark incident tiles that are involved in the terraforming. */ @@ -168,14 +165,14 @@ static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int if (abs(height_diff) > 1) { /* Terraform the neighboured corner. The resulting height difference should be 1. */ height_diff += (height_diff < 0 ? 1 : -1); - CommandCost cost = TerraformTileHeight(ts, tile, r + height_diff); - if (cost.Failed()) return cost; + auto [cost, err_tile] = TerraformTileHeight(ts, tile, r + height_diff); + if (cost.Failed()) return { cost, err_tile }; total_cost.AddCost(cost); } } } - return total_cost; + return { total_cost, INVALID_TILE }; } /** @@ -186,10 +183,8 @@ static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int * @param dir_up direction; eg up (true) or down (false) * @return the cost of this operation or an error */ -CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, bool dir_up) +std::tuple CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, bool dir_up) { - _terraform_err_tile = INVALID_TILE; - CommandCost total_cost(EXPENSES_CONSTRUCTION); int direction = (dir_up ? 1 : -1); TerraformerState ts; @@ -197,29 +192,29 @@ CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, b /* Compute the costs and the terraforming result in a model of the landscape */ if ((slope & SLOPE_W) != 0 && tile + TileDiffXY(1, 0) < MapSize()) { TileIndex t = tile + TileDiffXY(1, 0); - CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction); - if (cost.Failed()) return cost; + auto [cost, err_tile] = TerraformTileHeight(&ts, t, TileHeight(t) + direction); + if (cost.Failed()) return { cost, 0, err_tile }; total_cost.AddCost(cost); } if ((slope & SLOPE_S) != 0 && tile + TileDiffXY(1, 1) < MapSize()) { TileIndex t = tile + TileDiffXY(1, 1); - CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction); - if (cost.Failed()) return cost; + auto [cost, err_tile] = TerraformTileHeight(&ts, t, TileHeight(t) + direction); + if (cost.Failed()) return { cost, 0, err_tile }; total_cost.AddCost(cost); } if ((slope & SLOPE_E) != 0 && tile + TileDiffXY(0, 1) < MapSize()) { TileIndex t = tile + TileDiffXY(0, 1); - CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction); - if (cost.Failed()) return cost; + auto [cost, err_tile] = TerraformTileHeight(&ts, t, TileHeight(t) + direction); + if (cost.Failed()) return { cost, 0, err_tile }; total_cost.AddCost(cost); } if ((slope & SLOPE_N) != 0) { TileIndex t = tile + TileDiffXY(0, 0); - CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction); - if (cost.Failed()) return cost; + auto [cost, err_tile] = TerraformTileHeight(&ts, t, TileHeight(t) + direction); + if (cost.Failed()) return { cost, 0, err_tile }; total_cost.AddCost(cost); } @@ -259,20 +254,17 @@ CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, b /* Check if bridge would take damage. */ if (direction == 1 && bridge_height <= z_max) { - _terraform_err_tile = t; // highlight the tile under the bridge - return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + return { CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST), 0, t }; // highlight the tile under the bridge } /* Is the bridge above not too high afterwards? */ if (direction == -1 && bridge_height > (z_min + _settings_game.construction.max_bridge_height)) { - _terraform_err_tile = t; - return_cmd_error(STR_ERROR_BRIDGE_TOO_HIGH_AFTER_LOWER_LAND); + return { CommandCost(STR_ERROR_BRIDGE_TOO_HIGH_AFTER_LOWER_LAND), 0, t }; } } /* Check if tunnel would take damage */ if (direction == -1 && IsTunnelInWay(t, z_min)) { - _terraform_err_tile = t; // highlight the tile above the tunnel - return_cmd_error(STR_ERROR_EXCAVATION_WOULD_DAMAGE); + return { CommandCost(STR_ERROR_EXCAVATION_WOULD_DAMAGE), 0, t }; // highlight the tile above the tunnel } } @@ -296,8 +288,7 @@ CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, b } old_generating_world.Restore(); if (cost.Failed()) { - _terraform_err_tile = t; - return cost; + return { cost, 0, t }; } if (pass == 1) total_cost.AddCost(cost); } @@ -305,7 +296,7 @@ CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, b Company *c = Company::GetIfValid(_current_company); if (c != nullptr && GB(c->terraform_limit, 16, 16) < ts.tile_to_new_height.size()) { - return_cmd_error(STR_ERROR_TERRAFORM_LIMIT_REACHED); + return { CommandCost(STR_ERROR_TERRAFORM_LIMIT_REACHED), 0, INVALID_TILE }; } if (flags & DC_EXEC) { @@ -328,7 +319,7 @@ CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, b if (c != nullptr) c->terraform_limit -= (uint32)ts.tile_to_new_height.size() << 16; } - return total_cost; + return { total_cost, 0, total_cost.Succeeded() ? tile : INVALID_TILE }; } @@ -341,11 +332,9 @@ CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, b * @param LevelMode Mode of leveling \c LevelMode. * @return the cost of this operation or an error */ -std::tuple CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal, LevelMode lm) +std::tuple CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal, LevelMode lm) { - if (start_tile >= MapSize()) return { CMD_ERROR, 0 }; - - _terraform_err_tile = INVALID_TILE; + if (start_tile >= MapSize()) return { CMD_ERROR, 0, INVALID_TILE }; /* remember level height */ uint oldh = TileHeight(start_tile); @@ -356,11 +345,11 @@ std::tuple CmdLevelLand(DoCommandFlag flags, TileIndex tile, case LM_LEVEL: break; case LM_RAISE: h++; break; case LM_LOWER: h--; break; - default: return { CMD_ERROR, 0 }; + default: return { CMD_ERROR, 0, INVALID_TILE }; } /* Check range of destination height */ - if (h > _settings_game.construction.map_height_limit) return { CommandCost(oldh == 0 ? STR_ERROR_ALREADY_AT_SEA_LEVEL : STR_ERROR_TOO_HIGH), 0 }; + if (h > _settings_game.construction.map_height_limit) return { CommandCost(oldh == 0 ? STR_ERROR_ALREADY_AT_SEA_LEVEL : STR_ERROR_TOO_HIGH), 0, INVALID_TILE }; Money money = GetAvailableMoneyForCommand(); CommandCost cost(EXPENSES_CONSTRUCTION); @@ -369,14 +358,16 @@ std::tuple CmdLevelLand(DoCommandFlag flags, TileIndex tile, const Company *c = Company::GetIfValid(_current_company); int limit = (c == nullptr ? INT32_MAX : GB(c->terraform_limit, 16, 16)); - if (limit == 0) return { CommandCost(STR_ERROR_TERRAFORM_LIMIT_REACHED), 0 }; + if (limit == 0) return { CommandCost(STR_ERROR_TERRAFORM_LIMIT_REACHED), 0, INVALID_TILE }; + TileIndex error_tile = INVALID_TILE; TileIterator *iter = diagonal ? (TileIterator *)new DiagonalTileIterator(tile, start_tile) : new OrthogonalTileIterator(tile, start_tile); for (; *iter != INVALID_TILE; ++(*iter)) { TileIndex t = *iter; uint curh = TileHeight(t); while (curh != h) { - CommandCost ret = Command::Do(flags & ~DC_EXEC, t, SLOPE_N, curh <= h); + CommandCost ret; + std::tie(ret, std::ignore, error_tile) = Command::Do(flags & ~DC_EXEC, t, SLOPE_N, curh <= h); if (ret.Failed()) { last_error = ret; @@ -389,7 +380,7 @@ std::tuple CmdLevelLand(DoCommandFlag flags, TileIndex tile, money -= ret.GetCost(); if (money < 0) { delete iter; - return { cost, ret.GetCost() }; + return { cost, ret.GetCost(), error_tile }; } Command::Do(flags, t, SLOPE_N, curh <= h); } else { @@ -413,5 +404,6 @@ std::tuple CmdLevelLand(DoCommandFlag flags, TileIndex tile, } delete iter; - return { had_success ? cost : last_error, 0 }; + CommandCost cc_ret = had_success ? cost : last_error; + return { cc_ret, 0, cc_ret.Succeeded() ? tile : error_tile }; } diff --git a/src/terraform_cmd.h b/src/terraform_cmd.h index 52d7a78fe7..892b59b440 100644 --- a/src/terraform_cmd.h +++ b/src/terraform_cmd.h @@ -14,13 +14,13 @@ #include "map_type.h" #include "slope_type.h" -CommandCost CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, bool dir_up); -std::tuple CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal, LevelMode lm); +std::tuple CmdTerraformLand(DoCommandFlag flags, TileIndex tile, Slope slope, bool dir_up); +std::tuple CmdLevelLand(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal, LevelMode lm); DEF_CMD_TRAIT(CMD_TERRAFORM_LAND, CmdTerraformLand, CMD_ALL_TILES | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION) DEF_CMD_TRAIT(CMD_LEVEL_LAND, CmdLevelLand, CMD_ALL_TILES | CMD_AUTO | CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION) // test run might clear tiles multiple times, in execution that only happens once CommandCallback CcPlaySound_EXPLOSION; -CommandCallback CcTerraform; +void CcTerraform(Commands cmd, const CommandCost &result, Money, TileIndex tile); #endif /* TERRAFORM_CMD_H */ diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp index ecdc8a6dd4..260234efc8 100644 --- a/src/terraform_gui.cpp +++ b/src/terraform_gui.cpp @@ -44,13 +44,12 @@ #include "safeguards.h" -void CcTerraform(Commands cmd, const CommandCost &result, TileIndex tile) +void CcTerraform(Commands cmd, const CommandCost &result, Money, TileIndex tile) { if (result.Succeeded()) { if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile); } else { - extern TileIndex _terraform_err_tile; - SetRedErrorSquare(_terraform_err_tile); + SetRedErrorSquare(tile); } } diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 87d42628a3..a04b0364c4 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -959,8 +959,8 @@ 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 = Command::Do(DC_EXEC | DC_AUTO | DC_NO_WATER, - tile, Chance16(1, 16) ? cur_slope : cur_slope ^ SLOPE_ELEVATED, false); + res = std::get<0>(Command::Do(DC_EXEC | DC_AUTO | DC_NO_WATER, + tile, Chance16(1, 16) ? cur_slope : cur_slope ^ SLOPE_ELEVATED, false)); } if (res.Failed() && Chance16(1, 3)) { /* We can consider building on the slope, though. */ @@ -976,7 +976,7 @@ static bool TerraformTownTile(TileIndex tile, Slope edges, bool dir) { assert(tile < MapSize()); - CommandCost r = Command::Do(DC_AUTO | DC_NO_WATER, tile, edges, dir); + CommandCost r = std::get<0>(Command::Do(DC_AUTO | DC_NO_WATER, 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); return true; diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 4b95e2de0f..543ac8b284 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -755,7 +755,7 @@ CommandCost CmdBuildTunnel(DoCommandFlag flags, TileIndex start_tile, TransportT assert(coa_index < UINT_MAX); // more than 2**32 cleared areas would be a bug in itself coa = nullptr; - ret = Command::Do(flags, end_tile, end_tileh & start_tileh, false); + ret = std::get<0>(Command::Do(flags, end_tile, end_tileh & start_tileh, false)); _cleared_object_areas[(uint)coa_index].first_tile = old_first_tile; if (ret.Failed()) return_cmd_error(STR_ERROR_UNABLE_TO_EXCAVATE_LAND); cost.AddCost(ret);