From 2f020abe74d9bd54853b9e208043330707be3e90 Mon Sep 17 00:00:00 2001 From: Iris-Persephone <131392421+Iris-Persephone@users.noreply.github.com> Date: Fri, 2 May 2025 17:54:21 -0400 Subject: [PATCH] Add: "Many random towns" button in scenario editor now prompts for the number of towns, with defaults based on new game settings (#14158) --- src/town.h | 3 ++- src/town_cmd.cpp | 28 ++++++++++++++++++++++++---- src/town_gui.cpp | 28 ++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/town.h b/src/town.h index ba4310ad1f..7ddbc0f5ff 100644 --- a/src/town.h +++ b/src/town.h @@ -231,7 +231,8 @@ void ChangeTownRating(Town *t, int add, int max, DoCommandFlags flags); HouseZone GetTownRadiusGroup(const Town *t, TileIndex tile); void SetTownRatingTestMode(bool mode); TownActions GetMaskOfTownActions(CompanyID cid, const Town *t); -bool GenerateTowns(TownLayout layout); +uint GetDefaultTownsForMapSize(); +bool GenerateTowns(TownLayout layout, std::optional number = std::nullopt); const CargoSpec *FindFirstCargoWithTownAcceptanceEffect(TownAcceptanceEffect effect); CargoArray GetAcceptedCargoOfHouse(const HouseSpec *hs); diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 5f07725944..7ff6f6beb6 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -2372,19 +2372,39 @@ static Town *CreateRandomTown(uint attempts, uint32_t townnameparts, TownSize si return nullptr; } -static const uint8_t _num_initial_towns[4] = {5, 11, 23, 46}; // very low, low, normal, high +/** + * Calculate the number of towns which should be on the map according to the current "town density" newgame setting and the map size. + * If the number of towns is set to "custom", the function will always return that value instead. + * @return The number of towns. + */ +uint GetDefaultTownsForMapSize() +{ + static const uint8_t num_initial_towns[4] = {5, 11, 23, 46}; // very low, low, normal, high + if (_settings_game.difficulty.number_towns == static_cast(CUSTOM_TOWN_NUMBER_DIFFICULTY)) { + return _settings_newgame.game_creation.custom_town_number; + } + return Map::ScaleBySize(num_initial_towns[_settings_game.difficulty.number_towns]); +} /** * Generate a number of towns with a given layout. * This function is used by the Random Towns button in Scenario Editor as well as in world generation. * @param layout The road layout to build. + * @param number The number of towns to create. std::nullopt means to use the game settings. * @return true if towns have been successfully created. */ -bool GenerateTowns(TownLayout layout) +bool GenerateTowns(TownLayout layout, std::optional number) { uint current_number = 0; - uint difficulty = (_game_mode != GM_EDITOR) ? _settings_game.difficulty.number_towns : 0; - uint total = (difficulty == (uint)CUSTOM_TOWN_NUMBER_DIFFICULTY) ? _settings_game.game_creation.custom_town_number : Map::ScaleBySize(_num_initial_towns[difficulty] + (Random() & 7)); + uint total; + if (number.has_value()) { + total = number.value(); + } else if (_settings_game.difficulty.number_towns == static_cast(CUSTOM_TOWN_NUMBER_DIFFICULTY)) { + total = GetDefaultTownsForMapSize(); + } else { + total = GetDefaultTownsForMapSize() + (Random() & 7); + } + total = std::min(TownPool::MAX_SIZE, total); uint32_t townnameparts; TownNames town_names; diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 6f0ac5f9bf..b0bcfed5d1 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -34,6 +34,7 @@ #include "townname_func.h" #include "core/backup_type.hpp" #include "core/geometry_func.hpp" +#include "core/string_consumer.hpp" #include "genworld.h" #include "fios.h" #include "stringfilter_type.h" @@ -1249,16 +1250,10 @@ public: break; case WID_TF_MANY_RANDOM_TOWNS: { - Backup old_generating_world(_generating_world, true); - UpdateNearestTownForRoadTiles(true); - if (!GenerateTowns(this->town_layout)) { - ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_GENERATE_TOWN), GetEncodedString(STR_ERROR_NO_SPACE_FOR_TOWN), WL_INFO); - } - UpdateNearestTownForRoadTiles(false); - old_generating_world.Restore(); + std::string default_town_number = fmt::format("{}", GetDefaultTownsForMapSize()); + ShowQueryString(default_town_number, STR_MAPGEN_NUMBER_OF_TOWNS, 5, this, CS_NUMERAL, {QueryStringFlag::AcceptUnchanged}); break; } - case WID_TF_LOAD_FROM_FILE: ShowSaveLoadDialog(FT_TOWN_DATA, SLO_LOAD); break; @@ -1293,6 +1288,23 @@ public: } } + void OnQueryTextFinished(std::optional str) override + { + /* Was 'cancel' pressed? */ + if (!str.has_value()) return; + + auto value = ParseInteger(*str); + if (!value.has_value()) return; + + Backup old_generating_world(_generating_world, true); + UpdateNearestTownForRoadTiles(true); + if (!GenerateTowns(this->town_layout, value)) { + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_GENERATE_TOWN), GetEncodedString(STR_ERROR_NO_SPACE_FOR_TOWN), WL_INFO); + } + UpdateNearestTownForRoadTiles(false); + old_generating_world.Restore(); + } + void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override { this->ExecuteFoundTownCommand(tile, false, STR_ERROR_CAN_T_FOUND_TOWN_HERE, CcFoundTown);