From 898752b85b8eae0ca9bb586f91dfa284b6103388 Mon Sep 17 00:00:00 2001 From: Tyler Trahan Date: Thu, 22 Apr 2021 13:54:32 -0400 Subject: [PATCH] Feature: Configurable subsidy duration --- src/lang/english.txt | 16 +++++++++----- src/saveload/saveload.h | 1 + src/saveload/subsidy_sl.cpp | 17 ++++++++------- src/settings_gui.cpp | 1 + src/settings_type.h | 3 ++- src/subsidy.cpp | 43 ++++++++++++++++++++++++++++++------- src/subsidy_base.h | 11 ++++++++-- src/subsidy_func.h | 3 ++- src/subsidy_gui.cpp | 4 ++-- src/table/settings.ini | 13 +++++++++++ 10 files changed, 85 insertions(+), 27 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 125273ed940cb..b0e827ede68f3 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -883,11 +883,15 @@ STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO :{WHITE}{STATION STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIG_FONT}{BLACK}Offer of subsidy expired:{}{}{STRING} from {STRING2} to {STRING2} will now not attract a subsidy STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIG_FONT}{BLACK}Subsidy withdrawn:{}{}{STRING} service from {STRING2} to {STRING2} is no longer subsidised -STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Service subsidy offered:{}{}First {STRING} service from {STRING2} to {STRING2} will attract a year's subsidy from the local authority! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay 50% extra for the next year! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay double rates for the next year! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay triple rates for the next year! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay quadruple rates for the next year! +STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Service subsidy offered:{}{}First {STRING} service from {STRING2} to {STRING2} will attract a {STRING2} subsidy from the local authority! +STR_NEWS_SERVICE_SUBSIDY_OFFERED_ONE_YEAR :year's +STR_NEWS_SERVICE_SUBSIDY_OFFERED_MULTI_YEAR :{NUM} year +STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay 50% extra for the next {STRING2}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay double rates for the next {STRING2}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay triple rates for the next {STRING2}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay quadruple rates for the next {STRING2}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_ONE_YEAR :year +STR_NEWS_SERVICE_SUBSIDY_AWARDED_MULTI_YEAR :{NUM} years STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Traffic chaos in {TOWN}!{}{}Road rebuilding programme funded by {RAW_STRING} brings 6 months of misery to motorists! STR_NEWS_EXCLUSIVE_RIGHTS_TITLE :{BIG_FONT}{BLACK}Transport monopoly! @@ -1202,6 +1206,8 @@ STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS :Vehicle breakdo STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS_HELPTEXT :Control how often inadequately serviced vehicles may break down STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER :Subsidy multiplier: {STRING2} STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER_HELPTEXT :Set how much is paid for subsidised connections +STR_CONFIG_SETTING_SUBSIDY_DURATION :Subsidy duration: {STRING2} +STR_CONFIG_SETTING_SUBSIDY_DURATION_HELPTEXT :Set the number of years for which a subsidy is awarded. Set to 0 to disable subsidies. STR_CONFIG_SETTING_CONSTRUCTION_COSTS :Construction costs: {STRING2} STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HELPTEXT :Set level of construction and purchase costs STR_CONFIG_SETTING_RECESSIONS :Recessions: {STRING2} diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 8ad29a6407060..2ddb5e69f2380 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -325,6 +325,7 @@ enum SaveLoadVersion : uint16 { SLV_VEH_MOTION_COUNTER, ///< 288 PR#8591 Desync safe motion counter SLV_INDUSTRY_TEXT, ///< 289 PR#8576 v1.11.0-RC1 Additional GS text for industries. SLV_MAPGEN_SETTINGS_REVAMP, ///< 290 PR#8891 v1.11 Revamp of some mapgen settings (snow coverage, desert coverage, heightmap height, custom terrain type). + SLV_CUSTOM_SUBSIDY_DURATION, ///< 291 PR#9081 Configurable subsidy duration. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/saveload/subsidy_sl.cpp b/src/saveload/subsidy_sl.cpp index d0db78b61d47b..673a423abdd97 100644 --- a/src/saveload/subsidy_sl.cpp +++ b/src/saveload/subsidy_sl.cpp @@ -16,14 +16,15 @@ static const SaveLoad _subsidies_desc[] = { SLE_VAR(Subsidy, cargo_type, SLE_UINT8), - SLE_VAR(Subsidy, remaining, SLE_UINT8), - SLE_CONDVAR(Subsidy, awarded, SLE_UINT8, SLV_125, SL_MAX_VERSION), - SLE_CONDVAR(Subsidy, src_type, SLE_UINT8, SLV_125, SL_MAX_VERSION), - SLE_CONDVAR(Subsidy, dst_type, SLE_UINT8, SLV_125, SL_MAX_VERSION), - SLE_CONDVAR(Subsidy, src, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Subsidy, src, SLE_UINT16, SLV_5, SL_MAX_VERSION), - SLE_CONDVAR(Subsidy, dst, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), - SLE_CONDVAR(Subsidy, dst, SLE_UINT16, SLV_5, SL_MAX_VERSION), + SLE_VAR(Subsidy, remaining, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_CUSTOM_SUBSIDY_DURATION), + SLE_CONDVAR(Subsidy, remaining, SLE_UINT16, SLV_CUSTOM_SUBSIDY_DURATION, SL_MAX_VERSION), + SLE_CONDVAR(Subsidy, awarded, SLE_UINT8, SLV_125, SL_MAX_VERSION), + SLE_CONDVAR(Subsidy, src_type, SLE_UINT8, SLV_125, SL_MAX_VERSION), + SLE_CONDVAR(Subsidy, dst_type, SLE_UINT8, SLV_125, SL_MAX_VERSION), + SLE_CONDVAR(Subsidy, src, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Subsidy, src, SLE_UINT16, SLV_5, SL_MAX_VERSION), + SLE_CONDVAR(Subsidy, dst, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5), + SLE_CONDVAR(Subsidy, dst, SLE_UINT16, SLV_5, SL_MAX_VERSION), SLE_END() }; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 0705c3fcc70e3..5379e5389670e 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1707,6 +1707,7 @@ static SettingsContainer &GetSettingsTree() accounting->Add(new SettingEntry("difficulty.initial_interest")); accounting->Add(new SettingEntry("difficulty.max_loan")); accounting->Add(new SettingEntry("difficulty.subsidy_multiplier")); + accounting->Add(new SettingEntry("difficulty.subsidy_duration")); accounting->Add(new SettingEntry("economy.feeder_payment_share")); accounting->Add(new SettingEntry("economy.infrastructure_maintenance")); accounting->Add(new SettingEntry("difficulty.vehicle_costs")); diff --git a/src/settings_type.h b/src/settings_type.h index bb078205b8af8..355460fb75cd8 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -69,7 +69,8 @@ struct DifficultySettings { byte vehicle_costs; ///< amount of money spent on vehicle running cost byte competitor_speed; ///< the speed at which the AI builds byte vehicle_breakdowns; ///< likelihood of vehicles breaking down - byte subsidy_multiplier; ///< amount of subsidy + byte subsidy_multiplier; ///< payment multiplier for subsidized deliveries + byte subsidy_duration; ///< duration of subsidies byte construction_cost; ///< how expensive is building byte terrain_type; ///< the mountainousness of the landscape byte quantity_sea_lakes; ///< the amount of seas/lakes diff --git a/src/subsidy.cpp b/src/subsidy.cpp index 2668a62bd7864..87a763b66dcbd 100644 --- a/src/subsidy.cpp +++ b/src/subsidy.cpp @@ -41,7 +41,7 @@ void Subsidy::AwardTo(CompanyID company) assert(!this->IsAwarded()); this->awarded = company; - this->remaining = SUBSIDY_CONTRACT_MONTHS; + this->remaining = _settings_game.difficulty.subsidy_duration * 12; char company_name[MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH]; SetDParam(0, company); @@ -50,7 +50,7 @@ void Subsidy::AwardTo(CompanyID company) char *cn = stredup(company_name); /* Add a news item */ - std::pair reftype = SetupSubsidyDecodeParam(this, false); + std::pair reftype = SetupSubsidyDecodeParam(this, SUBSIDY_NEWS_AWARDED); InjectDParam(1); SetDParamStr(0, cn); @@ -72,14 +72,18 @@ void Subsidy::AwardTo(CompanyID company) * @param mode Unit of cargo used, \c true means general name, \c false means singular form. * @return Reference of the subsidy in the news system. */ -std::pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode) +std::pair SetupSubsidyDecodeParam(const Subsidy *s, SubsidyDecodeParamType mode) { NewsReferenceType reftype1 = NR_NONE; NewsReferenceType reftype2 = NR_NONE; - /* if mode is false, use the singular form */ + /* Choose whether to use the singular or plural form of the cargo name based on how we're printing the subsidy */ const CargoSpec *cs = CargoSpec::Get(s->cargo_type); - SetDParam(0, mode ? cs->name : cs->name_single); + if (mode == SUBSIDY_GUI || mode == SUBSIDY_NEWS_WITHDRAWN) { + SetDParam(0, cs->name); + } else { + SetDParam(0, cs->name_single); + } switch (s->src_type) { case ST_INDUSTRY: @@ -107,6 +111,26 @@ std::pair SetupSubsidyDecodeParam(const Su } SetDParam(5, s->dst); + /* If the subsidy is being offered or awarded, add the right string to communicate the duration. */ + if (mode == SUBSIDY_NEWS_OFFERED) { + if (_settings_game.difficulty.subsidy_duration == 1) { + SetDParam(7, STR_NEWS_SERVICE_SUBSIDY_OFFERED_ONE_YEAR); + } + else { + SetDParam(7, STR_NEWS_SERVICE_SUBSIDY_OFFERED_MULTI_YEAR); + SetDParam(8, _settings_game.difficulty.subsidy_duration); + } + } + else if (mode == SUBSIDY_NEWS_AWARDED) { + if (_settings_game.difficulty.subsidy_duration == 1) { + SetDParam(7, STR_NEWS_SERVICE_SUBSIDY_AWARDED_ONE_YEAR); + } + else { + SetDParam(7, STR_NEWS_SERVICE_SUBSIDY_AWARDED_MULTI_YEAR); + SetDParam(8, _settings_game.difficulty.subsidy_duration); + } + } + return std::pair(reftype1, reftype2); } @@ -216,7 +240,7 @@ void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType ds s->remaining = SUBSIDY_OFFER_MONTHS; s->awarded = INVALID_COMPANY; - std::pair reftype = SetupSubsidyDecodeParam(s, false); + std::pair reftype = SetupSubsidyDecodeParam(s, SUBSIDY_NEWS_OFFERED); AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC); SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST); @@ -491,13 +515,13 @@ void SubsidyMonthlyLoop() for (Subsidy *s : Subsidy::Iterate()) { if (--s->remaining == 0) { if (!s->IsAwarded()) { - std::pair reftype = SetupSubsidyDecodeParam(s, true); + std::pair reftype = SetupSubsidyDecodeParam(s, SUBSIDY_NEWS_WITHDRAWN); AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index)); } else { if (s->awarded == _local_company) { - std::pair reftype = SetupSubsidyDecodeParam(s, true); + std::pair reftype = SetupSubsidyDecodeParam(s, SUBSIDY_NEWS_WITHDRAWN); AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, reftype.first, s->src, reftype.second, s->dst); } AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index)); @@ -510,6 +534,9 @@ void SubsidyMonthlyLoop() if (modified) { RebuildSubsidisedSourceAndDestinationCache(); + } else if (_settings_game.difficulty.subsidy_duration == 0) { + /* If subsidy duration is set to 0, subsidies are disabled, so bail out. */ + return; } else if (_settings_game.linkgraph.distribution_pax != DT_MANUAL && _settings_game.linkgraph.distribution_mail != DT_MANUAL && _settings_game.linkgraph.distribution_armoured != DT_MANUAL && diff --git a/src/subsidy_base.h b/src/subsidy_base.h index ef9276433f20d..21635cf7a903a 100644 --- a/src/subsidy_base.h +++ b/src/subsidy_base.h @@ -21,7 +21,7 @@ extern SubsidyPool _subsidy_pool; /** Struct about subsidies, offered and awarded */ struct Subsidy : SubsidyPool::PoolItem<&_subsidy_pool> { CargoID cargo_type; ///< Cargo type involved in this subsidy, CT_INVALID for invalid subsidy - byte remaining; ///< Remaining months when this subsidy is valid + uint16 remaining; ///< Remaining months when this subsidy is valid CompanyID awarded; ///< Subsidy is awarded to this company; INVALID_COMPANY if it's not awarded to anyone SourceType src_type; ///< Source of subsidised path (ST_INDUSTRY or ST_TOWN) SourceType dst_type; ///< Destination of subsidised path (ST_INDUSTRY or ST_TOWN) @@ -52,11 +52,18 @@ struct Subsidy : SubsidyPool::PoolItem<&_subsidy_pool> { /** Constants related to subsidies */ static const uint SUBSIDY_OFFER_MONTHS = 12; ///< Duration of subsidy offer -static const uint SUBSIDY_CONTRACT_MONTHS = 12; ///< Duration of subsidy after awarding static const uint SUBSIDY_PAX_MIN_POPULATION = 400; ///< Min. population of towns for subsidised pax route static const uint SUBSIDY_CARGO_MIN_POPULATION = 900; ///< Min. population of destination town for cargo route static const uint SUBSIDY_MAX_PCT_TRANSPORTED = 42; ///< Subsidy will be created only for towns/industries with less % transported static const uint SUBSIDY_MAX_DISTANCE = 70; ///< Max. length of subsidised route (DistanceManhattan) static const uint SUBSIDY_TOWN_CARGO_RADIUS = 6; ///< Extent of a tile area around town center when scanning for town cargo acceptance and production (6 ~= min catchmement + min station / 2) +/** Types of subsidy news messages, which determine how the date is printed and whether to use singular or plural cargo names */ +enum SubsidyDecodeParamType { + SUBSIDY_NEWS_OFFERED = 0, ///< News item for an offered subsidy + SUBSIDY_NEWS_AWARDED = 1, ///< News item for an awarded subsidy + SUBSIDY_NEWS_WITHDRAWN = 2, ///< News item for a subsidy offer withdrawn, or expired subsidy + SUBSIDY_GUI = 3, ///< Subsidies listed in the Subsidy GUI +}; + #endif /* SUBSIDY_BASE_H */ diff --git a/src/subsidy_func.h b/src/subsidy_func.h index cc63577d33715..5cd7a31181b69 100644 --- a/src/subsidy_func.h +++ b/src/subsidy_func.h @@ -15,8 +15,9 @@ #include "company_type.h" #include "cargo_type.h" #include "news_type.h" +#include "subsidy_base.h" -std::pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode); +std::pair SetupSubsidyDecodeParam(const struct Subsidy *s, SubsidyDecodeParamType mode); void DeleteSubsidyWith(SourceType type, SourceID index); bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st); void RebuildSubsidisedSourceAndDestinationCache(); diff --git a/src/subsidy_gui.cpp b/src/subsidy_gui.cpp index fe3c55c111b9d..07e085056b96b 100644 --- a/src/subsidy_gui.cpp +++ b/src/subsidy_gui.cpp @@ -161,7 +161,7 @@ struct SubsidyListWindow : Window { if (!s->IsAwarded()) { if (IsInsideMM(pos, 0, cap)) { /* Displays the two offered towns */ - SetupSubsidyDecodeParam(s, true); + SetupSubsidyDecodeParam(s, SUBSIDY_GUI); SetDParam(7, _date - ymd.day + s->remaining * 32); DrawString(x, right, y + pos * FONT_HEIGHT_NORMAL, STR_SUBSIDIES_OFFERED_FROM_TO); } @@ -184,7 +184,7 @@ struct SubsidyListWindow : Window { for (const Subsidy *s : Subsidy::Iterate()) { if (s->IsAwarded()) { if (IsInsideMM(pos, 0, cap)) { - SetupSubsidyDecodeParam(s, true); + SetupSubsidyDecodeParam(s, SUBSIDY_GUI); SetDParam(7, s->awarded); SetDParam(8, _date - ymd.day + s->remaining * 32); diff --git a/src/table/settings.ini b/src/table/settings.ini index 391a5b427f0db..3ea8b288fa97b 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -243,6 +243,19 @@ str = STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER strhelp = STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER_HELPTEXT strval = STR_SUBSIDY_X1_5 +[SDT_VAR] +base = GameSettings +var = difficulty.subsidy_duration +type = SLE_UINT8 +from = SLV_CUSTOM_SUBSIDY_DURATION +def = 1 +min = 0 +max = 255 +interval = 1 +str = STR_CONFIG_SETTING_SUBSIDY_DURATION +strhelp = STR_CONFIG_SETTING_SUBSIDY_DURATION_HELPTEXT +strval = STR_JUST_INT + [SDT_VAR] base = GameSettings var = difficulty.construction_cost