From 6e10584b91e09279bc38671371f75d0bc41eec9f Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 23 Feb 2025 20:24:02 +0000 Subject: [PATCH] Codechange: Use EncodedStrings for News messages. (#13654) --- src/aircraft_cmd.cpp | 17 +++---- src/autoreplace_cmd.cpp | 16 +++--- src/company_cmd.cpp | 22 ++++---- src/crashlog.cpp | 4 +- src/currency.cpp | 3 +- src/disaster_vehicle.cpp | 14 ++---- src/economy.cpp | 25 ++++------ src/engine.cpp | 7 +-- src/industry_cmd.cpp | 42 +++++++--------- src/lang/english.txt | 20 ++++---- src/news_func.h | 22 ++++---- src/news_gui.cpp | 59 ++++++++++------------ src/news_type.h | 9 ++-- src/order_cmd.cpp | 3 +- src/roadveh_cmd.cpp | 19 +++---- src/ship_cmd.cpp | 3 +- src/source_type.h | 5 ++ src/station_cmd.cpp | 4 +- src/statusbar_gui.cpp | 6 +-- src/strings.cpp | 69 ++++++++++++++++++++++++++ src/strings_type.h | 1 + src/subsidy.cpp | 102 ++++++++++++++------------------------ src/subsidy_base.h | 8 --- src/subsidy_func.h | 4 -- src/subsidy_gui.cpp | 42 ++++++++++------ src/tests/string_func.cpp | 15 ++++++ src/town_cmd.cpp | 24 +++------ src/train_cmd.cpp | 9 ++-- src/vehicle.cpp | 20 +++----- src/water_cmd.cpp | 3 +- 30 files changed, 304 insertions(+), 293 deletions(-) diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 9914e93132..c0162d4ecc 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -1335,18 +1335,17 @@ static void CrashAirplane(Aircraft *v) CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); uint victims = v->Crash(); - SetDParam(0, victims); v->cargo.Truncate(); v->Next()->cargo.Truncate(); const Station *st = GetTargetAirportIfValid(v); - StringID newsitem; TileIndex vt = TileVirtXY(v->x_pos, v->y_pos); + + EncodedString headline; if (st == nullptr) { - newsitem = STR_NEWS_PLANE_CRASH_OUT_OF_FUEL; + headline = GetEncodedString(STR_NEWS_PLANE_CRASH_OUT_OF_FUEL, victims); } else { - SetDParam(1, st->index); - newsitem = STR_NEWS_AIRCRAFT_CRASH; + headline = GetEncodedString(STR_NEWS_AIRCRAFT_CRASH, victims, st->index); } AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, vt, st == nullptr ? ScriptEventVehicleCrashed::CRASH_AIRCRAFT_NO_AIRPORT : ScriptEventVehicleCrashed::CRASH_PLANE_LANDING, victims)); @@ -1357,7 +1356,7 @@ static void CrashAirplane(Aircraft *v) newstype = NewsType::AccidentOther; } - AddTileNewsItem(newsitem, newstype, vt, st != nullptr ? st->index : StationID::Invalid()); + AddTileNewsItem(std::move(headline), newstype, vt, st != nullptr ? st->index : StationID::Invalid()); ModifyStationRatingAround(vt, v->owner, -160, 30); if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v); @@ -1408,10 +1407,9 @@ static void AircraftEntersTerminal(Aircraft *v) /* Check if station was ever visited before */ if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) { st->had_vehicle_of_type |= HVOT_AIRCRAFT; - SetDParam(0, st->index); /* show newsitem of celebrating citizens */ AddVehicleNewsItem( - STR_NEWS_FIRST_AIRCRAFT_ARRIVAL, + GetEncodedString(STR_NEWS_FIRST_AIRCRAFT_ARRIVAL, st->index), (v->owner == _local_company) ? NewsType::ArrivalCompany : NewsType::ArrivalOther, v->index, st->index @@ -2063,8 +2061,7 @@ static void AircraftHandleDestTooFar(Aircraft *v, bool too_far) AI::NewEvent(v->owner, new ScriptEventAircraftDestTooFar(v->index)); if (v->owner == _local_company) { /* Post a news message. */ - SetDParam(0, v->index); - AddVehicleAdviceNewsItem(AdviceType::AircraftDestinationTooFar, STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index); + AddVehicleAdviceNewsItem(AdviceType::AircraftDestinationTooFar, GetEncodedString(STR_NEWS_AIRCRAFT_DEST_TOO_FAR, v->index), v->index); } } return; diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 35619f36eb..e1060fcd1e 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -335,20 +335,24 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic if (!IsLocalCompany() || !flags.Test(DoCommandFlag::Execute)) return CommandCost(); VehicleID old_veh_id = (old_veh->type == VEH_TRAIN) ? Train::From(old_veh)->First()->index : old_veh->index; - SetDParam(0, old_veh_id); + EncodedString headline; int order_id = GetIncompatibleRefitOrderIdForAutoreplace(old_veh, e); if (order_id != -1) { /* Orders contained a refit order that is incompatible with the new vehicle. */ - SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT); - SetDParam(2, order_id + 1); // 1-based indexing for display + headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED, + old_veh_id, + STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT, + order_id + 1); // 1-based indexing for display } else { /* Current cargo is incompatible with the new vehicle. */ - SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO); - SetDParam(2, CargoSpec::Get(old_veh->cargo_type)->name); + headline = GetEncodedString(STR_NEWS_VEHICLE_AUTORENEW_FAILED, + old_veh_id, + STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO, + CargoSpec::Get(old_veh->cargo_type)->name); } - AddVehicleAdviceNewsItem(AdviceType::AutorenewFailed, STR_NEWS_VEHICLE_AUTORENEW_FAILED, old_veh_id); + AddVehicleAdviceNewsItem(AdviceType::AutorenewFailed, std::move(headline), old_veh_id); return CommandCost(); } diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp index c919b5f557..e13233c5ca 100644 --- a/src/company_cmd.cpp +++ b/src/company_cmd.cpp @@ -419,12 +419,10 @@ set_name:; Game::NewEvent(new ScriptEventCompanyRenamed(c->index, name)); if (c->is_ai) { - auto cni = std::make_unique(c); - SetDParam(0, STR_NEWS_COMPANY_LAUNCH_TITLE); - SetDParam(1, STR_NEWS_COMPANY_LAUNCH_DESCRIPTION); - SetDParamStr(2, cni->company_name); - SetDParam(3, t->index); - AddNewsItem(STR_MESSAGE_NEWS_FORMAT, NewsType::CompanyInfo, NewsStyle::Company, {}, c->last_build_coordinate, {}, std::move(cni)); + auto cni = std::make_unique(STR_NEWS_COMPANY_LAUNCH_TITLE, c); + EncodedString headline = GetEncodedString(STR_NEWS_COMPANY_LAUNCH_DESCRIPTION, cni->company_name, t->index); + AddNewsItem(std::move(headline), + NewsType::CompanyInfo, NewsStyle::Company, {}, c->last_build_coordinate, {}, std::move(cni)); } return; } @@ -806,7 +804,7 @@ static IntervalTimer _economy_companies_yearly({TimerGameEcono * @param c the current company. * @param other the other company (use \c nullptr if not relevant). */ -CompanyNewsInformation::CompanyNewsInformation(const Company *c, const Company *other) +CompanyNewsInformation::CompanyNewsInformation(StringID title, const Company *c, const Company *other) { this->company_name = GetString(STR_COMPANY_NAME, c->index); @@ -817,6 +815,7 @@ CompanyNewsInformation::CompanyNewsInformation(const Company *c, const Company * this->president_name = GetString(STR_PRESIDENT_NAME_MANAGER, c->index); + this->title = title; this->colour = c->colour; this->face = c->face; @@ -934,13 +933,10 @@ CommandCost CmdCompanyCtrl(DoCommandFlags flags, CompanyCtrlAction cca, CompanyI if (!flags.Test(DoCommandFlag::Execute)) return CommandCost(); - auto cni = std::make_unique(c); - /* Show the bankrupt news */ - SetDParam(0, STR_NEWS_COMPANY_BANKRUPT_TITLE); - SetDParam(1, STR_NEWS_COMPANY_BANKRUPT_DESCRIPTION); - SetDParamStr(2, cni->company_name); - AddCompanyNewsItem(STR_MESSAGE_NEWS_FORMAT, std::move(cni)); + auto cni = std::make_unique(STR_NEWS_COMPANY_BANKRUPT_TITLE, c); + EncodedString headline = GetEncodedString(STR_NEWS_COMPANY_BANKRUPT_DESCRIPTION, cni->company_name); + AddCompanyNewsItem(std::move(headline), std::move(cni)); /* Remove the company */ ChangeOwnershipOfCompanyItems(c->index, INVALID_OWNER); diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 66cd09e78c..1c365a9ae0 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -59,8 +59,8 @@ static void SurveyRecentNews(nlohmann::json &json) int i = 0; for (const auto &news : GetNews()) { TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(news.date); - json.push_back(fmt::format("({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}", - ymd.year, ymd.month + 1, ymd.day, news.string_id, news.type, + json.push_back(fmt::format("({}-{:02}-{:02}) String: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}", + ymd.year, ymd.month + 1, ymd.day, news.GetStatusText(), news.type, news.ref1.index(), SerialiseNewsReference(news.ref1), news.ref2.index(), SerialiseNewsReference(news.ref2))); if (++i > 32) break; diff --git a/src/currency.cpp b/src/currency.cpp index aae59a5c5d..1e8389c2c4 100644 --- a/src/currency.cpp +++ b/src/currency.cpp @@ -14,6 +14,7 @@ #include "news_func.h" #include "settings_type.h" #include "string_type.h" +#include "strings_func.h" #include "timer/timer.h" #include "timer/timer_game_calendar.h" @@ -148,7 +149,7 @@ static IntervalTimer _check_switch_to_euro({TimerGameCalendar _currency_specs[_settings_game.locale.currency].to_euro != CF_ISEURO && TimerGameCalendar::year >= _currency_specs[_settings_game.locale.currency].to_euro) { _settings_game.locale.currency = 2; // this is the index of euro above. - AddNewsItem(STR_NEWS_EURO_INTRODUCTION, NewsType::Economy, NewsStyle::Normal, {}); + AddNewsItem(GetEncodedString(STR_NEWS_EURO_INTRODUCTION), NewsType::Economy, NewsStyle::Normal, {}); } }); diff --git a/src/disaster_vehicle.cpp b/src/disaster_vehicle.cpp index 6701364a6a..c8506c3672 100644 --- a/src/disaster_vehicle.cpp +++ b/src/disaster_vehicle.cpp @@ -245,8 +245,7 @@ static bool DisasterTick_Zeppeliner(DisasterVehicle *v) v->state = 1; v->age = CalendarTime::MIN_DATE; - SetDParam(0, GetStationIndex(v->tile)); - AddTileNewsItem(STR_NEWS_DISASTER_ZEPPELIN, NewsType::Accident, v->tile); + AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_ZEPPELIN, GetStationIndex(v->tile)), NewsType::Accident, v->tile); AI::NewEvent(GetTileOwner(v->tile), new ScriptEventDisasterZeppelinerCrashed(GetStationIndex(v->tile))); } } @@ -387,7 +386,7 @@ static bool DisasterTick_Ufo(DisasterVehicle *v) uint victims = u->Crash(); u->disaster_vehicle = VehicleID::Invalid(); - AddTileNewsItem(STR_NEWS_DISASTER_SMALL_UFO, NewsType::Accident, u->tile); + AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_SMALL_UFO), NewsType::Accident, u->tile); AI::NewEvent(u->owner, new ScriptEventVehicleCrashed(u->index, u->tile, ScriptEventVehicleCrashed::CRASH_RV_UFO, victims)); Game::NewEvent(new ScriptEventVehicleCrashed(u->index, u->tile, ScriptEventVehicleCrashed::CRASH_RV_UFO, victims)); @@ -465,8 +464,7 @@ static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16_t image_override, b Industry *i = Industry::Get(v->dest_tile.base()); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid DestructIndustry(i); - SetDParam(0, i->town->index); - AddIndustryNewsItem(news_message, NewsType::Accident, i->index); + AddIndustryNewsItem(GetEncodedString(news_message, i->town->index), NewsType::Accident, i->index); if (_settings_client.sound.disaster) SndPlayTileFx(SND_12_EXPLOSION, i->location.tile); } } else if (v->state == 0) { @@ -561,8 +559,7 @@ static bool DisasterTick_Big_Ufo(DisasterVehicle *v) } Town *t = ClosestTownFromTile(v->dest_tile, UINT_MAX); - SetDParam(0, t->index); - AddTileNewsItem(STR_NEWS_DISASTER_BIG_UFO, NewsType::Accident, v->tile); + AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_BIG_UFO, t->index), NewsType::Accident, v->tile); if (!Vehicle::CanAllocateItem(2)) { delete v; @@ -887,8 +884,7 @@ static void Disaster_CoalMine_Init() for (m = 0; m < 15; m++) { for (const Industry *i : Industry::Iterate()) { if (GetIndustrySpec(i->type)->behaviour.Test(IndustryBehaviour::CanSubsidence) && --index < 0) { - SetDParam(0, i->town->index); - AddTileNewsItem(STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE, NewsType::Accident, i->location.tile + TileDiffXY(1, 1)); // keep the news, even when the mine closes + AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE, i->town->index), NewsType::Accident, i->location.tile + TileDiffXY(1, 1)); // keep the news, even when the mine closes { TileIndex tile = i->location.tile; diff --git a/src/economy.cpp b/src/economy.cpp index 0cb6e9350d..88482c87ae 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -582,11 +582,9 @@ static void CompanyCheckBankrupt(Company *c) /* Warn about bankruptcy after 3 months */ case 4: { - auto cni = std::make_unique(c); - SetDParam(0, STR_NEWS_COMPANY_IN_TROUBLE_TITLE); - SetDParam(1, STR_NEWS_COMPANY_IN_TROUBLE_DESCRIPTION); - SetDParamStr(2, cni->company_name); - AddCompanyNewsItem(STR_MESSAGE_NEWS_FORMAT, std::move(cni)); + auto cni = std::make_unique(STR_NEWS_COMPANY_IN_TROUBLE_TITLE, c); + EncodedString headline = GetEncodedString(STR_NEWS_COMPANY_IN_TROUBLE_DESCRIPTION, cni->company_name); + AddCompanyNewsItem(std::move(headline), std::move(cni)); AI::BroadcastNewEvent(new ScriptEventCompanyInTrouble(c->index)); Game::NewEvent(new ScriptEventCompanyInTrouble(c->index)); break; @@ -861,10 +859,10 @@ static void HandleEconomyFluctuations() if (_economy.fluct == 0) { _economy.fluct = -(int)GB(Random(), 0, 2); - AddNewsItem(STR_NEWS_BEGIN_OF_RECESSION, NewsType::Economy, NewsStyle::Normal, {}); + AddNewsItem(GetEncodedString(STR_NEWS_BEGIN_OF_RECESSION), NewsType::Economy, NewsStyle::Normal, {}); } else if (_economy.fluct == -12) { _economy.fluct = GB(Random(), 0, 8) + 312; - AddNewsItem(STR_NEWS_END_OF_RECESSION, NewsType::Economy, NewsStyle::Normal, {}); + AddNewsItem(GetEncodedString(STR_NEWS_END_OF_RECESSION), NewsType::Economy, NewsStyle::Normal, {}); } } @@ -1995,14 +1993,11 @@ static void DoAcquireCompany(Company *c, bool hostile_takeover) { CompanyID ci = c->index; - auto cni = std::make_unique(c, Company::Get(_current_company)); - - SetDParam(0, STR_NEWS_COMPANY_MERGER_TITLE); - SetDParam(1, hostile_takeover ? STR_NEWS_MERGER_TAKEOVER_TITLE : STR_NEWS_COMPANY_MERGER_DESCRIPTION); - SetDParamStr(2, cni->company_name); - SetDParamStr(3, cni->other_company_name); - SetDParam(4, c->bankrupt_value); - AddCompanyNewsItem(STR_MESSAGE_NEWS_FORMAT, std::move(cni)); + auto cni = std::make_unique(STR_NEWS_COMPANY_MERGER_TITLE, c, Company::Get(_current_company)); + EncodedString headline = hostile_takeover + ? GetEncodedString(STR_NEWS_MERGER_TAKEOVER_TITLE, cni->company_name, cni->other_company_name) + : GetEncodedString(STR_NEWS_COMPANY_MERGER_DESCRIPTION, cni->company_name, cni->other_company_name, c->bankrupt_value); + AddCompanyNewsItem(std::move(headline), std::move(cni)); AI::BroadcastNewEvent(new ScriptEventCompanyMerger(ci, _current_company)); Game::NewEvent(new ScriptEventCompanyMerger(ci, _current_company)); diff --git a/src/engine.cpp b/src/engine.cpp index dbdf17d3b9..14e7868978 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -1129,9 +1129,10 @@ static void NewVehicleAvailable(Engine *e) /* Only provide the "New Vehicle available" news paper entry, if engine can be built. */ if (!IsVehicleTypeDisabled(e->type, false) && !e->info.extra_flags.Test(ExtraEngineFlag::NoNews)) { - SetDParam(0, GetEngineCategoryName(index)); - SetDParam(1, PackEngineNameDParam(index, EngineNameContext::PreviewNews)); - AddNewsItem(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NewsType::NewVehicles, NewsStyle::Vehicle, {}, index); + AddNewsItem(GetEncodedString(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, + GetEngineCategoryName(index), + PackEngineNameDParam(index, EngineNameContext::PreviewNews)), + NewsType::NewVehicles, NewsStyle::Vehicle, {}, index); } /* Update the toolbar. */ diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index 0714c8b7c9..f4bd261b20 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -1719,14 +1719,13 @@ static CommandCost CheckIfFarEnoughFromConflictingIndustry(TileIndex tile, Indus static void AdvertiseIndustryOpening(const Industry *ind) { const IndustrySpec *ind_spc = GetIndustrySpec(ind->type); - SetDParam(0, ind_spc->name); + EncodedString headline; if (ind_spc->new_industry_text > STR_LAST_STRINGID) { - SetDParam(1, STR_TOWN_NAME); - SetDParam(2, ind->town->index); + headline = GetEncodedString(ind_spc->new_industry_text, ind_spc->name, STR_TOWN_NAME, ind->town->index); } else { - SetDParam(1, ind->town->index); + headline = GetEncodedString(ind_spc->new_industry_text, ind_spc->name, ind->town->index); } - AddIndustryNewsItem(ind_spc->new_industry_text, NewsType::IndustryOpen, ind->index); + AddIndustryNewsItem(std::move(headline), NewsType::IndustryOpen, ind->index); AI::BroadcastNewEvent(new ScriptEventIndustryOpen(ind->index)); Game::NewEvent(new ScriptEventIndustryOpen(ind->index)); } @@ -2194,16 +2193,15 @@ CommandCost CmdIndustrySetProduction(DoCommandFlags flags, IndustryID ind_id, ui } /* Set parameters of news string */ + EncodedString headline; if (str == STR_NEWS_CUSTOM_ITEM) { - SetDParamStr(0, custom_news); + headline = GetEncodedString(str, custom_news); } else if (str > STR_LAST_STRINGID) { - SetDParam(0, STR_TOWN_NAME); - SetDParam(1, ind->town->index); - SetDParam(2, GetIndustrySpec(ind->type)->name); + headline = GetEncodedString(str, STR_TOWN_NAME, ind->town->index, GetIndustrySpec(ind->type)->name); } else { - SetDParam(0, ind->index); + headline = GetEncodedString(str, ind->index); } - AddIndustryNewsItem(str, nt, ind->index); + AddIndustryNewsItem(std::move(headline), nt, ind->index); } } @@ -2778,11 +2776,10 @@ static void ReportNewsProductionChangeIndustry(Industry *ind, CargoType type, in case 2: nt = NewsType::IndustryCompany; break; default: NOT_REACHED(); } - SetDParam(2, abs(percent)); - SetDParam(0, CargoSpec::Get(type)->name); - SetDParam(1, ind->index); AddIndustryNewsItem( - percent >= 0 ? STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_SMOOTH : STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_SMOOTH, + GetEncodedString(percent >= 0 ? STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_SMOOTH : STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_SMOOTH, + CargoSpec::Get(type)->name, ind->index, abs(percent) + ), nt, ind->index ); @@ -2985,22 +2982,19 @@ static void ChangeIndustryProduction(Industry *i, bool monthly) } } /* Set parameters of news string */ + EncodedString headline; if (str > STR_LAST_STRINGID) { - SetDParam(0, STR_TOWN_NAME); - SetDParam(1, i->town->index); - SetDParam(2, indspec->name); + headline = GetEncodedString(str, STR_TOWN_NAME, i->town->index, indspec->name); } else if (closeit) { - SetDParam(0, STR_FORMAT_INDUSTRY_NAME); - SetDParam(1, i->town->index); - SetDParam(2, indspec->name); + headline = GetEncodedString(str, STR_FORMAT_INDUSTRY_NAME, i->town->index, indspec->name); } else { - SetDParam(0, i->index); + headline = GetEncodedString(str, i->index); } /* and report the news to the user */ if (closeit) { - AddTileNewsItem(str, nt, i->location.tile + TileDiffXY(1, 1)); + AddTileNewsItem(std::move(headline), nt, i->location.tile + TileDiffXY(1, 1)); } else { - AddIndustryNewsItem(str, nt, i->index); + AddIndustryNewsItem(std::move(headline), nt, i->index); } } } diff --git a/src/lang/english.txt b/src/lang/english.txt index 1b6a54a484..ef9bbddada 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -838,7 +838,7 @@ STR_STATUSBAR_INFINITE_MONEY :{WHITE}(infinit # News message history STR_MESSAGE_HISTORY :{WHITE}Message History STR_MESSAGE_HISTORY_TOOLTIP :{BLACK}A list of the recent news messages -STR_MESSAGE_NEWS_FORMAT :{STRING} - {STRING5} +STR_MESSAGE_NEWS_FORMAT :{STRING} - {RAW_STRING} STR_NEWS_MESSAGE_CAPTION :{WHITE}Message STR_NEWS_CUSTOM_ITEM :{BIG_FONT}{BLACK}{RAW_STRING} @@ -934,14 +934,14 @@ STR_NEWS_SHOW_VEHICLE_GROUP_TOOLTIP :{BLACK}Open the STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_LIST :{WHITE}{STATION} no longer accepts: {CARGO_LIST} STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST :{WHITE}{STATION} now accepts: {CARGO_LIST} -STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIG_FONT}{BLACK}Offer of subsidy expired:{}{}{STRING} from {STRING2} to {STRING2} will now not attract a subsidy -STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIG_FONT}{BLACK}Subsidy withdrawn:{}{}{STRING} service from {STRING2} to {STRING2} is no longer subsidised -STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Service subsidy offered:{}{}First {STRING} from {STRING2} to {STRING2} will attract a {UNITS_YEARS_OR_MINUTES} subsidy from the local authority! +STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIG_FONT}{BLACK}Offer of subsidy expired:{}{}{STRING} from {STRING1} to {STRING1} will now not attract a subsidy +STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIG_FONT}{BLACK}Subsidy withdrawn:{}{}{STRING} service from {STRING1} to {STRING1} is no longer subsidised +STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Service subsidy offered:{}{}First {STRING} from {STRING1} to {STRING1} will attract a {UNITS_YEARS_OR_MINUTES} subsidy from the local authority! ###length 4 -STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay 50% extra for the next {UNITS_YEARS_OR_MINUTES}! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay double rates for the next {UNITS_YEARS_OR_MINUTES}! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay triple rates for the next {UNITS_YEARS_OR_MINUTES}! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay quadruple rates for the next {UNITS_YEARS_OR_MINUTES}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING1} to {STRING1} will pay 50% extra for the next {UNITS_YEARS_OR_MINUTES}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING1} to {STRING1} will pay double rates for the next {UNITS_YEARS_OR_MINUTES}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING1} to {STRING1} will pay triple rates for the next {UNITS_YEARS_OR_MINUTES}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING1} to {STRING1} will pay quadruple rates for the next {UNITS_YEARS_OR_MINUTES}! STR_NEWS_ROAD_REBUILDING_MONTHS :{BIG_FONT}{BLACK}Traffic chaos in {TOWN}!{}{}Road rebuilding programme funded by {RAW_STRING} brings 6 months of misery to motorists! STR_NEWS_ROAD_REBUILDING_MINUTES :{BIG_FONT}{BLACK}Traffic chaos in {TOWN}!{}{}Road rebuilding programme funded by {RAW_STRING} brings 6 minutes of misery to motorists! @@ -3784,10 +3784,10 @@ STR_GOAL_QUESTION_BUTTON_CLOSE :Close # Subsidies window STR_SUBSIDIES_CAPTION :{WHITE}Subsidies STR_SUBSIDIES_OFFERED_TITLE :{BLACK}Subsidies on offer for services taking: -STR_SUBSIDIES_OFFERED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} ({STRING1}) +STR_SUBSIDIES_OFFERED_FROM_TO :{ORANGE}{STRING} from {STRING1} to {STRING1}{YELLOW} ({STRING1}) STR_SUBSIDIES_NONE :{ORANGE}- None - STR_SUBSIDIES_SUBSIDISED_TITLE :{BLACK}Services already subsidised: -STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} ({COMPANY}{YELLOW}, {STRING1}) +STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} from {STRING1} to {STRING1}{YELLOW} ({COMPANY}{YELLOW}, {STRING1}) STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Click on service to centre main view on industry/town. Ctrl+Click to open a new viewport on industry/town location STR_SUBSIDIES_OFFERED_EXPIRY_DATE :by {DATE_SHORT} STR_SUBSIDIES_OFFERED_EXPIRY_TIME :within {UNITS_MONTHS_OR_MINUTES} diff --git a/src/news_func.h b/src/news_func.h index 79ff3ecbee..d1d5b69ac3 100644 --- a/src/news_func.h +++ b/src/news_func.h @@ -15,11 +15,11 @@ #include "station_type.h" #include "industry_type.h" -void AddNewsItem(StringID string, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1 = {}, NewsReference ref2 = {}, std::unique_ptr &&data = nullptr, AdviceType advice_type = AdviceType::Invalid); +void AddNewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1 = {}, NewsReference ref2 = {}, std::unique_ptr &&data = nullptr, AdviceType advice_type = AdviceType::Invalid); -inline void AddCompanyNewsItem(StringID string, std::unique_ptr cni) +inline void AddCompanyNewsItem(EncodedString &&headline, std::unique_ptr cni) { - AddNewsItem(string, NewsType::CompanyInfo, NewsStyle::Company, {}, {}, {}, std::move(cni)); + AddNewsItem(std::move(headline), NewsType::CompanyInfo, NewsStyle::Company, {}, {}, {}, std::move(cni)); } /** @@ -27,9 +27,9 @@ inline void AddCompanyNewsItem(StringID string, std::unique_ptrCreateNestedTree(); - /* For company news with a face we have a separate headline in param[0] */ - if (&desc == &_company_news_desc) this->GetWidget(WID_N_TITLE)->SetString(static_cast(std::get(this->ni->params[0]))); - NWidgetCore *nwid = this->GetWidget(WID_N_SHOW_GROUP); if (std::holds_alternative(ni->ref1) && nwid != nullptr) { const Vehicle *v = Vehicle::Get(std::get(ni->ref1)); @@ -446,12 +443,8 @@ struct NewsWindow : Window { break; case WID_N_MESSAGE: - CopyInDParam(this->ni->params); - str = GetString(this->ni->string_id); - break; - case WID_N_COMPANY_MSG: - str = GetString(this->GetCompanyMessageString()); + str = this->ni->headline.GetDecodedString(); break; case WID_N_VEH_NAME: @@ -499,7 +492,12 @@ struct NewsWindow : Window { void SetStringParameters(WidgetID widget) const override { - if (widget == WID_N_DATE) SetDParam(0, this->ni->date); + if (widget == WID_N_DATE) { + SetDParam(0, this->ni->date); + } else if (widget == WID_N_TITLE) { + const CompanyNewsInformation *cni = static_cast(this->ni->data.get()); + SetDParam(0, cni->title); + } } void DrawWidget(const Rect &r, WidgetID widget) const override @@ -514,8 +512,8 @@ struct NewsWindow : Window { break; case WID_N_MESSAGE: - CopyInDParam(this->ni->params); - DrawStringMultiLine(r.left, r.right, r.top, r.bottom, this->ni->string_id, TC_FROMSTRING, SA_CENTER); + case WID_N_COMPANY_MSG: + DrawStringMultiLine(r.left, r.right, r.top, r.bottom, this->ni->headline.GetDecodedString(), TC_FROMSTRING, SA_CENTER); break; case WID_N_MGR_FACE: { @@ -530,9 +528,6 @@ struct NewsWindow : Window { DrawStringMultiLine(r.left, r.right, r.top, r.bottom, STR_JUST_RAW_STRING, TC_FROMSTRING, SA_CENTER); break; } - case WID_N_COMPANY_MSG: - DrawStringMultiLine(r.left, r.right, r.top, r.bottom, this->GetCompanyMessageString(), TC_FROMSTRING, SA_CENTER); - break; case WID_N_VEH_BKGND: GfxFillRect(r.left, r.top, r.right, r.bottom, PC_GREY); @@ -671,13 +666,6 @@ private: AddDirtyBlock(this->left, mintop, this->left + this->width, maxtop + this->height); } - StringID GetCompanyMessageString() const - { - /* Company news with a face have a separate headline, so the normal message is shifted by two params */ - CopyInDParam(std::span(this->ni->params.data() + 2, this->ni->params.size() - 2)); - return std::get(this->ni->params[1]); - } - StringID GetNewVehicleMessageString(WidgetID widget) const { assert(std::holds_alternative(ni->ref1)); @@ -875,12 +863,22 @@ static std::list::iterator DeleteNewsItem(std::list::iterato * * @see NewsSubtype */ -NewsItem::NewsItem(StringID string_id, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr &&data, AdviceType advice_type) : - string_id(string_id), date(TimerGameCalendar::date), economy_date(TimerGameEconomy::date), type(type), advice_type(advice_type), style(style), flags(flags), ref1(ref1), ref2(ref2), data(std::move(data)) +NewsItem::NewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr &&data, AdviceType advice_type) : + headline(std::move(headline)), date(TimerGameCalendar::date), economy_date(TimerGameEconomy::date), type(type), advice_type(advice_type), style(style), flags(flags), ref1(ref1), ref2(ref2), data(std::move(data)) { /* show this news message in colour? */ if (TimerGameCalendar::year >= _settings_client.gui.coloured_news_year) this->flags.Set(NewsFlag::InColour); - CopyOutDParam(this->params, 10); +} + +std::string NewsItem::GetStatusText() const +{ + if (this->data != nullptr) { + /* CompanyNewsInformation is the only type of additional data used. */ + const CompanyNewsInformation &cni = *static_cast(this->data.get()); + return GetString(STR_MESSAGE_NEWS_FORMAT, cni.title, this->headline.GetDecodedString()); + } + + return this->headline.GetDecodedString(); } /** @@ -895,12 +893,12 @@ NewsItem::NewsItem(StringID string_id, NewsType type, NewsStyle style, NewsFlags * * @see NewsSubtype */ -void AddNewsItem(StringID string, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr &&data, AdviceType advice_type) +void AddNewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr &&data, AdviceType advice_type) { if (_game_mode == GM_MENU) return; /* Create new news item node */ - _news.emplace_front(string, type, style, flags, ref1, ref2, std::move(data), advice_type); + _news.emplace_front(std::move(headline), type, style, flags, ref1, ref2, std::move(data), advice_type); /* Keep the number of stored news items to a manageable number */ if (std::size(_news) > MAX_NEWS_AMOUNT) { @@ -963,8 +961,7 @@ CommandCost CmdCustomNewsItem(DoCommandFlags flags, NewsType type, CompanyID com if (company != INVALID_OWNER && company != _local_company) return CommandCost(); if (flags.Test(DoCommandFlag::Execute)) { - SetDParamStr(0, text); - AddNewsItem(STR_NEWS_CUSTOM_ITEM, type, NewsStyle::Normal, {}, reference, {}); + AddNewsItem(GetEncodedString(STR_NEWS_CUSTOM_ITEM, text), type, NewsStyle::Normal, {}, reference, {}); } return CommandCost(); @@ -1084,7 +1081,7 @@ void ChangeVehicleNews(VehicleID from_index, VehicleID to_index) for (auto &ni : _news) { ChangeObject(ni.ref1, from_index, to_index); ChangeObject(ni.ref2, from_index, to_index); - if (ni.flags.Test(NewsFlag::VehicleParam0) && std::get(ni.params[0]) == from_index) ni.params[0] = to_index.base(); + if (ni.flags.Test(NewsFlag::VehicleParam0) && IsReferenceObject(ni.ref1, to_index)) ni.headline = ni.headline.ReplaceParam(0, to_index.base()); } } @@ -1186,10 +1183,8 @@ void ShowLastNewsMessage() */ static void DrawNewsString(uint left, uint right, int y, TextColour colour, const NewsItem *ni) { - CopyInDParam(ni->params); - /* Get the string, replaces newlines with spaces and remove control codes from the string. */ - std::string message = StrMakeValid(GetString(ni->string_id), SVS_REPLACE_TAB_CR_NL_WITH_SPACE); + std::string message = StrMakeValid(ni->GetStatusText(), SVS_REPLACE_TAB_CR_NL_WITH_SPACE); /* Truncate and show string; postfixed by '...' if necessary */ DrawString(left, right, y, message, colour); diff --git a/src/news_type.h b/src/news_type.h index f43beed11b..f8a9d525cb 100644 --- a/src/news_type.h +++ b/src/news_type.h @@ -134,7 +134,7 @@ struct NewsAllocatedData { /** Information about a single item of news. */ struct NewsItem { - StringID string_id; ///< Message text + EncodedString headline; ///< Headline of news. TimerGameCalendar::Date date; ///< Calendar date to show for the news TimerGameEconomy::Date economy_date; ///< Economy date of the news item, never shown but used to calculate age NewsType type; ///< Type of the news @@ -147,9 +147,9 @@ struct NewsItem { std::unique_ptr data; ///< Custom data for the news item that will be deallocated (deleted) when the news item has reached its end. - std::vector params; ///< Parameters for string resolving. + NewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr &&data, AdviceType advice_type); - NewsItem(StringID string_id, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr &&data, AdviceType advice_type); + std::string GetStatusText() const; }; /** @@ -163,10 +163,11 @@ struct CompanyNewsInformation : NewsAllocatedData { std::string president_name; ///< The name of the president std::string other_company_name; ///< The name of the company taking over this one + StringID title; uint32_t face; ///< The face of the president Colours colour; ///< The colour related to the company - CompanyNewsInformation(const struct Company *c, const struct Company *other = nullptr); + CompanyNewsInformation(StringID title, const struct Company *c, const struct Company *other = nullptr); }; using NewsContainer = std::list; ///< Container type for storing news items. diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 4b8719ce43..32a33e4c4b 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -1756,8 +1756,7 @@ void CheckOrders(const Vehicle *v) /* We don't have a problem */ if (message == INVALID_STRING_ID) return; - SetDParam(0, v->index); - AddVehicleAdviceNewsItem(AdviceType::Order, message, v->index); + AddVehicleAdviceNewsItem(AdviceType::Order, GetEncodedString(message, v->index), v->index); } } diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 48948d74e5..91eec46aa3 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -548,15 +548,12 @@ static void RoadVehCrash(RoadVehicle *v) AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_RV_LEVEL_CROSSING, victims)); Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_RV_LEVEL_CROSSING, victims)); - SetDParam(0, victims); - StringID newsitem = (victims == 1) ? STR_NEWS_ROAD_VEHICLE_CRASH_DRIVER : STR_NEWS_ROAD_VEHICLE_CRASH; - NewsType newstype = NewsType::Accident; + EncodedString headline = (victims == 1) + ? GetEncodedString(STR_NEWS_ROAD_VEHICLE_CRASH_DRIVER) + : GetEncodedString(STR_NEWS_ROAD_VEHICLE_CRASH, victims); + NewsType newstype = v->owner == _local_company ? NewsType::Accident : NewsType::AccidentOther; - if (v->owner != _local_company) { - newstype = NewsType::AccidentOther; - } - - AddTileNewsItem(newsitem, newstype, v->tile); + AddTileNewsItem(std::move(headline), newstype, v->tile); ModifyStationRatingAround(v->tile, v->owner, -160, 22); if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v); @@ -689,9 +686,8 @@ static void RoadVehArrivesAt(const RoadVehicle *v, Station *st) /* Check if station was ever visited before */ if (!(st->had_vehicle_of_type & HVOT_BUS)) { st->had_vehicle_of_type |= HVOT_BUS; - SetDParam(0, st->index); AddVehicleNewsItem( - RoadTypeIsRoad(v->roadtype) ? STR_NEWS_FIRST_BUS_ARRIVAL : STR_NEWS_FIRST_PASSENGER_TRAM_ARRIVAL, + GetEncodedString(RoadTypeIsRoad(v->roadtype) ? STR_NEWS_FIRST_BUS_ARRIVAL : STR_NEWS_FIRST_PASSENGER_TRAM_ARRIVAL, st->index), (v->owner == _local_company) ? NewsType::ArrivalCompany : NewsType::ArrivalOther, v->index, st->index @@ -703,9 +699,8 @@ static void RoadVehArrivesAt(const RoadVehicle *v, Station *st) /* Check if station was ever visited before */ if (!(st->had_vehicle_of_type & HVOT_TRUCK)) { st->had_vehicle_of_type |= HVOT_TRUCK; - SetDParam(0, st->index); AddVehicleNewsItem( - RoadTypeIsRoad(v->roadtype) ? STR_NEWS_FIRST_TRUCK_ARRIVAL : STR_NEWS_FIRST_CARGO_TRAM_ARRIVAL, + GetEncodedString(RoadTypeIsRoad(v->roadtype) ? STR_NEWS_FIRST_TRUCK_ARRIVAL : STR_NEWS_FIRST_CARGO_TRAM_ARRIVAL, st->index), (v->owner == _local_company) ? NewsType::ArrivalCompany : NewsType::ArrivalOther, v->index, st->index diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 427374020f..04165b2000 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -471,9 +471,8 @@ static void ShipArrivesAt(const Vehicle *v, Station *st) if (!(st->had_vehicle_of_type & HVOT_SHIP)) { st->had_vehicle_of_type |= HVOT_SHIP; - SetDParam(0, st->index); AddVehicleNewsItem( - STR_NEWS_FIRST_SHIP_ARRIVAL, + GetEncodedString(STR_NEWS_FIRST_SHIP_ARRIVAL, st->index), (v->owner == _local_company) ? NewsType::ArrivalCompany : NewsType::ArrivalOther, v->index, st->index diff --git a/src/source_type.h b/src/source_type.h index f14b0fd4b3..1d75fda72a 100644 --- a/src/source_type.h +++ b/src/source_type.h @@ -12,6 +12,8 @@ #include "company_type.h" #include "industry_type.h" +#include "news_type.h" +#include "strings_type.h" #include "town_type.h" /** Types of cargo source and destination */ @@ -48,6 +50,9 @@ public: constexpr bool IsValid() const noexcept { return this->id != Source::Invalid; } auto operator<=>(const Source &source) const = default; + + NewsReference GetNewsReference() const; + StringID GetFormat() const; }; #endif /* SOURCE_TYPE_H */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 1992b89efb..28da85fc74 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -529,10 +529,8 @@ CargoTypes GetEmptyMask(const Station *st) */ static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool reject) { - SetDParam(0, st->index); - SetDParam(1, cargoes); StringID msg = reject ? STR_NEWS_STATION_NO_LONGER_ACCEPTS_CARGO_LIST : STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST; - AddNewsItem(msg, NewsType::Acceptance, NewsStyle::Small, NewsFlag::InColour, st->index); + AddNewsItem(GetEncodedString(msg, st->index, cargoes), NewsType::Acceptance, NewsStyle::Small, NewsFlag::InColour, st->index); } /** diff --git a/src/statusbar_gui.cpp b/src/statusbar_gui.cpp index 7ad81b216e..51b1e03074 100644 --- a/src/statusbar_gui.cpp +++ b/src/statusbar_gui.cpp @@ -38,10 +38,8 @@ static bool DrawScrollingStatusText(const NewsItem *ni, int scroll_pos, int left, int right, int top, int bottom) { - CopyInDParam(ni->params); - /* Replace newlines and the likes with spaces. */ - std::string message = StrMakeValid(GetString(ni->string_id), SVS_REPLACE_TAB_CR_NL_WITH_SPACE); + std::string message = StrMakeValid(ni->GetStatusText(), SVS_REPLACE_TAB_CR_NL_WITH_SPACE); DrawPixelInfo tmp_dpi; if (!FillDrawPixelInfo(&tmp_dpi, left, top, right - left, bottom)) return true; @@ -145,7 +143,7 @@ struct StatusBarWindow : Window { } else if (_pause_mode.Any()) { StringID msg = _pause_mode.Test(PauseMode::LinkGraph) ? STR_STATUSBAR_PAUSED_LINK_GRAPH : STR_STATUSBAR_PAUSED; DrawString(tr, msg, TC_FROMSTRING, SA_HOR_CENTER); - } else if (this->ticker_scroll < TICKER_STOP && GetStatusbarNews() != nullptr && GetStatusbarNews()->string_id != 0) { + } else if (this->ticker_scroll < TICKER_STOP && GetStatusbarNews() != nullptr && !GetStatusbarNews()->headline.empty()) { /* Draw the scrolling news text */ if (!DrawScrollingStatusText(GetStatusbarNews(), ScaleGUITrad(this->ticker_scroll), tr.left, tr.right, tr.top, tr.bottom)) { InvalidateWindowData(WC_STATUS_BAR, 0, SBI_NEWS_DELETED); diff --git a/src/strings.cpp b/src/strings.cpp index c6e8270979..e1178c6d96 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -146,6 +146,75 @@ EncodedString GetEncodedStringWithArgs(StringID str, std::spanempty()) return {}; + + std::vector params; + + /* We need char * for std::from_chars. Iterate the underlying data, as string's own iterators may interfere. */ + const char *p = this->string.data(); + const char *e = this->string.data() + this->string.length(); + + char32_t c = Utf8Consume(p); + if (c != SCC_ENCODED_INTERNAL) return {}; + + StringID str; + auto result = std::from_chars(p, e, str, 16); + if (result.ec != std::errc()) return {}; + if (result.ptr != e && *result.ptr != SCC_RECORD_SEPARATOR) return {}; + p = result.ptr; + + while (p != e) { + auto s = ++p; + + /* Find end of the parameter. */ + for (; p != e && *p != SCC_RECORD_SEPARATOR; ++p) {} + + if (s == p) { + /* This is an empty parameter. */ + params.emplace_back(std::monostate{}); + continue; + } + + /* Get the parameter type. */ + char32_t parameter_type; + size_t len = Utf8Decode(¶meter_type, s); + s += len; + + switch (parameter_type) { + case SCC_ENCODED_NUMERIC: { + uint64_t value; + result = std::from_chars(s, p, value, 16); + if (result.ec != std::errc() || result.ptr != p) return {}; + params.emplace_back(value); + break; + } + + case SCC_ENCODED_STRING: { + params.emplace_back(std::string(s, p)); + break; + } + + default: + /* Unknown parameter, make it blank. */ + params.emplace_back(std::monostate{}); + break; + } + } + + if (param >= std::size(params)) return {}; + params[param] = data; + return GetEncodedStringWithArgs(str, params); +} + /** * Decode the encoded string. * @returns Decoded raw string. diff --git a/src/strings_type.h b/src/strings_type.h index 567fd04e7a..8078d65dd5 100644 --- a/src/strings_type.h +++ b/src/strings_type.h @@ -102,6 +102,7 @@ public: auto operator<=>(const EncodedString &) const = default; std::string GetDecodedString() const; + EncodedString ReplaceParam(size_t param, StringParameter &&value) const; inline void clear() { this->string.clear(); } inline bool empty() const { return this->string.empty(); } diff --git a/src/subsidy.cpp b/src/subsidy.cpp index 7841b7354b..d3acb11910 100644 --- a/src/subsidy.cpp +++ b/src/subsidy.cpp @@ -36,6 +36,32 @@ SubsidyPool _subsidy_pool("Subsidy"); ///< Pool for the subsidies. INSTANTIATE_POOL_METHODS(Subsidy) +/** + * Get the NewsReference for a subsidy Source. + * @returns NewsReference. + */ +NewsReference Source::GetNewsReference() const +{ + switch (this->type) { + case SourceType::Industry: return static_cast(this->id); + case SourceType::Town: return static_cast(this->id); + default: NOT_REACHED(); + } +} + +/** + * Get the format string for a subsidy Source. + * @returns The format string. + */ +StringID Source::GetFormat() const +{ + switch (this->type) { + case SourceType::Industry: return STR_INDUSTRY_NAME; + case SourceType::Town: return STR_TOWN_NAME; + default: NOT_REACHED(); + } +} + /** * Marks subsidy as awarded, creates news and AI event * @param company awarded company @@ -50,70 +76,15 @@ void Subsidy::AwardTo(CompanyID company) std::string company_name = GetString(STR_COMPANY_NAME, company); /* Add a news item */ - std::pair references = SetupSubsidyDecodeParam(this, SubsidyDecodeParamType::NewsAwarded, 1); - - SetDParamStr(0, company_name); - AddNewsItem( - STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, - NewsType::Subsidies, NewsStyle::Normal, {}, - references.first, references.second - ); + const CargoSpec *cs = CargoSpec::Get(this->cargo_type); + EncodedString headline = GetEncodedString(STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, std::move(company_name), cs->name, this->src.GetFormat(), this->src.id, this->dst.GetFormat(), this->dst.id, _settings_game.difficulty.subsidy_duration); + AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, this->src.GetNewsReference(), this->dst.GetNewsReference()); AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index)); Game::NewEvent(new ScriptEventSubsidyAwarded(this->index)); InvalidateWindowData(WC_SUBSIDIES_LIST, 0); } -/** - * Setup the string parameters for printing the subsidy at the screen, and compute the news reference for the subsidy. - * @param s %Subsidy being printed. - * @param mode Type of subsidy news message to decide on parameter format. - * @param parameter_offset The location/index in the String DParams to start decoding the subsidy's parameters. Defaults to 0. - * @return Reference of the subsidy in the news system. - */ -std::pair SetupSubsidyDecodeParam(const Subsidy *s, SubsidyDecodeParamType mode, uint parameter_offset) -{ - NewsReference reference1{}; - NewsReference reference2{}; - - /* Always use the plural form of the cargo name - trying to decide between plural or singular causes issues for translations */ - const CargoSpec *cs = CargoSpec::Get(s->cargo_type); - SetDParam(parameter_offset, cs->name); - - switch (s->src.type) { - case SourceType::Industry: - reference1 = static_cast(s->src.id); - SetDParam(parameter_offset + 1, STR_INDUSTRY_NAME); - break; - case SourceType::Town: - reference1 = static_cast(s->src.id); - SetDParam(parameter_offset + 1, STR_TOWN_NAME); - break; - default: NOT_REACHED(); - } - SetDParam(parameter_offset + 2, s->src.id); - - switch (s->dst.type) { - case SourceType::Industry: - reference2 = static_cast(s->dst.id); - SetDParam(parameter_offset + 4, STR_INDUSTRY_NAME); - break; - case SourceType::Town: - reference2 = static_cast(s->dst.id); - SetDParam(parameter_offset + 4, STR_TOWN_NAME); - break; - default: NOT_REACHED(); - } - SetDParam(parameter_offset + 5, s->dst.id); - - /* If the subsidy is being offered or awarded, the news item mentions the subsidy duration. */ - if (mode == SubsidyDecodeParamType::NewsOffered || mode == SubsidyDecodeParamType::NewsAwarded) { - SetDParam(parameter_offset + 7, _settings_game.difficulty.subsidy_duration); - } - - return {reference1, reference2}; -} - /** * Sets a flag indicating that given town/industry is part of subsidised route. * @param source actual source @@ -203,8 +174,9 @@ void CreateSubsidy(CargoType cargo_type, Source src, Source dst) { Subsidy *s = new Subsidy(cargo_type, src, dst, SUBSIDY_OFFER_MONTHS); - std::pair references = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsOffered); - AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NewsType::Subsidies, NewsStyle::Normal, {}, references.first, references.second); + const CargoSpec *cs = CargoSpec::Get(s->cargo_type); + EncodedString headline = GetEncodedString(STR_NEWS_SERVICE_SUBSIDY_OFFERED, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id, _settings_game.difficulty.subsidy_duration); + AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference()); SetPartOfSubsidyFlag(s->src, POS_SRC); SetPartOfSubsidyFlag(s->dst, POS_DST); AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index)); @@ -457,14 +429,16 @@ static IntervalTimer _economy_subsidies_monthly({TimerGameEcon for (Subsidy *s : Subsidy::Iterate()) { if (--s->remaining == 0) { if (!s->IsAwarded()) { - std::pair references = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsWithdrawn); - AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NewsType::Subsidies, NewsStyle::Normal, {}, references.first, references.second); + const CargoSpec *cs = CargoSpec::Get(s->cargo_type); + EncodedString headline = GetEncodedString(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id); + AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference()); AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index)); } else { if (s->awarded == _local_company) { - std::pair references = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsWithdrawn); - AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NewsType::Subsidies, NewsStyle::Normal, {}, references.first, references.second); + const CargoSpec *cs = CargoSpec::Get(s->cargo_type); + EncodedString headline = GetEncodedString(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id); + AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference()); } AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index)); Game::NewEvent(new ScriptEventSubsidyExpired(s->index)); diff --git a/src/subsidy_base.h b/src/subsidy_base.h index dbf9d85b23..5bf8a4afcd 100644 --- a/src/subsidy_base.h +++ b/src/subsidy_base.h @@ -58,12 +58,4 @@ static const uint SUBSIDY_MAX_PCT_TRANSPORTED = 42; ///< Subsidy will be creat static const uint SUBSIDY_MAX_DISTANCE = 70; ///< Max. length of subsidised route (DistanceManhattan) static const uint SUBSIDY_TOWN_CARGO_RADIUS = 6; ///< Extent of a tile area around town center when scanning for town cargo acceptance and production (6 ~= min catchmement + min station / 2) -/** Types of subsidy news messages, which determine how the date is printed and whether to use singular or plural cargo names */ -enum class SubsidyDecodeParamType : uint8_t { - NewsOffered = 0, ///< News item for an offered subsidy - NewsAwarded = 1, ///< News item for an awarded subsidy - NewsWithdrawn = 2, ///< News item for a subsidy offer withdrawn, or expired subsidy - Gui = 3, ///< Subsidies listed in the Subsidy GUI -}; - #endif /* SUBSIDY_BASE_H */ diff --git a/src/subsidy_func.h b/src/subsidy_func.h index fdf121b8c0..89023b96cb 100644 --- a/src/subsidy_func.h +++ b/src/subsidy_func.h @@ -10,14 +10,10 @@ #ifndef SUBSIDY_FUNC_H #define SUBSIDY_FUNC_H -#include "core/geometry_type.hpp" #include "station_type.h" #include "company_type.h" #include "cargo_type.h" -#include "news_type.h" -#include "subsidy_base.h" -std::pair SetupSubsidyDecodeParam(const struct Subsidy *s, SubsidyDecodeParamType mode, uint parameter_offset = 0); void DeleteSubsidyWith(Source src); bool CheckSubsidised(CargoType cargo_type, CompanyID company, Source src, const Station *st); void RebuildSubsidisedSourceAndDestinationCache(); diff --git a/src/subsidy_gui.cpp b/src/subsidy_gui.cpp index e374fa0464..969dc143c6 100644 --- a/src/subsidy_gui.cpp +++ b/src/subsidy_gui.cpp @@ -174,18 +174,24 @@ struct SubsidyListWindow : Window { if (!s->IsAwarded()) { if (IsInsideMM(pos, 0, cap)) { /* Displays the two offered towns */ - SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::Gui); + const CargoSpec *cs = CargoSpec::Get(s->cargo_type); + std::string text; + /* If using wallclock units, show minutes remaining. Otherwise show the date when the subsidy ends. */ if (TimerGameEconomy::UsingWallclockUnits()) { - SetDParam(7, STR_SUBSIDIES_OFFERED_EXPIRY_TIME); - SetDParam(8, s->remaining + 1); // We get the rest of the current economy month for free, since the expiration is checked on each new month. + text = GetString(STR_SUBSIDIES_OFFERED_FROM_TO, + cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id, + STR_SUBSIDIES_OFFERED_EXPIRY_TIME, + s->remaining + 1); // We get the rest of the current economy month for free, since the expiration is checked on each new month. } else { - SetDParam(7, STR_SUBSIDIES_OFFERED_EXPIRY_DATE); - SetDParam(8, TimerGameEconomy::date - ymd.day + s->remaining * 32); + text = GetString(STR_SUBSIDIES_OFFERED_FROM_TO, + cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id, + STR_SUBSIDIES_OFFERED_EXPIRY_DATE, + TimerGameEconomy::date.base() - ymd.day + s->remaining * 32); } DrawCargoIcon(tr, pos * GetCharacterHeight(FS_NORMAL), s->cargo_type); - DrawString(sr.left, sr.right, sr.top + pos * GetCharacterHeight(FS_NORMAL), STR_SUBSIDIES_OFFERED_FROM_TO); + DrawString(sr.left, sr.right, sr.top + pos * GetCharacterHeight(FS_NORMAL), text); } pos++; num++; @@ -206,21 +212,27 @@ struct SubsidyListWindow : Window { for (const Subsidy *s : Subsidy::Iterate()) { if (s->IsAwarded()) { if (IsInsideMM(pos, 0, cap)) { - SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::Gui); - SetDParam(7, s->awarded); + const CargoSpec *cs = CargoSpec::Get(s->cargo_type); + std::string text; + /* If using wallclock units, show minutes remaining. Otherwise show the date when the subsidy ends. */ if (TimerGameEconomy::UsingWallclockUnits()) { - SetDParam(8, STR_SUBSIDIES_SUBSIDISED_EXPIRY_TIME); - SetDParam(9, s->remaining); - } - else { - SetDParam(8, STR_SUBSIDIES_SUBSIDISED_EXPIRY_DATE); - SetDParam(9, TimerGameEconomy::date - ymd.day + s->remaining * 32); + text = GetString(STR_SUBSIDIES_SUBSIDISED_FROM_TO, + cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id, + GetString(STR_COMPANY_NAME, s->awarded), + STR_SUBSIDIES_SUBSIDISED_EXPIRY_TIME, + s->remaining + 1); // We get the rest of the current economy month for free, since the expiration is checked on each new month. + } else { + text = GetString(STR_SUBSIDIES_SUBSIDISED_FROM_TO, + cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id, + GetString(STR_COMPANY_NAME, s->awarded), + STR_SUBSIDIES_SUBSIDISED_EXPIRY_DATE, + TimerGameEconomy::date.base() - ymd.day + s->remaining * 32); } /* Displays the two connected stations */ DrawCargoIcon(tr, pos * GetCharacterHeight(FS_NORMAL), s->cargo_type); - DrawString(sr.left, sr.right, sr.top + pos * GetCharacterHeight(FS_NORMAL), STR_SUBSIDIES_SUBSIDISED_FROM_TO); + DrawString(sr.left, sr.right, sr.top + pos * GetCharacterHeight(FS_NORMAL), text); } pos++; num++; diff --git a/src/tests/string_func.cpp b/src/tests/string_func.cpp index 49d9b1a5c9..93853ed795 100644 --- a/src/tests/string_func.cpp +++ b/src/tests/string_func.cpp @@ -12,8 +12,11 @@ #include "../3rdparty/catch2/catch.hpp" #include "../string_func.h" +#include "../strings_func.h" #include "../table/control_codes.h" +#include "table/strings.h" + /**** String compare/equals *****/ TEST_CASE("StrCompareIgnoreCase - std::string") @@ -473,3 +476,15 @@ TEST_CASE("FixSCCEncoded") /* Test conversion with one sub-string and two string parameters. */ CHECK(FixSCCEncodedWrapper("\uE000777:\uE0008888:\"Foo\":\"BarBaz\"", false) == Compose(SCC_ENCODED, "777", SCC_RECORD_SEPARATOR, SCC_ENCODED, "8888", SCC_RECORD_SEPARATOR, SCC_ENCODED_STRING, "Foo", SCC_RECORD_SEPARATOR, SCC_ENCODED_STRING, "BarBaz")); } + +TEST_CASE("EncodedString::ReplaceParam") +{ + /* Test that two encoded strings with different parameters are not the same. */ + EncodedString string1 = GetEncodedString(STR_NULL, "Foo", 10, "Bar"); + EncodedString string2 = GetEncodedString(STR_NULL, "Foo", 15, "Bar"); + CHECK(string1 != string2); + + /* Test that replacing parameter results in the same string. */ + EncodedString string3 = string1.ReplaceParam(1, 15); + CHECK(string2 == string3); +} diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 9b04ba493a..dc66a006f5 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -2232,15 +2232,10 @@ std::tuple CmdFoundTown(DoCommandFlags flags, TileIn assert(!random_location); if (_current_company == OWNER_DEITY) { - SetDParam(0, t->index); - AddTileNewsItem(STR_NEWS_NEW_TOWN_UNSPONSORED, NewsType::IndustryOpen, tile); + AddTileNewsItem(GetEncodedString(STR_NEWS_NEW_TOWN_UNSPONSORED, t->index), NewsType::IndustryOpen, tile); } else { std::string company_name = GetString(STR_COMPANY_NAME, _current_company); - - SetDParamStr(0, company_name); - SetDParam(1, t->index); - - AddTileNewsItem(STR_NEWS_NEW_TOWN, NewsType::IndustryOpen, tile); + AddTileNewsItem(GetEncodedString(STR_NEWS_NEW_TOWN, company_name, t->index), NewsType::IndustryOpen, tile); } AI::BroadcastNewEvent(new ScriptEventTownFounded(t->index)); Game::NewEvent(new ScriptEventTownFounded(t->index)); @@ -3411,11 +3406,8 @@ static CommandCost TownActionRoadRebuild(Town *t, DoCommandFlags flags) std::string company_name = GetString(STR_COMPANY_NAME, _current_company); - SetDParam(0, t->index); - SetDParamStr(1, company_name); - AddNewsItem( - TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_ROAD_REBUILDING_MINUTES : STR_NEWS_ROAD_REBUILDING_MONTHS, + GetEncodedString(TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_ROAD_REBUILDING_MINUTES : STR_NEWS_ROAD_REBUILDING_MONTHS, t->index, company_name), NewsType::General, NewsStyle::Normal, {}, t->index); AI::BroadcastNewEvent(new ScriptEventRoadReconstruction(_current_company, t->index)); Game::NewEvent(new ScriptEventRoadReconstruction(_current_company, t->index)); @@ -3566,12 +3558,10 @@ static CommandCost TownActionBuyRights(Town *t, DoCommandFlags flags) SetWindowClassesDirty(WC_STATION_VIEW); /* Spawn news message */ - auto cni = std::make_unique(Company::Get(_current_company)); - SetDParam(0, STR_NEWS_EXCLUSIVE_RIGHTS_TITLE); - SetDParam(1, TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION_MINUTES : STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION_MONTHS); - SetDParam(2, t->index); - SetDParamStr(3, cni->company_name); - AddNewsItem(STR_MESSAGE_NEWS_FORMAT, NewsType::General, NewsStyle::Company, {}, t->index, {}, std::move(cni)); + auto cni = std::make_unique(STR_NEWS_EXCLUSIVE_RIGHTS_TITLE, Company::Get(_current_company)); + EncodedString message = GetEncodedString(TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION_MINUTES : STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION_MONTHS, t->index, cni->company_name); + AddNewsItem(std::move(message), + NewsType::General, NewsStyle::Company, {}, t->index, {}, std::move(cni)); AI::BroadcastNewEvent(new ScriptEventExclusiveTransportRights(_current_company, t->index)); Game::NewEvent(new ScriptEventExclusiveTransportRights(_current_company, t->index)); } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index ed17b297c7..a6b64967c2 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -3006,9 +3006,8 @@ static void TrainEnterStation(Train *v, StationID station) Station *st = Station::Get(station); if (!(st->had_vehicle_of_type & HVOT_TRAIN)) { st->had_vehicle_of_type |= HVOT_TRAIN; - SetDParam(0, st->index); AddVehicleNewsItem( - STR_NEWS_FIRST_TRAIN_ARRIVAL, + GetEncodedString(STR_NEWS_FIRST_TRAIN_ARRIVAL, st->index), v->owner == _local_company ? NewsType::ArrivalCompany : NewsType::ArrivalOther, v->index, st->index @@ -3243,8 +3242,7 @@ static bool CheckTrainCollision(Train *v) /* any dead -> no crash */ if (tcc.num == 0) return false; - SetDParam(0, tcc.num); - AddTileNewsItem(STR_NEWS_TRAIN_CRASH, NewsType::Accident, v->tile); + AddTileNewsItem(GetEncodedString(STR_NEWS_TRAIN_CRASH, tcc.num), NewsType::Accident, v->tile); ModifyStationRatingAround(v->tile, v->owner, -160, 30); if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_13_TRAIN_COLLISION, v); @@ -4006,8 +4004,7 @@ static bool TrainLocoHandler(Train *v, bool mode) if (HasBit(v->flags, VRF_TRAIN_STUCK) && v->wait_counter > 2 * _settings_game.pf.wait_for_pbs_path * Ticks::DAY_TICKS) { /* Show message to player. */ if (_settings_client.gui.lost_vehicle_warn && v->owner == _local_company) { - SetDParam(0, v->index); - AddVehicleAdviceNewsItem(AdviceType::TrainStuck, STR_NEWS_TRAIN_IS_STUCK, v->index); + AddVehicleAdviceNewsItem(AdviceType::TrainStuck, GetEncodedString(STR_NEWS_TRAIN_IS_STUCK, v->index), v->index); } v->wait_counter = 0; } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 564bd116b2..1d3d892620 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -814,8 +814,7 @@ void Vehicle::HandlePathfindingResult(bool path_found) /* Notify user about the event. */ AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index)); if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) { - SetDParam(0, this->index); - AddVehicleAdviceNewsItem(AdviceType::VehicleLost, STR_NEWS_VEHICLE_IS_LOST, this->index); + AddVehicleAdviceNewsItem(AdviceType::VehicleLost, GetEncodedString(STR_NEWS_VEHICLE_IS_LOST, this->index), this->index); } } @@ -1106,9 +1105,7 @@ void CallVehicleTicks() message = STR_NEWS_VEHICLE_AUTORENEW_FAILED; } - SetDParam(0, v->index); - SetDParam(1, error_message); - AddVehicleAdviceNewsItem(AdviceType::AutorenewFailed, message, v->index); + AddVehicleAdviceNewsItem(AdviceType::AutorenewFailed, GetEncodedString(message, v->index, error_message), v->index); } cur_company.Restore(); @@ -1474,8 +1471,7 @@ void AgeVehicle(Vehicle *v) return; } - SetDParam(0, v->index); - AddVehicleAdviceNewsItem(AdviceType::VehicleOld, str, v->index); + AddVehicleAdviceNewsItem(AdviceType::VehicleOld, GetEncodedString(str, v->index), v->index); } /** @@ -1630,8 +1626,7 @@ void VehicleEnterDepot(Vehicle *v) _vehicles_to_autoreplace[v->index] = false; if (v->owner == _local_company) { /* Notify the user that we stopped the vehicle */ - SetDParam(0, v->index); - AddVehicleAdviceNewsItem(AdviceType::RefitFailed, STR_NEWS_ORDER_REFIT_FAILED, v->index); + AddVehicleAdviceNewsItem(AdviceType::RefitFailed, GetEncodedString(STR_NEWS_ORDER_REFIT_FAILED, v->index), v->index); } } else if (cost.GetCost() != 0) { v->profit_this_year -= cost.GetCost() << 8; @@ -1660,8 +1655,7 @@ void VehicleEnterDepot(Vehicle *v) /* Announce that the vehicle is waiting to players and AIs. */ if (v->owner == _local_company) { - SetDParam(0, v->index); - AddVehicleAdviceNewsItem(AdviceType::VehicleWaiting, STR_NEWS_TRAIN_IS_WAITING + v->type, v->index); + AddVehicleAdviceNewsItem(AdviceType::VehicleWaiting, GetEncodedString(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index), v->index); } AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index)); } @@ -3022,10 +3016,8 @@ static IntervalTimer _economy_vehicles_yearly({TimerGameEconom Money profit = v->GetDisplayProfitThisYear(); if (v->economy_age >= VEHICLE_PROFIT_MIN_AGE && profit < 0) { if (_settings_client.gui.vehicle_income_warn && v->owner == _local_company) { - SetDParam(0, v->index); - SetDParam(1, profit); AddVehicleAdviceNewsItem(AdviceType::VehicleUnprofitable, - TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_VEHICLE_UNPROFITABLE_PERIOD : STR_NEWS_VEHICLE_UNPROFITABLE_YEAR, + GetEncodedString(TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_VEHICLE_UNPROFITABLE_PERIOD : STR_NEWS_VEHICLE_UNPROFITABLE_YEAR, v->index, profit), v->index); } AI::NewEvent(v->owner, new ScriptEventVehicleUnprofitable(v->index)); diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp index 9e9ba73022..2e658d7c7d 100644 --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -1007,8 +1007,7 @@ static void FloodVehicle(Vehicle *v) AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED, victims)); Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED, victims)); - SetDParam(0, victims); - AddTileNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NewsType::Accident, v->tile); + AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_FLOOD_VEHICLE, victims), NewsType::Accident, v->tile); CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v); }