1
0
Fork 0

Add: NewGRF Badges.

pull/13588/head
Peter Nelson 2024-10-10 22:18:01 +01:00 committed by Peter Nelson
parent ca3b5ed6c1
commit 8f14894024
46 changed files with 1224 additions and 54 deletions

View File

@ -273,6 +273,9 @@ add_files(
newgrf_airporttiles.h
newgrf_animation_base.h
newgrf_animation_type.h
newgrf_badge.cpp
newgrf_badge.h
newgrf_badge_type.h
newgrf_callbacks.h
newgrf_canal.cpp
newgrf_canal.h

View File

@ -21,6 +21,7 @@
#include "company_base.h"
#include "station_type.h"
#include "newgrf_airport.h"
#include "newgrf_badge.h"
#include "newgrf_callbacks.h"
#include "dropdown_type.h"
#include "dropdown_func.h"
@ -445,6 +446,7 @@ public:
}
/* strings such as 'Size' and 'Coverage Area' */
r.top = DrawBadgeNameList(r, as->badges, GSF_AIRPORTS) + WidgetDimensions::scaled.vsep_normal;
r.top = DrawStationCoverageAreaText(r, SCT_ALL, rad, false) + WidgetDimensions::scaled.vsep_normal;
r.top = DrawStationCoverageAreaText(r, SCT_ALL, rad, true);
}

View File

@ -10,6 +10,7 @@
#include "stdafx.h"
#include "command_func.h"
#include "vehicle_gui.h"
#include "newgrf_badge.h"
#include "newgrf_engine.h"
#include "rail.h"
#include "road.h"
@ -89,6 +90,7 @@ class ReplaceVehicleWindow : public Window {
RailType sel_railtype; ///< Type of rail tracks selected. #INVALID_RAILTYPE to show all.
RoadType sel_roadtype; ///< Type of road selected. #INVALID_ROADTYPE to show all.
Scrollbar *vscroll[2];
GUIBadgeClasses badge_classes;
/**
* Figure out if an engine should be added to a list.
@ -276,6 +278,8 @@ public:
this->sel_engine[1] = EngineID::Invalid();
this->show_hidden_engines = _engine_sort_show_hidden_engines[vehicletype];
this->badge_classes = GUIBadgeClasses(static_cast<GrfSpecFeature>(GSF_TRAINS + vehicletype));
this->CreateNestedTree();
this->vscroll[0] = this->GetScrollbar(WID_RV_LEFT_SCROLLBAR);
this->vscroll[1] = this->GetScrollbar(WID_RV_RIGHT_SCROLLBAR);
@ -463,7 +467,7 @@ public:
int side = (widget == WID_RV_LEFT_MATRIX) ? 0 : 1;
/* Do the actual drawing */
DrawEngineList(this->window_number, r, this->engines[side], *this->vscroll[side], this->sel_engine[side], side == 0, this->sel_group);
DrawEngineList(this->window_number, r, this->engines[side], *this->vscroll[side], this->sel_engine[side], side == 0, this->sel_group, this->badge_classes);
break;
}
}

View File

