From 6e98c5c26ad8f5470244943651fe1574d1302ddb Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 21 Apr 2024 14:25:37 +0100 Subject: [PATCH 1/4] Codechange: Rename CBID_STATION_SPRITE_LAYOUT and CBID_STATION_TILE_LAYOUT. These callbacks both select rail station tile layouts, the difference is one happens when drawing, the other happens when building. Change the names to make this clearer. --- src/newgrf_callbacks.h | 8 ++++---- src/newgrf_station.cpp | 4 ++-- src/station_cmd.cpp | 8 ++++---- src/table/newgrf_debug_data.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index 056461c006..fac50c46a3 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -38,8 +38,8 @@ enum CallbackID { /** Determine whether a newstation should be made available to build. */ CBID_STATION_AVAILABILITY = 0x13, // 8 bit callback - /** Choose a sprite layout to draw, instead of the standard 0-7 range. */ - CBID_STATION_SPRITE_LAYOUT = 0x14, + /** Choose a tile layout to draw, instead of the standard 0-7 range. */ + CBID_STATION_DRAW_TILE_LAYOUT = 0x14, /** * Refit capacity, the passed vehicle needs to have its ->cargo_type set to @@ -93,7 +93,7 @@ enum CallbackID { CBID_VEHICLE_ADDITIONAL_TEXT = 0x23, /** Called when building a station to customize the tile layout */ - CBID_STATION_TILE_LAYOUT = 0x24, // 15 bit callback + CBID_STATION_BUILD_TILE_LAYOUT = 0x24, // 15 bit callback /** Called for periodically starting or stopping the animation. */ CBID_INDTILE_ANIM_START_STOP = 0x25, // 15 bit callback @@ -308,7 +308,7 @@ enum VehicleCallbackMask { */ enum StationCallbackMask { CBM_STATION_AVAIL = 0, ///< Availability of station in construction window - CBM_STATION_SPRITE_LAYOUT = 1, ///< Use callback to select a sprite layout to use + CBM_STATION_DRAW_TILE_LAYOUT = 1, ///< Use callback to select a tile layout to use when drawing. CBM_STATION_ANIMATION_NEXT_FRAME = 2, ///< Use a custom next frame callback CBM_STATION_ANIMATION_SPEED = 3, ///< Customize the animation speed of the station CBM_STATION_SLOPE_CHECK = 4, ///< Check slope of new station tiles diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index 1997b10274..0bef2e0a18 100644 --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -782,8 +782,8 @@ bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID const StationSpec *statspec = StationClass::Get(sclass)->GetSpec(station); if (statspec == nullptr) return false; - if (HasBit(statspec->callback_mask, CBM_STATION_SPRITE_LAYOUT)) { - uint16_t callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, nullptr, INVALID_TILE); + if (HasBit(statspec->callback_mask, CBM_STATION_DRAW_TILE_LAYOUT)) { + uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, nullptr, INVALID_TILE); if (callback != CALLBACK_FAILED) tile = callback & ~1; } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index dd32c4f006..582c0e1d04 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1461,12 +1461,12 @@ CommandCost CmdBuildRailStation(DoCommandFlag flags, TileIndex tile_org, RailTyp uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false); /* As the station is not yet completely finished, the station does not yet exist. */ - uint16_t callback = GetStationCallback(CBID_STATION_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile); + uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile); if (callback != CALLBACK_FAILED) { if (callback < 8) { SetStationGfx(tile, (callback & ~1) + axis); } else { - ErrorUnknownCallbackResult(statspec->grf_prop.grffile->grfid, CBID_STATION_TILE_LAYOUT, callback); + ErrorUnknownCallbackResult(statspec->grf_prop.grffile->grfid, CBID_STATION_BUILD_TILE_LAYOUT, callback); } } @@ -2978,8 +2978,8 @@ static void DrawTile_Station(TileInfo *ti) if (statspec != nullptr) { tile_layout = GetStationGfx(ti->tile); - if (HasBit(statspec->callback_mask, CBM_STATION_SPRITE_LAYOUT)) { - uint16_t callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile); + if (HasBit(statspec->callback_mask, CBM_STATION_DRAW_TILE_LAYOUT)) { + uint16_t callback = GetStationCallback(CBID_STATION_DRAW_TILE_LAYOUT, 0, 0, statspec, st, ti->tile); if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile); } diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 836cec3445..55a8df1db5 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -99,8 +99,8 @@ static const NIFeature _nif_vehicle = { #define NICS(cb_id, bit) NIC(cb_id, StationSpec, callback_mask, bit) static const NICallback _nic_stations[] = { NICS(CBID_STATION_AVAILABILITY, CBM_STATION_AVAIL), - NICS(CBID_STATION_SPRITE_LAYOUT, CBM_STATION_SPRITE_LAYOUT), - NICS(CBID_STATION_TILE_LAYOUT, CBM_NO_BIT), + NICS(CBID_STATION_DRAW_TILE_LAYOUT, CBM_STATION_DRAW_TILE_LAYOUT), + NICS(CBID_STATION_BUILD_TILE_LAYOUT,CBM_NO_BIT), NICS(CBID_STATION_ANIM_START_STOP, CBM_NO_BIT), NICS(CBID_STATION_ANIM_NEXT_FRAME, CBM_STATION_ANIMATION_NEXT_FRAME), NICS(CBID_STATION_ANIMATION_SPEED, CBM_STATION_ANIMATION_SPEED), From 52ad5d86f7a67a121a1872f6648ceffcbbf83da2 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 18 Apr 2024 12:09:44 +0100 Subject: [PATCH 2/4] Codechange: Store station layout tiles as std::span. Using std::span provides both the start and end of the list, which allows validating that the requested layout is in range. --- src/station_cmd.cpp | 4 +++- src/station_type.h | 3 ++- src/table/station_land.h | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 582c0e1d04..50e0bc955d 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2885,7 +2885,9 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags) const DrawTileSprites *GetStationTileLayout(StationType st, uint8_t gfx) { - return &_station_display_datas[st][gfx]; + const auto layouts = _station_display_datas[st]; + if (gfx < layouts.size()) return layouts.data() + gfx; + return layouts.data(); } /** diff --git a/src/station_type.h b/src/station_type.h index 4e6968ac31..26c5087764 100644 --- a/src/station_type.h +++ b/src/station_type.h @@ -28,7 +28,7 @@ static const StationID INVALID_STATION = 0xFFFF; typedef SmallStack StationIDStack; /** Station types */ -enum StationType { +enum StationType : uint8_t { STATION_RAIL, STATION_AIRPORT, STATION_TRUCK, @@ -37,6 +37,7 @@ enum StationType { STATION_DOCK, STATION_BUOY, STATION_WAYPOINT, + STATION_END, }; /** Types of RoadStops */ diff --git a/src/table/station_land.h b/src/table/station_land.h index 6e3db1d0d0..d14dd32cd5 100644 --- a/src/table/station_land.h +++ b/src/table/station_land.h @@ -990,7 +990,7 @@ static const DrawTileSprites _station_display_datas_waypoint[] = { * As these are drawn/build like stations, they may use the same number of layouts. */ static_assert(lengthof(_station_display_datas_rail) == lengthof(_station_display_datas_waypoint)); -static const DrawTileSprites * const _station_display_datas[] = { +static const std::array, STATION_END> _station_display_datas = {{ _station_display_datas_rail, _station_display_datas_airport, _station_display_datas_truck, @@ -999,4 +999,4 @@ static const DrawTileSprites * const _station_display_datas[] = { _station_display_datas_dock, _station_display_datas_buoy, _station_display_datas_waypoint, -}; +}}; From 879f06540f4acbd3a0988aedf3c99cccb030dc3d Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Thu, 18 Apr 2024 12:21:36 +0100 Subject: [PATCH 3/4] Feature: [NewGRF] Allow fixed layout up to 256 tiles per NewGRF rail station. Allow using up to 256 tile layouts in property 0E or callback 24, which defines the layout to be saved into the map. This was originally limited to 8, because station graphics above 8 referred to other station types but that was changed in 2007. 1) More efficient than using callback 14, as that needs to be checked every time a station tile is rendered. 2) The layout does not get changed when the station is changed (this may or may not be desirable!) Using more than 256 layouts still requires callback 14. --- src/newgrf.cpp | 6 +++--- src/newgrf_callbacks.h | 2 +- src/station_cmd.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 45208a3c9c..31963f5d27 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -2043,11 +2043,11 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte const uint8_t *layout = buf->ReadBytes(length * number); statspec->layouts[length - 1][number - 1].assign(layout, layout + length * number); - /* Validate tile values are only the permitted 00, 02, 04 and 06. */ + /* Ensure the first bit, axis, is zero. The rest of the value is validated during rendering, as we don't know the range yet. */ for (auto &tile : statspec->layouts[length - 1][number - 1]) { - if ((tile & 6) != tile) { + if ((tile & ~1U) != tile) { GrfMsg(1, "StationChangeInfo: Invalid tile {} in layout {}x{}", tile, length, number); - tile &= 6; + tile &= ~1U; } } } diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index fac50c46a3..017de166d3 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -38,7 +38,7 @@ enum CallbackID { /** Determine whether a newstation should be made available to build. */ CBID_STATION_AVAILABILITY = 0x13, // 8 bit callback - /** Choose a tile layout to draw, instead of the standard 0-7 range. */ + /** Choose a tile layout to draw, instead of the standard range. */ CBID_STATION_DRAW_TILE_LAYOUT = 0x14, /** diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 50e0bc955d..2e3e397be0 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1463,7 +1463,7 @@ CommandCost CmdBuildRailStation(DoCommandFlag flags, TileIndex tile_org, RailTyp /* As the station is not yet completely finished, the station does not yet exist. */ uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile); if (callback != CALLBACK_FAILED) { - if (callback < 8) { + if (callback <= UINT8_MAX) { SetStationGfx(tile, (callback & ~1) + axis); } else { ErrorUnknownCallbackResult(statspec->grf_prop.grffile->grfid, CBID_STATION_BUILD_TILE_LAYOUT, callback); From 6b8db74afd6c269cba526a0d73acbd7b5d789894 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sun, 21 Apr 2024 15:44:26 +0100 Subject: [PATCH 4/4] Add: [NewGRF] Station property 1E, extended station tile flags. Properties 11, 14 and 15 to set pylons/nowires/blocked intrinsically only support 8 station tiles. Add new property to define all three flags for each station tile layout. --- src/newgrf.cpp | 46 ++++++++++++++++++++++++++++++++++++++------ src/newgrf_station.h | 14 ++++++++++---- src/station_cmd.cpp | 21 +++++++++++--------- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 31963f5d27..bf7a34269f 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -2070,9 +2070,18 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte statspec->cargo_threshold = buf->ReadWord(); break; - case 0x11: // Pylon placement - statspec->pylons = buf->ReadByte(); + case 0x11: { // Pylon placement + uint8_t pylons = buf->ReadByte(); + if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8); + for (int j = 0; j < 8; ++j) { + if (HasBit(pylons, j)) { + statspec->tileflags[j] |= StationSpec::TileFlags::Pylons; + } else { + statspec->tileflags[j] &= ~StationSpec::TileFlags::Pylons; + } + } break; + } case 0x12: // Cargo types for random triggers if (_cur.grffile->grf_version >= 7) { @@ -2086,13 +2095,31 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte statspec->flags = buf->ReadByte(); break; - case 0x14: // Overhead wire placement - statspec->wires = buf->ReadByte(); + case 0x14: { // Overhead wire placement + uint8_t wires = buf->ReadByte(); + if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8); + for (int j = 0; j < 8; ++j) { + if (HasBit(wires, j)) { + statspec->tileflags[j] |= StationSpec::TileFlags::NoWires; + } else { + statspec->tileflags[j] &= ~StationSpec::TileFlags::NoWires; + } + } break; + } - case 0x15: // Blocked tiles - statspec->blocked = buf->ReadByte(); + case 0x15: { // Blocked tiles + uint8_t blocked = buf->ReadByte(); + if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8); + for (int j = 0; j < 8; ++j) { + if (HasBit(blocked, j)) { + statspec->tileflags[j] |= StationSpec::TileFlags::Blocked; + } else { + statspec->tileflags[j] &= ~StationSpec::TileFlags::Blocked; + } + } break; + } case 0x16: // Animation info statspec->animation.frames = buf->ReadByte(); @@ -2144,6 +2171,13 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte AddStringForMapping(buf->ReadWord(), [statspec](StringID str) { StationClass::Get(statspec->cls_id)->name = str; }); break; + case 0x1E: { // Extended tile flags (replaces prop 11, 14 and 15) + uint16_t tiles = buf->ReadExtendedByte(); + auto flags = reinterpret_cast(buf->ReadBytes(tiles)); + statspec->tileflags.assign(flags, flags + tiles); + break; + } + default: ret = CIR_UNKNOWN; break; diff --git a/src/newgrf_station.h b/src/newgrf_station.h index f1c619153e..a2da7537ef 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -10,6 +10,7 @@ #ifndef NEWGRF_STATION_H #define NEWGRF_STATION_H +#include "core/enum_type.hpp" #include "newgrf_animation_type.h" #include "newgrf_callbacks.h" #include "newgrf_class.h" @@ -112,7 +113,7 @@ struct StationSpec { StationSpec() : cls_id(STAT_CLASS_DFLT), name(0), disallowed_platforms(0), disallowed_lengths(0), cargo_threshold(0), cargo_triggers(0), - callback_mask(0), flags(0), pylons(0), wires(0), blocked(0), + callback_mask(0), flags(0), animation({0, 0, 0, 0}) {} /** * Properties related the the grf file. @@ -157,9 +158,13 @@ struct StationSpec { uint8_t flags; ///< Bitmask of flags, bit 0: use different sprite set; bit 1: divide cargo about by station size - uint8_t pylons; ///< Bitmask of base tiles (0 - 7) which should contain elrail pylons - uint8_t wires; ///< Bitmask of base tiles (0 - 7) which should contain elrail wires - uint8_t blocked; ///< Bitmask of base tiles (0 - 7) which are blocked to trains + enum class TileFlags : uint8_t { + None = 0, + Pylons = 1U << 0, ///< Tile should contain catenary pylons. + NoWires = 1U << 1, ///< Tile should NOT contain catenary wires. + Blocked = 1U << 2, ///< Tile is blocked to vehicles. + }; + std::vector tileflags; ///< List of tile flags. AnimationInfo animation; @@ -173,6 +178,7 @@ struct StationSpec { */ std::vector>> layouts; }; +DECLARE_ENUM_AS_BIT_SET(StationSpec::TileFlags); /** Class containing information relating to station classes. */ using StationClass = NewGRFClass; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 2e3e397be0..ad6a719882 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1294,6 +1294,14 @@ static CommandCost CalculateRailStationCost(TileArea tile_area, DoCommandFlag fl return cost; } +StationSpec::TileFlags GetStationTileFlags(TileIndex tile, const StationSpec *statspec) +{ + const StationGfx gfx = GetStationGfx(tile); + /* Default stations do not draw pylons under roofs (gfx >= 4) */ + if (statspec == nullptr || gfx >= statspec->tileflags.size()) return gfx < 4 ? StationSpec::TileFlags::Pylons : StationSpec::TileFlags::None; + return statspec->tileflags[gfx]; +} + /** * Set rail station tile flags for the given tile. * @param tile Tile to set flags on. @@ -1301,15 +1309,10 @@ static CommandCost CalculateRailStationCost(TileArea tile_area, DoCommandFlag fl */ void SetRailStationTileFlags(TileIndex tile, const StationSpec *statspec) { - const StationGfx gfx = GetStationGfx(tile); - bool blocked = statspec != nullptr && HasBit(statspec->blocked, gfx); - /* Default stations do not draw pylons under roofs (gfx >= 4) */ - bool pylons = statspec != nullptr ? HasBit(statspec->pylons, gfx) : gfx < 4; - bool wires = statspec == nullptr || !HasBit(statspec->wires, gfx); - - SetStationTileBlocked(tile, blocked); - SetStationTileHavePylons(tile, pylons); - SetStationTileHaveWires(tile, wires); + auto flags = GetStationTileFlags(tile, statspec); + SetStationTileBlocked(tile, (flags & StationSpec::TileFlags::Blocked) == StationSpec::TileFlags::Blocked); + SetStationTileHavePylons(tile, (flags & StationSpec::TileFlags::Pylons) == StationSpec::TileFlags::Pylons); + SetStationTileHaveWires(tile, (flags & StationSpec::TileFlags::NoWires) != StationSpec::TileFlags::NoWires); } /**