From 2d7d085e8e7d7b7e0d456a5e2b508462501d836d Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 16 Feb 2025 10:04:32 +0000 Subject: [PATCH] Codechange: Use EncodedString for error messages. (#13569) --- src/ai/ai_gui.cpp | 2 +- src/ai/ai_instance.cpp | 2 +- src/bridge_gui.cpp | 3 +- src/cheat_gui.cpp | 2 +- src/command.cpp | 27 ++++-- src/command_type.h | 46 +++++++++- src/company_cmd.cpp | 47 +++++----- src/company_func.h | 3 +- src/company_gui.cpp | 11 ++- src/depot_gui.cpp | 12 ++- src/driver.cpp | 9 +- src/engine.cpp | 7 +- src/error.h | 23 ++--- src/error_gui.cpp | 135 +++++++++------------------- src/fios_gui.cpp | 6 +- src/game/game_gui.cpp | 2 +- src/game/game_instance.cpp | 2 +- src/genworld.cpp | 25 +++--- src/heightmap.cpp | 19 ++-- src/industry_cmd.cpp | 15 ++-- src/industry_gui.cpp | 10 +-- src/intro_gui.cpp | 4 +- src/lang/english.txt | 4 +- src/misc_cmd.cpp | 6 +- src/misc_gui.cpp | 12 +-- src/network/core/tcp_game.cpp | 3 +- src/network/network.cpp | 4 +- src/network/network_client.cpp | 27 +++--- src/network/network_content.cpp | 13 ++- src/network/network_content_gui.cpp | 7 +- src/network/network_coordinator.cpp | 9 +- src/newgrf_commons.cpp | 13 ++- src/newgrf_gui.cpp | 26 +++--- src/newgrf_industries.cpp | 13 ++- src/openttd.cpp | 11 +-- src/order_gui.cpp | 10 +-- src/road_cmd.cpp | 15 +++- src/saveload/afterload.cpp | 9 +- src/saveload/saveload.cpp | 31 ++++--- src/saveload/saveload.h | 4 +- src/screenshot.cpp | 9 +- src/script/script_gui.cpp | 2 +- src/settings.cpp | 77 ++++++++-------- src/settings_gui.cpp | 8 +- src/settings_table.cpp | 18 ++-- src/spritecache.cpp | 5 +- src/spriteloader/grf.cpp | 3 +- src/station_cmd.cpp | 6 +- src/strings.cpp | 9 +- src/strings_func.h | 12 +++ src/town_cmd.cpp | 24 ++--- src/town_gui.cpp | 2 +- src/train_cmd.cpp | 4 +- src/tunnelbridge_cmd.cpp | 3 +- src/vehicle.cpp | 5 +- 55 files changed, 426 insertions(+), 390 deletions(-) diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp index 49a02f067c..5c9d73713d 100644 --- a/src/ai/ai_gui.cpp +++ b/src/ai/ai_gui.cpp @@ -281,7 +281,7 @@ struct AIConfigWindow : public Window { case WID_AIC_CONTENT_DOWNLOAD: if (!_network_available) { - ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR); } else { ShowNetworkContentListWindow(nullptr, CONTENT_TYPE_AI); } diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp index cb9ccd3965..7d8b38120c 100644 --- a/src/ai/ai_instance.cpp +++ b/src/ai/ai_instance.cpp @@ -71,7 +71,7 @@ void AIInstance::Died() const AIInfo *info = AIConfig::GetConfig(_current_company)->GetInfo(); if (info != nullptr) { - ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_ERROR_AI_PLEASE_REPORT_CRASH), {}, WL_WARNING); if (!info->GetURL().empty()) { ScriptLog::Info("Please report the error to the following URL:"); diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index 1a2d115a18..98d8932a0f 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -437,6 +437,7 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo if (!bl.empty()) { new BuildBridgeWindow(_build_bridge_desc, start, end, transport_type, road_rail_type, std::move(bl)); } else { - ShowErrorMessage(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE, errmsg, WL_INFO, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_BRIDGE_HERE), GetEncodedString(errmsg), + WL_INFO, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE); } } diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp index efe628f2d0..3e37322e31 100644 --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -151,7 +151,7 @@ static int32_t ClickChangeMaxHlCheat(int32_t new_value, int32_t) * If yes, disallow the change. */ for (const auto t : Map::Iterate()) { if ((int32_t)TileHeight(t) > new_value) { - ShowErrorMessage(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN), {}, WL_ERROR); /* Return old, unchanged value */ return _settings_game.construction.map_height_limit; } diff --git a/src/command.cpp b/src/command.cpp index 7d53af14f8..eccf4b2deb 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -218,7 +218,8 @@ std::tuple CommandHelperBase::InternalPostBefore(Commands cmd, bool only_sending = _networking && !network_command; if (_pause_mode.Any() && !IsCommandAllowedWhilePaused(cmd) && !estimate_only) { - ShowErrorMessage(err_message, STR_ERROR_NOT_ALLOWED_WHILE_PAUSED, WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE); + ShowErrorMessage(GetEncodedString(err_message), GetEncodedString(STR_ERROR_NOT_ALLOWED_WHILE_PAUSED), + WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE); return { true, estimate_only, only_sending }; } else { return { false, estimate_only, only_sending }; @@ -242,7 +243,7 @@ void CommandHelperBase::InternalPostResult(const CommandCost &res, TileIndex til if (res.Failed()) { /* Only show the error when it's for us. */ if (estimate_only || (IsLocalCompany() && err_message != 0 && my_cmd)) { - ShowErrorMessage(err_message, x, y, res); + ShowErrorMessage(GetEncodedString(err_message), x, y, res); } } else if (estimate_only) { ShowEstimatedCostOrIncome(res.GetCost(), x, y); @@ -371,8 +372,7 @@ CommandCost CommandHelperBase::InternalExecuteProcessResult(Commands cmd, Comman /* It could happen we removed rail, thus gained money, and deleted something else. * So make sure the signal buffer is empty even in this case */ UpdateSignalsInBuffer(); - SetDParam(0, extra_cash); - return CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); + return CommandCostWithParam(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, extra_cash); } /* update last build coordinate of company. */ @@ -412,7 +412,9 @@ void CommandCost::AddCost(const CommandCost &ret) * There is only one static instance of the array, just like there is only one * instance of normal DParams. */ -uint32_t CommandCost::textref_stack[16]; +/* static */ uint32_t CommandCost::textref_stack[16]; + +/* static */ EncodedString CommandCost::encoded_message; /** * Activate usage of the NewGRF #TextRefStack for the error message. @@ -430,3 +432,18 @@ void CommandCost::UseTextRefStack(const GRFFile *grffile, uint num_registers) textref_stack[i] = _temp_store.GetValue(0x100 + i); } } + +/** + * Return an error status, with string and parameter. + * @param str StringID of error. + * @param value Single parameter for error. + * @returns CommandCost representing the error. + */ +CommandCost CommandCostWithParam(StringID str, uint64_t value) +{ + CommandCost error = CommandCost(str); + if (IsLocalCompany()) { + error.SetEncodedMessage(GetEncodedString(str, value)); + } + return error; +} diff --git a/src/command_type.h b/src/command_type.h index fd5a98e7f9..ccc82534f2 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -10,6 +10,7 @@ #ifndef COMMAND_TYPE_H #define COMMAND_TYPE_H +#include "company_type.h" #include "economy_type.h" #include "strings_type.h" #include "tile_type.h" @@ -25,12 +26,15 @@ class CommandCost { StringID message; ///< Warning message for when success is unset ExpensesType expense_type; ///< the type of expence as shown on the finances view bool success; ///< Whether the command went fine up to this moment + Owner owner = INVALID_COMPANY; ///< Originator owner of error. const GRFFile *textref_stack_grffile = nullptr; ///< NewGRF providing the #TextRefStack content. uint textref_stack_size = 0; ///< Number of uint32_t values to put on the #TextRefStack for the error message. StringID extra_message = INVALID_STRING_ID; ///< Additional warning message for when success is unset static uint32_t textref_stack[16]; + static EncodedString encoded_message; ///< Encoded error message, used if the error message includes parameters. + public: /** * Creates a command cost return with no cost and no error @@ -55,6 +59,42 @@ public: */ CommandCost(ExpensesType ex_t, const Money &cst) : cost(cst), message(INVALID_STRING_ID), expense_type(ex_t), success(true) {} + /** + * Set the 'owner' (the originator) of this error message. This is used to show a company owner's face if you + * attempt an action on something owned by other company. + */ + inline void SetErrorOwner(Owner owner) + { + this->owner = owner; + } + + /** + * Set the encoded message string. If set, this is used by the error message window instead of the error StringID, + * to allow more information to be displayed to the local player. + * @note Do not set an encoded message if the error is not for the local player, as it will never be seen. + * @param message EncodedString message to set. + */ + static void SetEncodedMessage(EncodedString &&message) + { + CommandCost::encoded_message = std::move(message); + } + + /** + * Get the last encoded error message. + * @returns Reference to the encoded message. + */ + static EncodedString &GetEncodedMessage() + { + return CommandCost::encoded_message; + } + + /** + * Get the originator owner for this error. + */ + inline CompanyID GetErrorOwner() const + { + return this->owner; + } /** * Adds the given cost to the cost of the command. @@ -98,12 +138,12 @@ public: * Makes this #CommandCost behave like an error command. * @param message The error message. */ - void MakeError(StringID message, StringID extra_message = INVALID_STRING_ID) + void MakeError(StringID message) { assert(message != INVALID_STRING_ID); this->success = false; this->message = message; - this->extra_message = extra_message; + this->extra_message = INVALID_STRING_ID; } void UseTextRefStack(const GRFFile *grffile, uint num_registers); @@ -174,6 +214,8 @@ public: } }; +CommandCost CommandCostWithParam(StringID str, uint64_t value); + /** * List of commands. * diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index 9cfcacc93a..a38f57125b 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -245,8 +245,10 @@ bool CheckCompanyHasMoney(CommandCost &cost) const Company *c = Company::GetIfValid(_current_company); if (c != nullptr && cost.GetCost() > c->money) { - SetDParam(0, cost.GetCost()); cost.MakeError(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); + if (IsLocalCompany()) { + cost.SetEncodedMessage(GetEncodedString(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY, cost.GetCost())); + } return false; } return true; @@ -331,26 +333,20 @@ void UpdateLandscapingLimits() * @param tile optional tile to get the right town. * @pre if tile == 0, then owner can't be OWNER_TOWN. */ -void SetDParamsForOwnedBy(Owner owner, TileIndex tile) +std::array GetParamsForOwnedBy(Owner owner, TileIndex tile) { - SetDParam(OWNED_BY_OWNER_IN_PARAMETERS_OFFSET, owner); - - if (owner != OWNER_TOWN) { - if (!Company::IsValidID(owner)) { - SetDParam(0, STR_COMPANY_SOMEONE); - } else { - SetDParam(0, STR_COMPANY_NAME); - SetDParam(1, owner); - } - } else { + if (owner == OWNER_TOWN) { assert(tile != 0); const Town *t = ClosestTownFromTile(tile, UINT_MAX); - - SetDParam(0, STR_TOWN_NAME); - SetDParam(1, t->index); + return {STR_TOWN_NAME, t->index}; } -} + if (!Company::IsValidID(owner)) { + return {STR_COMPANY_SOMEONE, std::monostate{}}; + } + + return {STR_COMPANY_NAME, owner}; +} /** * Check whether the current owner owns something. @@ -367,8 +363,13 @@ CommandCost CheckOwnership(Owner owner, TileIndex tile) if (owner == _current_company) return CommandCost(); - SetDParamsForOwnedBy(owner, tile); - return CommandCost(STR_ERROR_OWNED_BY); + CommandCost error{STR_ERROR_OWNED_BY}; + if (IsLocalCompany()) { + auto params = GetParamsForOwnedBy(owner, tile); + error.SetEncodedMessage(GetEncodedStringWithArgs(STR_ERROR_OWNED_BY, params)); + error.SetErrorOwner(owner); + } + return error; } /** @@ -380,15 +381,7 @@ CommandCost CheckOwnership(Owner owner, TileIndex tile) */ CommandCost CheckTileOwnership(TileIndex tile) { - Owner owner = GetTileOwner(tile); - - assert(owner < OWNER_END); - - if (owner == _current_company) return CommandCost(); - - /* no need to get the name of the owner unless we're the local company (saves some time) */ - if (IsLocalCompany()) SetDParamsForOwnedBy(owner, tile); - return CommandCost(STR_ERROR_OWNED_BY); + return CheckOwnership(GetTileOwner(tile), tile); } /** diff --git a/src/company_func.h b/src/company_func.h index f2783d690d..5fc6c0d1df 100644 --- a/src/company_func.h +++ b/src/company_func.h @@ -17,8 +17,7 @@ bool CheckTakeoverVehicleLimit(CompanyID cbig, CompanyID small); void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner); -static const int OWNED_BY_OWNER_IN_PARAMETERS_OFFSET = 2; ///< The index in the parameters for the owner information. -void SetDParamsForOwnedBy(Owner owner, TileIndex tile); +std::array GetParamsForOwnedBy(Owner owner, TileIndex tile); void SetLocalCompany(CompanyID new_company); void ShowBuyCompanyDialog(CompanyID company, bool hostile_takeover); void CompanyAdminUpdate(const Company *company); diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 428194261b..b7861d143e 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -1624,7 +1624,7 @@ public: case WID_SCMF_LOAD: this->face = _company_manager_face; ScaleAllCompanyManagerFaceBits(this->face); - ShowErrorMessage(STR_FACE_LOAD_DONE, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_FACE_LOAD_DONE), {}, WL_INFO); this->UpdateData(); this->SetDirty(); break; @@ -1637,7 +1637,7 @@ public: /* Save button */ case WID_SCMF_SAVE: _company_manager_face = this->face; - ShowErrorMessage(STR_FACE_SAVE_DONE, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_FACE_SAVE_DONE), {}, WL_INFO); break; /* Toggle gender (male/female) button */ @@ -1714,11 +1714,11 @@ public: if (!str->empty()) { this->face = std::strtoul(str->c_str(), nullptr, 10); ScaleAllCompanyManagerFaceBits(this->face); - ShowErrorMessage(STR_FACE_FACECODE_SET, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_FACE_FACECODE_SET), {}, WL_INFO); this->UpdateData(); this->SetDirty(); } else { - ShowErrorMessage(STR_FACE_FACECODE_ERR, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_FACE_FACECODE_ERR), {}, WL_INFO); } } }; @@ -2635,8 +2635,7 @@ struct BuyCompanyWindow : Window { { switch (widget) { case WID_BC_CAPTION: - SetDParam(0, STR_COMPANY_NAME); - SetDParam(1, Company::Get(this->window_number)->index); + SetDParam(0, Company::Get(this->window_number)->index); break; } } diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 5a1c8ab645..6ca6778349 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -944,10 +944,12 @@ struct DepotWindow : Window { })) { OnVehicleSelect(*begin); } else { - ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type), + GetEncodedString(STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST), WL_INFO); } } else { - ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_CLONE_VEHICLE_LIST, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type), + GetEncodedString(STR_ERROR_CAN_T_CLONE_VEHICLE_LIST), WL_INFO); } } else { /* If CTRL is pressed: If all the vehicles in this list share orders, then copy orders */ @@ -959,10 +961,12 @@ struct DepotWindow : Window { })) { OnVehicleSelect(*begin); } else { - ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type), + GetEncodedString(STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST), WL_INFO); } } else { - ShowErrorMessage(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type, STR_ERROR_CAN_T_CLONE_VEHICLE_LIST, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUY_TRAIN + (*begin)->type), + GetEncodedString(STR_ERROR_CAN_T_CLONE_VEHICLE_LIST), WL_INFO); } } diff --git a/src/driver.cpp b/src/driver.cpp index f9c68fc266..1a180b148c 100644 --- a/src/driver.cpp +++ b/src/driver.cpp @@ -13,6 +13,7 @@ #include "error_func.h" #include "sound/sound_driver.hpp" #include "music/music_driver.hpp" +#include "strings_func.h" #include "video/video_driver.hpp" #include "string_func.h" #include "table/strings.h" @@ -128,8 +129,8 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t Debug(driver, 1, "Probing {} driver '{}' skipped due to earlier crash", GetDriverTypeName(type), d->name); _video_hw_accel = false; - ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH, true); - ScheduleErrorMessage(msg); + ErrorMessageData msg(GetEncodedString(STR_VIDEO_DRIVER_ERROR), GetEncodedString(STR_VIDEO_DRIVER_ERROR_HARDWARE_ACCELERATION_CRASH), true); + ScheduleErrorMessage(std::move(msg)); continue; } @@ -154,8 +155,8 @@ bool DriverFactoryBase::SelectDriverImpl(const std::string &name, Driver::Type t if (type == Driver::DT_VIDEO && _video_hw_accel && d->UsesHardwareAcceleration()) { _video_hw_accel = false; - ErrorMessageData msg(STR_VIDEO_DRIVER_ERROR, STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION, true); - ScheduleErrorMessage(msg); + ErrorMessageData msg(GetEncodedString(STR_VIDEO_DRIVER_ERROR), GetEncodedString(STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION), true); + ScheduleErrorMessage(std::move(msg)); } } } diff --git a/src/engine.cpp b/src/engine.cpp index dec6aabe8d..f404d5c5a4 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -1336,9 +1336,10 @@ void CheckEngines() } if (min_date < INT32_MAX) { - SetDParam(0, min_date); - ShowErrorMessage(STR_ERROR_NO_VEHICLES_AVAILABLE_YET, STR_ERROR_NO_VEHICLES_AVAILABLE_YET_EXPLANATION, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_YET), + GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_YET_EXPLANATION, min_date), WL_WARNING); } else { - ShowErrorMessage(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL, STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL_EXPLANATION, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL), + GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL_EXPLANATION), WL_WARNING); } } diff --git a/src/error.h b/src/error.h index 7cfe9addcb..3f8797a30d 100644 --- a/src/error.h +++ b/src/error.h @@ -31,31 +31,24 @@ enum WarningLevel : uint8_t { class ErrorMessageData { protected: bool is_critical; ///< Whether the error message is critical. - std::vector params; ///< Backup of parameters of the message strings. const GRFFile *textref_stack_grffile; ///< NewGRF that filled the #TextRefStack for the error message. uint textref_stack_size; ///< Number of uint32_t values to put on the #TextRefStack for the error message. uint32_t textref_stack[16]; ///< Values to put on the #TextRefStack for the error message. - StringID summary_msg; ///< General error message showed in first line. Must be valid. - StringID detailed_msg; ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID. - StringID extra_msg; ///< Extra error message shown in third line. Can be #INVALID_STRING_ID. + EncodedString summary_msg; ///< General error message showed in first line. Must be valid. + EncodedString detailed_msg; ///< Detailed error message showed in second line. Can be #INVALID_STRING_ID. + EncodedString extra_msg; ///< Extra error message shown in third line. Can be #INVALID_STRING_ID. Point position; ///< Position of the error message window. - CompanyID face; ///< Company belonging to the face being shown. #INVALID_COMPANY if no face present. + CompanyID company; ///< Company belonging to the face being shown. #INVALID_COMPANY if no face present. public: ErrorMessageData(const ErrorMessageData &data); - ErrorMessageData(StringID summary_msg, StringID detailed_msg, bool is_critical = false, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32_t *textref_stack = nullptr, StringID extra_msg = INVALID_STRING_ID); + ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical = false, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32_t *textref_stack = nullptr, EncodedString &&extra_msg = {}, CompanyID company = INVALID_COMPANY); /* Remove the copy assignment, as the default implementation will not do the right thing. */ ErrorMessageData &operator=(ErrorMessageData &rhs) = delete; /** Check whether error window shall display a company manager face */ - bool HasFace() const { return face != INVALID_COMPANY; } - - void SetDParam(uint n, uint64_t v); - void SetDParamStr(uint n, const char *str); - void SetDParamStr(uint n, const std::string &str); - - void CopyOutDParams(); + bool HasFace() const { return company != INVALID_COMPANY; } }; /** Define a queue with errors. */ @@ -64,8 +57,8 @@ typedef std::list ErrorList; void ScheduleErrorMessage(ErrorList &datas); void ScheduleErrorMessage(const ErrorMessageData &data); -void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc); -void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32_t *textref_stack = nullptr, StringID extra_msg = INVALID_STRING_ID); +void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc); +void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x = 0, int y = 0, const GRFFile *textref_stack_grffile = nullptr, uint textref_stack_size = 0, const uint32_t *textref_stack = nullptr, EncodedString &&extra_msg = {}, CompanyID company = INVALID_COMPANY); bool HideActiveErrorMessage(); void ClearErrorMessages(); diff --git a/src/error_gui.cpp b/src/error_gui.cpp index 824b3f9f26..316511f501 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -75,8 +75,8 @@ static WindowDesc _errmsg_face_desc( * @param data The data to copy. */ ErrorMessageData::ErrorMessageData(const ErrorMessageData &data) : - is_critical(data.is_critical), params(data.params), textref_stack_grffile(data.textref_stack_grffile), textref_stack_size(data.textref_stack_size), - summary_msg(data.summary_msg), detailed_msg(data.detailed_msg), extra_msg(data.extra_msg), position(data.position), face(data.face) + is_critical(data.is_critical), textref_stack_grffile(data.textref_stack_grffile), textref_stack_size(data.textref_stack_size), + summary_msg(data.summary_msg), detailed_msg(data.detailed_msg), extra_msg(data.extra_msg), position(data.position), company(data.company) { memcpy(this->textref_stack, data.textref_stack, sizeof(this->textref_stack)); } @@ -84,80 +84,30 @@ ErrorMessageData::ErrorMessageData(const ErrorMessageData &data) : /** * Display an error message in a window. * @param summary_msg General error message showed in first line. Must be valid. - * @param detailed_msg Detailed error message showed in second line. Can be INVALID_STRING_ID. + * @param detailed_msg Detailed error message showed in second line. Can be empty. * @param is_critical Whether the error is critical. Critical messages never go away on their own. * @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. * @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. * @param textref_stack_grffile NewGRF that provides the #TextRefStack for the error message. * @param textref_stack_size Number of uint32_t values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used. * @param textref_stack Values to put on the #TextRefStack. - * @param extra_msg Extra error message showed in third line. Can be INVALID_STRING_ID. + * @param extra_msg Extra error message showed in third line. Can be empty. */ -ErrorMessageData::ErrorMessageData(StringID summary_msg, StringID detailed_msg, bool is_critical, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, StringID extra_msg) : +ErrorMessageData::ErrorMessageData(EncodedString &&summary_msg, EncodedString &&detailed_msg, bool is_critical, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, EncodedString &&extra_msg, CompanyID company) : is_critical(is_critical), textref_stack_grffile(textref_stack_grffile), textref_stack_size(textref_stack_size), - summary_msg(summary_msg), - detailed_msg(detailed_msg), - extra_msg(extra_msg), - face(INVALID_COMPANY) + summary_msg(std::move(summary_msg)), + detailed_msg(std::move(detailed_msg)), + extra_msg(std::move(extra_msg)), + company(company) { this->position.x = x; this->position.y = y; if (textref_stack_size > 0) MemCpyT(this->textref_stack, textref_stack, textref_stack_size); - assert(summary_msg != INVALID_STRING_ID); -} - -/** - * Copy error parameters from current DParams. - */ -void ErrorMessageData::CopyOutDParams() -{ - if (this->detailed_msg == STR_ERROR_OWNED_BY) { - /* The parameters are set by SetDParamsForOwnedBy. */ - CompanyID company = (CompanyID)GetDParam(OWNED_BY_OWNER_IN_PARAMETERS_OFFSET); - if (company < MAX_COMPANIES) face = company; - } - - /* Get parameters using type information */ - if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_grffile, this->textref_stack_size, this->textref_stack); - CopyOutDParam(this->params, 20); - if (this->textref_stack_size > 0) StopTextRefStackUsage(); -} - -/** - * Set a error string parameter. - * @param n Parameter index - * @param v Parameter value - */ -void ErrorMessageData::SetDParam(uint n, uint64_t v) -{ - if (n >= this->params.size()) this->params.resize(n + 1); - this->params[n] = v; -} - -/** - * Set a rawstring parameter. - * @param n Parameter index - * @param str Raw string - */ -void ErrorMessageData::SetDParamStr(uint n, const char *str) -{ - if (n >= this->params.size()) this->params.resize(n + 1); - this->params[n] = str; -} - -/** - * Set a rawstring parameter. - * @param n Parameter index - * @param str Raw string - */ -void ErrorMessageData::SetDParamStr(uint n, const std::string &str) -{ - if (n >= this->params.size()) this->params.resize(n + 1); - this->params[n] = str; + assert(!this->summary_msg.empty()); } /** The actual queue with errors. */ @@ -193,18 +143,17 @@ public: { switch (widget) { case WID_EM_MESSAGE: { - CopyInDParam(this->params); if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_grffile, this->textref_stack_size, this->textref_stack); - this->height_summary = GetStringHeight(this->summary_msg, size.width); - this->height_detailed = (this->detailed_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->detailed_msg, size.width); - this->height_extra = (this->extra_msg == INVALID_STRING_ID) ? 0 : GetStringHeight(this->extra_msg, size.width); + this->height_summary = GetStringHeight(this->summary_msg.GetDecodedString(), size.width); + this->height_detailed = (this->detailed_msg.empty()) ? 0 : GetStringHeight(this->detailed_msg.GetDecodedString(), size.width); + this->height_extra = (this->extra_msg.empty()) ? 0 : GetStringHeight(this->extra_msg.GetDecodedString(), size.width); if (this->textref_stack_size > 0) StopTextRefStackUsage(); uint panel_height = this->height_summary; - if (this->detailed_msg != INVALID_STRING_ID) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide; - if (this->extra_msg != INVALID_STRING_ID) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide; + if (!this->detailed_msg.empty()) panel_height += this->height_detailed + WidgetDimensions::scaled.vsep_wide; + if (!this->extra_msg.empty()) panel_height += this->height_extra + WidgetDimensions::scaled.vsep_wide; size.height = std::max(size.height, panel_height); break; @@ -248,36 +197,35 @@ public: void OnInvalidateData([[maybe_unused]] int data = 0, [[maybe_unused]] bool gui_scope = true) override { /* If company gets shut down, while displaying an error about it, remove the error message. */ - if (this->face != INVALID_COMPANY && !Company::IsValidID(this->face)) this->Close(); + if (this->company != INVALID_COMPANY && !Company::IsValidID(this->company)) this->Close(); } void SetStringParameters(WidgetID widget) const override { - if (widget == WID_EM_CAPTION) CopyInDParam(this->params); + if (widget == WID_EM_CAPTION && this->company != INVALID_COMPANY) SetDParam(0, this->company); } void DrawWidget(const Rect &r, WidgetID widget) const override { switch (widget) { case WID_EM_FACE: { - const Company *c = Company::Get(this->face); + const Company *c = Company::Get(this->company); DrawCompanyManagerFace(c->face, c->colour, r); break; } case WID_EM_MESSAGE: - CopyInDParam(this->params); if (this->textref_stack_size > 0) StartTextRefStackUsage(this->textref_stack_grffile, this->textref_stack_size, this->textref_stack); - if (this->detailed_msg == INVALID_STRING_ID) { - DrawStringMultiLine(r, this->summary_msg, TC_FROMSTRING, SA_CENTER); - } else if (this->extra_msg == INVALID_STRING_ID) { + if (this->detailed_msg.empty()) { + DrawStringMultiLine(r, this->summary_msg.GetDecodedString(), TC_FROMSTRING, SA_CENTER); + } else if (this->extra_msg.empty()) { /* Extra space when message is shorter than company face window */ int extra = (r.Height() - this->height_summary - this->height_detailed - WidgetDimensions::scaled.vsep_wide) / 2; /* Note: NewGRF supplied error message often do not start with a colour code, so default to white. */ - DrawStringMultiLine(r.WithHeight(this->height_summary + extra, false), this->summary_msg, TC_WHITE, SA_CENTER); - DrawStringMultiLine(r.WithHeight(this->height_detailed + extra, true), this->detailed_msg, TC_WHITE, SA_CENTER); + DrawStringMultiLine(r.WithHeight(this->height_summary + extra, false), this->summary_msg.GetDecodedString(), TC_WHITE, SA_CENTER); + DrawStringMultiLine(r.WithHeight(this->height_detailed + extra, true), this->detailed_msg.GetDecodedString(), TC_WHITE, SA_CENTER); } else { /* Extra space when message is shorter than company face window */ int extra = (r.Height() - this->height_summary - this->height_detailed - this->height_extra - (WidgetDimensions::scaled.vsep_wide * 2)) / 3; @@ -286,9 +234,9 @@ public: Rect top_section = r.WithHeight(this->height_summary + extra, false); Rect bottom_section = r.WithHeight(this->height_extra + extra, true); Rect middle_section = { top_section.left, top_section.bottom, top_section.right, bottom_section.top }; - DrawStringMultiLine(top_section, this->summary_msg, TC_WHITE, SA_CENTER); - DrawStringMultiLine(middle_section, this->detailed_msg, TC_WHITE, SA_CENTER); - DrawStringMultiLine(bottom_section, this->extra_msg, TC_WHITE, SA_CENTER); + DrawStringMultiLine(top_section, this->summary_msg.GetDecodedString(), TC_WHITE, SA_CENTER); + DrawStringMultiLine(middle_section, this->detailed_msg.GetDecodedString(), TC_WHITE, SA_CENTER); + DrawStringMultiLine(bottom_section, this->extra_msg.GetDecodedString(), TC_WHITE, SA_CENTER); } if (this->textref_stack_size > 0) StopTextRefStackUsage(); @@ -362,43 +310,47 @@ void UnshowCriticalError() * @param summary_msg General error message showed in first line. Must be valid. * @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. * @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. - * @param cc CommandCost containing the optional detailed and extra error messages shown in the second and third lines (can be INVALID_STRING_ID) and TextRefStack info. + * @param cc CommandCost containing the optional detailed and extra error messages shown in the second and third lines (can be empty) and TextRefStack info. */ -void ShowErrorMessage(StringID summary_msg, int x, int y, CommandCost cc) +void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, const CommandCost &cc) { - ShowErrorMessage(summary_msg, cc.GetErrorMessage(), WL_INFO, x, y, cc.GetTextRefStackGRF(), cc.GetTextRefStackSize(), cc.GetTextRefStack(), cc.GetExtraErrorMessage()); + EncodedString error = std::move(cc.GetEncodedMessage()); + if (error.empty()) error = GetEncodedStringIfValid(cc.GetErrorMessage()); + + ShowErrorMessage(std::move(summary_msg), std::move(error), WL_INFO, x, y, + cc.GetTextRefStackGRF(), cc.GetTextRefStackSize(), cc.GetTextRefStack(), + GetEncodedStringIfValid(cc.GetExtraErrorMessage()), cc.GetErrorOwner()); } /** * Display an error message in a window. * @param summary_msg General error message showed in first line. Must be valid. - * @param detailed_msg Detailed error message showed in second line. Can be INVALID_STRING_ID. + * @param detailed_msg Detailed error message showed in second line. Can be empty. * @param wl Message severity. * @param x World X position (TileVirtX) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. * @param y World Y position (TileVirtY) of the error location. Set both x and y to 0 to just center the message when there is no related error tile. * @param textref_stack_grffile NewGRF providing the #TextRefStack for the error message. * @param textref_stack_size Number of uint32_t values to put on the #TextRefStack for the error message; 0 if the #TextRefStack shall not be used. * @param textref_stack Values to put on the #TextRefStack. - * @param extra_msg Extra error message shown in third line. Can be INVALID_STRING_ID. + * @param extra_msg Extra error message shown in third line. Can be empty. */ -void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel wl, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, StringID extra_msg) +void ShowErrorMessage(EncodedString &&summary_msg, EncodedString &&detailed_msg, WarningLevel wl, int x, int y, const GRFFile *textref_stack_grffile, uint textref_stack_size, const uint32_t *textref_stack, EncodedString &&extra_msg, CompanyID company) { assert(textref_stack_size == 0 || (textref_stack_grffile != nullptr && textref_stack != nullptr)); - if (summary_msg == STR_NULL) summary_msg = STR_EMPTY; if (wl != WL_INFO) { /* Print message to console */ if (textref_stack_size > 0) StartTextRefStackUsage(textref_stack_grffile, textref_stack_size, textref_stack); - std::string message = GetString(summary_msg); - if (detailed_msg != INVALID_STRING_ID) { + std::string message = summary_msg.GetDecodedString(); + if (!detailed_msg.empty()) { message += " "; - AppendStringInPlace(message, detailed_msg); + message += detailed_msg.GetDecodedString(); } - if (extra_msg != INVALID_STRING_ID) { + if (!extra_msg.empty()) { message += " "; - AppendStringInPlace(message, extra_msg); + message += extra_msg.GetDecodedString(); } if (textref_stack_size > 0) StopTextRefStackUsage(); @@ -411,8 +363,7 @@ void ShowErrorMessage(StringID summary_msg, StringID detailed_msg, WarningLevel if (_game_mode == GM_BOOTSTRAP) return; if (_settings_client.gui.errmsg_duration == 0 && !is_critical) return; - ErrorMessageData data(summary_msg, detailed_msg, is_critical, x, y, textref_stack_grffile, textref_stack_size, textref_stack, extra_msg); - data.CopyOutDParams(); + ErrorMessageData data(std::move(summary_msg), std::move(detailed_msg), is_critical, x, y, textref_stack_grffile, textref_stack_size, textref_stack, std::move(extra_msg), company); ErrmsgWindow *w = dynamic_cast(FindWindowById(WC_ERRMSG, 0)); if (w != nullptr) { diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index c116a9e3d0..b48a7ddf84 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -703,7 +703,7 @@ public: case WID_SL_MISSING_NEWGRFS: if (!_network_available) { - ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR); } else if (_load_check_data.HasNewGrfs()) { ShowMissingContentWindow(_load_check_data.grfconfig); } @@ -758,7 +758,7 @@ public: case WID_SL_CONTENT_DOWNLOAD: if (!_network_available) { - ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR); } else { assert(this->fop == SLO_LOAD); switch (this->abstract_filetype) { @@ -815,7 +815,7 @@ public: if (this->IsWidgetLowered(WID_SL_DELETE_SELECTION)) { // Delete button clicked if (!FiosDelete(this->filename_editbox.text.GetText())) { - ShowErrorMessage(STR_ERROR_UNABLE_TO_DELETE_FILE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_UNABLE_TO_DELETE_FILE), {}, WL_ERROR); } else { this->InvalidateData(SLIWD_RESCAN_FILES); /* Reset file name to current date on successful delete */ diff --git a/src/game/game_gui.cpp b/src/game/game_gui.cpp index a5494603f9..9ba418cbc3 100644 --- a/src/game/game_gui.cpp +++ b/src/game/game_gui.cpp @@ -265,7 +265,7 @@ struct GSConfigWindow : public Window { case WID_GSC_CONTENT_DOWNLOAD: if (!_network_available) { - ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR); } else { ShowNetworkContentListWindow(nullptr, CONTENT_TYPE_GAME); } diff --git a/src/game/game_instance.cpp b/src/game/game_instance.cpp index 13888f2432..e2d18bf917 100644 --- a/src/game/game_instance.cpp +++ b/src/game/game_instance.cpp @@ -74,7 +74,7 @@ void GameInstance::Died() const GameInfo *info = Game::GetInfo(); if (info != nullptr) { - ShowErrorMessage(STR_ERROR_AI_PLEASE_REPORT_CRASH, INVALID_STRING_ID, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_ERROR_AI_PLEASE_REPORT_CRASH), {}, WL_WARNING); if (!info->GetURL().empty()) { ScriptLog::Info("Please report the error to the following URL:"); diff --git a/src/genworld.cpp b/src/genworld.cpp index e145e63112..60aaf61b25 100644 --- a/src/genworld.cpp +++ b/src/genworld.cpp @@ -390,7 +390,8 @@ void LoadTownData() auto f = FioFOpenFile(_file_to_saveload.name, "rb", HEIGHTMAP_DIR, &filesize); if (!f.has_value()) { - ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_LOAD_FAILED), + GetEncodedString(STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY), WL_ERROR); return; } @@ -398,7 +399,8 @@ void LoadTownData() size_t len = fread(text.data(), filesize, 1, *f); f.reset(); if (len != 1) { - ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_LOAD_FAILED), + GetEncodedString(STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY), WL_ERROR); return; } @@ -407,13 +409,13 @@ void LoadTownData() try { town_data = nlohmann::json::parse(text); } catch (nlohmann::json::exception &) { - ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_LOAD_FAILED), GetEncodedString(STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY), WL_ERROR); return; } /* Check for JSON formatting errors with the array of towns. */ if (!town_data.is_array()) { - ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_LOAD_FAILED), GetEncodedString(STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY), WL_ERROR); return; } @@ -426,14 +428,14 @@ void LoadTownData() /* Ensure JSON is formatted properly. */ if (!feature.is_object()) { - ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_LOAD_FAILED), GetEncodedString(STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY), WL_ERROR); return; } /* Check to ensure all fields exist and are of the correct type. * If the town name is formatted wrong, all we can do is give a general warning. */ if (!feature.contains("name") || !feature.at("name").is_string()) { - ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_LOAD_FAILED), GetEncodedString(STR_TOWN_DATA_ERROR_JSON_FORMATTED_INCORRECTLY), WL_ERROR); return; } @@ -443,8 +445,8 @@ void LoadTownData() !feature.contains("x") || !feature.at("x").is_number() || !feature.contains("y") || !feature.at("y").is_number()) { feature.at("name").get_to(town.name); - SetDParamStr(0, town.name); - ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_TOWN_FORMATTED_INCORRECTLY, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_LOAD_FAILED), + GetEncodedString(STR_TOWN_DATA_ERROR_TOWN_FORMATTED_INCORRECTLY, town.name), WL_ERROR); return; } @@ -459,8 +461,8 @@ void LoadTownData() /* Check for improper coordinates and warn the player. */ if (town.x_proportion <= 0.0f || town.y_proportion <= 0.0f || town.x_proportion >= 1.0f || town.y_proportion >= 1.0f) { - SetDParamStr(0, town.name); - ShowErrorMessage(STR_TOWN_DATA_ERROR_LOAD_FAILED, STR_TOWN_DATA_ERROR_BAD_COORDINATE, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_LOAD_FAILED), + GetEncodedString(STR_TOWN_DATA_ERROR_BAD_COORDINATE, town.name), WL_ERROR); return; } @@ -496,8 +498,7 @@ void LoadTownData() /* If we couldn't found a town (or multiple), display a message to the player with the number of failed towns. */ if (failed_towns > 0) { - SetDParam(0, failed_towns); - ShowErrorMessage(STR_TOWN_DATA_ERROR_FAILED_TO_FOUND_TOWN, INVALID_STRING_ID, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_TOWN_DATA_ERROR_FAILED_TO_FOUND_TOWN, failed_towns), {}, WL_WARNING); } /* Now that we've created the towns, let's grow them to their target populations. */ diff --git a/src/heightmap.cpp b/src/heightmap.cpp index e80179e336..7520745cc0 100644 --- a/src/heightmap.cpp +++ b/src/heightmap.cpp @@ -10,6 +10,7 @@ #include "stdafx.h" #include "heightmap.h" #include "clear_map.h" +#include "strings_func.h" #include "void_map.h" #include "error.h" #include "saveload/saveload.h" @@ -141,19 +142,19 @@ static bool ReadHeightmapPNG(const char *filename, uint *x, uint *y, std::vector auto fp = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR); if (!fp.has_value()) { - ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_FILE_NOT_FOUND, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_FILE_NOT_FOUND), WL_ERROR); return false; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (png_ptr == nullptr) { - ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_MISC, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_MISC), WL_ERROR); return false; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == nullptr || setjmp(png_jmpbuf(png_ptr))) { - ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_MISC, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_MISC), WL_ERROR); png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); return false; } @@ -168,7 +169,7 @@ static bool ReadHeightmapPNG(const char *filename, uint *x, uint *y, std::vector /* Maps of wrong colour-depth are not used. * (this should have been taken care of by stripping alpha and 16-bit samples on load) */ if ((png_get_channels(png_ptr, info_ptr) != 1) && (png_get_channels(png_ptr, info_ptr) != 3) && (png_get_bit_depth(png_ptr, info_ptr) != 8)) { - ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_IMAGE_TYPE, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_PNGMAP_IMAGE_TYPE), WL_ERROR); png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); return false; } @@ -177,7 +178,7 @@ static bool ReadHeightmapPNG(const char *filename, uint *x, uint *y, std::vector uint height = png_get_image_height(png_ptr, info_ptr); if (!IsValidHeightmapDimension(width, height)) { - ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_PNGMAP), GetEncodedString(STR_ERROR_HEIGHTMAP_TOO_LARGE), WL_ERROR); png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); return false; } @@ -259,7 +260,7 @@ static bool ReadHeightmapBMP(const char *filename, uint *x, uint *y, std::vector { auto f = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR); if (!f.has_value()) { - ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_PNGMAP_FILE_NOT_FOUND, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_PNGMAP_FILE_NOT_FOUND), WL_ERROR); return false; } @@ -268,18 +269,18 @@ static bool ReadHeightmapBMP(const char *filename, uint *x, uint *y, std::vector BmpData data{}; if (!BmpReadHeader(file, info, data)) { - ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_BMPMAP_IMAGE_TYPE), WL_ERROR); return false; } if (!IsValidHeightmapDimension(info.width, info.height)) { - ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_HEIGHTMAP_TOO_LARGE), WL_ERROR); return false; } if (map != nullptr) { if (!BmpReadBitmap(file, info, data)) { - ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_BMPMAP), GetEncodedString(STR_ERROR_BMPMAP_IMAGE_TYPE), WL_ERROR); return false; } diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index b29e40a824..d0a92ce5e3 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -505,8 +505,11 @@ static CommandCost ClearTile_Industry(TileIndex tile, DoCommandFlags flags) (_current_company == OWNER_WATER && (indspec->behaviour.Test(IndustryBehaviour::BuiltOnWater) || HasBit(GetIndustryTileSpec(GetIndustryGfx(tile))->slopes_refused, 5)))) { - SetDParam(1, indspec->name); - return CommandCost(flags.Test(DoCommandFlag::Auto) ? STR_ERROR_GENERIC_OBJECT_IN_THE_WAY : INVALID_STRING_ID); + + if (flags.Test(DoCommandFlag::Auto)) { + return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, indspec->name); + } + return CommandCost(INVALID_STRING_ID); } if (flags.Test(DoCommandFlag::Execute)) { @@ -2103,9 +2106,9 @@ CommandCost CmdBuildIndustry(DoCommandFlags flags, TileIndex tile, IndustryType } if (ret.Failed() && IsLocalCompany()) { if (prospect_success) { - ShowErrorMessage(STR_ERROR_CAN_T_PROSPECT_INDUSTRY, STR_ERROR_NO_SUITABLE_PLACES_FOR_PROSPECTING, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_PROSPECT_INDUSTRY), GetEncodedString(STR_ERROR_NO_SUITABLE_PLACES_FOR_PROSPECTING), WL_INFO); } else { - ShowErrorMessage(STR_ERROR_CAN_T_PROSPECT_INDUSTRY, STR_ERROR_PROSPECTING_WAS_UNLUCKY, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_PROSPECT_INDUSTRY), GetEncodedString(STR_ERROR_PROSPECTING_WAS_UNLUCKY), WL_INFO); } } } @@ -3095,8 +3098,8 @@ void CheckIndustries() if (chance == 0 || !force_at_least_one) continue; // Types that are not available can be skipped. const IndustrySpec *is = GetIndustrySpec(it); - SetDParam(0, is->name); - ShowErrorMessage(STR_ERROR_NO_SUITABLE_PLACES_FOR_INDUSTRIES, STR_ERROR_NO_SUITABLE_PLACES_FOR_INDUSTRIES_EXPLANATION, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_ERROR_NO_SUITABLE_PLACES_FOR_INDUSTRIES, is->name), + GetEncodedString(STR_ERROR_NO_SUITABLE_PLACES_FOR_INDUSTRIES_EXPLANATION), WL_WARNING); count++; if (count >= 3) break; // Don't swamp the user with errors. diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 4b7779fde0..47934a62d8 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -259,8 +259,8 @@ void CcBuildIndustry(Commands, const CommandCost &result, TileIndex tile, Indust if (indtype < NUM_INDUSTRYTYPES) { const IndustrySpec *indsp = GetIndustrySpec(indtype); if (indsp->enabled) { - SetDParam(0, indsp->name); - ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, result.GetErrorMessage(), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_HERE, indsp->name), + GetEncodedString(result.GetErrorMessage()), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE); } } } @@ -605,7 +605,7 @@ public: if (!confirmed) return; if (Town::GetNumItems() == 0) { - ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_INDUSTRIES, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_GENERATE_INDUSTRIES), GetEncodedString(STR_ERROR_MUST_FOUND_TOWN_FIRST), WL_INFO); } else { Backup old_generating_world(_generating_world, true); BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP); @@ -712,8 +712,8 @@ public: if (_game_mode == GM_EDITOR) { /* Show error if no town exists at all */ if (Town::GetNumItems() == 0) { - SetDParam(0, indsp->name); - ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO, pt.x, pt.y); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_HERE, indsp->name), + GetEncodedString(STR_ERROR_MUST_FOUND_TOWN_FIRST), WL_INFO, pt.x, pt.y); return; } diff --git a/src/intro_gui.cpp b/src/intro_gui.cpp index 0855b25d36..7150b77ad5 100644 --- a/src/intro_gui.cpp +++ b/src/intro_gui.cpp @@ -345,7 +345,7 @@ struct SelectGameWindow : public Window { case WID_SGI_PLAY_NETWORK: if (!_network_available) { - ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR); } else { ShowNetworkGameWindow(); } @@ -363,7 +363,7 @@ struct SelectGameWindow : public Window { case WID_SGI_GRF_SETTINGS: ShowNewGRFSettings(true, true, false, _grfconfig_newgame); break; case WID_SGI_CONTENT_DOWNLOAD: if (!_network_available) { - ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR); } else { ShowNetworkContentListWindow(); } diff --git a/src/lang/english.txt b/src/lang/english.txt index aa446b8f0e..3a37226f31 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -4999,7 +4999,7 @@ STR_ERROR_SCREENSHOT_FAILED :{WHITE}Screensh # Error message titles STR_ERROR_MESSAGE_CAPTION :{YELLOW}Message -STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY :{YELLOW}Message from {STRING1} +STR_ERROR_MESSAGE_CAPTION_OTHER_COMPANY :{YELLOW}Message from {COMPANY} # Generic construction errors STR_ERROR_OFF_EDGE_OF_MAP :{WHITE}Off edge of map @@ -5018,7 +5018,7 @@ STR_ERROR_TERRAFORM_LIMIT_REACHED :{WHITE}... land STR_ERROR_CLEARING_LIMIT_REACHED :{WHITE}... tile clearing limit reached STR_ERROR_TREE_PLANT_LIMIT_REACHED :{WHITE}... tree planting limit reached STR_ERROR_NAME_MUST_BE_UNIQUE :{WHITE}Name must be unique -STR_ERROR_GENERIC_OBJECT_IN_THE_WAY :{WHITE}{1:STRING} in the way +STR_ERROR_GENERIC_OBJECT_IN_THE_WAY :{WHITE}{STRING} in the way STR_ERROR_NOT_ALLOWED_WHILE_PAUSED :{WHITE}Not allowed while paused # Local authority errors diff --git a/src/misc_cmd.cpp b/src/misc_cmd.cpp index c80c75483a..9b25d741fa 100644 --- a/src/misc_cmd.cpp +++ b/src/misc_cmd.cpp @@ -41,8 +41,7 @@ CommandCost CmdIncreaseLoan(DoCommandFlags flags, LoanCommand cmd, Money amount) Company *c = Company::Get(_current_company); Money max_loan = c->GetMaxLoan(); if (c->current_loan >= max_loan) { - SetDParam(0, max_loan); - return CommandCost(STR_ERROR_MAXIMUM_PERMITTED_LOAN); + return CommandCostWithParam(STR_ERROR_MAXIMUM_PERMITTED_LOAN, max_loan); } Money loan; @@ -106,8 +105,7 @@ CommandCost CmdDecreaseLoan(DoCommandFlags flags, LoanCommand cmd, Money amount) } if (GetAvailableMoneyForCommand() < loan) { - SetDParam(0, loan); - return CommandCost(STR_ERROR_CURRENCY_REQUIRED); + return CommandCostWithParam(STR_ERROR_CURRENCY_REQUIRED, loan); } if (flags.Test(DoCommandFlag::Execute)) { diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index d65403a9c7..e1404197f0 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -185,9 +185,12 @@ public: for (uint i = 0; i < 4; i++) { if (td.owner_type[i] == STR_NULL) continue; - SetDParam(0, STR_LAND_AREA_INFORMATION_OWNER_N_A); - if (td.owner[i] != OWNER_NONE && td.owner[i] != OWNER_WATER) SetDParamsForOwnedBy(td.owner[i], tile); - this->landinfo_data.push_back(GetString(td.owner_type[i])); + if (td.owner[i] != OWNER_NONE && td.owner[i] != OWNER_WATER) { + this->landinfo_data.push_back(GetString(td.owner_type[i], STR_LAND_AREA_INFORMATION_OWNER_N_A)); + } else { + auto params = GetParamsForOwnedBy(td.owner[i], tile); + this->landinfo_data.push_back(GetStringWithArgs(td.owner_type[i], params)); + } } /* Cost to clear/revenue when cleared */ @@ -522,8 +525,7 @@ void ShowEstimatedCostOrIncome(Money cost, int x, int y) cost = -cost; msg = STR_MESSAGE_ESTIMATED_INCOME; } - SetDParam(0, cost); - ShowErrorMessage(msg, INVALID_STRING_ID, WL_INFO, x, y); + ShowErrorMessage(GetEncodedString(msg, cost), {}, WL_INFO, x, y); } /** diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp index 2555967058..5d072dbb92 100644 --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -15,6 +15,7 @@ #include "../network_internal.h" #include "../../debug.h" #include "../../error.h" +#include "../../strings_func.h" #include "table/strings.h" @@ -43,7 +44,7 @@ NetworkRecvStatus NetworkGameSocketHandler::CloseConnection(bool) ClientNetworkEmergencySave(); _switch_mode = SM_MENU; _networking = false; - ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_LOSTCONNECTION), {}, WL_CRITICAL); return this->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT); } diff --git a/src/network/network.cpp b/src/network/network.cpp index 9050c316ed..f61a627656 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -302,7 +302,7 @@ uint NetworkCalculateLag(const NetworkClientSocket *cs) void ShowNetworkError(StringID error_string) { _switch_mode = SM_MENU; - ShowErrorMessage(error_string, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(error_string), {}, WL_CRITICAL); } /** @@ -862,7 +862,7 @@ bool NetworkValidateServerName(std::string &server_name) StrTrimInPlace(server_name); if (!server_name.empty()) return true; - ShowErrorMessage(STR_NETWORK_ERROR_BAD_SERVER_NAME, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_BAD_SERVER_NAME), {}, WL_ERROR); return false; } diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index 34adfdb2d2..a51756dc67 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -508,7 +508,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FULL(Packet &) Debug(net, 9, "Client::Receive_SERVER_FULL()"); /* We try to join a server which is full */ - ShowErrorMessage(STR_NETWORK_ERROR_SERVER_FULL, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_SERVER_FULL), {}, WL_CRITICAL); return NETWORK_RECV_STATUS_SERVER_FULL; } @@ -518,7 +518,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet & Debug(net, 9, "Client::Receive_SERVER_BANNED()"); /* We try to join a server where we are banned */ - ShowErrorMessage(STR_NETWORK_ERROR_SERVER_BANNED, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_SERVER_BANNED), {}, WL_CRITICAL); return NETWORK_RECV_STATUS_SERVER_BANNED; } @@ -624,10 +624,11 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet &p if (error < (ptrdiff_t)lengthof(network_error_strings)) err = network_error_strings[error]; /* In case of kicking a client, we assume there is a kick message in the packet if we can read one byte */ if (error == NETWORK_ERROR_KICKED && p.CanReadFromPacket(1)) { - SetDParamStr(0, p.Recv_string(NETWORK_CHAT_LENGTH)); - ShowErrorMessage(err, STR_NETWORK_ERROR_KICK_MESSAGE, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(err), + GetEncodedString(STR_NETWORK_ERROR_KICK_MESSAGE, p.Recv_string(NETWORK_CHAT_LENGTH)), + WL_CRITICAL); } else { - ShowErrorMessage(err, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(err), {}, WL_CRITICAL); } /* Perform an emergency save if we had already entered the game */ @@ -665,7 +666,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P } /* NewGRF mismatch, bail out */ - ShowErrorMessage(STR_NETWORK_ERROR_NEWGRF_MISMATCH, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NEWGRF_MISMATCH), {}, WL_CRITICAL); return ret; } @@ -831,7 +832,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet this->last_packet = std::chrono::steady_clock::now(); if (!load_success) { - ShowErrorMessage(STR_NETWORK_ERROR_SAVEGAMEERROR, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_SAVEGAMEERROR), {}, WL_CRITICAL); return NETWORK_RECV_STATUS_SAVEGAME; } /* If the savegame has successfully loaded, ALL windows have been removed, @@ -1074,7 +1075,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet /* Only when we're trying to join we really * care about the server shutting down. */ if (this->status >= STATUS_JOIN) { - ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_SHUTDOWN, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_NETWORK_MESSAGE_SERVER_SHUTDOWN), {}, WL_CRITICAL); } if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); @@ -1093,7 +1094,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEWGAME(Packet * Client ID modulo 16 + 1 (value 0 means no reconnect). * This way reconnects should be spread out a bit. */ _network_reconnect = _network_own_client_id % 16 + 1; - ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_REBOOT, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_NETWORK_MESSAGE_SERVER_REBOOT), {}, WL_CRITICAL); } if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); @@ -1185,8 +1186,10 @@ void ClientNetworkGameSocketHandler::CheckConnection() if (std::chrono::duration_cast(last_lag) == std::chrono::duration_cast(lag)) return; last_lag = lag; - SetDParam(0, std::chrono::duration_cast(lag).count()); - ShowErrorMessage(STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION, STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION, WL_INFO); + ShowErrorMessage( + GetEncodedString(STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION), + GetEncodedString(STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION, std::chrono::duration_cast(lag).count()), + WL_INFO); } @@ -1276,7 +1279,7 @@ bool NetworkValidateClientName(std::string &client_name) StrTrimInPlace(client_name); if (NetworkIsValidClientName(client_name)) return true; - ShowErrorMessage(STR_NETWORK_ERROR_BAD_PLAYER_NAME, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_BAD_PLAYER_NAME), {}, WL_ERROR); return false; } diff --git a/src/network/network_content.cpp b/src/network/network_content.cpp index 1cc833add8..638fdfe931 100644 --- a/src/network/network_content.cpp +++ b/src/network/network_content.cpp @@ -16,6 +16,7 @@ #include "../fileio_func.h" #include "../base_media_base.h" #include "../settings_type.h" +#include "../strings_func.h" #include "network_content.h" #include "table/strings.h" @@ -496,7 +497,10 @@ bool ClientNetworkContentSocketHandler::Receive_SERVER_CONTENT(Packet &p) size_t toRead = p.RemainingBytesToTransfer(); if (toRead != 0 && static_cast(p.TransferOut(TransferOutFWrite, std::ref(this->curFile))) != toRead) { CloseWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD); - ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR); + ShowErrorMessage( + GetEncodedString(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD), + GetEncodedString(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE), + WL_ERROR); this->CloseConnection(); this->curFile.reset(); @@ -529,7 +533,10 @@ bool ClientNetworkContentSocketHandler::BeforeDownload() if (filename.empty() || !(this->curFile = FileHandle::Open(filename, "wb")).has_value()) { /* Unless that fails of course... */ CloseWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_CONTENT_DOWNLOAD); - ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD, STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE, WL_ERROR); + ShowErrorMessage( + GetEncodedString(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD), + GetEncodedString(STR_CONTENT_ERROR_COULD_NOT_DOWNLOAD_FILE_NOT_WRITABLE), + WL_ERROR); return false; } } @@ -568,7 +575,7 @@ void ClientNetworkContentSocketHandler::AfterDownload() this->OnDownloadComplete(this->curInfo->id); } else { - ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_EXTRACT, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONTENT_ERROR_COULD_NOT_EXTRACT), {}, WL_ERROR); } } diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp index c0edb4d4bc..1260e32674 100644 --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -957,7 +957,7 @@ public: void OnConnect(bool success) override { if (!success) { - ShowErrorMessage(STR_CONTENT_ERROR_COULD_NOT_CONNECT, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONTENT_ERROR_COULD_NOT_CONNECT), {}, WL_ERROR); this->Close(); return; } @@ -1150,7 +1150,10 @@ void ShowNetworkContentListWindow(ContentVector *cv, ContentType type1, ContentT CloseWindowById(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_CONTENT_LIST); new NetworkContentListWindow(_network_content_list_desc, cv != nullptr, types); #else - ShowErrorMessage(STR_CONTENT_NO_ZLIB, STR_CONTENT_NO_ZLIB_SUB, WL_ERROR); + ShowErrorMessage( + GetEncodedString(STR_CONTENT_NO_ZLIB), + GetEncodedString(STR_CONTENT_NO_ZLIB_SUB), + WL_ERROR); /* Connection failed... clean up the mess */ if (cv != nullptr) { for (ContentInfo *ci : *cv) delete ci; diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp index b8ba523ee5..64bd109261 100644 --- a/src/network/network_coordinator.cpp +++ b/src/network/network_coordinator.cpp @@ -135,7 +135,7 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet &p) return false; case NETWORK_COORDINATOR_ERROR_REGISTRATION_FAILED: - ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_REGISTRATION_FAILED), {}, WL_ERROR); /* To prevent that we constantly try to reconnect, switch to local game. */ _settings_client.network.server_game_type = SERVER_GAME_TYPE_LOCAL; @@ -159,7 +159,7 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_ERROR(Packet &p) } case NETWORK_COORDINATOR_ERROR_REUSE_OF_INVITE_CODE: - ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_REUSE_OF_INVITE_CODE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_REUSE_OF_INVITE_CODE), {}, WL_ERROR); /* To prevent that we constantly battle for the same invite-code, switch to local game. */ _settings_client.network.server_game_type = SERVER_GAME_TYPE_LOCAL; @@ -184,7 +184,10 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet &p) _network_server_connection_type = (ConnectionType)p.Recv_uint8(); if (_network_server_connection_type == CONNECTION_TYPE_ISOLATED) { - ShowErrorMessage(STR_NETWORK_ERROR_COORDINATOR_ISOLATED, STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL, WL_ERROR); + ShowErrorMessage( + GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_ISOLATED), + GetEncodedString(STR_NETWORK_ERROR_COORDINATOR_ISOLATED_DETAIL), + WL_ERROR); } /* Users can change the invite code in the settings, but this has no effect diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp index 932bf290fd..b20fa230bf 100644 --- a/src/newgrf_commons.cpp +++ b/src/newgrf_commons.cpp @@ -16,6 +16,7 @@ #include "house.h" #include "industrytype.h" #include "newgrf_config.h" +#include "company_func.h" #include "clear_map.h" #include "station_map.h" #include "tree_map.h" @@ -489,8 +490,13 @@ CommandCost GetErrorMessageFromLocationCallbackResult(uint16_t cb_res, const GRF } } + /* If this error isn't for the local player then it won't be seen, so don't bother encoding anything. */ + if (!IsLocalCompany()) return res; + /* Copy some parameters from the registers to the error message text ref. stack */ + std::array params{}; res.UseTextRefStack(grffile, 4); + res.SetEncodedMessage(GetEncodedStringWithArgs(res.GetErrorMessage(), params)); return res; } @@ -508,10 +514,9 @@ void ErrorUnknownCallbackResult(uint32_t grfid, uint16_t cbid, uint16_t cb_res) if (grfconfig->grf_bugs.Test(GRFBug::UnknownCbResult)) { grfconfig->grf_bugs.Set(GRFBug::UnknownCbResult); - SetDParamStr(0, grfconfig->GetName()); - SetDParam(1, cbid); - SetDParam(2, cb_res); - ShowErrorMessage(STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_NEWGRF_BUGGY, grfconfig->GetName()), + GetEncodedString(STR_NEWGRF_BUGGY_UNKNOWN_CALLBACK_RESULT, std::monostate{}, cbid, cb_res), + WL_CRITICAL); } /* debug output */ diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 3d90b6eb0e..5e418a36b9 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -51,18 +51,18 @@ void ShowNewGRFError() /* Only show Fatal and Error level messages */ if (!c->error.has_value() || (c->error->severity != STR_NEWGRF_ERROR_MSG_FATAL && c->error->severity != STR_NEWGRF_ERROR_MSG_ERROR)) continue; - SetDParamStr(0, c->GetName()); - SetDParam (1, c->error->message != STR_NULL ? c->error->message : STR_JUST_RAW_STRING); - SetDParamStr(2, c->error->custom_message); - SetDParamStr(3, c->filename); - SetDParamStr(4, c->error->data); - for (uint i = 0; i < c->error->param_value.size(); i++) { - SetDParam(5 + i, c->error->param_value[i]); - } + std::vector params; + params.emplace_back(c->GetName()); + params.emplace_back(c->error->message != STR_NULL ? c->error->message : STR_JUST_RAW_STRING); + params.emplace_back(c->error->custom_message); + params.emplace_back(c->filename); + params.emplace_back(c->error->data); + for (const uint32_t &value : c->error->param_value) params.emplace_back(value); + if (c->error->severity == STR_NEWGRF_ERROR_MSG_FATAL) { - ShowErrorMessage(STR_NEWGRF_ERROR_FATAL_POPUP, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedStringWithArgs(STR_NEWGRF_ERROR_FATAL_POPUP, params), {}, WL_CRITICAL); } else { - ShowErrorMessage(STR_NEWGRF_ERROR_POPUP, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedStringWithArgs(STR_NEWGRF_ERROR_POPUP, params), {}, WL_ERROR); } break; } @@ -1131,7 +1131,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { case WID_NS_CONTENT_DOWNLOAD: case WID_NS_CONTENT_DOWNLOAD2: if (!_network_available) { - ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NETWORK_ERROR_NOTAVAILABLE), {}, WL_ERROR); } else { this->CloseChildWindows(WC_QUERY_STRING); // Remove the parameter query window @@ -1487,13 +1487,13 @@ private: /* Get number of non-static NewGRFs. */ size_t count = std::ranges::count_if(this->actives, [](const auto &gc) { return !gc->flags.Test(GRFConfigFlag::Static); }); if (count >= NETWORK_MAX_GRF_COUNT) { - ShowErrorMessage(STR_NEWGRF_TOO_MANY_NEWGRFS, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_NEWGRF_TOO_MANY_NEWGRFS), {}, WL_INFO); return false; } /* Check for duplicate GRF ID. */ if (std::ranges::any_of(this->actives, [&grfid = this->avail_sel->ident.grfid](const auto &gc) { return gc->ident.grfid == grfid; })) { - ShowErrorMessage(STR_NEWGRF_DUPLICATE_GRFID, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_NEWGRF_DUPLICATE_GRFID), {}, WL_INFO); return false; } diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 4eb3123b4c..3bcf48eda7 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -615,9 +615,9 @@ void IndustryProductionCallback(Industry *ind, int reason) * 'loop' is provided as 16 bits to the newgrf, so abort when those are exceeded. */ if (loop >= 0x10000) { /* display error message */ - SetDParamStr(0, spec->grf_prop.grffile->filename); - SetDParam(1, spec->name); - ShowErrorMessage(STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_ENDLESS_PRODUCTION_CALLBACK, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_NEWGRF_BUGGY, spec->grf_prop.grffile->filename), + GetEncodedString(STR_NEWGRF_BUGGY_ENDLESS_PRODUCTION_CALLBACK, std::monostate{}, spec->name), + WL_WARNING); /* abort the function early, this error isn't critical and will allow the game to continue to run */ break; @@ -630,10 +630,9 @@ void IndustryProductionCallback(Industry *ind, int reason) if (group->version == 0xFF) { /* Result was marked invalid on load, display error message */ - SetDParamStr(0, spec->grf_prop.grffile->filename); - SetDParam(1, spec->name); - SetDParam(2, ind->location.tile); - ShowErrorMessage(STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_INVALID_CARGO_PRODUCTION_CALLBACK, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_NEWGRF_BUGGY, spec->grf_prop.grffile->filename), + GetEncodedString(STR_NEWGRF_BUGGY_INVALID_CARGO_PRODUCTION_CALLBACK, std::monostate{}, spec->name, ind->location.tile), + WL_WARNING); /* abort the function early, this error isn't critical and will allow the game to continue to run */ break; diff --git a/src/openttd.cpp b/src/openttd.cpp index ce3a9d2784..8099a15147 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -721,8 +721,7 @@ int openttd_main(std::span arguments) if (!valid_graphics_set) { BaseGraphics::SetSet(nullptr); - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_BASE_GRAPHICS_NOT_FOUND); - msg.SetDParamStr(0, graphics_set); + ErrorMessageData msg(GetEncodedString(STR_CONFIG_ERROR), GetEncodedString(STR_CONFIG_ERROR_INVALID_BASE_GRAPHICS_NOT_FOUND, graphics_set)); ScheduleErrorMessage(msg); } @@ -775,8 +774,7 @@ int openttd_main(std::span arguments) if (sounds_set.empty() || !BaseSounds::SetSet({})) { UserError("Failed to find a sounds set. Please acquire a sounds set for OpenTTD. See section 1.4 of README.md."); } else { - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_BASE_SOUNDS_NOT_FOUND); - msg.SetDParamStr(0, sounds_set); + ErrorMessageData msg(GetEncodedString(STR_CONFIG_ERROR), GetEncodedString(STR_CONFIG_ERROR_INVALID_BASE_SOUNDS_NOT_FOUND, sounds_set)); ScheduleErrorMessage(msg); } } @@ -787,8 +785,7 @@ int openttd_main(std::span arguments) if (music_set.empty() || !BaseMusic::SetSet({})) { UserError("Failed to find a music set. Please acquire a music set for OpenTTD. See section 1.4 of README.md."); } else { - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_BASE_MUSIC_NOT_FOUND); - msg.SetDParamStr(0, music_set); + ErrorMessageData msg(GetEncodedString(STR_CONFIG_ERROR), GetEncodedString(STR_CONFIG_ERROR_INVALID_BASE_MUSIC_NOT_FOUND, music_set)); ScheduleErrorMessage(msg); } } @@ -1159,7 +1156,7 @@ void SwitchToMode(SwitchMode new_mode) case SM_MENU: // Switch to game intro menu LoadIntroGame(); if (BaseSounds::ini_set.empty() && BaseSounds::GetUsedSet()->fallback && SoundDriver::GetInstance()->HasOutput()) { - ShowErrorMessage(STR_WARNING_FALLBACK_SOUNDSET, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_WARNING_FALLBACK_SOUNDSET), {}, WL_CRITICAL); BaseSounds::ini_set = BaseSounds::GetUsedSet()->name; } if (_settings_client.network.participate_survey == PS_ASK) { diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 026aced011..7338cceade 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -393,15 +393,15 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile) bool failed = false; if (v->HasFullLoadOrder()) { /* We don't allow unbunching if the vehicle has a full load order. */ - ShowErrorMessage(STR_ERROR_CAN_T_INSERT_NEW_ORDER, STR_ERROR_UNBUNCHING_NO_UNBUNCHING_FULL_LOAD, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_INSERT_NEW_ORDER), GetEncodedString(STR_ERROR_UNBUNCHING_NO_UNBUNCHING_FULL_LOAD), WL_INFO); failed = true; } else if (v->HasUnbunchingOrder()) { /* Don't allow a new unbunching order if we already have one. */ - ShowErrorMessage(STR_ERROR_CAN_T_INSERT_NEW_ORDER, STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_INSERT_NEW_ORDER), GetEncodedString(STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED), WL_INFO); failed = true; } else if (v->HasConditionalOrder()) { /* We don't allow unbunching if the vehicle has a conditional order. */ - ShowErrorMessage(STR_ERROR_CAN_T_INSERT_NEW_ORDER, STR_ERROR_UNBUNCHING_NO_UNBUNCHING_CONDITIONAL, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_INSERT_NEW_ORDER), GetEncodedString(STR_ERROR_UNBUNCHING_NO_UNBUNCHING_CONDITIONAL), WL_INFO); failed = true; } @@ -1543,7 +1543,7 @@ public: })) { OnVehicleSelect(*begin); } else { - ShowErrorMessage(STR_ERROR_CAN_T_COPY_ORDER_LIST, STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_COPY_ORDER_LIST), GetEncodedString(STR_ERROR_CAN_T_COPY_ORDER_VEHICLE_LIST), WL_INFO); } } else { /* If CTRL is pressed: If all the vehicles in this list share orders, then copy orders */ @@ -1552,7 +1552,7 @@ public: })) { OnVehicleSelect(*begin); } else { - ShowErrorMessage(STR_ERROR_CAN_T_SHARE_ORDER_LIST, STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_SHARE_ORDER_LIST), GetEncodedString(STR_ERROR_CAN_T_SHARE_ORDER_VEHICLE_LIST), WL_INFO); } } diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 3f235a6437..72169bf6cc 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -303,8 +303,7 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R if (KillFirstBit(n) != ROAD_NONE && (n & remove) != ROAD_NONE) { /* you can remove all kind of roads with extra dynamite */ if (!_settings_game.construction.extra_dynamite) { - SetDParam(0, t->index); - return CommandCost(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS); + return CommandCostWithParam(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS, t->index); } rating_decrease = RATING_ROAD_DOWN_STEP_INNER; } @@ -2515,8 +2514,12 @@ CommandCost CmdConvertRoad(DoCommandFlags flags, TileIndex tile, TileIndex area_ } if (rtt == RTT_ROAD && owner == OWNER_TOWN) { - SetDParamsForOwnedBy(OWNER_TOWN, tile); error.MakeError(STR_ERROR_OWNED_BY); + if (IsLocalCompany()) { + auto params = GetParamsForOwnedBy(OWNER_TOWN, tile); + error.SetEncodedMessage(GetEncodedStringWithArgs(STR_ERROR_OWNED_BY, params)); + error.SetErrorOwner(owner); + } continue; } } @@ -2568,8 +2571,12 @@ CommandCost CmdConvertRoad(DoCommandFlags flags, TileIndex tile, TileIndex area_ } if (rtt == RTT_ROAD && owner == OWNER_TOWN) { - SetDParamsForOwnedBy(OWNER_TOWN, tile); error.MakeError(STR_ERROR_OWNED_BY); + if (IsLocalCompany()) { + auto params = GetParamsForOwnedBy(OWNER_TOWN, tile); + error.SetEncodedMessage(GetEncodedStringWithArgs(STR_ERROR_OWNED_BY, params)); + error.SetErrorOwner(owner); + } continue; } } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 63cf1e4150..2eb538fc42 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -22,6 +22,8 @@ #include "../clear_map.h" #include "../vehicle_func.h" #include "../string_func.h" +#include "../strings_func.h" +#include "../window_func.h" #include "../roadveh.h" #include "../roadveh_cmd.h" #include "../train.h" @@ -70,7 +72,6 @@ #include #include "../safeguards.h" -#include "../window_func.h" extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY); @@ -722,8 +723,8 @@ bool AfterLoadGame() } switch (gcf_res) { - case GLC_COMPATIBLE: ShowErrorMessage(STR_NEWGRF_COMPATIBLE_LOAD_WARNING, INVALID_STRING_ID, WL_CRITICAL); break; - case GLC_NOT_FOUND: ShowErrorMessage(STR_NEWGRF_DISABLED_WARNING, INVALID_STRING_ID, WL_CRITICAL); _pause_mode = PauseMode::Error; break; + case GLC_COMPATIBLE: ShowErrorMessage(GetEncodedString(STR_NEWGRF_COMPATIBLE_LOAD_WARNING), {}, WL_CRITICAL); break; + case GLC_NOT_FOUND: ShowErrorMessage(GetEncodedString(STR_NEWGRF_DISABLED_WARNING), {}, WL_CRITICAL); _pause_mode = PauseMode::Error; break; default: break; } @@ -1952,7 +1953,7 @@ bool AfterLoadGame() * There would be trams without tram track under causing crashes sooner or later. */ for (RoadVehicle *v : RoadVehicle::Iterate()) { if (v->First() == v && EngInfo(v->engine_type)->misc_flags.Test(EngineMiscFlag::RoadIsTram)) { - ShowErrorMessage(STR_WARNING_LOADGAME_REMOVED_TRAMS, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_WARNING_LOADGAME_REMOVED_TRAMS), {}, WL_CRITICAL); delete v; } } diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 7cd4d987c5..b40a47ef0a 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -42,6 +42,7 @@ #include "../string_func.h" #include "../fios.h" #include "../error.h" +#include "../strings_type.h" #include "../newgrf_railtype.h" #include "../newgrf_roadtype.h" @@ -2827,8 +2828,10 @@ static std::pair GetSavegameFormat(const std::s size_t processed; long level = std::stol(complevel, &processed, 10); if (processed == 0 || level != Clamp(level, slf.min_compression, slf.max_compression)) { - SetDParamStr(0, complevel); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, WL_CRITICAL); + ShowErrorMessage( + GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_LEVEL, complevel), + WL_CRITICAL); } else { return {slf, ClampTo(level)}; } @@ -2837,9 +2840,10 @@ static std::pair GetSavegameFormat(const std::s } } - SetDParamStr(0, name); - SetDParamStr(1, def.name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, WL_CRITICAL); + ShowErrorMessage( + GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_INVALID_SAVEGAME_COMPRESSION_ALGORITHM, name, def.name), + WL_CRITICAL); } return {def, def.default_compression}; } @@ -2900,16 +2904,15 @@ void SetSaveLoadError(StringID str) } /** Return the appropriate initial string for an error depending on whether we are saving or loading. */ -StringID GetSaveLoadErrorType() +EncodedString GetSaveLoadErrorType() { - return _sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED; + return GetEncodedString(_sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED); } /** Return the description of the error. **/ -StringID GetSaveLoadErrorMessage() +EncodedString GetSaveLoadErrorMessage() { - SetDParamStr(0, _sl.extra_msg); - return _sl.error_str; + return GetEncodedString(_sl.error_str, _sl.extra_msg); } /** Show a gui message when saving has failed */ @@ -2949,7 +2952,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded) * cancelled due to a client disconnecting. */ if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) { /* Skip the "colour" character */ - Debug(sl, 0, "{}", GetString(GetSaveLoadErrorType()).substr(3) + GetString(GetSaveLoadErrorMessage())); + Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString()); asfp = SaveFileError; } @@ -3197,7 +3200,7 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, /* An instance of saving is already active, so don't go saving again */ if (_sl.saveinprogress && fop == SLO_SAVE && dft == DFT_GAME_FILE && threaded) { /* if not an autosave, but a user action, show error message */ - if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR); + if (!_do_autosave) ShowErrorMessage(GetEncodedString(STR_ERROR_SAVE_STILL_IN_PROGRESS), {}, WL_ERROR); return SL_OK; } WaitTillSaved(); @@ -3271,7 +3274,7 @@ SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, ClearSaveLoadState(); /* Skip the "colour" character */ - if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetString(GetSaveLoadErrorType()).substr(3) + GetString(GetSaveLoadErrorMessage())); + if (fop != SLO_CHECK) Debug(sl, 0, "{}", GetSaveLoadErrorType().GetDecodedString().substr(3) + GetSaveLoadErrorMessage().GetDecodedString()); /* A saver/loader exception!! reinitialize all variables to prevent crash! */ return (fop == SLO_LOAD) ? SL_REINIT : SL_ERROR; @@ -3295,7 +3298,7 @@ void DoAutoOrNetsave(FiosNumberedSaveName &counter) Debug(sl, 2, "Autosaving to '{}'", filename); if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) { - ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_AUTOSAVE_FAILED), {}, WL_ERROR); } } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 246b598694..59c78f8d52 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -437,8 +437,8 @@ extern FileToSaveLoad _file_to_saveload; std::string GenerateDefaultSaveName(); void SetSaveLoadError(StringID str); -StringID GetSaveLoadErrorType(); -StringID GetSaveLoadErrorMessage(); +EncodedString GetSaveLoadErrorType(); +EncodedString GetSaveLoadErrorMessage(); SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true); void WaitTillSaved(); void ProcessAsyncSaveFinish(); diff --git a/src/screenshot.cpp b/src/screenshot.cpp index a9ffe6a2dc..4651c1be24 100644 --- a/src/screenshot.cpp +++ b/src/screenshot.cpp @@ -433,15 +433,12 @@ static bool RealMakeScreenshot(ScreenshotType t, std::string name, uint32_t widt if (ret) { if (t == SC_HEIGHTMAP) { - SetDParamStr(0, _screenshot_name); - SetDParam(1, _heightmap_highest_peak); - ShowErrorMessage(STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY, _screenshot_name, _heightmap_highest_peak), {}, WL_WARNING); } else { - SetDParamStr(0, _screenshot_name); - ShowErrorMessage(STR_MESSAGE_SCREENSHOT_SUCCESSFULLY, INVALID_STRING_ID, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_MESSAGE_SCREENSHOT_SUCCESSFULLY, _screenshot_name), {}, WL_WARNING); } } else { - ShowErrorMessage(STR_ERROR_SCREENSHOT_FAILED, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_SCREENSHOT_FAILED), {}, WL_ERROR); } return ret; diff --git a/src/script/script_gui.cpp b/src/script/script_gui.cpp index e02403c0fc..5b0988d59b 100644 --- a/src/script/script_gui.cpp +++ b/src/script/script_gui.cpp @@ -1324,7 +1324,7 @@ Window *ShowScriptDebugWindow(CompanyID show_company, bool new_window) } return new ScriptDebugWindow(_script_debug_desc, i, show_company); } else { - ShowErrorMessage(STR_ERROR_AI_DEBUG_SERVER_ONLY, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_AI_DEBUG_SERVER_ONLY), {}, WL_INFO); } return nullptr; diff --git a/src/settings.cpp b/src/settings.cpp index cf0db3dac3..013941f8fd 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -33,6 +33,7 @@ #include "console_func.h" #include "genworld.h" #include "string_func.h" +#include "strings_func.h" #include "window_func.h" #include "company_func.h" #include "rev.h" @@ -381,16 +382,15 @@ size_t IntSettingDesc::ParseValue(const char *str) const char *end; size_t val = std::strtoul(str, &end, 0); if (end == str) { - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); - msg.SetDParamStr(0, str); - msg.SetDParamStr(1, this->GetName()); - _settings_error_list.push_back(msg); + _settings_error_list.emplace_back( + GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName())); return this->GetDefaultValue(); } if (*end != '\0') { - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_TRAILING_CHARACTERS); - msg.SetDParamStr(0, this->GetName()); - _settings_error_list.push_back(msg); + _settings_error_list.emplace_back( + GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_TRAILING_CHARACTERS, this->GetName())); } return val; } @@ -403,10 +403,9 @@ size_t OneOfManySettingDesc::ParseValue(const char *str) const if (r == SIZE_MAX && this->many_cnvt != nullptr) r = this->many_cnvt(str); if (r != SIZE_MAX) return r; // and here goes converted value - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); - msg.SetDParamStr(0, str); - msg.SetDParamStr(1, this->GetName()); - _settings_error_list.push_back(msg); + _settings_error_list.emplace_back( + GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName())); return this->GetDefaultValue(); } @@ -414,10 +413,10 @@ size_t ManyOfManySettingDesc::ParseValue(const char *str) const { size_t r = LookupManyOfMany(this->many, str); if (r != SIZE_MAX) return r; - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); - msg.SetDParamStr(0, str); - msg.SetDParamStr(1, this->GetName()); - _settings_error_list.push_back(msg); + + _settings_error_list.emplace_back( + GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName())); return this->GetDefaultValue(); } @@ -426,10 +425,9 @@ size_t BoolSettingDesc::ParseValue(const char *str) const auto r = BoolSettingDesc::ParseSingleValue(str); if (r.has_value()) return *r; - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE); - msg.SetDParamStr(0, str); - msg.SetDParamStr(1, this->GetName()); - _settings_error_list.push_back(msg); + _settings_error_list.emplace_back( + GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_INVALID_VALUE, str, this->GetName())); return this->GetDefaultValue(); } @@ -690,9 +688,9 @@ void ListSettingDesc::ParseValue(const IniItem *item, void *object) const const char *str = (item == nullptr) ? this->def : item->value.has_value() ? item->value->c_str() : nullptr; void *ptr = GetVariableAddress(object, this->save); if (!LoadIntList(str, ptr, this->save.length, GetVarMemType(this->save.conv))) { - ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY); - msg.SetDParamStr(0, this->GetName()); - _settings_error_list.push_back(msg); + _settings_error_list.emplace_back( + GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_ARRAY, this->GetName())); /* Use default */ LoadIntList(this->def, ptr, this->save.length, GetVarMemType(this->save.conv)); @@ -1035,8 +1033,9 @@ static void GraphicsSetLoadConfig(IniFile &ini) if (params.has_value()) { BaseGraphics::ini_data.extra_params = params.value(); } else { - SetDParamStr(0, BaseGraphics::ini_data.name); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_ARRAY, BaseGraphics::ini_data.name), + WL_CRITICAL); } } } @@ -1101,36 +1100,39 @@ static GRFConfigList GRFLoadConfig(const IniFile &ini, const char *grpname, bool if (params.has_value()) { c->SetParams(params.value()); } else { - SetDParamStr(0, filename); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_ARRAY, filename), + WL_CRITICAL); } } /* Check if item is valid */ if (!FillGRFDetails(*c, is_static) || c->flags.Test(GRFConfigFlag::Invalid)) { + StringID reason; if (c->status == GCS_NOT_FOUND) { - SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND); + reason = STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND; } else if (c->flags.Test(GRFConfigFlag::Unsafe)) { - SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNSAFE); + reason = STR_CONFIG_ERROR_INVALID_GRF_UNSAFE; } else if (c->flags.Test(GRFConfigFlag::System)) { - SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_SYSTEM); + reason = STR_CONFIG_ERROR_INVALID_GRF_SYSTEM; } else if (c->flags.Test(GRFConfigFlag::Invalid)) { - SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE); + reason = STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE; } else { - SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN); + reason = STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN; } - SetDParamStr(0, filename.empty() ? item.name.c_str() : filename); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_GRF, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_INVALID_GRF, filename.empty() ? item.name.c_str() : filename, reason), + WL_CRITICAL); continue; } /* Check for duplicate GRFID (will also check for duplicate filenames) */ auto found = std::ranges::find_if(list, [&c](const auto &gc) { return gc->ident.grfid == c->ident.grfid; }); if (found != std::end(list)) { - SetDParamStr(0, c->filename); - SetDParamStr(1, (*found)->filename); - ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_DUPLICATE_GRFID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_CONFIG_ERROR_DUPLICATE_GRFID, c->filename, (*found)->filename), + WL_CRITICAL); continue; } @@ -1139,7 +1141,8 @@ static GRFConfigList GRFLoadConfig(const IniFile &ini, const char *grpname, bool c->flags.Set(GRFConfigFlag::Static); } else if (++num_grfs > NETWORK_MAX_GRF_COUNT) { /* Check we will not load more non-static NewGRFs than allowed. This could trigger issues for game servers. */ - ShowErrorMessage(STR_CONFIG_ERROR, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_CONFIG_ERROR), + GetEncodedString(STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED), WL_CRITICAL); break; } diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index a2a1605c1b..509f9d6308 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -717,7 +717,7 @@ struct GameOptionsWindow : Window { case WID_GO_FULLSCREEN_BUTTON: // Click fullscreen on/off /* try to toggle full-screen on/off */ if (!ToggleFullScreen(!_fullscreen)) { - ShowErrorMessage(STR_ERROR_FULLSCREEN_FAILED, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_ERROR_FULLSCREEN_FAILED), {}, WL_ERROR); } this->SetWidgetLoweredState(WID_GO_FULLSCREEN_BUTTON, _fullscreen); this->SetWidgetDirty(WID_GO_FULLSCREEN_BUTTON); @@ -725,7 +725,7 @@ struct GameOptionsWindow : Window { case WID_GO_VIDEO_ACCEL_BUTTON: _video_hw_accel = !_video_hw_accel; - ShowErrorMessage(STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_GAME_OPTIONS_VIDEO_ACCELERATION_RESTART), {}, WL_INFO); this->SetWidgetLoweredState(WID_GO_VIDEO_ACCEL_BUTTON, _video_hw_accel); this->SetWidgetDirty(WID_GO_VIDEO_ACCEL_BUTTON); #ifndef __APPLE__ @@ -880,7 +880,7 @@ struct GameOptionsWindow : Window { if (!list.empty()) { ShowDropDownList(this, std::move(list), selected, widget); } else { - if (widget == WID_GO_RESOLUTION_DROPDOWN) ShowErrorMessage(STR_ERROR_RESOLUTION_LIST_FAILED, INVALID_STRING_ID, WL_ERROR); + if (widget == WID_GO_RESOLUTION_DROPDOWN) ShowErrorMessage(GetEncodedString(STR_ERROR_RESOLUTION_LIST_FAILED), {}, WL_ERROR); } break; } @@ -936,7 +936,7 @@ struct GameOptionsWindow : Window { if (_settings_client.gui.refresh_rate > 60) { /* Show warning to the user that this refresh rate might not be suitable on * larger maps with many NewGRFs and vehicles. */ - ShowErrorMessage(STR_GAME_OPTIONS_REFRESH_RATE_WARNING, INVALID_STRING_ID, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_GAME_OPTIONS_REFRESH_RATE_WARNING), {}, WL_INFO); } break; } diff --git a/src/settings_table.cpp b/src/settings_table.cpp index 67be775e7d..0d560525e4 100644 --- a/src/settings_table.cpp +++ b/src/settings_table.cpp @@ -421,7 +421,7 @@ static void MaxNoAIsChange(int32_t) if (GetGameSettings().difficulty.max_no_competitors != 0 && AI::GetInfoList()->empty() && (!_networking || _network_server)) { - ShowErrorMessage(STR_WARNING_NO_SUITABLE_AI, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_WARNING_NO_SUITABLE_AI), {}, WL_CRITICAL); } InvalidateWindowClassesData(WC_GAME_OPTIONS, 0); @@ -457,39 +457,39 @@ static bool CheckFreeformEdges(int32_t &new_value) for (Ship *s : Ship::Iterate()) { /* Check if there is a ship on the northern border. */ if (TileX(s->tile) == 0 || TileY(s->tile) == 0) { - ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_EMPTY, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_EDGES_NOT_EMPTY), {}, WL_ERROR); return false; } } for (const BaseStation *st : BaseStation::Iterate()) { /* Check if there is a non-deleted buoy on the northern border. */ if (st->IsInUse() && (TileX(st->xy) == 0 || TileY(st->xy) == 0)) { - ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_EMPTY, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_EDGES_NOT_EMPTY), {}, WL_ERROR); return false; } } } else { for (uint i = 0; i < Map::MaxX(); i++) { if (TileHeight(TileXY(i, 1)) != 0) { - ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_EDGES_NOT_WATER), {}, WL_ERROR); return false; } } for (uint i = 1; i < Map::MaxX(); i++) { if (!IsTileType(TileXY(i, Map::MaxY() - 1), MP_WATER) || TileHeight(TileXY(1, Map::MaxY())) != 0) { - ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_EDGES_NOT_WATER), {}, WL_ERROR); return false; } } for (uint i = 0; i < Map::MaxY(); i++) { if (TileHeight(TileXY(1, i)) != 0) { - ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_EDGES_NOT_WATER), {}, WL_ERROR); return false; } } for (uint i = 1; i < Map::MaxY(); i++) { if (!IsTileType(TileXY(Map::MaxX() - 1, i), MP_WATER) || TileHeight(TileXY(Map::MaxX(), i)) != 0) { - ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_EDGES_NOT_WATER), {}, WL_ERROR); return false; } } @@ -527,7 +527,7 @@ static bool CheckDynamicEngines(int32_t &) if (_game_mode == GM_MENU) return true; if (!EngineOverrideManager::ResetToCurrentNewGRFConfig()) { - ShowErrorMessage(STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES), {}, WL_ERROR); return false; } @@ -543,7 +543,7 @@ static bool CheckMaxHeightLevel(int32_t &new_value) * If yes, disallow the change. */ for (const auto t : Map::Iterate()) { if ((int32_t)TileHeight(t) > new_value) { - ShowErrorMessage(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN), {}, WL_ERROR); /* Return old, unchanged value */ return false; } diff --git a/src/spritecache.cpp b/src/spritecache.cpp index 6f56d4fd16..3940956681 100644 --- a/src/spritecache.cpp +++ b/src/spritecache.cpp @@ -14,6 +14,7 @@ #include "gfx_func.h" #include "error.h" #include "error_func.h" +#include "strings_func.h" #include "zoom_func.h" #include "settings_type.h" #include "blitter/factory.hpp" @@ -1028,9 +1029,7 @@ static void GfxInitSpriteCache() if (_allocated_sprite_cache_size != target_size) { Debug(misc, 0, "Not enough memory to allocate {} MiB of spritecache. Spritecache was reduced to {} MiB.", target_size / 1024 / 1024, _allocated_sprite_cache_size / 1024 / 1024); - ErrorMessageData msg(STR_CONFIG_ERROR_OUT_OF_MEMORY, STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG); - msg.SetDParam(0, target_size); - msg.SetDParam(1, _allocated_sprite_cache_size); + ErrorMessageData msg(GetEncodedString(STR_CONFIG_ERROR_OUT_OF_MEMORY), GetEncodedString(STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG, target_size, _allocated_sprite_cache_size)); ScheduleErrorMessage(msg); } } diff --git a/src/spriteloader/grf.cpp b/src/spriteloader/grf.cpp index 64f93a4373..e2d4bdedc9 100644 --- a/src/spriteloader/grf.cpp +++ b/src/spriteloader/grf.cpp @@ -36,8 +36,7 @@ static bool WarnCorruptSprite(const SpriteFile &file, size_t file_pos, int line) { static uint8_t warning_level = 0; if (warning_level == 0) { - SetDParamStr(0, file.GetSimplifiedFilename()); - ShowErrorMessage(STR_NEWGRF_ERROR_CORRUPT_SPRITE, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_NEWGRF_ERROR_CORRUPT_SPRITE, file.GetSimplifiedFilename()), {}, WL_ERROR); } Debug(sprite, warning_level, "[{}] Loading corrupted sprite from {} at position {}", line, file.GetSimplifiedFilename(), file_pos); warning_level = 6; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index d0f1635cbb..c0c3eae90d 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2580,8 +2580,7 @@ CommandCost CmdBuildAirport(DoCommandFlags flags, TileIndex tile, uint8_t airpor } if (authority_refuse_message != STR_NULL) { - SetDParam(0, authority_refuse_town->index); - return CommandCost(authority_refuse_message); + return CommandCostWithParam(authority_refuse_message, authority_refuse_town->index); } Station *st = nullptr; @@ -4709,8 +4708,7 @@ CommandCost ClearTile_Station(TileIndex tile, DoCommandFlags flags) case StationType::Buoy: return CommandCost(STR_ERROR_BUOY_IN_THE_WAY); case StationType::Dock: return CommandCost(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST); case StationType::Oilrig: - SetDParam(1, STR_INDUSTRY_NAME_OIL_RIG); - return CommandCost(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY); + return CommandCostWithParam(STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_INDUSTRY_NAME_OIL_RIG); } } diff --git a/src/strings.cpp b/src/strings.cpp index 74b96a804c..d8e9078fd5 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -2404,8 +2404,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) * with the colour marker. */ static std::string err_str("XXXThe current font is missing some of the characters used in the texts for this language. Using system fallback font instead."); Utf8Encode(err_str.data(), SCC_YELLOW); - SetDParamStr(0, err_str); - ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_WARNING); } if (bad_font && base_font) { @@ -2425,8 +2424,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) * the string, which takes exactly three characters, so it replaces the "XXX" with the colour marker. */ static std::string err_str("XXXThe current font is missing some of the characters used in the texts for this language. Go to Help & Manuals > Fonts, or read the file docs/fonts.md in your OpenTTD directory, to see how to solve this."); Utf8Encode(err_str.data(), SCC_YELLOW); - SetDParamStr(0, err_str); - ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_WARNING); + ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_WARNING); /* Reset the font width */ LoadStringWidthTable(searcher->Monospace()); @@ -2453,8 +2451,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) if (_current_text_dir != TD_LTR) { static std::string err_str("XXXThis version of OpenTTD does not support right-to-left languages. Recompile with ICU + Harfbuzz enabled."); Utf8Encode(err_str.data(), SCC_YELLOW); - SetDParamStr(0, err_str); - ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR); + ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, err_str), {}, WL_ERROR); } #endif /* !(WITH_ICU_I18N && WITH_HARFBUZZ) && !WITH_UNISCRIBE && !WITH_COCOA */ } diff --git a/src/strings_func.h b/src/strings_func.h index 307d57e7bc..16c6a8042d 100644 --- a/src/strings_func.h +++ b/src/strings_func.h @@ -138,6 +138,18 @@ std::string GetString(StringID string, Args &&... args) EncodedString GetEncodedString(StringID str); EncodedString GetEncodedStringWithArgs(StringID str, std::span params); +/** + * Encode a string with no parameters into an encoded string, if the string id is valid. + * @note the return encoded string will be empty if the string id is not valid. + * @param str String to encode. + * @returns an EncodedString. + */ +static inline EncodedString GetEncodedStringIfValid(StringID str) +{ + if (str == INVALID_STRING_ID) return {}; + return GetEncodedString(str); +} + /** * Get an encoded string with parameters. * @param string String ID to encode. diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index c3278a3b93..52a983a432 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -731,13 +731,11 @@ static CommandCost ClearTile_Town(TileIndex tile, DoCommandFlags flags) if (!_cheats.magic_bulldozer.value && !flags.Test(DoCommandFlag::NoTestTownRating)) { /* NewGRFs can add indestructible houses. */ if (rating > RATING_MAXIMUM) { - SetDParam(0, t->index); return CommandCost(CMD_ERROR); } /* If town authority controls removal, check the company's rating. */ if (rating > t->ratings[_current_company] && _settings_game.difficulty.town_council_tolerance != TOWN_COUNCIL_PERMISSIVE) { - SetDParam(0, t->index); - return CommandCost(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS); + return CommandCostWithParam(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS, t->index); } } } @@ -1020,10 +1018,14 @@ bool CheckTownRoadTypes() if (min_date <= TimerGameCalendar::date) return true; if (min_date < INT32_MAX) { - SetDParam(0, min_date); - ShowErrorMessage(STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_YET, STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_YET_EXPLANATION, WL_CRITICAL); + ShowErrorMessage( + GetEncodedString(STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_YET), + GetEncodedString(STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_YET_EXPLANATION, min_date), + WL_CRITICAL); } else { - ShowErrorMessage(STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_AT_ALL, STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_AT_ALL_EXPLANATION, WL_CRITICAL); + ShowErrorMessage( + GetEncodedString(STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_AT_ALL), + GetEncodedString(STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_AT_ALL_EXPLANATION), WL_CRITICAL); } return false; } @@ -2468,7 +2470,7 @@ bool GenerateTowns(TownLayout layout) /* If there are no towns at all and we are generating new game, bail out */ if (Town::GetNumItems() == 0 && _game_mode != GM_EDITOR) { - ShowErrorMessage(STR_ERROR_COULD_NOT_CREATE_TOWN, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_ERROR_COULD_NOT_CREATE_TOWN), {}, WL_CRITICAL); } return false; // we are still without a town? we failed, simply @@ -3598,7 +3600,7 @@ static CommandCost TownActionBribe(Town *t, DoCommandFlags flags) /* only show error message to the executing player. All errors are handled command.c * but this is special, because it can only 'fail' on a DoCommandFlag::Execute */ - if (IsLocalCompany()) ShowErrorMessage(STR_ERROR_BRIBE_FAILED, INVALID_STRING_ID, WL_INFO); + if (IsLocalCompany()) ShowErrorMessage(GetEncodedString(STR_ERROR_BRIBE_FAILED), {}, WL_INFO); /* decrease by a lot! * ChangeTownRating is only for stuff in demolishing. Bribe failure should @@ -3898,8 +3900,7 @@ CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlags flag if (t->ratings[_current_company] > RATING_VERYPOOR) return CommandCost(); - SetDParam(0, t->index); - return CommandCost(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS); + return CommandCostWithParam(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS, t->index); } /** @@ -4068,8 +4069,7 @@ CommandCost CheckforTownRating(DoCommandFlags flags, Town *t, TownRatingCheckTyp int needed = needed_rating[_settings_game.difficulty.town_council_tolerance][type]; if (GetRating(t) < needed) { - SetDParam(0, t->index); - return CommandCost(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS); + return CommandCostWithParam(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS, t->index); } return CommandCost(); diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 3cf8fc5f02..d04c045304 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -1274,7 +1274,7 @@ public: Backup old_generating_world(_generating_world, true); UpdateNearestTownForRoadTiles(true); if (!GenerateTowns(this->town_layout)) { - ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_TOWN, STR_ERROR_NO_SPACE_FOR_TOWN, WL_INFO); + ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_GENERATE_TOWN), GetEncodedString(STR_ERROR_NO_SPACE_FOR_TOWN), WL_INFO); } UpdateNearestTownForRoadTiles(false); old_generating_world.Restore(); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 1bca2a99ac..8cd0e57dd5 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -84,9 +84,7 @@ void CheckTrainsLengths() if ((w->track != TRACK_BIT_DEPOT && std::max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) || (w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) { - SetDParam(0, v->index); - SetDParam(1, v->owner); - ShowErrorMessage(STR_BROKEN_VEHICLE_LENGTH, INVALID_STRING_ID, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(STR_BROKEN_VEHICLE_LENGTH, v->index, v->owner), {}, WL_CRITICAL); if (!_networking && first) { first = false; diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 27ed438e77..95f6962b1d 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -385,8 +385,7 @@ CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex t if (t == nullptr) return CMD_ERROR; if (GetBridgeSpec(bridge_type)->speed < GetBridgeSpec(GetBridgeType(tile_start))->speed) { - SetDParam(0, t->index); - return CommandCost(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS); + return CommandCostWithParam(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS, t->index); } else { ChangeTownRating(t, RATING_TUNNEL_BRIDGE_UP_STEP, RATING_MAXIMUM, flags); } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 8af11d87f8..33e372f8ec 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -324,9 +324,8 @@ void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRF if (!grfconfig->grf_bugs.Test(bug_type)) { grfconfig->grf_bugs.Set(bug_type); - SetDParamStr(0, grfconfig->GetName()); - SetDParam(1, engine); - ShowErrorMessage(part1, part2, WL_CRITICAL); + ShowErrorMessage(GetEncodedString(part1, grfconfig->GetName()), + GetEncodedString(part2, std::monostate{}, engine), WL_CRITICAL); if (!_networking) Command::Do(DoCommandFlag::Execute, critical ? PauseMode::Error : PauseMode::Normal, true); }