@ -17,6 +17,7 @@
#include "command_func.h"
#include "company_func.h"
#include "vehicle_gui.h"
#include "newgrf_badge.h"
#include "newgrf_engine.h"
#include "newgrf_text.h"
#include "group.h"
@ -974,6 +975,8 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number);
y = DrawBadgeNameList({left, y, right, INT16_MAX}, e->badges, static_cast<GrfSpecFeature>(GSF_TRAINS + e->type));
/* Additional text from NewGRF */
y = ShowAdditionalText(left, right, y, engine_number);
@ -988,6 +991,11 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
return y;
}
static void DrawEngineBadgeColumn(const Rect &r, int column_group, const GUIBadgeClasses &badge_classes, const Engine *e, PaletteID remap)
{
DrawBadgeColumn(r, column_group, badge_classes, e->badges, static_cast<GrfSpecFeature>(GSF_TRAINS + e->type), e->info.base_intro, remap);
}
/**
* Engine drawing loop
* @param type Type of vehicle (VEH_*)
@ -998,9 +1006,9 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
* @param show_count Whether to show the amount of engines or not
* @param selected_group the group to list the engines of
*/
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group)
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group, const GUIBadgeClasses &badge_classes)
{
static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
static const std::array<int8_t, VehicleType::VEH_COMPANY_END> sprite_y_offsets = { 0, 0, -1, -1 };
auto [first, last] = sb.GetVisibleRangeIterators(eng_list);
@ -1012,7 +1020,9 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
int circle_width = std::max(GetScaledSpriteSize(SPR_CIRCLE_FOLDED).width, GetScaledSpriteSize(SPR_CIRCLE_UNFOLDED).width);
int linecolour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix);
auto badge_column_widths = badge_classes.GetColumnWidths();
Rect ir = r.WithHeight(step_size).Shrink(WidgetDimensions::scaled.matrix, RectPadding::zero);
int sprite_y_offset = ScaleSpriteTrad(sprite_y_offsets[type]) + ir.Height() / 2;
Dimension replace_icon = {0, 0};
@ -1030,44 +1040,88 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
count_width = GetStringBoundingBox(STR_JUST_COMMA, FS_SMALL).width;
}
Rect tr = ir.Indent(circle_width + WidgetDimensions::scaled.hsep_normal + sprite_width + WidgetDimensions::scaled.hsep_wide, rtl); // Name position
Rect cr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl).WithWidth(count_width, !rtl); // Count position
Rect rr = tr.WithWidth(replace_icon.width, !rtl); // Replace icon position
if (show_count) tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal + replace_icon.width + WidgetDimensions::scaled.hsep_wide, !rtl);
int normal_text_y_offset = (ir.Height() - GetCharacterHeight(FS_NORMAL)) / 2;
int small_text_y_offset = ir.Height() - GetCharacterHeight(FS_SMALL);
int replace_icon_y_offset = (ir.Height() - replace_icon.height) / 2;
const int text_row_height = ir.Shrink(WidgetDimensions::scaled.matrix).Height();
const int normal_text_y_offset = (text_row_height - GetCharacterHeight(FS_NORMAL)) / 2;
const int small_text_y_offset = text_row_height - GetCharacterHeight(FS_SMALL);
const int offset = (rtl ? -circle_width : circle_width) / 2;
const int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
int y = ir.top;
for (auto it = first; it != last; ++it) {
const auto &item = *it;
const Engine *e = Engine::Get(item.engine_id);
uint indent = item.indent * WidgetDimensions::scaled.hsep_indent;
bool has_variants = item.flags.Test(EngineDisplayFlag::HasVariants);
bool is_folded = item.flags.Test(EngineDisplayFlag::IsFolded);
bool shaded = item.flags.Test(EngineDisplayFlag::Shaded);
Rect textr = ir.Shrink(WidgetDimensions::scaled.matrix);
Rect tr = ir.Indent(indent, rtl);
if (item.indent > 0) {
/* Draw tree continuation lines. */
int tx = (rtl ? ir.right : ir.left) + offset;
int ty = y - WidgetDimensions::scaled.matrix.top;
for (uint lvl = 1; lvl <= item.indent; ++lvl) {
if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ty, tx, ty + step_size - 1, linecolour, WidgetDimensions::scaled.fullbevel.top);
if (HasBit(item.level_mask, lvl)) GfxDrawLine(tx, ir.top, tx, ir.bottom, linecolour, WidgetDimensions::scaled.fullbevel.top);
if (lvl < item.indent) tx += level_width;
}
/* Draw our node in the tree. */
int ycentre = y + normal_text_y_offset + GetCharacterHeight(FS_NORMAL) / 2 - 1;
if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ty, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
int ycentre = CenterBounds(textr.top, textr.bottom, WidgetDimensions::scaled.fullbevel.top);
if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ir.top, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
}
if (has_variants) {
Rect fr = tr.WithWidth(circle_width, rtl);
DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, textr.top, fr.right, textr.bottom}, SA_CENTER);
}
tr = tr.Indent(circle_width + WidgetDimensions::scaled.hsep_normal, rtl);
/* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
const uint num_engines = GetGroupNumEngines(_local_company, selected_group, item.engine_id);
const PaletteID pal = (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company);
if (badge_column_widths.size() >= 1 && badge_column_widths[0] > 0) {
Rect br = tr.WithWidth(badge_column_widths[0], rtl);
DrawEngineBadgeColumn(br, 0, badge_classes, e, pal);
tr = tr.Indent(badge_column_widths[0], rtl);
}
int sprite_x = tr.WithWidth(sprite_width, rtl).left + sprite_left;
DrawVehicleEngine(r.left, r.right, sprite_x, tr.top + sprite_y_offset, item.engine_id, pal, EIT_PURCHASE);
tr = tr.Indent(sprite_width + WidgetDimensions::scaled.hsep_wide, rtl);
if (badge_column_widths.size() >= 2 && badge_column_widths[1] > 0) {
Rect br = tr.WithWidth(badge_column_widths[1], rtl);
DrawEngineBadgeColumn(br, 1, badge_classes, e, pal);
tr = tr.Indent(badge_column_widths[1], rtl);
}
if (show_count) {
/* Rect for replace-protection icon. */
Rect rr = tr.WithWidth(replace_icon.width, !rtl);
tr = tr.Indent(replace_icon.width + WidgetDimensions::scaled.hsep_normal, !rtl);
/* Rect for engine type count text. */
Rect cr = tr.WithWidth(count_width, !rtl);
tr = tr.Indent(count_width + WidgetDimensions::scaled.hsep_normal, !rtl);
SetDParam(0, num_engines);
DrawString(cr.left, cr.right, textr.top + small_text_y_offset, STR_JUST_COMMA, TC_BLACK, SA_RIGHT | SA_FORCE, false, FS_SMALL);
if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) {
DrawSpriteIgnorePadding(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr, SA_CENTER);
}
}
if (badge_column_widths.size() >= 3 && badge_column_widths[2] > 0) {
Rect br = tr.WithWidth(badge_column_widths[2], !rtl).Indent(WidgetDimensions::scaled.hsep_wide, rtl);
DrawEngineBadgeColumn(br, 2, badge_classes, e, pal);
tr = tr.Indent(badge_column_widths[2], !rtl);
}
const Engine *e = Engine::Get(item.engine_id);
bool hidden = e->company_hidden.Test(_local_company);
StringID str = hidden ? STR_HIDDEN_ENGINE_NAME : STR_ENGINE_NAME;
TextColour tc = (item.engine_id == selected_id) ? TC_WHITE : ((hidden | shaded) ? (TC_GREY | TC_FORCED | TC_NO_SHADE) : TC_BLACK);
@ -1078,20 +1132,9 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
} else {
SetDParam(0, PackEngineNameDParam(item.engine_id, EngineNameContext::PurchaseList, item.indent));
}
Rect itr = tr.Indent(indent, rtl);
DrawString(itr.left, itr.right, y + normal_text_y_offset, str, tc);
int sprite_x = ir.Indent(indent + circle_width + WidgetDimensions::scaled.hsep_normal, rtl).WithWidth(sprite_width, rtl).left + sprite_left;
DrawVehicleEngine(r.left, r.right, sprite_x, y + sprite_y_offset, item.engine_id, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(item.engine_id, _local_company), EIT_PURCHASE);
if (show_count) {
SetDParam(0, num_engines);
DrawString(cr.left, cr.right, y + small_text_y_offset, STR_JUST_COMMA, TC_BLACK, SA_RIGHT | SA_FORCE, false, FS_SMALL);
if (EngineHasReplacementForCompany(Company::Get(_local_company), item.engine_id, selected_group)) DrawSprite(SPR_GROUP_REPLACE_ACTIVE, num_engines == 0 ? PALETTE_CRASH : PAL_NONE, rr.left, y + replace_icon_y_offset);
}
if (has_variants) {
Rect fr = ir.Indent(indent, rtl).WithWidth(circle_width, rtl);
DrawSpriteIgnorePadding(is_folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED, PAL_NONE, {fr.left, y, fr.right, y + ir.Height() - 1}, SA_CENTER);
}
y += step_size;
DrawString(tr.left, tr.right, textr.top + normal_text_y_offset, str, tc);
ir = ir.Translate(0, step_size);
}
}
@ -1179,6 +1222,7 @@ struct BuildVehicleWindow : Window {
int details_height; ///< Minimal needed height of the details panels, in text lines (found so far).
Scrollbar *vscroll;
TestedEngineDetails te; ///< Tested cost and capacity after refit.
GUIBadgeClasses badge_classes;
StringFilter string_filter; ///< Filter for vehicle name
QueryString vehicle_editbox; ///< Filter editbox
@ -1197,6 +1241,11 @@ struct BuildVehicleWindow : Window {
}
}
void BuildBadgeClasses()
{
this->badge_classes = GUIBadgeClasses(static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type));
}
BuildVehicleWindow(WindowDesc &desc, TileIndex tile, VehicleType type) : Window(desc), vehicle_editbox(MAX_LENGTH_VEHICLE_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_VEHICLE_NAME_CHARS)
{
this->vehicle_type = type;
@ -1209,6 +1258,8 @@ struct BuildVehicleWindow : Window {
this->descending_sort_order = _engine_sort_last_order[type];
this->show_hidden_engines = _engine_sort_show_hidden_engines[type];
this->BuildBadgeClasses();
this->UpdateFilterByTile();
this->CreateNestedTree();
@ -1764,7 +1815,7 @@ struct BuildVehicleWindow : Window {
case WID_BV_LIST:
resize.height = GetEngineListHeight(this->vehicle_type);
size.height = 3 * resize.height;
size.width = std::max(size.width, GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width;
size.width = std::max(size.width, this->badge_classes.GetTotalColumnsWidth() + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width;
break;
case WID_BV_PANEL:
@ -1810,7 +1861,8 @@ struct BuildVehicleWindow : Window {
*this->vscroll,
this->sel_engine,
false,
DEFAULT_GROUP
DEFAULT_GROUP,
this->badge_classes
);
break;

View File

@ -82,6 +82,7 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
*/
VariableGRFFileProps grf_prop;
std::vector<WagonOverride> overrides;
std::vector<BadgeID> badges;
Engine() {}
Engine(VehicleType type, uint16_t local_id);

View File

@ -12,6 +12,7 @@
#include "engine_type.h"
#include "group_type.h"
#include "newgrf_badge.h"
#include "sortlist_type.h"
#include "gfx_type.h"
#include "vehicle_type.h"
@ -52,7 +53,7 @@ extern EngList_SortTypeFunction * const _engine_sort_functions[][11];
/* Functions in build_vehicle_gui.cpp */
uint GetEngineListHeight(VehicleType type);
void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selected, WidgetID button);
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group);
void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_list, const Scrollbar &sb, EngineID selected_id, bool show_count, GroupID selected_group, const GUIBadgeClasses &badge_classes);
void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, EngineID parent = EngineID::Invalid(), uint8_t indent = 0);
#endif /* ENGINE_GUI_H */

View File

@ -19,6 +19,7 @@
#include "timer/timer_game_calendar.h"
#include "sound_type.h"
#include "strings_type.h"
#include "newgrf_badge_type.h"
/** Unique identification number of an engine. */
using EngineID = PoolID<uint16_t, struct EngineIDTag, 64000, 0xFFFF>;

View File

@ -14,6 +14,7 @@
#include "timer/timer_game_calendar.h"
#include "house_type.h"
#include "newgrf_animation_type.h"
#include "newgrf_badge_type.h"
#include "newgrf_callbacks.h"
#include "newgrf_commons.h"
@ -115,6 +116,7 @@ struct HouseSpec {
uint8_t processing_time; ///< Periodic refresh multiplier
uint8_t minimum_life; ///< The minimum number of years this house will survive before the town rebuilds it
CargoTypes watched_cargoes; ///< Cargo types watched for acceptance.
std::vector<BadgeID> badges;
CargoLabel accepts_cargo_label[HOUSE_ORIGINAL_NUM_ACCEPTS]; ///< input landscape cargo slots

View File

@ -19,6 +19,7 @@
#include "industry.h"
#include "town.h"
#include "cheat_type.h"
#include "newgrf_badge.h"
#include "newgrf_industries.h"
#include "newgrf_text.h"
#include "newgrf_debug.h"
@ -579,6 +580,8 @@ public:
cargostring = this->MakeCargoListString(indsp->produced_cargo, cargo_suffix, STR_INDUSTRY_VIEW_PRODUCES_N_CARGO);
ir.top = DrawStringMultiLine(ir, cargostring);
ir.top = DrawBadgeNameList(ir, indsp->badges, GSF_INDUSTRIES);
/* Get the additional purchase info text, if it has not already been queried. */
if (indsp->callback_mask.Test(IndustryCallbackMask::FundMoreText)) {
uint16_t callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, nullptr, this->selected_type, INVALID_TILE);

View File

@ -16,6 +16,7 @@
#include "landscape_type.h"
#include "cargo_type.h"
#include "newgrf_animation_type.h"
#include "newgrf_badge_type.h"
#include "newgrf_callbacks.h"
#include "newgrf_commons.h"
@ -132,6 +133,7 @@ struct IndustrySpec {
bool enabled; ///< entity still available (by default true).newgrf can disable it, though
GRFFileProps grf_prop; ///< properties related to the grf file
std::vector<uint8_t> random_sounds; ///< Random sounds;
std::vector<BadgeID> badges;
std::array<std::variant<CargoLabel, MixedCargoType>, INDUSTRY_ORIGINAL_NUM_OUTPUTS> produced_cargo_label; ///< Cargo labels of produced cargo for default industries.
std::array<std::variant<CargoLabel, MixedCargoType>, INDUSTRY_ORIGINAL_NUM_INPUTS> accepts_cargo_label; ///< Cargo labels of accepted cargo for default industries.
@ -164,6 +166,7 @@ struct IndustryTileSpec {
IndustryTileSpecialFlags special_flags; ///< Bitmask of extra flags used by the tile
bool enabled; ///< entity still available (by default true).newgrf can disable it, though
GRFFileProps grf_prop; ///< properties related to the grf file
std::vector<BadgeID> badges;
std::array<std::variant<CargoLabel, MixedCargoType>, INDUSTRY_ORIGINAL_NUM_INPUTS> accepts_cargo_label; ///< Cargo labels of accepted cargo for default industry tiles.
};

View File

@ -5925,3 +5925,5 @@ STR_PLANE :{BLACK}{PLANE}
STR_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY})
STR_BADGE_NAME_LIST :{STRING}: {GOLD}{RAW_STRING}

View File

@ -22,6 +22,8 @@
#include "fontcache.h"
#include "currency.h"
#include "landscape.h"
#include "newgrf_badge.h"
#include "newgrf_badge_type.h"
#include "newgrf_cargo.h"
#include "newgrf_house.h"
#include "newgrf_sound.h"
@ -1049,6 +1051,50 @@ static ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, ByteRe
return CIR_SUCCESS;
}
/**
* Skip a list of badges.
* @param buf Buffer reader containing list of badges to skip.
*/
static void SkipBadgeList(ByteReader &buf)
{
uint16_t count = buf.ReadWord();
while (count-- > 0) {
buf.ReadWord();
}
}
/**
* Read a list of badges.
* @param buf Buffer reader containing list of badges to read.
* @param feature The feature of the badge list.
* @returns list of badges.
*/
static std::vector<BadgeID> ReadBadgeList(ByteReader &buf, GrfSpecFeature feature)
{
uint16_t count = buf.ReadWord();
std::vector<BadgeID> badges;
badges.reserve(count);
while (count-- > 0) {
uint16_t local_index = buf.ReadWord();
if (local_index >= std::size(_cur.grffile->badge_list)) {
GrfMsg(1, "ReadBadgeList: Badge label {} out of range (max {}), skipping.", local_index, std::size(_cur.grffile->badge_list) - 1);
continue;
}
BadgeID index = _cur.grffile->badge_list[local_index];
/* Is badge already present? */
if (std::ranges::find(badges, index) != std::end(badges)) continue;
badges.push_back(index);
MarkBadgeSeen(index, feature);
}
return badges;
}
/**
* Define properties for rail vehicles
* @param first Local ID of the first vehicle.
@ -1354,6 +1400,10 @@ static ChangeInfoResult RailVehicleChangeInfo(uint first, uint last, int prop, B
_gted[e->index].cargo_allowed_required = CargoClasses{buf.ReadWord()};
break;
case 0x33: // Badge list
e->badges = ReadBadgeList(buf, GSF_TRAINS);
break;
default:
ret = CommonVehicleChangeInfo(ei, prop, buf);
break;
@ -1565,6 +1615,10 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint first, uint last, int prop, B
_gted[e->index].cargo_allowed_required = CargoClasses{buf.ReadWord()};
break;
case 0x2A: // Badge list
e->badges = ReadBadgeList(buf, GSF_ROADVEHICLES);
break;
default:
ret = CommonVehicleChangeInfo(ei, prop, buf);
break;
@ -1762,6 +1816,10 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint first, uint last, int prop, B
_gted[e->index].cargo_allowed_required = CargoClasses{buf.ReadWord()};
break;
case 0x26: // Badge list
e->badges = ReadBadgeList(buf, GSF_SHIPS);
break;
default:
ret = CommonVehicleChangeInfo(ei, prop, buf);
break;
@ -1937,6 +1995,10 @@ static ChangeInfoResult AircraftVehicleChangeInfo(uint first, uint last, int pro
_gted[e->index].cargo_allowed_required = CargoClasses{buf.ReadWord()};
break;
case 0x24: // Badge list
e->badges = ReadBadgeList(buf, GSF_AIRCRAFT);
break;
default:
ret = CommonVehicleChangeInfo(ei, prop, buf);
break;
@ -2217,6 +2279,10 @@ static ChangeInfoResult StationChangeInfo(uint first, uint last, int prop, ByteR
break;
}
case 0x1F: // Badge list
statspec->badges = ReadBadgeList(buf, GSF_STATIONS);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -2698,6 +2764,10 @@ static ChangeInfoResult TownHouseChangeInfo(uint first, uint last, int prop, Byt
break;
}
case 0x24: // Badge list
housespec->badges = ReadBadgeList(buf, GSF_HOUSES);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -2760,6 +2830,23 @@ static ChangeInfoResult LoadTranslationTable(uint first, uint last, ByteReader &
return CIR_SUCCESS;
}
static ChangeInfoResult LoadBadgeTranslationTable(uint first, uint last, ByteReader &buf, std::vector<BadgeID> &translation_table, const char *name)
{
if (first != 0 && first != std::size(translation_table)) {
GrfMsg(1, "LoadBadgeTranslationTable: {} translation table must start at zero or {}", name, std::size(translation_table));
return CIR_INVALID_ID;
}
if (first == 0) translation_table.clear();
translation_table.reserve(last);
for (uint id = first; id < last; ++id) {
std::string_view label = buf.ReadString();
translation_table.push_back(GetOrCreateBadge(label).index);
}
return CIR_SUCCESS;
}
/**
* Helper to read a DWord worth of bytes from the reader
* and to return it as a valid string.
@ -2797,6 +2884,9 @@ static ChangeInfoResult GlobalVarChangeInfo(uint first, uint last, int prop, Byt
case 0x17: // Tram type translation table; loading during both reservation and activation stage (in case it is selected depending on defined tramtypes)
return LoadTranslationTable<RoadTypeLabel>(first, last, buf, [](GRFFile &grf) -> std::vector<RoadTypeLabel> & { return grf.tramtype_list; }, "Tram type");
case 0x18: // Badge translation table
return LoadBadgeTranslationTable(first, last, buf, _cur.grffile->badge_list, "Badge");
default:
break;
}
@ -3020,6 +3110,9 @@ static ChangeInfoResult GlobalVarReserveInfo(uint first, uint last, int prop, By
case 0x17: // Tram type translation table; loading during both reservation and activation stage (in case it is selected depending on defined tramtypes)
return LoadTranslationTable<RoadTypeLabel>(first, last, buf, [](GRFFile &grf) -> std::vector<RoadTypeLabel> & { return grf.tramtype_list; }, "Tram type");
case 0x18: // Badge translation table
return LoadBadgeTranslationTable(first, last, buf, _cur.grffile->badge_list, "Badge");
default:
break;
}
@ -3455,6 +3548,10 @@ static ChangeInfoResult IndustrytilesChangeInfo(uint first, uint last, int prop,
break;
}
case 0x14: // Badge list
tsp->badges = ReadBadgeList(buf, GSF_INDUSTRYTILES);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -3548,6 +3645,10 @@ static ChangeInfoResult IgnoreIndustryProperty(int prop, ByteReader &buf)
break;
}
case 0x29: // Badge list
SkipBadgeList(buf);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -3951,6 +4052,10 @@ static ChangeInfoResult IndustriesChangeInfo(uint first, uint last, int prop, By
break;
}
case 0x29: // Badge list
indsp->badges = ReadBadgeList(buf, GSF_INDUSTRIES);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4106,6 +4211,10 @@ static ChangeInfoResult AirportChangeInfo(uint first, uint last, int prop, ByteR
as->maintenance_cost = buf.ReadWord();
break;
case 0x12: // Badge list
as->badges = ReadBadgeList(buf, GSF_AIRPORTS);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4152,6 +4261,10 @@ static ChangeInfoResult IgnoreObjectProperty(uint prop, ByteReader &buf)
buf.ReadDWord();
break;
case 0x19: // Badge list
SkipBadgeList(buf);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4281,6 +4394,10 @@ static ChangeInfoResult ObjectChangeInfo(uint first, uint last, int prop, ByteRe
spec->generate_amount = buf.ReadByte();
break;
case 0x19: // Badge list
spec->badges = ReadBadgeList(buf, GSF_OBJECTS);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4419,6 +4536,10 @@ static ChangeInfoResult RailTypeChangeInfo(uint first, uint last, int prop, Byte
for (int j = buf.ReadByte(); j != 0; j--) buf.ReadDWord();
break;
case 0x1E: // Badge list
rti->badges = ReadBadgeList(buf, GSF_RAILTYPES);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4499,6 +4620,10 @@ static ChangeInfoResult RailTypeReserveInfo(uint first, uint last, int prop, Byt
buf.ReadDWord();
break;
case 0x1E: // Badge list
SkipBadgeList(buf);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4624,6 +4749,10 @@ static ChangeInfoResult RoadTypeChangeInfo(uint first, uint last, int prop, Byte
for (int j = buf.ReadByte(); j != 0; j--) buf.ReadDWord();
break;
case 0x1E: // Badge list
rti->badges = ReadBadgeList(buf, GSF_ROADTYPES);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4713,6 +4842,10 @@ static ChangeInfoResult RoadTypeReserveInfo(uint first, uint last, int prop, Byt
buf.ReadDWord();
break;
case 0x1E: // Badge list
SkipBadgeList(buf);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4808,6 +4941,10 @@ static ChangeInfoResult AirportTilesChangeInfo(uint first, uint last, int prop,
tsp->animation.triggers = buf.ReadByte();
break;
case 0x12: // Badge list
tsp->badges = ReadBadgeList(buf, GSF_TRAMTYPES);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4849,6 +4986,10 @@ static ChangeInfoResult IgnoreRoadStopProperty(uint prop, ByteReader &buf)
buf.ReadDWord();
break;
case 0x16: // Badge list
SkipBadgeList(buf);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -4857,6 +4998,45 @@ static ChangeInfoResult IgnoreRoadStopProperty(uint prop, ByteReader &buf)
return ret;
}
static ChangeInfoResult BadgeChangeInfo(uint first, uint last, int prop, ByteReader &buf)
{
ChangeInfoResult ret = CIR_SUCCESS;
if (last >= UINT16_MAX) {
GrfMsg(1, "BadgeChangeInfo: Tag {} is invalid, max {}, ignoring", last, UINT16_MAX - 1);
return CIR_INVALID_ID;
}
for (uint id = first; id < last; ++id) {
auto it = _cur.grffile->badge_map.find(id);
if (prop != 0x08 && it == std::end(_cur.grffile->badge_map)) {
GrfMsg(1, "BadgeChangeInfo: Attempt to modify undefined tag {}, ignoring", id);
return CIR_INVALID_ID;
}
Badge *badge = nullptr;
if (prop != 0x08) badge = GetBadge(it->second);
switch (prop) {
case 0x08: { // Label
std::string_view label = buf.ReadString();
_cur.grffile->badge_map[id] = GetOrCreateBadge(label).index;
break;
}
case 0x09: // Flags
badge->flags = static_cast<BadgeFlags>(buf.ReadDWord());
break;
default:
ret = CIR_UNKNOWN;
break;
}
}
return ret;
}
static ChangeInfoResult RoadStopChangeInfo(uint first, uint last, int prop, ByteReader &buf)
{
ChangeInfoResult ret = CIR_SUCCESS;
@ -4935,6 +5115,10 @@ static ChangeInfoResult RoadStopChangeInfo(uint first, uint last, int prop, Byte
rs->clear_cost_multiplier = buf.ReadByte();
break;
case 0x16: // Badge list
rs->badges = ReadBadgeList(buf, GSF_ROADSTOPS);
break;
default:
ret = CIR_UNKNOWN;
break;
@ -5009,6 +5193,7 @@ static void FeatureChangeInfo(ByteReader &buf)
/* GSF_ROADTYPES */ RoadTypeChangeInfo,
/* GSF_TRAMTYPES */ TramTypeChangeInfo,
/* GSF_ROADSTOPS */ RoadStopChangeInfo,
/* GSF_BADGES */ BadgeChangeInfo,
};
static_assert(GSF_END == std::size(handler));
@ -5439,6 +5624,7 @@ static void NewSpriteGroup(ByteReader &buf)
case GSF_RAILTYPES:
case GSF_ROADTYPES:
case GSF_TRAMTYPES:
case GSF_BADGES:
{
uint8_t num_loaded = type;
uint8_t num_loading = buf.ReadByte();
@ -6211,6 +6397,56 @@ static void RoadStopMapSpriteGroup(ByteReader &buf, uint8_t idcount)
}
}
static void BadgeMapSpriteGroup(ByteReader &buf, uint8_t idcount)
{
if (_cur.grffile->badge_map.empty()) {
GrfMsg(1, "BadgeMapSpriteGroup: No badges defined, skipping");
return;
}
std::vector<uint16_t> local_ids;
local_ids.reserve(idcount);
for (uint i = 0; i < idcount; i++) {
local_ids.push_back(buf.ReadExtendedByte());
}
uint8_t cidcount = buf.ReadByte();
for (uint c = 0; c < cidcount; c++) {
uint8_t ctype = buf.ReadByte();
uint16_t groupid = buf.ReadWord();
if (!IsValidGroupID(groupid, "BadgeMapSpriteGroup")) continue;
if (ctype >= GSF_END) continue;
for (const auto &local_id : local_ids) {
auto found = _cur.grffile->badge_map.find(local_id);
if (found == std::end(_cur.grffile->badge_map)) {
GrfMsg(1, "BadgeMapSpriteGroup: Badge {} undefined, skipping", local_id);
continue;
}
auto &badge = *GetBadge(found->second);
badge.grf_prop.SetSpriteGroup(ctype, _cur.spritegroups[groupid]);
}
}
uint16_t groupid = buf.ReadWord();
if (!IsValidGroupID(groupid, "BadgeMapSpriteGroup")) return;
for (auto &local_id : local_ids) {
auto found = _cur.grffile->badge_map.find(local_id);
if (found == std::end(_cur.grffile->badge_map)) {
GrfMsg(1, "BadgeMapSpriteGroup: Badge {} undefined, skipping", local_id);
continue;
}
auto &badge = *GetBadge(found->second);
badge.grf_prop.SetSpriteGroup(GSF_END, _cur.spritegroups[groupid]);
badge.grf_prop.grffile = _cur.grffile;
badge.grf_prop.local_id = local_id;
}
}
/* Action 0x03 */
static void FeatureMapSpriteGroup(ByteReader &buf)
{
@ -6314,6 +6550,10 @@ static void FeatureMapSpriteGroup(ByteReader &buf)
RoadStopMapSpriteGroup(buf, idcount);
return;
case GSF_BADGES:
BadgeMapSpriteGroup(buf, idcount);
break;
default:
GrfMsg(1, "FeatureMapSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
return;
@ -6353,7 +6593,7 @@ static void FeatureNewName(ByteReader &buf)
uint16_t id;
if (generic) {
id = buf.ReadWord();
} else if (feature <= GSF_AIRCRAFT) {
} else if (feature <= GSF_AIRCRAFT || feature == GSF_BADGES) {
id = buf.ReadExtendedByte();
} else {
id = buf.ReadByte();
@ -6388,6 +6628,21 @@ static void FeatureNewName(ByteReader &buf)
}
break;
case GSF_BADGES: {
if (!generic) {
auto found = _cur.grffile->badge_map.find(id);
if (found == std::end(_cur.grffile->badge_map)) {
GrfMsg(1, "FeatureNewName: Attempt to name undefined badge 0x{:X}, ignoring", id);
} else {
Badge &badge = *GetBadge(found->second);
badge.name = AddGRFString(_cur.grffile->grfid, GRFStringID{feature_overlay | id}, lang, true, false, name, STR_UNDEFINED);
}
} else {
AddGRFString(_cur.grffile->grfid, GRFStringID{id}, lang, new_scheme, true, name, STR_UNDEFINED);
}
break;
}
default:
if (IsInsideMM(id, 0xD000, 0xD400) || IsInsideMM(id, 0xD800, 0x10000)) {
AddGRFString(_cur.grffile->grfid, GRFStringID{id}, lang, new_scheme, true, name, STR_UNDEFINED);
@ -8811,6 +9066,8 @@ void ResetNewGRFData()
CleanUpStrings();
CleanUpGRFTownNames();
ResetBadges();
/* Copy/reset original engine info data */
SetupEngines();
@ -9253,6 +9510,12 @@ static void FinaliseEngineArray()
if (!e->info.climates.Test(_settings_game.game_creation.landscape)) continue;
switch (e->type) {
case VEH_TRAIN: AppendCopyableBadgeList(e->badges, GetRailTypeInfo(e->u.rail.railtype)->badges, GSF_TRAINS); break;
case VEH_ROAD: AppendCopyableBadgeList(e->badges, GetRoadTypeInfo(e->u.road.roadtype)->badges, GSF_ROADVEHICLES); break;
default: break;
}
/* Skip wagons, there livery is defined via the engine */
if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) {
LiveryScheme ls = GetEngineLiveryScheme(e->index, EngineID::Invalid(), nullptr);
@ -9944,6 +10207,42 @@ static void FinalisePriceBaseMultipliers()
}
}
template <typename T>
void AddBadgeToSpecs(T &specs, GrfSpecFeature feature, Badge &badge)
{
for (auto &spec : specs) {
if (spec == nullptr) continue;
spec->badges.push_back(badge.index);
badge.features.Set(feature);
}
}
/** Finish up applying badges to things */
static void FinaliseBadges()
{
for (GRFFile * const file : _grf_files) {
Badge *badge = GetBadgeByLabel(fmt::format("newgrf/{:08x}", std::byteswap(file->grfid)));
if (badge == nullptr) continue;
for (Engine *e : Engine::Iterate()) {
if (e->grf_prop.grffile != file) continue;
e->badges.push_back(badge->index);
badge->features.Set(static_cast<GrfSpecFeature>(GSF_TRAINS + e->type));
}
AddBadgeToSpecs(file->stations, GSF_STATIONS, *badge);
AddBadgeToSpecs(file->housespec, GSF_HOUSES, *badge);
AddBadgeToSpecs(file->industryspec, GSF_INDUSTRIES, *badge);
AddBadgeToSpecs(file->indtspec, GSF_INDUSTRYTILES, *badge);
AddBadgeToSpecs(file->objectspec, GSF_OBJECTS, *badge);
AddBadgeToSpecs(file->airportspec, GSF_AIRPORTS, *badge);
AddBadgeToSpecs(file->airtspec, GSF_AIRPORTTILES, *badge);
AddBadgeToSpecs(file->roadstops, GSF_ROADSTOPS, *badge);
}
ApplyBadgeFeaturesToClassBadges();
}
extern void InitGRFTownGeneratorNames();
/** Finish loading NewGRFs and execute needed post-processing */
@ -9961,6 +10260,8 @@ static void AfterLoadGRFs()
/* Clear the action 6 override sprites. */
_grf_line_to_action6_sprite_override.clear();
FinaliseBadges();
/* Polish cargoes */
FinaliseCargoArray();

View File

@ -14,6 +14,7 @@
#include "rail_type.h"
#include "road_type.h"
#include "fileio_type.h"
#include "newgrf_badge_type.h"
#include "newgrf_callbacks.h"
#include "newgrf_text_type.h"
#include "core/bitmath_func.hpp"
@ -87,6 +88,7 @@ enum GrfSpecFeature : uint8_t {
GSF_ROADTYPES,
GSF_TRAMTYPES,
GSF_ROADSTOPS,
GSF_BADGES,
GSF_END,
GSF_FAKE_TOWNS = GSF_END, ///< Fake town GrfSpecFeature for NewGRF debugging (parent scope)
@ -94,6 +96,7 @@ enum GrfSpecFeature : uint8_t {
GSF_INVALID = 0xFF, ///< An invalid spec feature
};
using GrfSpecFeatures = EnumBitSet<GrfSpecFeature, uint32_t, GrfSpecFeature::GSF_END>;
static const uint32_t INVALID_GRFID = 0xFFFFFFFF;
@ -130,6 +133,9 @@ struct GRFFile : ZeroedMemoryAllocator {
std::vector<CargoLabel> cargo_list; ///< Cargo translation table (local ID -> label)
std::array<uint8_t, NUM_CARGO> cargo_map{}; ///< Inverse cargo translation table (CargoType -> local ID)
std::vector<BadgeID> badge_list; ///< Badge translation table (local index -> global index)
std::unordered_map<uint16_t, BadgeID> badge_map;
std::vector<RailTypeLabel> railtype_list; ///< Railtype translation table
std::array<RailType, RAILTYPE_END> railtype_map{};

View File

@ -10,6 +10,7 @@
#include "stdafx.h"
#include "debug.h"
#include "timer/timer_game_calendar.h"
#include "newgrf_badge.h"
#include "newgrf_spritegroup.h"
#include "newgrf_text.h"
#include "station_base.h"
@ -158,6 +159,8 @@ void AirportOverrideManager::SetEntitySpec(AirportSpec *as)
{
switch (variable) {
case 0x40: return this->layout;
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->spec->badges, parameter);
}
if (this->st == nullptr) {

View File

@ -12,6 +12,7 @@
#include "airport.h"
#include "timer/timer_game_calendar.h"
#include "newgrf_badge_type.h"
#include "newgrf_class.h"
#include "newgrf_commons.h"
#include "newgrf_spritegroup.h"
@ -119,6 +120,7 @@ struct AirportSpec : NewGRFSpecBase<AirportClassID> {
/* Newgrf data */
bool enabled; ///< Entity still available (by default true). Newgrf can disable it, though.
struct GRFFileProps grf_prop; ///< Properties related to the grf file.
std::vector<BadgeID> badges;
static const AirportSpec *Get(uint8_t type);
static AirportSpec *GetWithoutOverride(uint8_t type);

View File

@ -10,6 +10,7 @@
#include "stdafx.h"
#include "debug.h"
#include "newgrf_airporttiles.h"
#include "newgrf_badge.h"
#include "newgrf_spritegroup.h"
#include "newgrf_sound.h"
#include "station_base.h"
@ -190,6 +191,8 @@ static uint32_t GetAirportTileIDAtOffset(TileIndex tile, const Station *st, uint
/* Get airport tile ID at offset */
case 0x62: return GetAirportTileIDAtOffset(GetNearbyTile(parameter, this->tile), this->st, this->ro.grffile->grfid);
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->ats->badges, parameter);
}
Debug(grf, 1, "Unhandled airport tile variable 0x{:X}", variable);

View File

@ -13,6 +13,7 @@
#include "airport.h"
#include "station_map.h"
#include "newgrf_animation_type.h"
#include "newgrf_badge_type.h"
#include "newgrf_callbacks.h"
#include "newgrf_commons.h"
#include "newgrf_spritegroup.h"
@ -73,6 +74,7 @@ struct AirportTileSpec {
uint8_t animation_special_flags; ///< Extra flags to influence the animation
bool enabled; ///< entity still available (by default true). newgrf can disable it, though
GRFFileProps grf_prop; ///< properties related the the grf file
std::vector<BadgeID> badges;
static const AirportTileSpec *Get(StationGfx gfx);
static const AirportTileSpec *GetByTile(TileIndex tile);

View File

@ -0,0 +1,516 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file newgrf_badge.cpp Functionality for NewGRF badges. */
#include "stdafx.h"
#include "dropdown_type.h"
#include "dropdown_common_type.h"
#include "newgrf.h"
#include "newgrf_badge.h"
#include "newgrf_badge_type.h"
#include "newgrf_spritegroup.h"
#include "strings_func.h"
#include "timer/timer_game_calendar.h"
#include "window_gui.h"
#include "zoom_func.h"
#include "table/strings.h"
#include "safeguards.h"
/** Separator to identify badge classes from a label. */
static constexpr char BADGE_CLASS_SEPARATOR = '/';
/** Global state for badge definitions. */
class Badges {
public:
std::vector<BadgeID> classes; ///< List of known badge classes.
std::vector<Badge> specs; ///< List of known badges.
};
/** Static instance of badge state. */
static Badges _badges = {};
/**
* Assign a BadgeClassID to the given badge.
* @param index Badge ID of badge that should be assigned.
* @returns new or existing BadgeClassID.
*/
static BadgeClassID GetOrCreateBadgeClass(BadgeID index)
{
auto it = std::ranges::find(_badges.classes, index);
if (it == std::end(_badges.classes)) {
it = _badges.classes.emplace(it, index);
}
return static_cast<BadgeClassID>(std::distance(std::begin(_badges.classes), it));
}
/**
* Reset badges to the default state.
*/
void ResetBadges()
{
_badges = {};
}
/**
* Register a badge label and return its global index.
* @param label Badge label to register.
* @returns Global index of the badge.
*/
Badge &GetOrCreateBadge(std::string_view label)
{
/* Check if the label exists. */
auto it = std::ranges::find(_badges.specs, label, &Badge::label);
if (it != std::end(_badges.specs)) return *it;
BadgeClassID class_index;
/* Extract class. */
auto sep = label.find_first_of(BADGE_CLASS_SEPARATOR);
if (sep != std::string_view::npos) {
/* There is a separator, find (and create if necessary) the class label. */
class_index = GetOrCreateBadge(label.substr(0, sep)).class_index;
it = std::end(_badges.specs);
}
BadgeID index = BadgeID(std::distance(std::begin(_badges.specs), it));
if (sep == std::string_view::npos) {
/* There is no separator, so this badge is a class badge. */
class_index = GetOrCreateBadgeClass(index);
}
it = _badges.specs.emplace(it, label, index, class_index);
return *it;
}
/**
* Get a badge if it exists.
* @param index Index of badge.
* @returns Badge with specified index, or nullptr if it does not exist.
*/
Badge *GetBadge(BadgeID index)
{
if (index.base() >= std::size(_badges.specs)) return nullptr;
return &_badges.specs[index.base()];
}
/**
* Get a badge by label if it exists.
* @param label Label of badge.
* @returns Badge with specified label, or nullptr if it does not exist.
*/
Badge *GetBadgeByLabel(std::string_view label)
{
auto it = std::ranges::find(_badges.specs, label, &Badge::label);
if (it == std::end(_badges.specs)) return nullptr;
return &*it;
}
/**
* Get the badge class of a badge label.
* @param label Label to get class of.
* @returns Badge class index of label.
*/
Badge *GetClassBadge(BadgeClassID class_index)
{
if (class_index.base() >= std::size(_badges.classes)) return nullptr;
return GetBadge(_badges.classes[class_index.base()]);
}
/** Resolver for a badge scope. */
struct BadgeScopeResolver : public ScopeResolver {
const Badge &badge;
const std::optional<TimerGameCalendar::Date> introduction_date;
/**
* Scope resolver of a badge.
* @param ro Surrounding resolver.
* @param badge Badge to resolve.
* @param introduction_date Introduction date of entity.
*/
BadgeScopeResolver(ResolverObject &ro, const Badge &badge, const std::optional<TimerGameCalendar::Date> introduction_date)
: ScopeResolver(ro), badge(badge), introduction_date(introduction_date) { }
uint32_t GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const override;
};
/* virtual */ uint32_t BadgeScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const
{
switch (variable) {
case 0x40:
if (this->introduction_date.has_value()) return this->introduction_date->base();
return TimerGameCalendar::date.base();
default: break;
}
available = false;
return UINT_MAX;
}
/** Resolver of badges. */
struct BadgeResolverObject : public ResolverObject {
BadgeScopeResolver self_scope;
BadgeResolverObject(const Badge &badge, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, CallbackID callback = CBID_NO_CALLBACK, uint32_t callback_param1 = 0, uint32_t callback_param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, uint8_t relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->self_scope;
default: return ResolverObject::GetScope(scope, relative);
}
}
GrfSpecFeature GetFeature() const override;
uint32_t GetDebugID() const override;
};
GrfSpecFeature BadgeResolverObject::GetFeature() const
{
return GSF_BADGES;
}
uint32_t BadgeResolverObject::GetDebugID() const
{
return this->self_scope.badge.index.base();
}
/**
* Constructor of the badge resolver.
* @param badge Badge being resolved.
* @param feature GRF feature being used.
* @param introduction_date Optional introduction date of entity.
* @param callback Callback ID.
* @param callback_param1 First parameter (var 10) of the callback.
* @param callback_param2 Second parameter (var 18) of the callback.
*/
BadgeResolverObject::BadgeResolverObject(const Badge &badge, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, CallbackID callback, uint32_t callback_param1, uint32_t callback_param2)
: ResolverObject(badge.grf_prop.grffile, callback, callback_param1, callback_param2), self_scope(*this, badge, introduction_date)
{
assert(feature <= GSF_END);
this->root_spritegroup = this->self_scope.badge.grf_prop.GetSpriteGroup(feature);
if (this->root_spritegroup == nullptr) this->root_spritegroup = this->self_scope.badge.grf_prop.GetSpriteGroup(GSF_END);
}
/**
* Test for a matching badge in a list of badges, returning the number of matching bits.
* @param grffile GRF file of the current varaction.
* @param badges List of badges to test.
* @param parameter GRF-local badge index.
* @returns true iff the badge is present.
*/
uint32_t GetBadgeVariableResult(const GRFFile &grffile, std::span<const BadgeID> badges, uint32_t parameter)
{
if (parameter >= std::size(grffile.badge_list)) return UINT_MAX;
BadgeID index = grffile.badge_list[parameter];
return std::ranges::find(badges, index) != std::end(badges);
}
/**
* Mark a badge a seen (used) by a feature.
*/
void MarkBadgeSeen(BadgeID index, GrfSpecFeature feature)
{
Badge *b = GetBadge(index);
assert(b != nullptr);
b->features.Set(feature);
}
/**
* Append copyable badges from a list onto another.
* Badges must exist and be marked with the Copy flag.
* @param dst Destination badge list.
* @param src Source badge list.
* @param feature Feature of list.
*/
void AppendCopyableBadgeList(std::vector<BadgeID> &dst, std::span<const BadgeID> src, GrfSpecFeature feature)
{
for (const BadgeID &index : src) {
/* Is badge already present? */
if (std::ranges::find(dst, index) != std::end(dst)) continue;
/* Is badge copyable? */
Badge *badge = GetBadge(index);
if (badge == nullptr) continue;
if (!badge->flags.Test(BadgeFlag::Copy)) continue;
dst.push_back(index);
badge->features.Set(feature);
}
}
/** Apply features from all badges to their badge classes. */
void ApplyBadgeFeaturesToClassBadges()
{
for (const Badge &badge : _badges.specs) {
Badge *class_badge = GetClassBadge(badge.class_index);
assert(class_badge != nullptr);
class_badge->features.Set(badge.features);
}
}
static constexpr uint MAX_BADGE_HEIGHT = 12; ///< Maximal height of a badge sprite.
static constexpr uint MAX_BADGE_WIDTH = MAX_BADGE_HEIGHT * 2; ///< Maximal width.
/**
* Get sprite for the given badge.
* @param badge Badge being queried.
* @param feature GRF feature being used.
* @param introduction_date Introduction date of the item, if it has one.
* @param remap Palette remap to use if the flag is company-coloured.
* @returns Custom sprite to draw, or \c 0 if not available.
*/
static PalSpriteID GetBadgeSprite(const Badge &badge, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, PaletteID remap)
{
BadgeResolverObject object(badge, feature, introduction_date);
const SpriteGroup *group = object.Resolve();
if (group == nullptr) return {0, PAL_NONE};
PaletteID pal = badge.flags.Test(BadgeFlag::UseCompanyColour) ? remap : PAL_NONE;
return {group->GetResult(), pal};
}
/**
* Get the largest badge size (within limits) for a badge class.
* @param badge_class Badge class.
* @param feature Feature being used.
* @returns Largest base size of the badge class for the feature.
*/
static Dimension GetBadgeMaximalDimension(BadgeClassID class_index, GrfSpecFeature feature)
{
Dimension d = { 0, MAX_BADGE_HEIGHT };
for (const auto &badge : _badges.specs) {
if (badge.class_index != class_index) continue;
PalSpriteID ps = GetBadgeSprite(badge, feature, std::nullopt, PAL_NONE);
if (ps.sprite == 0) continue;
d.width = std::max(d.width, GetSpriteSize(ps.sprite, nullptr, ZOOM_LVL_NORMAL).width);
if (d.width > MAX_BADGE_WIDTH) break;
}
d.width = std::min(d.width, MAX_BADGE_WIDTH);
return d;
}
/** Utility class to create a list of badge classes used by a feature. */
class UsedBadgeClasses {
public:
/**
* Create a list of used badge classes for a feature.
* @param feature GRF feature being used.
*/
explicit UsedBadgeClasses(GrfSpecFeature feature)
{
for (auto index : _badges.classes) {
Badge *class_badge = GetBadge(index);
if (!class_badge->features.Test(feature)) continue;
this->classes.push_back(class_badge->class_index);
}
std::ranges::sort(this->classes, [](const BadgeClassID &a, const BadgeClassID &b)
{
return GetClassBadge(a)->label < GetClassBadge(b)->label;
});
}
std::span<const BadgeClassID> Classes() const { return this->classes; }
private:
std::vector<BadgeClassID> classes; ///< List of badge classes.
};
static bool operator<(const GUIBadgeClasses::Element &a, const GUIBadgeClasses::Element &b)
{
if (a.column_group != b.column_group) return a.column_group < b.column_group;
if (a.sort_order != b.sort_order) return a.sort_order < b.sort_order;
return a.label < b.label;
}
/**
* Construct of list of badge classes and column groups to display.
* @param feature feature being used.
*/
GUIBadgeClasses::GUIBadgeClasses(GrfSpecFeature feature)
{
/* Get list of classes used by feature. */
UsedBadgeClasses used(feature);
uint max_column = 0;
for (BadgeClassID class_index : used.Classes()) {
Dimension size = GetBadgeMaximalDimension(class_index, feature);
if (size.width == 0) continue;
uint8_t column = 0;
bool visible = true;
uint sort_order = UINT_MAX;
std::string_view label = GetClassBadge(class_index)->label;
this->gui_classes.emplace_back(class_index, column, visible, sort_order, size, label);
if (visible) max_column = std::max<uint>(max_column, column);
}
std::sort(std::begin(this->gui_classes), std::end(this->gui_classes));
/* Determine total width of visible badge columns. */
this->column_widths.resize(max_column + 1);
for (const auto &el : this->gui_classes) {
if (!el.visible) continue;
this->column_widths[el.column_group] += ScaleGUITrad(el.size.width) + WidgetDimensions::scaled.hsep_normal;
}
/* Replace trailing `hsep_normal` spacer with wider `hsep_wide` spacer. */
for (uint &badge_width : this->column_widths) {
if (badge_width == 0) continue;
badge_width = badge_width - WidgetDimensions::scaled.hsep_normal + WidgetDimensions::scaled.hsep_wide;
}
}
/**
* Get total width of all columns.
* @returns sum of all column widths.
*/
uint GUIBadgeClasses::GetTotalColumnsWidth() const
{
return std::accumulate(std::begin(this->column_widths), std::end(this->column_widths), 0U);
}
/**
* Draw names for a list of badge labels.
* @param r Rect to draw in.
* @param badges List of badges.
* @param feature GRF feature being used.
* @returns Vertical position after drawing is complete.
*/
int DrawBadgeNameList(Rect r, std::span<const BadgeID> badges, GrfSpecFeature)
{
if (badges.empty()) return r.top;
std::set<BadgeClassID> classes;
for (const BadgeID &index : badges) classes.insert(GetBadge(index)->class_index);
for (const BadgeClassID &class_index : classes) {
const Badge *class_badge = GetClassBadge(class_index);
if (class_badge == nullptr || class_badge->name == STR_NULL) continue;
std::string s;
for (const BadgeID &index : badges) {
const Badge *badge = GetBadge(index);
if (badge == nullptr || badge->name == STR_NULL) continue;
if (badge->class_index != class_index) continue;
if (!s.empty()) {
if (badge->flags.Test(BadgeFlag::NameListFirstOnly)) continue;
s += ", ";
}
AppendStringInPlace(s, badge->name);
if (badge->flags.Test(BadgeFlag::NameListStop)) break;
}
if (s.empty()) continue;
SetDParam(0, class_badge->name);
SetDParamStr(1, std::move(s));
r.top = DrawStringMultiLine(r, STR_BADGE_NAME_LIST, TC_BLACK);
}
return r.top;
}
/**
* Draw a badge column group.
* @param r rect to draw within.
* @param column_group column to draw.
* @param badge_classes badge classes.
* @param badges badges to draw.
* @param feature feature being used.
* @param introduction_date introduction date of item.
* @param remap palette remap to for company-coloured badges.
*/
void DrawBadgeColumn(Rect r, int column_group, const GUIBadgeClasses &badge_classes, std::span<const BadgeID> badges, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, PaletteID remap)
{
bool rtl = _current_text_dir == TD_RTL;
for (const auto &badge_class : badge_classes.GetClasses()) {
if (badge_class.column_group != column_group) continue;
if (!badge_class.visible) continue;
int width = ScaleGUITrad(badge_class.size.width);
for (const BadgeID &index : badges) {
const Badge &badge = *GetBadge(index);
if (badge.class_index != badge_class.badge_class) continue;
PalSpriteID ps = GetBadgeSprite(badge, feature, introduction_date, remap);
if (ps.sprite == 0) continue;
DrawSpriteIgnorePadding(ps.sprite, ps.pal, r.WithWidth(width, rtl), SA_CENTER);
break;
}
r = r.Indent(width + WidgetDimensions::scaled.hsep_normal, rtl);
}
}
/** Drop down element that draws a list of badges. */
template <class TBase, bool TEnd = true, FontSize TFs = FS_NORMAL>
class DropDownBadges : public TBase {
public:
template <typename... Args>
explicit DropDownBadges(const std::shared_ptr<GUIBadgeClasses> &badge_classes, std::span<const BadgeID> badges, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, Args&&... args)
: TBase(std::forward<Args>(args)...), badge_classes(badge_classes), badges(badges), feature(feature), introduction_date(introduction_date)
{
for (const auto &badge_class : badge_classes->GetClasses()) {
if (badge_class.column_group != 0) continue;
dim.width += badge_class.size.width + WidgetDimensions::scaled.hsep_normal;
dim.height = std::max(dim.height, badge_class.size.height);
}
}
uint Height() const override { return std::max<uint>(this->dim.height, this->TBase::Height()); }
uint Width() const override { return this->dim.width + WidgetDimensions::scaled.hsep_wide + this->TBase::Width(); }
void Draw(const Rect &full, const Rect &r, bool sel, Colours bg_colour) const override
{
bool rtl = TEnd ^ (_current_text_dir == TD_RTL);
DrawBadgeColumn(r.WithWidth(this->dim.width, rtl), 0, *this->badge_classes, this->badges, this->feature, this->introduction_date, PAL_NONE);
this->TBase::Draw(full, r.Indent(this->dim.width + WidgetDimensions::scaled.hsep_wide, rtl), sel, bg_colour);
}
private:
std::shared_ptr<GUIBadgeClasses> badge_classes;
const std::span<const BadgeID> badges;
const GrfSpecFeature feature;
const std::optional<TimerGameCalendar::Date> introduction_date;
Dimension dim{};
};
using DropDownListBadgeItem = DropDownBadges<DropDownListStringItem>;
using DropDownListBadgeIconItem = DropDownBadges<DropDownListIconItem>;
std::unique_ptr<DropDownListItem> MakeDropDownListBadgeItem(const std::shared_ptr<GUIBadgeClasses> &badge_classes, std::span<const BadgeID> badges, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, StringID str, int value, bool masked, bool shaded)
{
return std::make_unique<DropDownListBadgeItem>(badge_classes, badges, feature, introduction_date, str, value, masked, shaded);
}
std::unique_ptr<DropDownListItem> MakeDropDownListBadgeIconItem(const std::shared_ptr<GUIBadgeClasses> &badge_classes, std::span<const BadgeID> badges, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, const Dimension &dim, SpriteID sprite, PaletteID palette, StringID str, int value, bool masked, bool shaded)
{
return std::make_unique<DropDownListBadgeIconItem>(badge_classes, badges, feature, introduction_date, dim, sprite, palette, str, value, masked, shaded);
}

80
src/newgrf_badge.h 100644
View File

@ -0,0 +1,80 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file newgrf_badge.h Functions related to NewGRF badges. */
#ifndef NEWGRF_BADGE_H
#define NEWGRF_BADGE_H
#include "dropdown_type.h"
#include "newgrf.h"
#include "newgrf_badge_type.h"
#include "newgrf_commons.h"
#include "strings_type.h"
#include "timer/timer_game_calendar.h"
class Badge {
public:
std::string label; ///< Label of badge.
BadgeID index; ///< Index assigned to badge.
BadgeClassID class_index; ///< Index of class this badge belongs to.
BadgeFlags flags = {}; ///< Display flags
StringID name = 0; ///< Short name.
GrfSpecFeatures features{}; ///< Bitmask of which features use this badge.
VariableGRFFileProps grf_prop; ///< Sprite information.
Badge(std::string_view label, BadgeID index, BadgeClassID class_index) : label(label), index(index), class_index(class_index) {}
};
void ResetBadges();
Badge &GetOrCreateBadge(std::string_view label);
void MarkBadgeSeen(BadgeID index, GrfSpecFeature feature);
void AppendCopyableBadgeList(std::vector<BadgeID> &dst, std::span<const BadgeID> src, GrfSpecFeature feature);
void ApplyBadgeFeaturesToClassBadges();
Badge *GetBadge(BadgeID index);
Badge *GetBadgeByLabel(std::string_view label);
Badge *GetClassBadge(BadgeClassID class_index);
class GUIBadgeClasses {
public:
struct Element {
BadgeClassID badge_class; ///< Badge class index.
uint8_t column_group; ///< Column group in UI. 0 = left, 1 = centre, 2 = right.
bool visible; ///< Whether this element is visible.
uint sort_order; ///< Order of element.
Dimension size; ///< Maximal size of this element.
std::string_view label; ///< Class label (string owned by the class badge)
constexpr Element(BadgeClassID badge_class, uint8_t column_group, bool visible, uint sort_order, Dimension size, std::string_view label) :
badge_class(badge_class), column_group(column_group), visible(visible), sort_order(sort_order), size(size), label(label) {}
};
GUIBadgeClasses() = default;
explicit GUIBadgeClasses(GrfSpecFeature feature);
inline std::span<const Element> GetClasses() const { return this->gui_classes; }
inline std::span<const uint> GetColumnWidths() const { return this->column_widths; }
uint GetTotalColumnsWidth() const;
private:
std::vector<Element> gui_classes{};
std::vector<uint> column_widths{};
};
int DrawBadgeNameList(Rect r, std::span<const BadgeID> badges, GrfSpecFeature feature);
void DrawBadgeColumn(Rect r, int column_group, const GUIBadgeClasses &badge_classes, std::span<const BadgeID> badges, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, PaletteID remap);
uint32_t GetBadgeVariableResult(const struct GRFFile &grffile, std::span<const BadgeID> badges, uint32_t parameter);
std::unique_ptr<DropDownListItem> MakeDropDownListBadgeItem(const std::shared_ptr<GUIBadgeClasses> &gui_classes, std::span<const BadgeID> badges, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, StringID str, int value, bool masked = false, bool shaded = false);
std::unique_ptr<DropDownListItem> MakeDropDownListBadgeIconItem(const std::shared_ptr<GUIBadgeClasses> &gui_classes, std::span<const BadgeID> badges, GrfSpecFeature feature, std::optional<TimerGameCalendar::Date> introduction_date, const Dimension &dim, SpriteID sprite, PaletteID palette, StringID str, int value, bool masked = false, bool shaded = false);
#endif /* NEWGRF_BADGE_H */

View File

@ -0,0 +1,27 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file newgrf_badge_type.h Types related to NewGRF badges. */
#ifndef NEWGRF_BADGE_TYPE_H
#define NEWGRF_BADGE_TYPE_H
#include "core/enum_type.hpp"
#include "core/strong_typedef_type.hpp"
using BadgeID = StrongType::Typedef<uint16_t, struct BadgeIDTag, StrongType::Compare>;
using BadgeClassID = StrongType::Typedef<uint16_t, struct BadgeClassIDTag, StrongType::Compare>;
enum class BadgeFlag : uint8_t {
Copy = 0, ///< Copy badge to related things.
NameListStop = 1, ///< Stop adding names to the name list after this badge.
NameListFirstOnly = 2, ///< Don't add this name to the name list if not first.
UseCompanyColour = 3, ///< Apply company colour palette to this badge.
};
using BadgeFlags = EnumBitSet<BadgeFlag, uint8_t>;
#endif /* NEWGRF_BADGE_TYPE_H */

View File

@ -12,6 +12,7 @@
#include "train.h"
#include "roadveh.h"
#include "company_func.h"
#include "newgrf_badge.h"
#include "newgrf_cargo.h"
#include "newgrf_spritegroup.h"
#include "timer/timer_game_calendar.h"
@ -692,6 +693,8 @@ static uint32_t VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *objec
default: return 0x00;
}
case 0x7A: return GetBadgeVariableResult(*object->ro.grffile, v->GetEngine()->badges, parameter);
case 0xFE:
case 0xFF: {
uint16_t modflags = 0;
@ -959,6 +962,9 @@ static uint32_t VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *objec
case 0x48: return Engine::Get(this->self_type)->flags.base(); // Vehicle Type Info
case 0x49: return TimerGameCalendar::year.base(); // 'Long' format build year
case 0x4B: return TimerGameCalendar::date.base(); // Long date of last service
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, Engine::Get(this->self_type)->badges, parameter);
case 0x92: return ClampTo<uint16_t>(TimerGameCalendar::date - CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR); // Date of last service
case 0x93: return GB(ClampTo<uint16_t>(TimerGameCalendar::date - CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR), 8, 8);
case 0xC4: return (Clamp(TimerGameCalendar::year, CalendarTime::ORIGINAL_BASE_YEAR, CalendarTime::ORIGINAL_MAX_YEAR) - CalendarTime::ORIGINAL_BASE_YEAR).base(); // Build year

View File

@ -10,6 +10,7 @@
#include "stdafx.h"
#include "debug.h"
#include "landscape.h"
#include "newgrf_badge.h"
#include "newgrf_house.h"
#include "newgrf_spritegroup.h"
#include "newgrf_town.h"
@ -391,6 +392,8 @@ static uint32_t GetDistanceFromNearbyHouse(uint8_t parameter, TileIndex tile, Ho
case 0x65: return 0;
case 0x66: return 0xFFFFFFFF; /* Class and ID of nearby house. */
case 0x67: return 0;
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, HouseSpec::Get(this->house_id)->badges, parameter);
}
Debug(grf, 1, "Unhandled house variable 0x{:X}", variable);
@ -507,6 +510,8 @@ static uint32_t GetDistanceFromNearbyHouse(uint8_t parameter, TileIndex tile, Ho
* in case the newgrf was removed. */
return _house_mngr.GetGRFID(house_id);
}
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, HouseSpec::Get(this->house_id)->badges, parameter);
}
Debug(grf, 1, "Unhandled house variable 0x{:X}", variable);

