diff --git a/src/genworld.cpp b/src/genworld.cpp index 870e9dc082..fe7a8b86d5 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -501,7 +501,7 @@ void LoadTownData() do { uint before = t->cache.num_houses; - Command::Post(t->index, HOUSES_TO_GROW); + Command::Post(t->index, HOUSES_TO_GROW, {TownExpandMode::Buildings, TownExpandMode::Roads}); if (t->cache.num_houses <= before) fail_limit--; } while (fail_limit > 0 && try_limit-- > 0 && t->cache.population < population); } diff --git a/src/lang/english.txt b/src/lang/english.txt index bf32eb9b84..21d0e08940 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3073,6 +3073,12 @@ STR_FOUND_TOWN_INITIAL_SIZE_TOOLTIP :{BLACK}Select t STR_FOUND_TOWN_CITY :{BLACK}City STR_FOUND_TOWN_CITY_TOOLTIP :{BLACK}Cities grow faster than regular towns{}Depending on settings, they are bigger when founded +STR_FOUND_TOWN_EXPAND_MODE :{YELLOW}Town expansion: +STR_FOUND_TOWN_EXPAND_BUILDINGS :Buildings +STR_FOUND_TOWN_EXPAND_BUILDINGS_TOOLTIP :Increase buildings of towns +STR_FOUND_TOWN_EXPAND_ROADS :Roads +STR_FOUND_TOWN_EXPAND_ROADS_TOOLTIP :Increase roads of towns + STR_FOUND_TOWN_ROAD_LAYOUT :{YELLOW}Town road layout: STR_FOUND_TOWN_SELECT_LAYOUT_TOOLTIP :{BLACK}Select road layout used for this town STR_FOUND_TOWN_SELECT_LAYOUT_ORIGINAL :{BLACK}Original @@ -3696,6 +3702,10 @@ STR_TOWN_VIEW_RENAME_TOOLTIP :{BLACK}Change t STR_TOWN_VIEW_EXPAND_BUTTON :{BLACK}Expand STR_TOWN_VIEW_EXPAND_TOOLTIP :{BLACK}Increase size of town +STR_TOWN_VIEW_EXPAND_BUILDINGS_BUTTON :{BLACK}Expand buildings +STR_TOWN_VIEW_EXPAND_BUILDINGS_TOOLTIP :{BLACK}Increase buildings of town +STR_TOWN_VIEW_EXPAND_ROADS_BUTTON :{BLACK}Expand roads +STR_TOWN_VIEW_EXPAND_ROADS_TOOLTIP :{BLACK}Increase roads of town STR_TOWN_VIEW_DELETE_BUTTON :{BLACK}Delete STR_TOWN_VIEW_DELETE_TOOLTIP :{BLACK}Delete this town completely diff --git a/src/script/api/script_town.cpp b/src/script/api/script_town.cpp index 730d1829ee..35b84d84af 100644 --- a/src/script/api/script_town.cpp +++ b/src/script/api/script_town.cpp @@ -278,7 +278,7 @@ houses = std::min(houses, UINT32_MAX); - return ScriptObject::Command::Do(town_id, houses); + return ScriptObject::Command::Do(town_id, houses, {TownExpandMode::Buildings, TownExpandMode::Roads}); } /* static */ bool ScriptTown::FoundTown(TileIndex tile, TownSize size, bool city, RoadLayout layout, Text *name) diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 8022e1e0d3..e9e72f1aec 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -228,7 +228,7 @@ Money HouseSpec::GetRemovalCost() const return (_price[PR_CLEAR_HOUSE] * this->removal_cost) >> 8; } -static bool TryBuildTownHouse(Town *t, TileIndex tile); +static bool TryBuildTownHouse(Town *t, TileIndex tile, TownExpandModes modes); static Town *CreateRandomTown(uint attempts, uint32_t townnameparts, TownSize size, bool city, TownLayout layout); static void TownDrawHouseLift(const TileInfo *ti) @@ -691,7 +691,10 @@ static void TileLoop_Town(TileIndex tile) } } - TryBuildTownHouse(t, tile); + TownExpandModes modes{TownExpandMode::Buildings}; + if (_settings_game.economy.allow_town_roads) modes.Set(TownExpandMode::Roads); + + TryBuildTownHouse(t, tile, modes); } } @@ -902,7 +905,7 @@ static void ChangeTileOwner_Town(TileIndex, Owner, Owner) /* not used */ } -static bool GrowTown(Town *t); +static bool GrowTown(Town *t, TownExpandModes modes); /** * Handle the town tick for a single town, by growing the town if desired. @@ -911,9 +914,11 @@ static bool GrowTown(Town *t); static void TownTickHandler(Town *t) { if (HasBit(t->flags, TOWN_IS_GROWING)) { + TownExpandModes modes{TownExpandMode::Buildings}; + if (_settings_game.economy.allow_town_roads) modes.Set(TownExpandMode::Roads); int i = (int)t->grow_counter - 1; if (i < 0) { - if (GrowTown(t)) { + if (GrowTown(t, modes)) { i = t->growth_rate; } else { /* If growth failed wait a bit before retrying */ @@ -1204,7 +1209,7 @@ static RoadBits GetTownRoadGridElement(Town *t, TileIndex tile, DiagDirection di * @param tile The target tile for the extra house. * @return true if an extra house has been added. */ -static bool GrowTownWithExtraHouse(Town *t, TileIndex tile) +static bool GrowTownWithExtraHouse(Town *t, TileIndex tile, TownExpandModes modes) { /* We can't look further than that. */ if (DistanceFromEdge(tile) == 0) return false; @@ -1228,7 +1233,7 @@ static bool GrowTownWithExtraHouse(Town *t, TileIndex tile) /* If there are enough neighbours stop here */ if (counter >= 3) { - return TryBuildTownHouse(t, tile); + return TryBuildTownHouse(t, tile, modes); } } return false; @@ -1498,9 +1503,9 @@ static bool TownCanGrowRoad(TileIndex tile) * Check if the town is allowed to build roads. * @return true If the town is allowed to build roads. */ -static inline bool TownAllowedToBuildRoads() +static inline bool TownAllowedToBuildRoads(TownExpandModes modes) { - return _settings_game.economy.allow_town_roads || _generating_world || _game_mode == GM_EDITOR; + return modes.Test(TownExpandMode::Roads); } /* The possible states of town growth. */ @@ -1528,7 +1533,7 @@ enum class TownGrowthResult { * @param t1 The current town * @return Result so far. */ -static TownGrowthResult GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection target_dir, Town *t1) +static TownGrowthResult GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection target_dir, Town *t1, TownExpandModes modes) { RoadBits rcmd = ROAD_NONE; // RoadBits for the road construction command TileIndex tile = *tile_ptr; // The main tile on which we base our growth @@ -1539,7 +1544,7 @@ static TownGrowthResult GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, Dia /* Tile has no road. * We will return TownGrowthResult::SearchStopped to say that this is the last iteration. */ - if (!TownAllowedToBuildRoads()) return TownGrowthResult::SearchStopped; + if (!TownAllowedToBuildRoads(modes)) return TownGrowthResult::SearchStopped; if (!_settings_game.economy.allow_town_level_crossings && IsTileType(tile, MP_RAILWAY)) return TownGrowthResult::SearchStopped; /* Remove hills etc */ @@ -1588,7 +1593,7 @@ static TownGrowthResult GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, Dia } else if (target_dir < DIAGDIR_END && !(cur_rb & DiagDirToRoadBits(ReverseDiagDir(target_dir)))) { if (!TownCanGrowRoad(tile)) return TownGrowthResult::Continue; - if (!TownAllowedToBuildRoads()) return TownGrowthResult::SearchStopped; + if (!TownAllowedToBuildRoads(modes)) return TownGrowthResult::SearchStopped; /* Continue building on a partial road. * Should be always OK, so we only generate @@ -1664,12 +1669,12 @@ static TownGrowthResult GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, Dia TownGrowthResult result = TownGrowthResult::Continue; - if (target_dir != DIAGDIR_END && TownAllowedToBuildRoads()) { + if (target_dir != DIAGDIR_END && TownAllowedToBuildRoads(modes)) { switch (t1->layout) { default: NOT_REACHED(); case TL_3X3_GRID: // Use 2x2 grid afterwards! - if (GrowTownWithExtraHouse(t1, TileAddByDiagDir(house_tile, target_dir))) { + if (GrowTownWithExtraHouse(t1, TileAddByDiagDir(house_tile, target_dir), modes)) { result = TownGrowthResult::Succeed; } [[fallthrough]]; @@ -1680,7 +1685,7 @@ static TownGrowthResult GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, Dia break; case TL_BETTER_ROADS: // Use original afterwards! - if (GrowTownWithExtraHouse(t1, TileAddByDiagDir(house_tile, target_dir))) { + if (GrowTownWithExtraHouse(t1, TileAddByDiagDir(house_tile, target_dir), modes)) { result = TownGrowthResult::Succeed; } [[fallthrough]]; @@ -1704,7 +1709,7 @@ static TownGrowthResult GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, Dia /* And build a house. * Set result to -1 if we managed to build it. */ - if (TryBuildTownHouse(t1, house_tile)) { + if (TryBuildTownHouse(t1, house_tile, modes)) { result = TownGrowthResult::Succeed; } } @@ -1744,14 +1749,14 @@ static TownGrowthResult GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, Dia * @param dir Direction for road to follow or build. * @return true If road is or can be connected in the specified direction. */ -static bool CanFollowRoad(TileIndex tile, DiagDirection dir) +static bool CanFollowRoad(TileIndex tile, DiagDirection dir, TownExpandModes modes) { TileIndex target_tile = tile + TileOffsByDiagDir(dir); if (!IsValidTile(target_tile)) return false; if (HasTileWaterGround(target_tile)) return false; RoadBits target_rb = GetTownRoadBits(target_tile); - if (TownAllowedToBuildRoads()) { + if (TownAllowedToBuildRoads(modes)) { /* Check whether a road connection exists or can be build. */ switch (GetTileType(target_tile)) { case MP_ROAD: @@ -1786,7 +1791,7 @@ static bool CanFollowRoad(TileIndex tile, DiagDirection dir) * @param tile The road tile to try growing from. * @return true if we successfully expanded the town. */ -static bool GrowTownAtRoad(Town *t, TileIndex tile) +static bool GrowTownAtRoad(Town *t, TileIndex tile, TownExpandModes modes) { /* Special case. * @see GrowTownInTile Check the else if @@ -1818,7 +1823,7 @@ static bool GrowTownAtRoad(Town *t, TileIndex tile) RoadBits cur_rb = GetTownRoadBits(tile); // The RoadBits of the current tile /* Try to grow the town from this point */ - switch (GrowTownInTile(&tile, cur_rb, target_dir, t)) { + switch (GrowTownInTile(&tile, cur_rb, target_dir, t, modes)) { case TownGrowthResult::Succeed: return true; case TownGrowthResult::SearchStopped: @@ -1847,7 +1852,7 @@ static bool GrowTownAtRoad(Town *t, TileIndex tile) target_bits = DiagDirToRoadBits(target_dir); } while (!(cur_rb & target_bits)); cur_rb &= ~target_bits; - } while (!CanFollowRoad(tile, target_dir)); + } while (!CanFollowRoad(tile, target_dir, modes)); } tile = TileAddByDiagDir(tile, target_dir); @@ -1890,7 +1895,7 @@ static RoadBits GenRandomRoadBits() * @param t The town to grow * @return true if we successfully grew the town with a road or house. */ -static bool GrowTown(Town *t) +static bool GrowTown(Town *t, TownExpandModes modes) { static const TileIndexDiffC _town_coord_mod[] = { {-1, 0}, @@ -1916,7 +1921,7 @@ static bool GrowTown(Town *t) /* Find a road that we can base the construction on. */ for (const auto &ptr : _town_coord_mod) { if (GetTownRoadBits(tile) != ROAD_NONE) { - bool success = GrowTownAtRoad(t, tile); + bool success = GrowTownAtRoad(t, tile, modes); cur_company.Restore(); return success; } @@ -1925,7 +1930,7 @@ static bool GrowTown(Town *t) /* No road available, try to build a random road block by * clearing some land and then building a road there. */ - if (TownAllowedToBuildRoads()) { + if (TownAllowedToBuildRoads(modes)) { tile = t->xy; for (const auto &ptr : _town_coord_mod) { /* Only work with plain land that not already has a house */ @@ -2084,7 +2089,7 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32_t townnameparts, TownSi int i = x * 4; do { - GrowTown(t); + GrowTown(t, {TownExpandMode::Buildings, TownExpandMode::Roads}); } while (--i); t->UpdateVirtCoord(); @@ -2605,10 +2610,12 @@ static bool CheckFree2x2Area(TileIndex tile, int z, bool noslope) * @return true iff town layout allows building here. * @note see layouts */ -static inline bool TownLayoutAllowsHouseHere(Town *t, TileIndex tile) +static inline bool TownLayoutAllowsHouseHere(Town *t, TileIndex tile, TownExpandModes modes) { + if (!modes.Test(TownExpandMode::Buildings)) return false; + /* Allow towns everywhere when we don't build roads */ - if (!TownAllowedToBuildRoads()) return true; + if (!TownAllowedToBuildRoads(modes)) return true; TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, tile); @@ -2636,10 +2643,12 @@ static inline bool TownLayoutAllowsHouseHere(Town *t, TileIndex tile) * @return true iff town layout allows a 2x2 building here. * @note see layouts */ -static inline bool TownLayoutAllows2x2HouseHere(Town *t, TileIndex tile) +static inline bool TownLayoutAllows2x2HouseHere(Town *t, TileIndex tile, TownExpandModes modes) { + if (!modes.Test(TownExpandMode::Buildings)) return false; + /* Allow towns everywhere when we don't build roads */ - if (!TownAllowedToBuildRoads()) return true; + if (!TownAllowedToBuildRoads(modes)) return true; /* Compute relative position of tile. (Positive offsets are towards north) */ TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, tile); @@ -2673,15 +2682,15 @@ static inline bool TownLayoutAllows2x2HouseHere(Town *t, TileIndex tile) * @param noslope Are foundations disallowed for this house? * @param second The diagdir from the first tile to the second tile. */ -static bool CheckTownBuild2House(TileIndex *tile, Town *t, int maxz, bool noslope, DiagDirection second) +static bool CheckTownBuild2House(TileIndex *tile, Town *t, int maxz, bool noslope, DiagDirection second, TownExpandModes modes) { /* 'tile' is already checked in BuildTownHouse() - CanBuildHouseHere() and slope test */ TileIndex tile2 = *tile + TileOffsByDiagDir(second); - if (TownLayoutAllowsHouseHere(t, tile2) && CheckBuildHouseSameZ(tile2, maxz, noslope)) return true; + if (TownLayoutAllowsHouseHere(t, tile2, modes) && CheckBuildHouseSameZ(tile2, maxz, noslope)) return true; tile2 = *tile + TileOffsByDiagDir(ReverseDiagDir(second)); - if (TownLayoutAllowsHouseHere(t, tile2) && CheckBuildHouseSameZ(tile2, maxz, noslope)) { + if (TownLayoutAllowsHouseHere(t, tile2, modes) && CheckBuildHouseSameZ(tile2, maxz, noslope)) { *tile = tile2; return true; } @@ -2698,12 +2707,12 @@ static bool CheckTownBuild2House(TileIndex *tile, Town *t, int maxz, bool noslop * @param maxz The maximum Z level, since all tiles must have the same height. * @param noslope Are foundations disallowed for this house? */ -static bool CheckTownBuild2x2House(TileIndex *tile, Town *t, int maxz, bool noslope) +static bool CheckTownBuild2x2House(TileIndex *tile, Town *t, int maxz, bool noslope, TownExpandModes modes) { TileIndex tile2 = *tile; for (DiagDirection d = DIAGDIR_SE;; d++) { // 'd' goes through DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_END - if (TownLayoutAllows2x2HouseHere(t, tile2) && CheckFree2x2Area(tile2, maxz, noslope)) { + if (TownLayoutAllows2x2HouseHere(t, tile2, modes) && CheckFree2x2Area(tile2, maxz, noslope)) { *tile = tile2; return true; } @@ -2763,10 +2772,10 @@ static void BuildTownHouse(Town *t, TileIndex tile, const HouseSpec *hs, HouseID * @param tile The tile to try building on. * @return false iff no house can be built on this tile. */ -static bool TryBuildTownHouse(Town *t, TileIndex tile) +static bool TryBuildTownHouse(Town *t, TileIndex tile, TownExpandModes modes) { /* forbidden building here by town layout */ - if (!TownLayoutAllowsHouseHere(t, tile)) return false; + if (!TownLayoutAllowsHouseHere(t, tile, modes)) return false; /* no house allowed at all, bail out */ if (!CanBuildHouseHere(tile, false)) return false; @@ -2860,11 +2869,11 @@ static bool TryBuildTownHouse(Town *t, TileIndex tile) if (noslope && slope != SLOPE_FLAT) continue; if (hs->building_flags.Test(BuildingFlag::Size2x2)) { - if (!CheckTownBuild2x2House(&tile, t, maxz, noslope)) continue; + if (!CheckTownBuild2x2House(&tile, t, maxz, noslope, modes)) continue; } else if (hs->building_flags.Test(BuildingFlag::Size2x1)) { - if (!CheckTownBuild2House(&tile, t, maxz, noslope, DIAGDIR_SW)) continue; + if (!CheckTownBuild2House(&tile, t, maxz, noslope, DIAGDIR_SW, modes)) continue; } else if (hs->building_flags.Test(BuildingFlag::Size1x2)) { - if (!CheckTownBuild2House(&tile, t, maxz, noslope, DIAGDIR_SE)) continue; + if (!CheckTownBuild2House(&tile, t, maxz, noslope, DIAGDIR_SE, modes)) continue; } else { /* 1x1 house checks are already done */ } @@ -3199,9 +3208,10 @@ CommandCost CmdTownRating(DoCommandFlags flags, TownID town_id, CompanyID compan * @param grow_amount Amount to grow, or 0 to grow a random size up to the current amount of houses. * @return Empty cost or an error. */ -CommandCost CmdExpandTown(DoCommandFlags flags, TownID town_id, uint32_t grow_amount) +CommandCost CmdExpandTown(DoCommandFlags flags, TownID town_id, uint32_t grow_amount, TownExpandModes modes) { if (_game_mode != GM_EDITOR && _current_company != OWNER_DEITY) return CMD_ERROR; + if (modes.None()) return CMD_ERROR; Town *t = Town::GetIfValid(town_id); if (t == nullptr) return CMD_ERROR; @@ -3213,13 +3223,13 @@ CommandCost CmdExpandTown(DoCommandFlags flags, TownID town_id, uint32_t grow_am UpdateTownRadius(t); uint n = amount * 10; - do GrowTown(t); while (--n); + do GrowTown(t, modes); while (--n); t->cache.num_houses -= amount; } else { for (; grow_amount > 0; grow_amount--) { /* Try several times to grow, as we are really suppose to grow */ - for (uint i = 0; i < 25; i++) if (GrowTown(t)) break; + for (uint i = 0; i < 25; i++) if (GrowTown(t, modes)) break; } } UpdateTownRadius(t); diff --git a/src/town_cmd.h b/src/town_cmd.h index 79151162dc..7d247ce7cf 100644 --- a/src/town_cmd.h +++ b/src/town_cmd.h @@ -25,7 +25,7 @@ CommandCost CmdTownGrowthRate(DoCommandFlags flags, TownID town_id, uint16_t gro CommandCost CmdTownRating(DoCommandFlags flags, TownID town_id, CompanyID company_id, int16_t rating); CommandCost CmdTownCargoGoal(DoCommandFlags flags, TownID town_id, TownAcceptanceEffect tae, uint32_t goal); CommandCost CmdTownSetText(DoCommandFlags flags, TownID town_id, const EncodedString &text); -CommandCost CmdExpandTown(DoCommandFlags flags, TownID town_id, uint32_t grow_amount); +CommandCost CmdExpandTown(DoCommandFlags flags, TownID town_id, uint32_t grow_amount, TownExpandModes modes); CommandCost CmdDeleteTown(DoCommandFlags flags, TownID town_id); CommandCost CmdPlaceHouse(DoCommandFlags flags, TileIndex tile, HouseID house, bool house_protected); diff --git a/src/town_gui.cpp b/src/town_gui.cpp index e6b6621164..141872f7bb 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -488,10 +488,17 @@ public: SetViewportCatchmentTown(Town::Get(this->window_number), !this->IsWidgetLowered(WID_TV_CATCHMENT)); break; - case WID_TV_EXPAND: { // expand town - only available on Scenario editor - Command::Post(STR_ERROR_CAN_T_EXPAND_TOWN, static_cast(this->window_number), 0); + case WID_TV_EXPAND: // expand town - only available on Scenario editor + Command::Post(STR_ERROR_CAN_T_EXPAND_TOWN, static_cast(this->window_number), 0, {TownExpandMode::Buildings, TownExpandMode::Roads}); + break; + + case WID_TV_EXPAND_BUILDINGS: // expand buildings of town - only available on Scenario editor + Command::Post(STR_ERROR_CAN_T_EXPAND_TOWN, static_cast(this->window_number), 0, {TownExpandMode::Buildings}); + break; + + case WID_TV_EXPAND_ROADS: // expand roads of town - only available on Scenario editor + Command::Post(STR_ERROR_CAN_T_EXPAND_TOWN, static_cast(this->window_number), 0, {TownExpandMode::Roads}); break; - } case WID_TV_DELETE: // delete town - only available on Scenario editor Command::Post(STR_ERROR_TOWN_CAN_T_DELETE, static_cast(this->window_number)); @@ -638,10 +645,14 @@ static constexpr NWidgetPart _nested_town_editor_view_widgets[] = { EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_TV_INFO), SetMinimalSize(260, 32), SetResize(1, 0), SetFill(1, 0), EndContainer(), - NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), - NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_EXPAND), SetMinimalSize(80, 12), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TOWN_VIEW_EXPAND_BUTTON, STR_TOWN_VIEW_EXPAND_TOOLTIP), - NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_DELETE), SetMinimalSize(80, 12), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TOWN_VIEW_DELETE_BUTTON, STR_TOWN_VIEW_DELETE_TOOLTIP), - NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_TV_CATCHMENT), SetMinimalSize(40, 12), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_BUTTON_CATCHMENT, STR_TOOLTIP_CATCHMENT), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_EXPAND), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TOWN_VIEW_EXPAND_BUTTON, STR_TOWN_VIEW_EXPAND_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_EXPAND_BUILDINGS), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TOWN_VIEW_EXPAND_BUILDINGS_BUTTON, STR_TOWN_VIEW_EXPAND_BUILDINGS_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_EXPAND_ROADS), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TOWN_VIEW_EXPAND_ROADS_BUTTON, STR_TOWN_VIEW_EXPAND_ROADS_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_TV_DELETE), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_TOWN_VIEW_DELETE_BUTTON, STR_TOWN_VIEW_DELETE_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_BROWN, WID_TV_CATCHMENT), SetFill(1, 1), SetResize(1, 0), SetStringTip(STR_BUTTON_CATCHMENT, STR_TOOLTIP_CATCHMENT), NWidget(WWT_RESIZEBOX, COLOUR_BROWN), EndContainer(), }; @@ -1078,7 +1089,6 @@ static constexpr NWidgetPart _nested_found_town_widgets[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_RANDOM_TOWN), SetStringTip(STR_FOUND_TOWN_RANDOM_TOWN_BUTTON, STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP), SetFill(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_MANY_RANDOM_TOWNS), SetStringTip(STR_FOUND_TOWN_MANY_RANDOM_TOWNS, STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP), SetFill(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_LOAD_FROM_FILE), SetStringTip(STR_FOUND_TOWN_LOAD_FROM_FILE, STR_FOUND_TOWN_LOAD_FROM_FILE_TOOLTIP), SetFill(1, 0), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_EXPAND_ALL_TOWNS), SetStringTip(STR_FOUND_TOWN_EXPAND_ALL_TOWNS, STR_FOUND_TOWN_EXPAND_ALL_TOWNS_TOOLTIP), SetFill(1, 0), EndContainer(), EndContainer(), @@ -1120,6 +1130,18 @@ static constexpr NWidgetPart _nested_found_town_widgets[] = { EndContainer(), EndContainer(), EndContainer(), + + /* Town expansion selection. */ + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_TF_TOWN_EXPAND_SEL), + NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), + NWidget(WWT_LABEL, INVALID_COLOUR), SetStringTip(STR_FOUND_TOWN_EXPAND_MODE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TF_EXPAND_ALL_TOWNS), SetStringTip(STR_FOUND_TOWN_EXPAND_ALL_TOWNS, STR_FOUND_TOWN_EXPAND_ALL_TOWNS_TOOLTIP), SetFill(1, 0), + NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_EXPAND_BUILDINGS), SetStringTip(STR_FOUND_TOWN_EXPAND_BUILDINGS, STR_FOUND_TOWN_EXPAND_BUILDINGS_TOOLTIP), SetFill(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_TF_EXPAND_ROADS), SetStringTip(STR_FOUND_TOWN_EXPAND_ROADS, STR_FOUND_TOWN_EXPAND_ROADS_TOOLTIP), SetFill(1, 0), + EndContainer(), + EndContainer(), + EndContainer(), EndContainer(), EndContainer(), }; @@ -1134,6 +1156,7 @@ private: bool townnamevalid = false; ///< Is generated town name valid? uint32_t townnameparts = 0; ///< Generated town name TownNameParams params; ///< Town name parameters + static inline TownExpandModes expand_modes{TownExpandMode::Buildings, TownExpandMode::Roads}; public: FoundTownWindow(WindowDesc &desc, WindowNumber window_number) : @@ -1153,6 +1176,7 @@ public: if (_game_mode == GM_EDITOR) return; this->GetWidget(WID_TF_TOWN_ACTION_SEL)->SetDisplayedPlane(SZSP_HORIZONTAL); + this->GetWidget(WID_TF_TOWN_EXPAND_SEL)->SetDisplayedPlane(SZSP_HORIZONTAL); this->GetWidget(WID_TF_SIZE_SEL)->SetDisplayedPlane(SZSP_VERTICAL); if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT) { this->GetWidget(WID_TF_ROAD_LAYOUT_SEL)->SetDisplayedPlane(SZSP_HORIZONTAL); @@ -1192,6 +1216,9 @@ public: this->SetWidgetLoweredState(i, i == WID_TF_LAYOUT_ORIGINAL + this->town_layout); } + this->SetWidgetLoweredState(WID_TF_EXPAND_BUILDINGS, FoundTownWindow::expand_modes.Test(TownExpandMode::Buildings)); + this->SetWidgetLoweredState(WID_TF_EXPAND_ROADS, FoundTownWindow::expand_modes.Test(TownExpandMode::Roads)); + this->SetDirty(); } @@ -1242,7 +1269,7 @@ public: case WID_TF_EXPAND_ALL_TOWNS: for (Town *t : Town::Iterate()) { - Command::Do(DoCommandFlag::Execute, t->index, 0); + Command::Do(DoCommandFlag::Execute, t->index, 0, FoundTownWindow::expand_modes); } break; @@ -1257,6 +1284,16 @@ public: this->SetDirty(); break; + case WID_TF_EXPAND_BUILDINGS: + FoundTownWindow::expand_modes.Flip(TownExpandMode::Buildings); + this->UpdateButtons(false); + break; + + case WID_TF_EXPAND_ROADS: + FoundTownWindow::expand_modes.Flip(TownExpandMode::Roads); + this->UpdateButtons(false); + break; + case WID_TF_LAYOUT_ORIGINAL: case WID_TF_LAYOUT_BETTER: case WID_TF_LAYOUT_GRID2: case WID_TF_LAYOUT_GRID3: case WID_TF_LAYOUT_RANDOM: this->town_layout = (TownLayout)(widget - WID_TF_LAYOUT_ORIGINAL); diff --git a/src/town_type.h b/src/town_type.h index dffd93bb68..b3bca9b3e1 100644 --- a/src/town_type.h +++ b/src/town_type.h @@ -91,6 +91,14 @@ enum TownLayout : uint8_t { }; DECLARE_ENUM_AS_ADDABLE(TownLayout) +/** Options for growing towns. */ +enum class TownExpandMode : uint8_t { + Buildings, ///< Allow town to place buildings. + Roads, ///< Allow town to place roads. +}; + +using TownExpandModes = EnumBitSet; + /** Town founding setting values. It needs to be 8bits, because we save and load it as such */ enum TownFounding : uint8_t { TF_BEGIN = 0, ///< Used for iterations and limit testing diff --git a/src/widgets/town_widget.h b/src/widgets/town_widget.h index 25446fc825..fe901339ac 100644 --- a/src/widgets/town_widget.h +++ b/src/widgets/town_widget.h @@ -42,6 +42,8 @@ enum TownViewWidgets : WidgetID { WID_TV_CHANGE_NAME, ///< Change the name of this town. WID_TV_CATCHMENT, ///< Toggle catchment area highlight. WID_TV_EXPAND, ///< Expand this town (scenario editor only). + WID_TV_EXPAND_BUILDINGS, ///< Expand number of buildings this town (scenario editor only). + WID_TV_EXPAND_ROADS, ///< Expand roads of this town (scenario editor only). WID_TV_DELETE, ///< Delete this town (scenario editor only). }; @@ -67,6 +69,9 @@ enum TownFoundingWidgets : WidgetID { WID_TF_LAYOUT_GRID2, ///< Selection for the 2x2 grid town layout. WID_TF_LAYOUT_GRID3, ///< Selection for the 3x3 grid town layout. WID_TF_LAYOUT_RANDOM, ///< Selection for a randomly chosen town layout. + WID_TF_TOWN_EXPAND_SEL, ///< Container of town expansion buttons. + WID_TF_EXPAND_BUILDINGS, ///< Expand buildings toggle. + WID_TF_EXPAND_ROADS, ///< Expand roads toggle. }; /** Widgets of the #BuildHouseWindow class. */