1
0
Fork 0

Codechange: use std::variant instead of a custom version for news references

pull/13579/head
Rubidium 2025-01-19 18:31:28 +01:00 committed by rubidium42
parent 2cb59b1856
commit 380e7b48ce
14 changed files with 175 additions and 137 deletions

View File

@ -1357,7 +1357,7 @@ static void CrashAirplane(Aircraft *v)
newstype = NewsType::AccidentOther;
}
AddTileNewsItem(newsitem, newstype, vt, nullptr, st != nullptr ? st->index : INVALID_STATION);
AddTileNewsItem(newsitem, newstype, vt, st != nullptr ? st->index : INVALID_STATION);
ModifyStationRatingAround(vt, v->owner, -160, 30);
if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);

View File

@ -425,7 +425,7 @@ set_name:;
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, {}, NewsReferenceType::Tile, c->last_build_coordinate.base(), NewsReferenceType::None, UINT32_MAX, std::move(cni));
AddNewsItem(STR_MESSAGE_NEWS_FORMAT, NewsType::CompanyInfo, NewsStyle::Company, {}, c->last_build_coordinate, {}, std::move(cni));
}
return;
}

View File

@ -18,6 +18,7 @@
#include "saveload/saveload.h"
#include "screenshot.h"
#include "network/network_survey.h"
#include "news_func.h"
#include "news_gui.h"
#include "fileio_func.h"
#include "fileio_type.h"
@ -60,7 +61,8 @@ static void SurveyRecentNews(nlohmann::json &json)
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,
news.reftype1, news.ref1, news.reftype2, news.ref2));
news.ref1.index(), SerialiseNewsReference(news.ref1),
news.ref2.index(), SerialiseNewsReference(news.ref2)));
if (++i > 32) break;
}
}

View File

@ -1131,7 +1131,7 @@ static void NewVehicleAvailable(Engine *e)
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, {}, NewsReferenceType::Engine, index);
AddNewsItem(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NewsType::NewVehicles, NewsStyle::Vehicle, {}, index);
}
/* Update the toolbar. */

View File

@ -10,11 +10,11 @@
#ifndef NEWS_CMD_H
#define NEWS_CMD_H
#include "command_type.h"
#include "command_func.h"
#include "company_type.h"
#include "news_type.h"
#include "news_func.h"
CommandCost CmdCustomNewsItem(DoCommandFlags flags, NewsType type, NewsReferenceType reftype1, CompanyID company, uint32_t reference, const std::string &text);
CommandCost CmdCustomNewsItem(DoCommandFlags flags, NewsType type, CompanyID company, NewsReference reference, const std::string &text);
DEF_CMD_TRAIT(CMD_CUSTOM_NEWS_ITEM, CmdCustomNewsItem, CommandFlags({CommandFlag::StrCtrl, CommandFlag::Deity}), CMDT_OTHER_MANAGEMENT)

View File