View File

@ -10,6 +10,7 @@
#include "stdafx.h"
#include "debug.h"
#include "industry.h"
#include "newgrf_badge.h"
#include "newgrf_industries.h"
#include "newgrf_town.h"
#include "newgrf_cargo.h"
@ -166,6 +167,8 @@ static uint32_t GetCountAndDistanceOfClosestInstance(uint8_t param_setID, uint8_
/* Variables available during construction check. */
switch (variable) {
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, GetIndustrySpec(this->type)->badges, parameter);
case 0x80: return this->tile.base();
case 0x81: return GB(this->tile.base(), 8, 8);
@ -350,6 +353,8 @@ static uint32_t GetCountAndDistanceOfClosestInstance(uint8_t param_setID, uint8_
NOT_REACHED();
}
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, GetIndustrySpec(this->type)->badges, parameter);
/* Get a variable from the persistent storage */
case 0x7C: return (this->industry->psa != nullptr) ? this->industry->psa->GetValue(parameter) : 0;

View File

@ -10,6 +10,7 @@
#include "stdafx.h"
#include "debug.h"
#include "landscape.h"
#include "newgrf_badge.h"
#include "newgrf_industrytiles.h"
#include "newgrf_sound.h"
#include "industry.h"
@ -91,6 +92,8 @@ uint32_t GetRelativePosition(TileIndex tile, TileIndex ind_tile)
/* Get industry tile ID at offset */
case 0x62: return GetIndustryIDAtOffset(GetNearbyTile(parameter, this->tile), this->industry, this->ro.grffile->grfid);
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, GetIndustryTileSpec(GetIndustryGfx(this->tile))->badges, parameter);
}
Debug(grf, 1, "Unhandled industry tile variable 0x{:X}", variable);

