diff --git a/src/command.cpp b/src/command.cpp index 8cc6ad77cf..cb97886d2a 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -295,7 +295,7 @@ static const Command _command_proc_table[] = { {CmdSellShareInCompany, 0}, // CMD_SELL_SHARE_IN_COMPANY {CmdBuyCompany, 0}, // CMD_BUY_COMANY - {CmdFoundTown, CMD_NO_TEST | CMD_OFFLINE}, // CMD_FOUND_TOWN + {CmdFoundTown, CMD_NO_TEST}, // CMD_FOUND_TOWN; founding random town can fail only in exec run {CmdRenameTown, CMD_SERVER}, // CMD_RENAME_TOWN {CmdDoTownAction, 0}, // CMD_DO_TOWN_ACTION diff --git a/src/command_type.h b/src/command_type.h index 8ac3786a14..a5a7bf1a6c 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -243,7 +243,6 @@ enum { CMD_BUY_COMPANY, ///< buy a company which is bankrupt CMD_FOUND_TOWN, ///< found a town - CMD_RENAME_TOWN, ///< rename a town CMD_DO_TOWN_ACTION, ///< do a action from the town detail window (like advertises or bribe) diff --git a/src/lang/english.txt b/src/lang/english.txt index cd5e77ce42..66c3bc4d8b 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -368,7 +368,9 @@ STR_MAP_MENU_EXTRA_VIEW_PORT :Extra viewport STR_MAP_MENU_SIGN_LIST :Sign list ############ range for town menu starts, yet the town directory is shown in the map menu in the scenario editor STR_TOWN_MENU_TOWN_DIRECTORY :Town directory -############ both ranges ends here +############ end of the 'Display map' dropdown +STR_TOWN_MENU_FOUND_TOWN :Found town +############ end of the 'Town' dropdown ############ range for subsidies menu starts STR_SUBSIDIES_MENU_SUBSIDIES :Subsidies @@ -760,6 +762,8 @@ STR_NEWS_COMPANY_LAUNCH_DESCRIPTION :{BIGFONT}{BLACK STR_NEWS_MERGER_TAKEOVER_TITLE :{BIGFONT}{BLACK}{RAW_STRING} has been taken over by {RAW_STRING}! STR_PRESIDENT_NAME_MANAGER :{BLACK}{PRESIDENTNAME}{}(Manager) +STR_NEWS_NEW_TOWN :{BLACK}{BIGFONT}{RAW_STRING} sponsored construction of new town {TOWN}! + STR_NEWS_INDUSTRY_CONSTRUCTION :{BIGFONT}{BLACK}New {STRING} under construction near {TOWN}! STR_NEWS_INDUSTRY_PLANTED :{BIGFONT}{BLACK}New {STRING} being planted near {TOWN}! @@ -1237,6 +1241,10 @@ STR_CONFIG_SETTING_TOWN_LAYOUT_3X3_GRID :3x3 grid STR_CONFIG_SETTING_TOWN_LAYOUT_RANDOM :random STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :{LTBLUE}Towns are allowed to build roads: {ORANGE}{STRING1} STR_CONFIG_SETTING_NOISE_LEVEL :{LTBLUE}Allow town controlled noise level for airports: {ORANGE}{STRING} +STR_CONFIG_SETTING_TOWN_FOUNDING :{LTBLUE}Founding towns in game: {ORANGE}{STRING1} +STR_CONFIG_SETTING_TOWN_FOUNDING_FORBIDDEN :forbidden +STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED :allowed +STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT :allowed, custom town layout STR_CONFIG_SETTING_TOOLBAR_POS :{LTBLUE}Position of main toolbar: {ORANGE}{STRING1} STR_CONFIG_SETTING_TOOLBAR_POS_LEFT :Left diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 76025aee77..cbf24248b4 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -47,7 +47,7 @@ #include "saveload_internal.h" -extern const uint16 SAVEGAME_VERSION = 127; +extern const uint16 SAVEGAME_VERSION = 128; SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/settings.cpp b/src/settings.cpp index 2ea624c331..f45adbe2d0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -753,6 +753,17 @@ static bool DragSignalsDensityChanged(int32) return true; } +static bool TownFoundingChanged(int32 p1) +{ + if (_game_mode != GM_EDITOR && _settings_game.economy.found_town == TF_FORBIDDEN) { + DeleteWindowById(WC_FOUND_TOWN, 0); + return true; + } + InvalidateWindowData(WC_FOUND_TOWN, 0); + return true; +} + + /* * A: competitors * B: competitor start time. Deprecated since savegame version 110. diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index f3d7a08c38..6b172f2d94 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1298,6 +1298,7 @@ static SettingEntry _settings_economy_towns[] = { SettingEntry("economy.exclusive_rights"), SettingEntry("economy.town_layout"), SettingEntry("economy.allow_town_roads"), + SettingEntry("economy.found_town"), SettingEntry("economy.mod_road_rebuild"), SettingEntry("economy.town_growth_rate"), SettingEntry("economy.larger_towns"), diff --git a/src/settings_type.h b/src/settings_type.h index 1ad6324361..02e997f616 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -327,8 +327,9 @@ struct EconomySettings { uint8 town_growth_rate; ///< town growth rate uint8 larger_towns; ///< the number of cities to build. These start off larger and grow twice as fast uint8 initial_city_size; ///< multiplier for the initial size of the cities compared to towns - TownLayoutByte town_layout; ///< select town layout + TownLayoutByte town_layout; ///< select town layout, @see TownLayout bool allow_town_roads; ///< towns are allowed to build roads (always allowed when generating world / in SE) + TownFoundingByte found_town; ///< town founding, @see TownFounding bool station_noise_level; ///< build new airports when the town noise level is still within accepted limits uint16 town_noise_population[3]; ///< population to base decision on noise evaluation (@see town_council_tolerance) }; diff --git a/src/table/settings.h b/src/table/settings.h index 623ed658d0..045721de2b 100644 --- a/src/table/settings.h +++ b/src/table/settings.h @@ -23,6 +23,7 @@ static bool UpdateConsists(int32 p1); static bool CheckInterval(int32 p1); static bool TrainAccelerationModelChanged(int32 p1); static bool DragSignalsDensityChanged(int32); +static bool TownFoundingChanged(int32 p1); static bool DifficultyReset(int32 level); static bool DifficultyChange(int32); static bool DifficultyNoiseChange(int32 i); @@ -364,8 +365,9 @@ const SettingDesc _settings[] = { SDT_BOOL(GameSettings, construction.longbridges, 0,NN, true, STR_CONFIG_SETTING_LONGBRIDGES, NULL), SDT_BOOL(GameSettings, construction.signal_side, N,NN, true, STR_CONFIG_SETTING_SIGNALSIDE, RedrawScreen), SDT_BOOL(GameSettings, station.never_expire_airports, 0,NN, false, STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS, NULL), - SDT_CONDVAR(GameSettings, economy.town_layout, SLE_UINT8, 59, SL_MAX_VERSION, 0,MS,TL_ORIGINAL,TL_BEGIN,NUM_TLS-1,1, STR_CONFIG_SETTING_TOWN_LAYOUT, NULL), + SDT_CONDVAR(GameSettings, economy.town_layout, SLE_UINT8, 59, SL_MAX_VERSION, 0,MS,TL_ORIGINAL,TL_BEGIN,NUM_TLS - 1, 1, STR_CONFIG_SETTING_TOWN_LAYOUT, TownFoundingChanged), SDT_CONDBOOL(GameSettings, economy.allow_town_roads, 113, SL_MAX_VERSION, 0, 0, true, STR_CONFIG_SETTING_ALLOW_TOWN_ROADS, NULL), + SDT_CONDVAR(GameSettings, economy.found_town, SLE_UINT8,128, SL_MAX_VERSION, 0,MS,TF_FORBIDDEN,TF_BEGIN,TF_END - 1, 1, STR_CONFIG_SETTING_TOWN_FOUNDING, TownFoundingChanged), SDT_VAR(GameSettings, vehicle.train_acceleration_model, SLE_UINT8, 0,MS, 0, 0, 1, 1, STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL, TrainAccelerationModelChanged), SDT_BOOL(GameSettings, pf.forbid_90_deg, 0, 0, false, STR_CONFIG_SETTING_FORBID_90_DEG, NULL), diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index 369a532e05..8c258db74b 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -405,12 +405,17 @@ static void MenuClickMap(int index) static void ToolbarTownClick(Window *w) { - PopupMainToolbMenu(w, TBN_TOWNDIRECTORY, STR_TOWN_MENU_TOWN_DIRECTORY, 1); + PopupMainToolbMenu(w, TBN_TOWNDIRECTORY, STR_TOWN_MENU_TOWN_DIRECTORY, (_settings_game.economy.found_town == TF_FORBIDDEN) ? 1 : 2); } static void MenuClickTown(int index) { - ShowTownDirectory(); + switch (index) { + case 0: ShowTownDirectory(); break; + case 1: // setting could be changed when the dropdown was open + if (_settings_game.economy.found_town != TF_FORBIDDEN) ShowFoundTownWindow(); + break; + } } /* --- Subidies button menu --- */ diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 30c4806b1d..3257ef54f4 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1458,7 +1458,7 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize int x = (int)size * 16 + 3; if (size == TS_RANDOM) x = (Random() & 0xF) + 8; - if (city) x *= _settings_game.economy.initial_city_size; + if (city && _game_mode == GM_EDITOR) x *= _settings_game.economy.initial_city_size; t->num_houses += x; UpdateTownRadius(t); @@ -1515,9 +1515,8 @@ static bool IsUniqueTownName(const char *name) return true; } -/** Create a new town. - * This obviously only works in the scenario editor. Function not removed - * as it might be possible in the future to fund your own town :) +/** + * Create a new town. * @param tile coordinates where town is built * @param flags type of operation * @param p1 0..1 size of the town (@see TownSize) @@ -1530,9 +1529,6 @@ static bool IsUniqueTownName(const char *name) */ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { - /* Only in the scenario editor */ - if (_game_mode != GM_EDITOR) return CMD_ERROR; - TownSize size = (TownSize)GB(p1, 0, 2); bool city = HasBit(p1, 2); TownLayout layout = (TownLayout)GB(p1, 3, 3); @@ -1543,6 +1539,16 @@ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 if (size > TS_RANDOM) return CMD_ERROR; if (layout > TL_RANDOM) return CMD_ERROR; + /* Some things are allowed only in the scenario editor */ + if (_game_mode != GM_EDITOR) { + if (_settings_game.economy.found_town == TF_FORBIDDEN) return CMD_ERROR; + if (size == TS_LARGE) return CMD_ERROR; + if (random) return CMD_ERROR; + if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT && layout != _settings_game.economy.town_layout) { + return CMD_ERROR; + } + } + if (StrEmpty(text)) { /* If supplied name is empty, townnameparts has to generate unique automatic name */ if (!VerifyTownName(townnameparts, &par)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE); @@ -1555,10 +1561,23 @@ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 /* Allocate town struct */ if (!Town::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_TOWNS); - CommandCost cost(EXPENSES_OTHER); if (!random) { - cost = TownCanBePlacedHere(tile); - if (CmdFailed(cost)) return cost; + CommandCost ret = TownCanBePlacedHere(tile); + if (CmdFailed(ret)) return ret; + } + + static const byte price_mult[][TS_RANDOM + 1] = {{ 15, 25, 40, 25 }, { 20, 35, 55, 35 }}; + /* multidimensional arrays have to have defined length of non-first dimension */ + assert_compile(lengthof(price_mult[0]) == 4); + + CommandCost cost(EXPENSES_OTHER, _price[PR_BUILD_INDUSTRY]); + byte mult = price_mult[city][size]; + + cost.MultiplyCost(mult); + + if (cost.GetCost() > GetAvailableMoneyForCommand()) { + _additional_cash_required = cost.GetCost(); + return CommandCost(EXPENSES_OTHER); } /* Create the town */ @@ -1579,10 +1598,25 @@ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 } UpdateNearestTownForRoadTiles(false); _generating_world = false; + if (t != NULL && !StrEmpty(text)) { t->name = strdup(text); t->UpdateVirtCoord(); } + + if (_game_mode != GM_EDITOR) { + /* 't' can't be NULL since 'random' is false outside scenedit */ + assert(!random); + char company_name[MAX_LENGTH_COMPANY_NAME_BYTES]; + SetDParam(0, _current_company); + GetString(company_name, STR_COMPANY_NAME, lastof(company_name)); + + char *cn = strdup(company_name); + SetDParamStr(0, cn); + SetDParam(1, t->index); + + AddNewsItem(STR_NEWS_NEW_TOWN, NS_INDUSTRY_OPEN, NR_TILE, tile, NR_NONE, UINT32_MAX, cn); + } } return cost; } diff --git a/src/town_gui.cpp b/src/town_gui.cpp index cbf033115b..b1fea3616e 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -991,7 +991,7 @@ public: this->InitNested(desc, window_number); InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, MAX_LENGTH_TOWN_NAME_PIXELS); this->RandomTownName(); - this->UpdateButtons(); + this->UpdateButtons(true); } void RandomTownName() @@ -1010,8 +1010,15 @@ public: this->SetWidgetDirty(TSEW_TOWNNAME_EDITBOX); } - void UpdateButtons() + void UpdateButtons(bool check_availability) { + if (check_availability && _game_mode != GM_EDITOR) { + this->SetWidgetsDisabledState(true, TSEW_RANDOMTOWN, TSEW_MANYRANDOMTOWNS, TSEW_SIZE_LARGE, WIDGET_LIST_END); + this->SetWidgetsDisabledState(_settings_game.economy.found_town != TF_CUSTOM_LAYOUT, + TSEW_LAYOUT_ORIGINAL, TSEW_LAYOUT_BETTER, TSEW_LAYOUT_GRID2, TSEW_LAYOUT_GRID3, TSEW_LAYOUT_RANDOM, WIDGET_LIST_END); + if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT) town_layout = _settings_game.economy.town_layout; + } + for (int i = TSEW_SIZE_SMALL; i <= TSEW_SIZE_RANDOM; i++) { this->SetWidgetLoweredState(i, i == TSEW_SIZE_SMALL + this->town_size); } @@ -1080,7 +1087,7 @@ public: case TSEW_SIZE_SMALL: case TSEW_SIZE_MEDIUM: case TSEW_SIZE_LARGE: case TSEW_SIZE_RANDOM: this->town_size = (TownSize)(widget - TSEW_SIZE_SMALL); - this->UpdateButtons(); + this->UpdateButtons(false); break; case TSEW_CITY: @@ -1092,7 +1099,7 @@ public: case TSEW_LAYOUT_ORIGINAL: case TSEW_LAYOUT_BETTER: case TSEW_LAYOUT_GRID2: case TSEW_LAYOUT_GRID3: case TSEW_LAYOUT_RANDOM: this->town_layout = (TownLayout)(widget - TSEW_LAYOUT_ORIGINAL); - this->UpdateButtons(); + this->UpdateButtons(false); break; } } @@ -1124,7 +1131,12 @@ public: virtual void OnPlaceObjectAbort() { this->RaiseButtons(); - this->UpdateButtons(); + this->UpdateButtons(false); + } + + virtual void OnInvalidateData(int) + { + this->UpdateButtons(true); } }; diff --git a/src/town_type.h b/src/town_type.h index dd2220b66b..266e952148 100644 --- a/src/town_type.h +++ b/src/town_type.h @@ -87,10 +87,20 @@ enum TownLayout { NUM_TLS, ///< Number of town layouts }; - /** It needs to be 8bits, because we save and load it as such */ typedef SimpleTinyEnumT TownLayoutByte; // typedefing-enumification of TownLayout +/** Town founding setting values */ +enum TownFounding { + TF_BEGIN = 0, ///< Used for iterations and limit testing + TF_FORBIDDEN = 0, ///< Forbidden + TF_ALLOWED, ///< Allowed + TF_CUSTOM_LAYOUT, ///< Allowed, with custom town layout + TF_END, ///< Used for iterations and limit testing +}; +/** It needs to be 8bits, because we save and load it as such */ +typedef SimpleTinyEnumT TownFoundingByte; + enum { MAX_LENGTH_TOWN_NAME_BYTES = 31, ///< The maximum length of a town name in bytes including '\0' MAX_LENGTH_TOWN_NAME_PIXELS = 130, ///< The maximum length of a town name in pixels