@ -15,11 +15,11 @@
#include "station_type.h"
#include "industry_type.h"
void AddNewsItem(StringID string, NewsType type, NewsStyle style, NewsFlags flags, NewsReferenceType reftype1 = NewsReferenceType::None, uint32_t ref1 = UINT32_MAX, NewsReferenceType reftype2 = NewsReferenceType::None, uint32_t ref2 = UINT32_MAX, std::unique_ptr<NewsAllocatedData> &&data = nullptr, AdviceType advice_type = AdviceType::Invalid);
void AddNewsItem(StringID string, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1 = {}, NewsReference ref2 = {}, std::unique_ptr<NewsAllocatedData> &&data = nullptr, AdviceType advice_type = AdviceType::Invalid);
inline void AddCompanyNewsItem(StringID string, std::unique_ptr<CompanyNewsInformation> cni)
{
AddNewsItem(string, NewsType::CompanyInfo, NewsStyle::Company, {}, NewsReferenceType::None, UINT32_MAX, NewsReferenceType::None, UINT32_MAX, std::move(cni));
AddNewsItem(string, NewsType::CompanyInfo, NewsStyle::Company, {}, {}, {}, std::move(cni));
}
/**
@ -29,7 +29,7 @@ inline void AddCompanyNewsItem(StringID string, std::unique_ptr<CompanyNewsInfor
*/
inline void AddVehicleNewsItem(StringID string, NewsType type, VehicleID vehicle, StationID station = INVALID_STATION)
{
AddNewsItem(string, type, NewsStyle::Thin, {NewsFlag::NoTransparency, NewsFlag::Shaded}, NewsReferenceType::Vehicle, vehicle, station == INVALID_STATION ? NewsReferenceType::None : NewsReferenceType::Station, station);
AddNewsItem(string, type, NewsStyle::Thin, {NewsFlag::NoTransparency, NewsFlag::Shaded}, vehicle, station == INVALID_STATION ? NewsReference{} : station);
}
/**
@ -39,17 +39,17 @@ inline void AddVehicleNewsItem(StringID string, NewsType type, VehicleID vehicle
*/
inline void AddVehicleAdviceNewsItem(AdviceType advice_type, StringID string, VehicleID vehicle)
{
AddNewsItem(string, NewsType::Advice, NewsStyle::Small, {NewsFlag::InColour, NewsFlag::VehicleParam0}, NewsReferenceType::Vehicle, vehicle, NewsReferenceType::None, {}, nullptr, advice_type);
AddNewsItem(string, NewsType::Advice, NewsStyle::Small, {NewsFlag::InColour, NewsFlag::VehicleParam0}, vehicle, {}, nullptr, advice_type);
}
inline void AddTileNewsItem(StringID string, NewsType type, TileIndex tile, std::unique_ptr<NewsAllocatedData> &&data = nullptr, StationID station = INVALID_STATION)
inline void AddTileNewsItem(StringID string, NewsType type, TileIndex tile, StationID station = INVALID_STATION)
{
AddNewsItem(string, type, NewsStyle::Thin, {NewsFlag::NoTransparency, NewsFlag::Shaded}, NewsReferenceType::Tile, tile.base(), station == INVALID_STATION ? NewsReferenceType::None : NewsReferenceType::Station, station, std::move(data));
AddNewsItem(string, type, NewsStyle::Thin, {NewsFlag::NoTransparency, NewsFlag::Shaded}, tile, station == INVALID_STATION ? NewsReference{} : station);
}
inline void AddIndustryNewsItem(StringID string, NewsType type, IndustryID industry, std::unique_ptr<NewsAllocatedData> &&data = nullptr)
inline void AddIndustryNewsItem(StringID string, NewsType type, IndustryID industry)
{
AddNewsItem(string, type, NewsStyle::Thin, {NewsFlag::NoTransparency, NewsFlag::Shaded}, NewsReferenceType::Industry, industry, NewsReferenceType::None, UINT32_MAX, std::move(data));
AddNewsItem(string, type, NewsStyle::Thin, {NewsFlag::NoTransparency, NewsFlag::Shaded}, industry, {});
}
void NewsLoop();
@ -62,4 +62,6 @@ void DeleteVehicleNews(VehicleID vid, AdviceType advice_type = AdviceType::Inval
void DeleteStationNews(StationID sid);
void DeleteIndustryNews(IndustryID iid);
uint32_t SerialiseNewsReference(const NewsReference &reference);
#endif /* NEWS_FUNC_H */

View File

@ -83,19 +83,22 @@ const NewsContainer &GetNews()
/**
* Get the position a news-reference is referencing.
* @param reftype The type of reference.
* @param ref The reference.
* @param reference The reference.
* @return A tile for the referenced object, or INVALID_TILE if none.
*/
static TileIndex GetReferenceTile(NewsReferenceType reftype, uint32_t ref)
static TileIndex GetReferenceTile(const NewsReference &reference)
{
switch (reftype) {
case NewsReferenceType::Tile: return (TileIndex)ref;
case NewsReferenceType::Station: return Station::Get((StationID)ref)->xy;
case NewsReferenceType::Industry: return Industry::Get((IndustryID)ref)->location.tile + TileDiffXY(1, 1);
case NewsReferenceType::Town: return Town::Get((TownID)ref)->xy;
default: return INVALID_TILE;
}
struct visitor {
TileIndex operator()(const std::monostate &) { return INVALID_TILE; }
TileIndex operator()(const TileIndex &t) { return t; }
TileIndex operator()(const VehicleID) { return INVALID_TILE; }
TileIndex operator()(const StationID s) { return Station::Get(s)->xy; }
TileIndex operator()(const IndustryID i) { return Industry::Get(i)->location.tile + TileDiffXY(1, 1); }
TileIndex operator()(const TownID t) { return Town::Get(t)->xy; }
TileIndex operator()(const EngineID) { return INVALID_TILE; }
};
return std::visit(visitor{}, reference);
}
/* Normal news items. */
@ -366,8 +369,8 @@ struct NewsWindow : Window {
if (&desc == &_company_news_desc) this->GetWidget<NWidgetCore>(WID_N_TITLE)->SetString(static_cast<StringID>(std::get<uint64_t>(this->ni->params[0])));
NWidgetCore *nwid = this->GetWidget<NWidgetCore>(WID_N_SHOW_GROUP);
if (ni->reftype1 == NewsReferenceType::Vehicle && nwid != nullptr) {
const Vehicle *v = Vehicle::Get(ni->ref1);
if (std::holds_alternative<VehicleID>(ni->ref1) && nwid != nullptr) {
const Vehicle *v = Vehicle::Get(std::get<VehicleID>(ni->ref1));
switch (v->type) {
case VEH_TRAIN:
nwid->SetString(STR_TRAIN);
@ -391,10 +394,10 @@ struct NewsWindow : Window {
/* Initialize viewport if it exists. */
NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_N_VIEWPORT);
if (nvp != nullptr) {
if (ni->reftype1 == NewsReferenceType::Vehicle) {
nvp->InitializeViewport(this, static_cast<VehicleID>(ni->ref1), ScaleZoomGUI(ZOOM_LVL_NEWS));
if (std::holds_alternative<VehicleID>(ni->ref1)) {
nvp->InitializeViewport(this, std::get<VehicleID>(ni->ref1), ScaleZoomGUI(ZOOM_LVL_NEWS));
} else {
nvp->InitializeViewport(this, GetReferenceTile(ni->reftype1, ni->ref1), ScaleZoomGUI(ZOOM_LVL_NEWS));
nvp->InitializeViewport(this, GetReferenceTile(ni->ref1), ScaleZoomGUI(ZOOM_LVL_NEWS));
}
if (this->ni->flags.Test(NewsFlag::NoTransparency)) nvp->disp_flags.Set(NWidgetDisplayFlag::NoTransparency);
if (!this->ni->flags.Test(NewsFlag::InColour)) {
@ -457,14 +460,14 @@ struct NewsWindow : Window {
break;
case WID_N_VEH_INFO: {
assert(this->ni->reftype1 == NewsReferenceType::Engine);
EngineID engine = static_cast<EngineID>(this->ni->ref1);
assert(std::holds_alternative<EngineID>(ni->ref1));
EngineID engine = std::get<EngineID>(this->ni->ref1);
str = GetEngineInfoString(engine);
break;
}
case WID_N_SHOW_GROUP:
if (this->ni->reftype1 == NewsReferenceType::Vehicle) {
if (std::holds_alternative<VehicleID>(ni->ref1)) {
Dimension d2 = GetStringBoundingBox(this->GetWidget<NWidgetCore>(WID_N_SHOW_GROUP)->GetString());
d2.height += WidgetDimensions::scaled.captiontext.Vertical();
d2.width += WidgetDimensions::scaled.captiontext.Horizontal();
@ -541,15 +544,15 @@ struct NewsWindow : Window {
break;
case WID_N_VEH_SPR: {
assert(this->ni->reftype1 == NewsReferenceType::Engine);
EngineID engine = static_cast<EngineID>(this->ni->ref1);
assert(std::holds_alternative<EngineID>(ni->ref1));
EngineID engine = std::get<EngineID>(this->ni->ref1);
DrawVehicleEngine(r.left, r.right, CenterBounds(r.left, r.right, 0), CenterBounds(r.top, r.bottom, 0), engine, GetEnginePalette(engine, _local_company), EIT_PREVIEW);
GfxFillRect(r.left, r.top, r.right, r.bottom, PALETTE_NEWSPAPER, FILLRECT_RECOLOUR);
break;
}
case WID_N_VEH_INFO: {
assert(this->ni->reftype1 == NewsReferenceType::Engine);
EngineID engine = static_cast<EngineID>(this->ni->ref1);
assert(std::holds_alternative<EngineID>(ni->ref1));
EngineID engine = std::get<EngineID>(this->ni->ref1);
DrawStringMultiLine(r.left, r.right, r.top, r.bottom, GetEngineInfoString(engine), TC_FROMSTRING, SA_CENTER);
break;
}
@ -566,8 +569,8 @@ struct NewsWindow : Window {
break;
case WID_N_CAPTION:
if (this->ni->reftype1 == NewsReferenceType::Vehicle) {
const Vehicle *v = Vehicle::Get(this->ni->ref1);
if (std::holds_alternative<VehicleID>(ni->ref1)) {
const Vehicle *v = Vehicle::Get(std::get<VehicleID>(this->ni->ref1));
ShowVehicleViewWindow(v);
}
break;
@ -576,18 +579,18 @@ struct NewsWindow : Window {
break; // Ignore clicks
case WID_N_SHOW_GROUP:
if (this->ni->reftype1 == NewsReferenceType::Vehicle) {
const Vehicle *v = Vehicle::Get(this->ni->ref1);
if (std::holds_alternative<VehicleID>(ni->ref1)) {
const Vehicle *v = Vehicle::Get(std::get<VehicleID>(this->ni->ref1));
ShowCompanyGroupForVehicle(v);
}
break;
default:
if (this->ni->reftype1 == NewsReferenceType::Vehicle) {
const Vehicle *v = Vehicle::Get(this->ni->ref1);
if (std::holds_alternative<VehicleID>(ni->ref1)) {
const Vehicle *v = Vehicle::Get(std::get<VehicleID>(this->ni->ref1));
ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos);
} else {
TileIndex tile1 = GetReferenceTile(this->ni->reftype1, this->ni->ref1);
TileIndex tile2 = GetReferenceTile(this->ni->reftype2, this->ni->ref2);
TileIndex tile1 = GetReferenceTile(this->ni->ref1);
TileIndex tile2 = GetReferenceTile(this->ni->ref2);
if (_ctrl_pressed) {
if (tile1 != INVALID_TILE) ShowExtraViewportWindow(tile1);
if (tile2 != INVALID_TILE) ShowExtraViewportWindow(tile2);
@ -607,8 +610,8 @@ struct NewsWindow : Window {
NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_N_VIEWPORT);
nvp->UpdateViewportCoordinates(this);
if (ni->reftype1 != NewsReferenceType::Vehicle) {
ScrollWindowToTile(GetReferenceTile(ni->reftype1, ni->ref1), this, true); // Re-center viewport.
if (!std::holds_alternative<VehicleID>(ni->ref1)) {
ScrollWindowToTile(GetReferenceTile(ni->ref1), this, true); // Re-center viewport.
}
}
@ -677,8 +680,8 @@ private:
StringID GetNewVehicleMessageString(WidgetID widget) const
{
assert(this->ni->reftype1 == NewsReferenceType::Engine);
EngineID engine = static_cast<EngineID>(this->ni->ref1);
assert(std::holds_alternative<EngineID>(ni->ref1));
EngineID engine = std::get<EngineID>(this->ni->ref1);
switch (widget) {
case WID_N_VEH_TITLE:
@ -865,17 +868,15 @@ static std::list<NewsItem>::iterator DeleteNewsItem(std::list<NewsItem>::iterato
* @param string_id String to display.
* @param type The type of news.
* @param flags Flags related to how to display the news.
* @param reftype1 Type of ref1.
* @param ref1 Reference 1 to some object: Used for a possible viewport, scrolling after clicking on the news, and for deleting the news when the object is deleted.
* @param reftype2 Type of ref2.
* @param ref2 Reference 2 to some object: Used for scrolling after clicking on the news, and for deleting the news when the object is deleted.
* @param data Pointer to data that must be released once the news message is cleared.
* @param advice_type Sub-type in case the news type is #NewsType::Advice.
*
* @see NewsSubtype
*/
NewsItem::NewsItem(StringID string_id, NewsType type, NewsStyle style, NewsFlags flags, NewsReferenceType reftype1, uint32_t ref1, NewsReferenceType reftype2, uint32_t ref2, std::unique_ptr<NewsAllocatedData> &&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), reftype1(reftype1), reftype2(reftype2), ref1(ref1), ref2(ref2), data(std::move(data))
NewsItem::NewsItem(StringID string_id, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr<NewsAllocatedData> &&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))
{
/* show this news message in colour? */
if (TimerGameCalendar::year >= _settings_client.gui.coloured_news_year) this->flags.Set(NewsFlag::InColour);
@ -887,21 +888,19 @@ NewsItem::NewsItem(StringID string_id, NewsType type, NewsStyle style, NewsFlags
* @param string String to display
* @param type news category
* @param flags display flags for the news
* @param reftype1 Type of ref1
* @param ref1 Reference 1 to some object: Used for a possible viewport, scrolling after clicking on the news, and for deleting the news when the object is deleted.
* @param reftype2 Type of ref2
* @param ref2 Reference 2 to some object: Used for scrolling after clicking on the news, and for deleting the news when the object is deleted.
* @param data Pointer to data that must be released once the news message is cleared.
* @param advice_type Sub-type in case the news type is #NewsType::Advice.
*
* @see NewsSubtype
*/
void AddNewsItem(StringID string, NewsType type, NewsStyle style, NewsFlags flags, NewsReferenceType reftype1, uint32_t ref1, NewsReferenceType reftype2, uint32_t ref2, std::unique_ptr<NewsAllocatedData> &&data, AdviceType advice_type)
void AddNewsItem(StringID string, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr<NewsAllocatedData> &&data, AdviceType advice_type)
{
if (_game_mode == GM_MENU) return;
/* Create new news item node */
_news.emplace_front(string, type, style, flags, reftype1, ref1, reftype2, ref2, std::move(data), advice_type);
_news.emplace_front(string, 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) {
@ -911,17 +910,37 @@ void AddNewsItem(StringID string, NewsType type, NewsStyle style, NewsFlags flag
InvalidateWindowData(WC_MESSAGE_HISTORY, 0);
}
/**
* Encode a NewsReference for serialisation, e.g. for writing in the crash log.
* @param reference The reference to serialise.
* @return Reference serialised into a single uint32_t.
*/
uint32_t SerialiseNewsReference(const NewsReference &reference)
{
struct visitor {
uint32_t operator()(const std::monostate &) { return 0; }
uint32_t operator()(const TileIndex &t) { return t.base(); }
uint32_t operator()(const VehicleID v) { return v; }
uint32_t operator()(const StationID s) { return s; }
uint32_t operator()(const IndustryID i) { return i; }
uint32_t operator()(const TownID t) { return t; }
uint32_t operator()(const EngineID e) { return e; }
};
return std::visit(visitor{}, reference);
}
/**
* Create a new custom news item.
* @param flags type of operation
* @aram type NewsType of the message.
* @param reftype1 NewsReferenceType of first reference.
* @param company Company this news message is for.
* @param reference First reference of the news message.
* @param reference_id First reference of the news message.
* @param text The text of the news message.
* @return the cost of this operation or an error
*/
CommandCost CmdCustomNewsItem(DoCommandFlags flags, NewsType type, NewsReferenceType reftype1, CompanyID company, uint32_t reference, const std::string &text)
CommandCost CmdCustomNewsItem(DoCommandFlags flags, NewsType type, CompanyID company, NewsReference reference, const std::string &text)
{
if (_current_company != OWNER_DEITY) return CMD_ERROR;
@ -929,40 +948,23 @@ CommandCost CmdCustomNewsItem(DoCommandFlags flags, NewsType type, NewsReference
if (type >= NewsType::End) return CMD_ERROR;
if (text.empty()) return CMD_ERROR;
switch (reftype1) {
case NewsReferenceType::None: break;
case NewsReferenceType::Tile:
if (!IsValidTile(reference)) return CMD_ERROR;
break;
struct visitor {
bool operator()(const std::monostate &) { return true; }
bool operator()(const TileIndex t) { return IsValidTile(t); }
bool operator()(const VehicleID v) { return Vehicle::IsValidID(v); }
bool operator()(const StationID s) { return Station::IsValidID(s); }
bool operator()(const IndustryID i) { return Industry::IsValidID(i); }
bool operator()(const TownID t) { return Town::IsValidID(t); }
bool operator()(const EngineID e) { return Engine::IsValidID(e); }
};
case NewsReferenceType::Vehicle:
if (!Vehicle::IsValidID(reference)) return CMD_ERROR;
break;
case NewsReferenceType::Station:
if (!Station::IsValidID(reference)) return CMD_ERROR;
break;
case NewsReferenceType::Industry:
if (!Industry::IsValidID(reference)) return CMD_ERROR;
break;
case NewsReferenceType::Town:
if (!Town::IsValidID(reference)) return CMD_ERROR;
break;
case NewsReferenceType::Engine:
if (!Engine::IsValidID(reference)) return CMD_ERROR;
break;
default: return CMD_ERROR;
}
if (!std::visit(visitor{}, reference)) return CMD_ERROR;
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, {}, reftype1, reference, NewsReferenceType::None, UINT32_MAX);
AddNewsItem(STR_NEWS_CUSTOM_ITEM, type, NewsStyle::Normal, {}, reference, {});
}
return CommandCost();
@ -991,6 +993,18 @@ void DeleteNews(Tpredicate predicate)
if (dirty) InvalidateWindowData(WC_MESSAGE_HISTORY, 0);
}
template <typename T>
static bool IsReferenceObject(const NewsReference &reference, T id)
{
return std::holds_alternative<T>(reference) && std::get<T>(reference) == id;
}
template <typename T>
static bool HasReferenceObject(const NewsItem &ni, T id)
{
return IsReferenceObject(ni.ref1, id) || IsReferenceObject(ni.ref2, id);
}
/**
* Delete news with a given advice type about a vehicle.
* When the advice_type is #AdviceType::Invalid all news about the vehicle gets deleted.
@ -1000,7 +1014,7 @@ void DeleteNews(Tpredicate predicate)
void DeleteVehicleNews(VehicleID vid, AdviceType advice_type)
{
DeleteNews([&](const auto &ni) {
return ((ni.reftype1 == NewsReferenceType::Vehicle && ni.ref1 == vid) || (ni.reftype2 == NewsReferenceType::Vehicle && ni.ref2 == vid)) && (advice_type == AdviceType::Invalid || ni.advice_type == advice_type);
return HasReferenceObject(ni, vid) && (advice_type == AdviceType::Invalid || ni.advice_type == advice_type);
});
}
@ -1012,7 +1026,7 @@ void DeleteVehicleNews(VehicleID vid, AdviceType advice_type)
void DeleteStationNews(StationID sid)
{
DeleteNews([&](const auto &ni) {
return (ni.reftype1 == NewsReferenceType::Station && ni.ref1 == sid) || (ni.reftype2 == NewsReferenceType::Station && ni.ref2 == sid);
return HasReferenceObject(ni, sid);
});
}
@ -1023,18 +1037,25 @@ void DeleteStationNews(StationID sid)
void DeleteIndustryNews(IndustryID iid)
{
DeleteNews([&](const auto &ni) {
return (ni.reftype1 == NewsReferenceType::Industry && ni.ref1 == iid) || (ni.reftype2 == NewsReferenceType::Industry && ni.ref2 == iid);
return HasReferenceObject(ni, iid);
});
}
bool IsInvalidEngineNews(const NewsReference &reference)
{
if (!std::holds_alternative<EngineID>(reference)) return false;
EngineID eid = std::get<EngineID>(reference);
return !Engine::IsValidID(eid) || !Engine::Get(eid)->IsEnabled();
}
/**
* Remove engine announcements for invalid engines.
*/
void DeleteInvalidEngineNews()
{
DeleteNews([](const auto &ni) {
return (ni.reftype1 == NewsReferenceType::Engine && (!Engine::IsValidID(ni.ref1) || !Engine::Get(ni.ref1)->IsEnabled())) ||
(ni.reftype2 == NewsReferenceType::Engine && (!Engine::IsValidID(ni.ref2) || !Engine::Get(ni.ref2)->IsEnabled()));
return IsInvalidEngineNews(ni.ref1) || IsInvalidEngineNews(ni.ref2);
});
}
@ -1045,6 +1066,13 @@ static void RemoveOldNewsItems()
});
}
template <typename T>
static void ChangeObject(NewsReference reference, T from, T to)
{
if (!std::holds_alternative<T>(reference)) return;
if (std::get<T>(reference) == from) reference = to;
}
/**
* Report a change in vehicle IDs (due to autoreplace) to affected vehicle news.
* @note Viewports of currently displayed news is changed via #ChangeVehicleViewports
@ -1054,8 +1082,8 @@ static void RemoveOldNewsItems()
void ChangeVehicleNews(VehicleID from_index, VehicleID to_index)
{
for (auto &ni : _news) {
if (ni.reftype1 == NewsReferenceType::Vehicle && ni.ref1 == from_index) ni.ref1 = to_index;
if (ni.reftype2 == NewsReferenceType::Vehicle && ni.ref2 == from_index) ni.ref2 = to_index;
ChangeObject(ni.ref1, from_index, to_index);
ChangeObject(ni.ref2, from_index, to_index);
if (ni.flags.Test(NewsFlag::VehicleParam0) && std::get<uint64_t>(ni.params[0]) == from_index) ni.params[0] = to_index;
}
}

View File

@ -11,11 +11,16 @@
#define NEWS_TYPE_H
#include "core/enum_type.hpp"
#include "engine_type.h"
#include "industry_type.h"
#include "gfx_type.h"
#include "sound_type.h"
#include "station_type.h"
#include "strings_type.h"
#include "timer/timer_game_calendar.h"
#include "timer/timer_game_economy.h"
#include "strings_type.h"
#include "sound_type.h"
#include "town_type.h"
#include "vehicle_type.h"
/**
* Type of news.
@ -65,15 +70,7 @@ enum class AdviceType : uint8_t {
* You have to make sure, #ChangeVehicleNews catches the DParams of your message.
* This is NOT ensured by the references.
*/
enum class NewsReferenceType : uint8_t {
None, ///< Empty reference
Tile, ///< Reference tile. Scroll to tile when clicking on the news.
Vehicle, ///< Reference vehicle. Scroll to vehicle when clicking on the news. Delete news when vehicle is deleted.
Station, ///< Reference station. Scroll to station when clicking on the news. Delete news when station is deleted.
Industry, ///< Reference industry. Scroll to industry when clicking on the news. Delete news when industry is deleted.
Town, ///< Reference town. Scroll to town when clicking on the news.
Engine, ///< Reference engine.
};
using NewsReference = std::variant<std::monostate, TileIndex, VehicleID, StationID, IndustryID, TownID, EngineID>;
/** News Window Styles. */
enum class NewsStyle : uint8_t {
@ -145,16 +142,14 @@ struct NewsItem {
NewsStyle style; /// Window style for the news.
NewsFlags flags; ///< NewsFlags bits @see NewsFlag
NewsReferenceType reftype1; ///< Type of ref1
NewsReferenceType reftype2; ///< Type of ref2
uint32_t ref1; ///< Reference 1 to some object: Used for a possible viewport, scrolling after clicking on the news, and for deleting the news when the object is deleted.
uint32_t ref2; ///< Reference 2 to some object: Used for scrolling after clicking on the news, and for deleting the news when the object is deleted.
NewsReference ref1; ///< Reference 1 to some object: Used for a possible viewport, scrolling after clicking on the news, and for deleting the news when the object is deleted.
NewsReference ref2; ///< Reference 2 to some object: Used for scrolling after clicking on the news, and for deleting the news when the object is deleted.
std::unique_ptr<NewsAllocatedData> data; ///< Custom data for the news item that will be deallocated (deleted) when the news item has reached its end.
std::vector<StringParameterData> params; ///< Parameters for string resolving.
NewsItem(StringID string_id, NewsType type, NewsStyle style, NewsFlags flags, NewsReferenceType reftype1, uint32_t ref1, NewsReferenceType reftype2, uint32_t ref2, std::unique_ptr<NewsAllocatedData> &&data, AdviceType advice_type);
NewsItem(StringID string_id, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1, NewsReference ref2, std::unique_ptr<NewsAllocatedData> &&data, AdviceType advice_type);
};
/**

View File

@ -20,6 +20,18 @@
#include "../../safeguards.h"
static NewsReference CreateReference(ScriptNews::NewsReferenceType ref_type, SQInteger reference)
{
switch (ref_type) {
case ScriptNews::NR_NONE: return {};
case ScriptNews::NR_TILE: return ::TileIndex(reference);
case ScriptNews::NR_STATION: return static_cast<StationID>(reference);
case ScriptNews::NR_INDUSTRY: return static_cast<IndustryID>(reference);
case ScriptNews::NR_TOWN: return static_cast<TownID>(reference);
default: NOT_REACHED();
}
}
/* static */ bool ScriptNews::Create(NewsType type, Text *text, ScriptCompany::CompanyID company, NewsReferenceType ref_type, SQInteger reference)
{
ScriptObjectRef counter(text);
@ -38,6 +50,5 @@
::CompanyID c = ScriptCompany::FromScriptCompanyID(company);
if (ref_type == NR_NONE) reference = 0;
return ScriptObject::Command<CMD_CUSTOM_NEWS_ITEM>::Do((::NewsType)type, (::NewsReferenceType)ref_type, c, reference, encoded);
return ScriptObject::Command<CMD_CUSTOM_NEWS_ITEM>::Do((::NewsType)type, c, CreateReference(ref_type, reference), encoded);
}

View File

@ -38,11 +38,11 @@ public:
*/
enum NewsReferenceType {
/* Selection of useful game elements to refer to. */
NR_NONE = to_underlying(::NewsReferenceType::None), ///< No reference supplied.
NR_TILE = to_underlying(::NewsReferenceType::Tile), ///< Reference location, scroll to the location when clicking on the news.
NR_STATION = to_underlying(::NewsReferenceType::Station), ///< Reference station, scroll to the station when clicking on the news. Delete news when the station is deleted.
NR_INDUSTRY = to_underlying(::NewsReferenceType::Industry), ///< Reference industry, scrolls to the industry when clicking on the news. Delete news when the industry is deleted.
NR_TOWN = to_underlying(::NewsReferenceType::Town), ///< Reference town, scroll to the town when clicking on the news.
NR_NONE, ///< No reference supplied.
NR_TILE, ///< Reference location, scroll to the location when clicking on the news.
NR_STATION, ///< Reference station, scroll to the station when clicking on the news. Delete news when the station is deleted.
NR_INDUSTRY, ///< Reference industry, scrolls to the industry when clicking on the news. Delete news when the industry is deleted.
NR_TOWN, ///< Reference town, scroll to the town when clicking on the news.
};
/**

View File

@ -532,7 +532,7 @@ static void ShowRejectOrAcceptNews(const Station *st, CargoTypes cargoes, bool r
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, NewsReferenceType::Station, st->index);
AddNewsItem(msg, NewsType::Acceptance, NewsStyle::Small, NewsFlag::InColour, st->index);
}
/**

View File

@ -50,13 +50,13 @@ void Subsidy::AwardTo(CompanyID company)
std::string company_name = GetString(STR_COMPANY_NAME, company);
/* Add a news item */
std::pair<NewsReferenceType, NewsReferenceType> reftype = SetupSubsidyDecodeParam(this, SubsidyDecodeParamType::NewsAwarded, 1);
std::pair<NewsReference, NewsReference> 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, {},
reftype.first, this->src.id, reftype.second, this->dst.id
references.first, references.second
);
AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index));
Game::NewEvent(new ScriptEventSubsidyAwarded(this->index));
@ -71,10 +71,10 @@ void Subsidy::AwardTo(CompanyID company)
* @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<NewsReferenceType, NewsReferenceType> SetupSubsidyDecodeParam(const Subsidy *s, SubsidyDecodeParamType mode, uint parameter_offset)
std::pair<NewsReference, NewsReference> SetupSubsidyDecodeParam(const Subsidy *s, SubsidyDecodeParamType mode, uint parameter_offset)
{
NewsReferenceType reftype1 = NewsReferenceType::None;
NewsReferenceType reftype2 = NewsReferenceType::None;
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);
@ -82,11 +82,11 @@ std::pair<NewsReferenceType, NewsReferenceType> SetupSubsidyDecodeParam(const Su
switch (s->src.type) {
case SourceType::Industry:
reftype1 = NewsReferenceType::Industry;
reference1 = static_cast<IndustryID>(s->src.id);
SetDParam(parameter_offset + 1, STR_INDUSTRY_NAME);
break;
case SourceType::Town:
reftype1 = NewsReferenceType::Town;
reference1 = static_cast<TownID>(s->src.id);
SetDParam(parameter_offset + 1, STR_TOWN_NAME);
break;
default: NOT_REACHED();
@ -95,11 +95,11 @@ std::pair<NewsReferenceType, NewsReferenceType> SetupSubsidyDecodeParam(const Su
switch (s->dst.type) {
case SourceType::Industry:
reftype2 = NewsReferenceType::Industry;
reference2 = static_cast<IndustryID>(s->dst.id);
SetDParam(parameter_offset + 4, STR_INDUSTRY_NAME);
break;
case SourceType::Town:
reftype2 = NewsReferenceType::Town;
reference2 = static_cast<TownID>(s->dst.id);
SetDParam(parameter_offset + 4, STR_TOWN_NAME);
break;
default: NOT_REACHED();
@ -111,7 +111,7 @@ std::pair<NewsReferenceType, NewsReferenceType> SetupSubsidyDecodeParam(const Su
SetDParam(parameter_offset + 7, _settings_game.difficulty.subsidy_duration);
}
return std::pair<NewsReferenceType, NewsReferenceType>(reftype1, reftype2);
return {reference1, reference2};
}
/**
@ -208,8 +208,8 @@ void CreateSubsidy(CargoType cargo_type, Source src, Source dst)
s->remaining = SUBSIDY_OFFER_MONTHS;
s->awarded = INVALID_COMPANY;
std::pair<NewsReferenceType, NewsReferenceType> reftype = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsOffered);
AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NewsType::Subsidies, NewsStyle::Normal, {}, reftype.first, s->src.id, reftype.second, s->dst.id);
std::pair<NewsReference, NewsReference> references = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsOffered);
AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NewsType::Subsidies, NewsStyle::Normal, {}, references.first, references.second);
SetPartOfSubsidyFlag(s->src, POS_SRC);
SetPartOfSubsidyFlag(s->dst, POS_DST);
AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
@ -462,14 +462,14 @@ static IntervalTimer<TimerGameEconomy> _economy_subsidies_monthly({TimerGameEcon
for (Subsidy *s : Subsidy::Iterate()) {
if (--s->remaining == 0) {
if (!s->IsAwarded()) {
std::pair<NewsReferenceType, NewsReferenceType> reftype = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsWithdrawn);
AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NewsType::Subsidies, NewsStyle::Normal, {}, reftype.first, s->src.id, reftype.second, s->dst.id);
std::pair<NewsReference, NewsReference> references = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsWithdrawn);
AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NewsType::Subsidies, NewsStyle::Normal, {}, references.first, references.second);
AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
} else {
if (s->awarded == _local_company) {
std::pair<NewsReferenceType, NewsReferenceType> reftype = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsWithdrawn);
AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NewsType::Subsidies, NewsStyle::Normal, {}, reftype.first, s->src.id, reftype.second, s->dst.id);
std::pair<NewsReference, NewsReference> references = SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::NewsWithdrawn);
AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NewsType::Subsidies, NewsStyle::Normal, {}, references.first, references.second);
}
AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
Game::NewEvent(new ScriptEventSubsidyExpired(s->index));

View File

@ -17,7 +17,7 @@
#include "news_type.h"
#include "subsidy_base.h"
std::pair<NewsReferenceType, NewsReferenceType> SetupSubsidyDecodeParam(const struct Subsidy *s, SubsidyDecodeParamType mode, uint parameter_offset = 0);
std::pair<NewsReference, NewsReference> 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();

View File

@ -3416,7 +3416,7 @@ static CommandCost TownActionRoadRebuild(Town *t, DoCommandFlags flags)
AddNewsItem(
TimerGameEconomy::UsingWallclockUnits() ? STR_NEWS_ROAD_REBUILDING_MINUTES : STR_NEWS_ROAD_REBUILDING_MONTHS,
NewsType::General, NewsStyle::Normal, {}, NewsReferenceType::Town, t->index, NewsReferenceType::None, UINT32_MAX);
NewsType::General, NewsStyle::Normal, {}, t->index);
AI::BroadcastNewEvent(new ScriptEventRoadReconstruction(_current_company, t->index));
Game::NewEvent(new ScriptEventRoadReconstruction(_current_company, t->index));
}
@ -3571,7 +3571,7 @@ static CommandCost TownActionBuyRights(Town *t, DoCommandFlags flags)
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, {}, NewsReferenceType::Town, t->index, NewsReferenceType::None, UINT32_MAX, std::move(cni));
AddNewsItem(STR_MESSAGE_NEWS_FORMAT, 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));
}