View File

@ -12,6 +12,7 @@
#include "company_func.h"
#include "debug.h"
#include "genworld.h"
#include "newgrf_badge.h"
#include "newgrf_object.h"
#include "newgrf_class_func.h"
#include "newgrf_sound.h"
@ -286,6 +287,8 @@ static uint32_t GetCountAndDistanceOfClosestInstance(uint8_t local_id, uint32_t
/* Object view */
case 0x48: return this->view;
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->spec->badges, parameter);
/*
* Disallow the rest:
* 0x40: Relative position is passed as parameter during construction.
@ -356,6 +359,8 @@ static uint32_t GetCountAndDistanceOfClosestInstance(uint8_t local_id, uint32_t
/* Count of object, distance of closest instance */
case 0x64: return GetCountAndDistanceOfClosestInstance(parameter, this->ro.grffile->grfid, this->tile, this->obj);
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->spec->badges, parameter);
}
unhandled:

View File

@ -17,6 +17,7 @@
#include "timer/timer_game_calendar.h"
#include "object_type.h"
#include "newgrf_animation_type.h"
#include "newgrf_badge_type.h"
#include "newgrf_class.h"
#include "newgrf_commons.h"
@ -73,6 +74,7 @@ struct ObjectSpec : NewGRFSpecBase<ObjectClassID> {
uint8_t height; ///< The height of this structure, in heightlevels; max MAX_TILE_HEIGHT.
uint8_t views; ///< The number of views.
uint8_t generate_amount; ///< Number of objects which are attempted to be generated per 256^2 map during world generation.
std::vector<BadgeID> badges;
/**
* Test if this object is enabled.

View File

@ -11,6 +11,7 @@
#include "debug.h"
#include "station_base.h"
#include "roadstop_base.h"
#include "newgrf_badge.h"
#include "newgrf_roadstop.h"
#include "newgrf_class_func.h"
#include "newgrf_cargo.h"
@ -200,6 +201,8 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u
return 0xFFFE;
}
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->roadstopspec->badges, parameter);
case 0xF0: return this->st == nullptr ? 0 : this->st->facilities.base(); // facilities
case 0xFA: return ClampTo<uint16_t>((this->st == nullptr ? TimerGameCalendar::date : this->st->build_date) - CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR); // build date

View File

@ -14,6 +14,7 @@
#include "newgrf_animation_type.h"
#include "newgrf_spritegroup.h"
#include "newgrf_badge_type.h"
#include "newgrf_callbacks.h"
#include "newgrf_class.h"
#include "newgrf_commons.h"
@ -158,6 +159,8 @@ struct RoadStopSpec : NewGRFSpecBase<RoadStopClassID> {
uint8_t build_cost_multiplier = 16; ///< Build cost multiplier per tile.
uint8_t clear_cost_multiplier = 16; ///< Clear cost multiplier per tile.
std::vector<BadgeID> badges;
/**
* Get the cost for building a road stop of this type.
* @return The cost for building.

View File

@ -12,6 +12,7 @@
#include "station_base.h"
#include "waypoint_base.h"
#include "roadstop_base.h"
#include "newgrf_badge.h"
#include "newgrf_cargo.h"
#include "newgrf_station.h"
#include "newgrf_spritegroup.h"
@ -292,6 +293,8 @@ TownScopeResolver *StationResolverObject::GetTown()
}
break;
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->statspec->badges, parameter);
case 0xFA: return ClampTo<uint16_t>(TimerGameCalendar::date - CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR); // Build date, clamped to a 16 bit value
}
@ -393,6 +396,8 @@ TownScopeResolver *StationResolverObject::GetTown()
return 0xFFFE;
}
case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, this->statspec->badges, parameter);
/* General station variables */
case 0x82: return 50;
case 0x84: return this->st->string_id;

View File

@ -12,6 +12,7 @@
#include "core/enum_type.hpp"
#include "newgrf_animation_type.h"
#include "newgrf_badge_type.h"
#include "newgrf_callbacks.h"
#include "newgrf_class.h"
#include "newgrf_commons.h"
@ -173,6 +174,8 @@ struct StationSpec : NewGRFSpecBase<StationClassID> {
/** Custom platform layouts, keyed by platform and length combined. */
std::unordered_map<uint16_t, std::vector<uint8_t>> layouts;
std::vector<BadgeID> badges;
};
/** Class containing information relating to station classes. */

View File

@ -12,6 +12,7 @@
#include "company_func.h"
#include "hotkeys.h"
#include "newgrf.h"
#include "newgrf_badge.h"
#include "newgrf_object.h"
#include "newgrf_text.h"
#include "object.h"
@ -45,6 +46,8 @@ class ObjectPickerCallbacks : public PickerCallbacksNewGRFClass<ObjectClass> {
public:
ObjectPickerCallbacks() : PickerCallbacksNewGRFClass<ObjectClass>("fav_objects") {}
GrfSpecFeature GetFeature() const override { return GSF_OBJECTS; }
StringID GetClassTooltip() const override { return STR_PICKER_OBJECT_CLASS_TOOLTIP; }
StringID GetTypeTooltip() const override { return STR_PICKER_OBJECT_TYPE_TOOLTIP; }
@ -77,6 +80,13 @@ public:
return (spec == nullptr || !spec->IsEverAvailable()) ? INVALID_STRING_ID : spec->name;
}
std::span<const BadgeID> GetTypeBadges(int cls_id, int id) const override
{
const auto *spec = this->GetSpec(cls_id, id);
if (spec == nullptr || !spec->IsEverAvailable()) return {};
return spec->badges;
}
bool IsTypeAvailable(int cls_id, int id) const override
{
const auto *spec = this->GetSpec(cls_id, id);
@ -222,6 +232,11 @@ public:
const ObjectSpec *spec = objclass->GetSpec(_object_gui.sel_type);
if (spec == nullptr) break;
Rect tr = r;
const int bottom = tr.bottom;
tr.bottom = INT16_MAX;
tr.top = DrawBadgeNameList(tr, spec->badges, GSF_OBJECTS);
/* Get the extra message for the GUI */
if (spec->callback_mask.Test(ObjectCallbackMask::FundMoreText)) {
uint16_t callback_res = GetObjectCallback(CBID_OBJECT_FUND_MORE_TEXT, 0, 0, spec, nullptr, INVALID_TILE, _object_gui.sel_view);
@ -235,17 +250,19 @@ public:
/* Use all the available space left from where we stand up to the
* end of the window. We ALSO enlarge the window if needed, so we
* can 'go' wild with the bottom of the window. */
int y = DrawStringMultiLine(r.left, r.right, r.top, UINT16_MAX, message, TC_ORANGE) - r.top - 1;
tr.top = DrawStringMultiLine(tr, message, TC_ORANGE);
StopTextRefStackUsage();
if (y > this->info_height) {
}
}
}
}
if (tr.top > bottom) {
BuildObjectWindow *bow = const_cast<BuildObjectWindow *>(this);
bow->info_height = y;
bow->info_height += tr.top - bottom;
bow->ReInit();
}
}
}
}
}
break;
}

