Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: [NewGRF] Allow fixed layout up to 256 tiles per NewGRF rail station. #12554

Merged
merged 4 commits into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 43 additions & 9 deletions src/newgrf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2048,11 +2048,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;
}
}
}
Expand All @@ -2075,9 +2075,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) {
Expand All @@ -2091,13 +2100,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();
Expand Down Expand Up @@ -2149,6 +2176,13 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte
AddStringForMapping(buf.ReadWord(), [statspec](StringID str) { StationClass::Get(statspec->class_index)->name = str; });
break;

case 0x1E: { // Extended tile flags (replaces prop 11, 14 and 15)
uint16_t tiles = buf.ReadExtendedByte();
auto flags = reinterpret_cast<const StationSpec::TileFlags *>(buf.ReadBytes(tiles));
statspec->tileflags.assign(flags, flags + tiles);
break;
}

default:
ret = CIR_UNKNOWN;
break;
Expand Down
8 changes: 4 additions & 4 deletions src/newgrf_callbacks.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 range. */
CBID_STATION_DRAW_TILE_LAYOUT = 0x14,

/**
* Refit capacity, the passed vehicle needs to have its ->cargo_type set to
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/newgrf_station.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
14 changes: 10 additions & 4 deletions src/newgrf_station.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -115,7 +116,7 @@ struct StationSpec : NewGRFSpecBase<StationClassID> {
StationSpec() : 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.
Expand Down Expand Up @@ -159,9 +160,13 @@ struct StationSpec : NewGRFSpecBase<StationClassID> {

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> tileflags; ///< List of tile flags.

AnimationInfo animation;

Expand All @@ -175,6 +180,7 @@ struct StationSpec : NewGRFSpecBase<StationClassID> {
*/
std::vector<std::vector<std::vector<uint8_t>>> layouts;
};
DECLARE_ENUM_AS_BIT_SET(StationSpec::TileFlags);

/** Class containing information relating to station classes. */
using StationClass = NewGRFClass<StationSpec, StationClassID, STAT_CLASS_MAX>;
Expand Down
46 changes: 31 additions & 15 deletions src/station_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1305,22 +1305,30 @@ static CommandCost CalculateRailStationCost(TileArea tile_area, DoCommandFlag fl
return cost;
}

/**
* Get station tile flags for the given StationGfx.
* @param gfx StationGfx of station tile.
* @param statspec Station spec of station tile.
* @return Tile flags to apply.
*/
static StationSpec::TileFlags GetStationTileFlags(StationGfx gfx, const StationSpec *statspec)
{
/* 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.
* @param statspec Statspec of the tile.
*/
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);
const auto flags = GetStationTileFlags(GetStationGfx(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);
}

/**
Expand Down Expand Up @@ -1473,12 +1481,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) {
if (callback <= UINT8_MAX) {
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);
}
}

Expand Down Expand Up @@ -3008,9 +3016,17 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlag flags)

#include "table/station_land.h"

/**
* Get station tile layout for a station type and its station gfx.
* @param st Station type to draw.
* @param gfx StationGfx of tile to draw.
* @return Tile layout to draw.
*/
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()) gfx &= 1;
return layouts.data() + gfx;
}

/**
Expand Down Expand Up @@ -3103,8 +3119,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);
}

Expand Down
3 changes: 2 additions & 1 deletion src/station_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static const StationID INVALID_STATION = 0xFFFF;
typedef SmallStack<StationID, StationID, INVALID_STATION, 8, 0xFFFD> StationIDStack;

/** Station types */
enum StationType {
enum StationType : uint8_t {
STATION_RAIL,
STATION_AIRPORT,
STATION_TRUCK,
Expand All @@ -38,6 +38,7 @@ enum StationType {
STATION_BUOY,
STATION_WAYPOINT,
STATION_ROADWAYPOINT,
STATION_END,
};

/** Types of RoadStops */
Expand Down
4 changes: 2 additions & 2 deletions src/table/newgrf_debug_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions src/table/station_land.h
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,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<std::span<const DrawTileSprites>, STATION_END> _station_display_datas = {{
_station_display_datas_rail,
_station_display_datas_airport,
_station_display_datas_truck,
Expand All @@ -1023,4 +1023,4 @@ static const DrawTileSprites * const _station_display_datas[] = {
_station_display_datas_buoy,
_station_display_datas_waypoint,
_station_display_datas_road_waypoint,
};
}};