View File

@ -9,9 +9,11 @@
#include "stdafx.h"
#include "core/backup_type.hpp"
#include "company_func.h"
#include "gui.h"
#include "hotkeys.h"
#include "ini_type.h"
#include "newgrf_badge.h"
#include "picker_gui.h"
#include "querystring_gui.h"
#include "settings_type.h"
@ -234,6 +236,8 @@ void PickerWindow::ConstructWindow()
this->FinishInitNested(this->window_number);
this->badge_classes = GUIBadgeClasses(this->callbacks.GetFeature());
this->InvalidateData(PICKER_INVALIDATION_ALL);
}
@ -301,6 +305,12 @@ void PickerWindow::DrawWidget(const Rect &r, WidgetID widget) const
int y = (ir.Height() + ScaleSpriteTrad(PREVIEW_HEIGHT)) / 2 - ScaleSpriteTrad(PREVIEW_BOTTOM);
this->callbacks.DrawType(x, y, item.class_index, item.index);
int by = ir.Height() - ScaleGUITrad(12);
GrfSpecFeature feature = this->callbacks.GetFeature();
DrawBadgeColumn({0, by, ir.Width() - 1, ir.Height() - 1}, 0, this->badge_classes, this->callbacks.GetTypeBadges(item.class_index, item.index), feature, std::nullopt, PAL_NONE);
if (this->callbacks.saved.contains(item)) {
DrawSprite(SPR_BLOT, PALETTE_TO_YELLOW, 0, 0);
}

View File

@ -10,6 +10,7 @@
#ifndef PICKER_GUI_H
#define PICKER_GUI_H
#include "newgrf_badge.h"
#include "querystring_gui.h"
#include "sortlist_type.h"
#include "stringfilter_type.h"
@ -41,6 +42,7 @@ public:
virtual void Close(int) { }
virtual GrfSpecFeature GetFeature() const = 0;
/** Should picker class/type selection be enabled? */
virtual bool IsActive() const = 0;
/** Are there multiple classes to chose from? */
@ -72,6 +74,8 @@ public:
virtual PickerItem GetPickerItem(int cls_id, int id) const = 0;
/** Get the item of a type. */
virtual StringID GetTypeName(int cls_id, int id) const = 0;
/** Get the item of a type. */
virtual std::span<const BadgeID> GetTypeBadges(int cls_id, int id) const = 0;
/** Test if an item is currently buildable. */
virtual bool IsTypeAvailable(int cls_id, int id) const = 0;
/** Draw preview image of an item. */
@ -213,6 +217,8 @@ private:
void EnsureSelectedTypeIsValid();
void EnsureSelectedTypeIsVisible();
GUIBadgeClasses badge_classes;
IntervalTimer<TimerGameCalendar> yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) {
this->SetDirty();
}};

View File

@ -21,6 +21,7 @@
#include "timer/timer_game_calendar.h"
#include "signal_type.h"
#include "settings_type.h"
#include "newgrf_badge_type.h"
/** Railtype flag bit numbers. */
enum class RailTypeFlag : uint8_t {
@ -270,6 +271,8 @@ public:
*/
const SpriteGroup *group[RTSG_END];
std::vector<BadgeID> badges;
inline bool UsesOverlay() const
{
return this->group[RTSG_GROUND] != nullptr;

View File

@ -17,6 +17,7 @@
#include "viewport_func.h"
#include "command_func.h"
#include "waypoint_func.h"
#include "newgrf_badge.h"
#include "newgrf_station.h"
#include "company_base.h"
#include "strings_func.h"
@ -969,6 +970,8 @@ class StationPickerCallbacks : public PickerCallbacksNewGRFClass<StationClass> {
public:
StationPickerCallbacks() : PickerCallbacksNewGRFClass<StationClass>("fav_stations") {}
GrfSpecFeature GetFeature() const override { return GSF_STATIONS; }
StringID GetClassTooltip() const override { return STR_PICKER_STATION_CLASS_TOOLTIP; }
StringID GetTypeTooltip() const override { return STR_PICKER_STATION_TYPE_TOOLTIP; }
@ -1007,6 +1010,13 @@ public:
return (spec == nullptr) ? STR_STATION_CLASS_DFLT_STATION : spec->name;
}
std::span<const BadgeID> GetTypeBadges(int cls_id, int id) const override
{
const auto *spec = this->GetSpec(cls_id, id);
if (spec == nullptr) return {};
return spec->badges;
}
bool IsTypeAvailable(int cls_id, int id) const override
{
return IsStationAvailable(this->GetSpec(cls_id, id));
@ -1151,6 +1161,7 @@ public:
Rect r = this->GetWidget<NWidgetBase>(WID_BRAS_COVERAGE_TEXTS)->GetCurrentRect();
const int bottom = r.bottom;
r.bottom = INT_MAX; // Allow overflow as we want to know the required height.
if (statspec != nullptr) r.top = DrawBadgeNameList(r, statspec->badges, GSF_STATIONS);
r.top = DrawStationCoverageAreaText(r, SCT_ALL, rad, false) + WidgetDimensions::scaled.vsep_normal;
r.top = DrawStationCoverageAreaText(r, SCT_ALL, rad, true);
/* Resize background if the window is too small.
@ -1780,6 +1791,8 @@ class WaypointPickerCallbacks : public PickerCallbacksNewGRFClass<StationClass>
public:
WaypointPickerCallbacks() : PickerCallbacksNewGRFClass<StationClass>("fav_waypoints") {}
GrfSpecFeature GetFeature() const override { return GSF_STATIONS; }
StringID GetClassTooltip() const override { return STR_PICKER_WAYPOINT_CLASS_TOOLTIP; }
StringID GetTypeTooltip() const override { return STR_PICKER_WAYPOINT_TYPE_TOOLTIP; }
@ -1819,6 +1832,13 @@ public:
return (spec == nullptr) ? STR_STATION_CLASS_WAYP_WAYPOINT : spec->name;
}
std::span<const BadgeID> GetTypeBadges(int cls_id, int id) const override
{
const auto *spec = this->GetSpec(cls_id, id);
if (spec == nullptr) return {};
return spec->badges;
}
bool IsTypeAvailable(int cls_id, int id) const override
{
return IsStationAvailable(this->GetSpec(cls_id, id));
@ -2039,6 +2059,9 @@ DropDownList GetRailTypeDropDownList(bool for_replacement, bool all_option)
}
}
/* Shared list so that each item can take ownership. */
auto badge_class_list = std::make_shared<GUIBadgeClasses>(GSF_RAILTYPES);
for (const auto &rt : _sorted_railtypes) {
/* If it's not used ever, don't show it to the user. */
if (!HasBit(used_railtypes, rt)) continue;
@ -2048,10 +2071,10 @@ DropDownList GetRailTypeDropDownList(bool for_replacement, bool all_option)
SetDParam(0, rti->strings.menu_text);
SetDParam(1, rti->max_speed);
if (for_replacement) {
list.push_back(MakeDropDownListStringItem(rti->strings.replace_text, rt, !HasBit(avail_railtypes, rt)));
list.push_back(MakeDropDownListBadgeItem(badge_class_list, rti->badges, GSF_RAILTYPES, rti->introduction_date, rti->strings.replace_text, rt, !HasBit(avail_railtypes, rt)));
} else {
StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING;
list.push_back(MakeDropDownListIconItem(d, rti->gui_sprites.build_x_rail, PAL_NONE, str, rt, !HasBit(avail_railtypes, rt)));
list.push_back(MakeDropDownListBadgeIconItem(badge_class_list, rti->badges, GSF_RAILTYPES, rti->introduction_date, d, rti->gui_sprites.build_x_rail, PAL_NONE, str, rt, !HasBit(avail_railtypes, rt)));
}
}

View File

@ -17,6 +17,7 @@
#include "timer/timer_game_calendar.h"
#include "core/enum_type.hpp"
#include "newgrf.h"
#include "newgrf_badge_type.h"
#include "economy_func.h"
@ -181,6 +182,8 @@ public:
*/
const SpriteGroup *group[ROTSG_END];
std::vector<BadgeID> badges;
inline bool UsesOverlay() const
{
return this->group[ROTSG_GROUND] != nullptr;

View File

@ -39,6 +39,7 @@
#include "waypoint_cmd.h"
#include "road_cmd.h"
#include "tunnelbridge_cmd.h"
#include "newgrf_badge.h"
#include "newgrf_roadstop.h"
#include "picker_gui.h"
#include "timer/timer.h"
@ -1184,6 +1185,8 @@ class RoadStopPickerCallbacks : public PickerCallbacksNewGRFClass<RoadStopClass>
public:
RoadStopPickerCallbacks(const std::string &ini_group) : PickerCallbacksNewGRFClass<RoadStopClass>(ini_group) {}
GrfSpecFeature GetFeature() const override { return GSF_ROADSTOPS; }
StringID GetClassTooltip() const override;
StringID GetTypeTooltip() const override;
@ -1231,6 +1234,14 @@ public:
return (spec == nullptr) ? STR_STATION_CLASS_DFLT_ROADSTOP : spec->name;
}
std::span<const BadgeID> GetTypeBadges(int cls_id, int id) const override
{
const auto *spec = this->GetSpec(cls_id, id);
if (!IsRoadStopEverAvailable(spec, roadstoptype == RoadStopType::Bus ? StationType::Bus : StationType::Truck)) return {};
if (spec == nullptr) return {};
return spec->badges;
}
bool IsTypeAvailable(int cls_id, int id) const override
{
const auto *spec = this->GetSpec(cls_id, id);
@ -1347,6 +1358,8 @@ public:
void OnPaint() override
{
const RoadStopSpec *spec = RoadStopClass::Get(_roadstop_gui.sel_class)->GetSpec(_roadstop_gui.sel_type);
this->DrawWidgets();
int rad = _settings_game.station.modified_catchment ? ((this->window_class == WC_BUS_STATION) ? CA_BUS : CA_TRUCK) : CA_UNMODIFIED;
@ -1363,6 +1376,7 @@ public:
Rect r = this->GetWidget<NWidgetBase>(WID_BROS_ACCEPTANCE)->GetCurrentRect();
const int bottom = r.bottom;
r.bottom = INT_MAX; // Allow overflow as we want to know the required height.
if (spec != nullptr) r.top = DrawBadgeNameList(r, spec->badges, GSF_ROADSTOPS);
r.top = DrawStationCoverageAreaText(r, sct, rad, false) + WidgetDimensions::scaled.vsep_normal;
r.top = DrawStationCoverageAreaText(r, sct, rad, true);
/* Resize background if the window is too small.
@ -1603,6 +1617,8 @@ class RoadWaypointPickerCallbacks : public PickerCallbacksNewGRFClass<RoadStopCl
public:
RoadWaypointPickerCallbacks() : PickerCallbacksNewGRFClass<RoadStopClass>("fav_road_waypoints") {}
GrfSpecFeature GetFeature() const override { return GSF_ROADSTOPS; }
StringID GetClassTooltip() const override { return STR_PICKER_WAYPOINT_CLASS_TOOLTIP; }
StringID GetTypeTooltip() const override { return STR_PICKER_WAYPOINT_TYPE_TOOLTIP; }
@ -1642,6 +1658,13 @@ public:
return (spec == nullptr) ? STR_STATION_CLASS_WAYP_WAYPOINT : spec->name;
}
std::span<const BadgeID> GetTypeBadges(int cls_id, int id) const override
{
const auto *spec = this->GetSpec(cls_id, id);
if (spec == nullptr) return {};
return spec->badges;
}
bool IsTypeAvailable(int cls_id, int id) const override
{
return IsRoadStopAvailable(this->GetSpec(cls_id, id), StationType::RoadWaypoint);
@ -1765,6 +1788,9 @@ DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement, b
}
}
/* Shared list so that each item can take ownership. */
auto badge_class_list = std::make_shared<GUIBadgeClasses>(GSF_ROADTYPES);
for (const auto &rt : _sorted_roadtypes) {
/* If it's not used ever, don't show it to the user. */
if (!HasBit(used_roadtypes, rt)) continue;
@ -1774,10 +1800,10 @@ DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement, b
SetDParam(0, rti->strings.menu_text);
SetDParam(1, rti->max_speed / 2);
if (for_replacement) {
list.push_back(MakeDropDownListStringItem(rti->strings.replace_text, rt, !HasBit(avail_roadtypes, rt)));
list.push_back(MakeDropDownListBadgeItem(badge_class_list, rti->badges, GSF_ROADTYPES, rti->introduction_date, rti->strings.replace_text, rt, !HasBit(avail_roadtypes, rt)));
} else {
StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING;
list.push_back(MakeDropDownListIconItem(d, rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt)));
list.push_back(MakeDropDownListBadgeIconItem(badge_class_list, rti->badges, GSF_ROADTYPES, rti->introduction_date, d, rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt)));
}
}
@ -1808,6 +1834,10 @@ DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts)
const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road));
}
/* Shared list so that each item can take ownership. */
auto badge_class_list = std::make_shared<GUIBadgeClasses>(GSF_ROADTYPES);
for (const auto &rt : _sorted_roadtypes) {
if (!HasBit(used_roadtypes, rt)) continue;
@ -1816,7 +1846,7 @@ DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts)
SetDParam(0, rti->strings.menu_text);
SetDParam(1, rti->max_speed / 2);
StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING;
list.push_back(MakeDropDownListIconItem(d, rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt)));
list.push_back(MakeDropDownListBadgeIconItem(badge_class_list, rti->badges, GSF_ROADTYPES, rti->introduction_date, d, rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt)));
}
if (list.empty()) {

View File

@ -378,7 +378,7 @@ static const std::initializer_list<AirportTileLayout> _tile_table_helistation =
/** General AirportSpec definition. */
#define AS_GENERIC(fsm, layouts, depots, size_x, size_y, noise, catchment, min_year, max_year, maint_cost, ttdpatch_type, class_id, name, preview, enabled) \
{{class_id, 0}, fsm, layouts, depots, size_x, size_y, noise, catchment, TimerGameCalendar::Year{min_year}, TimerGameCalendar::Year{max_year}, name, ttdpatch_type, preview, maint_cost, enabled, GRFFileProps(AT_INVALID)}
{{class_id, 0}, fsm, layouts, depots, size_x, size_y, noise, catchment, TimerGameCalendar::Year{min_year}, TimerGameCalendar::Year{max_year}, name, ttdpatch_type, preview, maint_cost, enabled, GRFFileProps(AT_INVALID), {}}
/** AirportSpec definition for airports without any depot. */
#define AS_ND(ap_name, size_x, size_y, min_year, max_year, catchment, noise, maint_cost, ttdpatch_type, class_id, name, preview) \

View File

@ -11,9 +11,9 @@
#define AIRPORTTILES_H
/** Writes all airport tile properties in the AirportTile struct */
#define AT(num_frames, anim_speed) {{num_frames, ANIM_STATUS_LOOPING, anim_speed, 0}, STR_NULL, AirportTileCallbackMasks{}, 0, true, GRFFileProps(INVALID_AIRPORTTILE)}
#define AT(num_frames, anim_speed) {{num_frames, ANIM_STATUS_LOOPING, anim_speed, 0}, STR_NULL, AirportTileCallbackMasks{}, 0, true, GRFFileProps(INVALID_AIRPORTTILE), {}}
/** Writes an airport tile without animation in the AirportTile struct */
#define AT_NOANIM {{0, ANIM_STATUS_NO_ANIMATION, 2, 0}, STR_NULL, AirportTileCallbackMasks{}, 0, true, GRFFileProps(INVALID_AIRPORTTILE)}
#define AT_NOANIM {{0, ANIM_STATUS_NO_ANIMATION, 2, 0}, STR_NULL, AirportTileCallbackMasks{}, 0, true, GRFFileProps(INVALID_AIRPORTTILE), {}}
/**
* All default airport tiles.

View File

@ -1133,7 +1133,7 @@ enum IndustryTypes : uint8_t {
{INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \
{{im1, 0}, {im2, 0}, {im3, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, \
pr, clim, bev, col, in, intx, s1, s2, s3, STR_UNDEFINED, {ai1, ai2, ai3, ai4}, {ag1, ag2, ag3, ag4}, \
IndustryCallbackMasks{}, true, GRFFileProps(IT_INVALID), snd, \
IndustryCallbackMasks{}, true, GRFFileProps(IT_INVALID), snd, {}, \
{{p1, p2}}, {{a1, a2, a3}}}
/* Format:
tile table count and sounds table
@ -1533,7 +1533,7 @@ static const IndustrySpec _origin_industry_specs[NEW_INDUSTRYOFFSET] = {
*/
#define MT(ca1, c1, ca2, c2, ca3, c3, sl, a1, a2, a3) { \
{INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \
{ca1, ca2, ca3}, sl, a1, a2, a3, IndustryTileCallbackMasks{}, {0, ANIM_STATUS_NO_ANIMATION, 2, 0}, IndustryTileSpecialFlags{}, true, GRFFileProps(INVALID_INDUSTRYTILE), {c1, c2, c3} \
{ca1, ca2, ca3}, sl, a1, a2, a3, IndustryTileCallbackMasks{}, {0, ANIM_STATUS_NO_ANIMATION, 2, 0}, IndustryTileSpecialFlags{}, true, GRFFileProps(INVALID_INDUSTRYTILE), {}, {c1, c2, c3} \
}
static const IndustryTileSpec _origin_industry_tile_specs[NEW_INDUSTRYTILEOFFSET] = {
/* Coal Mine */

View File

@ -724,5 +724,6 @@ static const NIFeature * const _nifeatures[] = {
&_nif_tramtype, // GSF_TRAMTYPES
&_nif_roadstop, // GSF_ROADSTOPS
&_nif_town, // GSF_FAKE_TOWNS
nullptr,
};
static_assert(lengthof(_nifeatures) == GSF_FAKE_END);

View File

@ -104,7 +104,7 @@ static const DrawTileSpriteSpan _object_hq[] = {
#undef TILE_SPRITE_LINE
#undef TILE_SPRITE_LINE_NOTHING
#define M(name, size, build_cost_multiplier, clear_cost_multiplier, height, climate, gen_amount, flags) {{INVALID_OBJECT_CLASS, 0}, FixedGRFFileProps<2>{}, {0, 0, 0, 0}, name, climate, size, build_cost_multiplier, clear_cost_multiplier, TimerGameCalendar::Date{}, CalendarTime::MAX_DATE + 1, flags, ObjectCallbackMasks{}, height, 1, gen_amount}
#define M(name, size, build_cost_multiplier, clear_cost_multiplier, height, climate, gen_amount, flags) {{INVALID_OBJECT_CLASS, 0}, FixedGRFFileProps<2>{}, {0, 0, 0, 0}, name, climate, size, build_cost_multiplier, clear_cost_multiplier, TimerGameCalendar::Date{}, CalendarTime::MAX_DATE + 1, flags, ObjectCallbackMasks{}, height, 1, gen_amount, {}}
/* Climates
* T = Temperate

View File

@ -112,6 +112,7 @@ static const RailTypeInfo _original_railtypes[] = {
{ nullptr },
{ nullptr },
{},
},
/** Electrified railway */
@ -213,6 +214,7 @@ static const RailTypeInfo _original_railtypes[] = {
{ nullptr },
{ nullptr },
{},
},
/** Monorail */
@ -310,6 +312,7 @@ static const RailTypeInfo _original_railtypes[] = {
{ nullptr },
{ nullptr },
{},
},
/** Maglev */
@ -407,6 +410,7 @@ static const RailTypeInfo _original_railtypes[] = {
{ nullptr },
{ nullptr },
{},
},
};

View File

@ -95,6 +95,7 @@ static const RoadTypeInfo _original_roadtypes[] = {
{ nullptr },
{ nullptr },
{},
},
/* Electrified Tram */
@ -175,6 +176,7 @@ static const RoadTypeInfo _original_roadtypes[] = {
{ nullptr },
{ nullptr },
{},
},
};

View File

@ -1814,7 +1814,7 @@ static_assert(lengthof(_town_draw_tile_data) == (NEW_HOUSE_OFFSET) * 4 * 4);
{ca1, ca2, ca3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, \
{INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO, INVALID_CARGO}, \
bf, ba, true, GRFFileProps(INVALID_HOUSE_ID), HouseCallbackMasks{}, {COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN}, \
16, HouseExtraFlags{}, HOUSE_NO_CLASS, {0, 2, 0, 0}, 0, 0, 0, {cg1, cg2, cg3}, }
16, HouseExtraFlags{}, HOUSE_NO_CLASS, {0, 2, 0, 0}, 0, 0, 0, {}, {cg1, cg2, cg3}, }
/** House specifications from original data */
extern const HouseSpec _original_house_specs[] = {
/**

View File

@ -1524,6 +1524,8 @@ public:
STR_HOUSE_PICKER_CLASS_ZONE5,
};
GrfSpecFeature GetFeature() const override { return GSF_HOUSES; }
StringID GetClassTooltip() const override { return STR_PICKER_HOUSE_CLASS_TOOLTIP; }
StringID GetTypeTooltip() const override { return STR_PICKER_HOUSE_TYPE_TOOLTIP; }
bool IsActive() const override { return true; }
@ -1574,6 +1576,21 @@ public:
return GetHouseName(spec);
}
std::span<const BadgeID> GetTypeBadges(int cls_id, int id) const override
{
const auto *spec = HouseSpec::Get(id);
if (spec == nullptr) return {};
if (!spec->enabled) return {};
if ((spec->building_availability & climate_mask) == 0) return {};
if (!HasBit(spec->building_availability, cls_id)) return {};
for (int i = 0; i < cls_id; i++) {
/* Don't include if it's already included in an earlier zone. */
if (HasBit(spec->building_availability, i)) return {};
}
return spec->badges;
}
bool IsTypeAvailable(int, int id) const override
{
const HouseSpec *hs = HouseSpec::Get(id);