Compare commits

...

8 Commits

Author SHA1 Message Date
Peter Nelson c319246715
Merge 6b8db74afd into bf8de188ec 2024-04-27 00:37:15 +02:00
Peter Nelson bf8de188ec
Codechange: Use member initialization of GRFFilePropsBase. (#12581)
Don't blame compilers for our sloppy initialisation.

Removes memset, and lengthof.
2024-04-26 22:58:54 +01:00
Peter Nelson 72c55128d2
Codechange: Remove write-only spec_id from RoadStopSpec. (#12582)
Comment is incorrect about its value too.
2024-04-26 21:56:30 +01:00
Peter Nelson a6d401debf
Fix: Properly test for presence of waypoint in NewGRF resolver. (#12579)
Test whether the BaseStation itself a Station or Waypoint, instead of by the station class ID assigned to it.
2024-04-26 17:47:53 +01:00
Peter Nelson 6b8db74afd
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.
2024-04-21 16:20:44 +01:00
Peter Nelson 879f06540f
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.
2024-04-21 15:45:49 +01:00
Peter Nelson 52ad5d86f7
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.
2024-04-21 14:53:20 +01:00
Peter Nelson 6e98c5c26a
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.
2024-04-21 14:53:19 +01:00
11 changed files with 92 additions and 58 deletions

View File

@ -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;
}
}
}
@ -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<const StationSpec::TileFlags *>(buf->ReadBytes(tiles));
statspec->tileflags.assign(flags, flags + tiles);
break;
}
default:
ret = CIR_UNKNOWN;
break;
@ -4846,7 +4880,6 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, ByteR
uint32_t classid = buf->ReadDWord();
rs->cls_id = RoadStopClass::Allocate(BSWAP32(classid));
rs->spec_id = id + i;
break;
}

View File

@ -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
@ -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

View File

@ -308,25 +308,15 @@ bool Convert8bitBooleanCallback(const struct GRFFile *grffile, uint16_t cbid, ui
*/
template <size_t Tcnt>
struct GRFFilePropsBase {
GRFFilePropsBase() : local_id(0), grffile(nullptr)
{
/* The lack of some compilers to provide default constructors complying to the specs
* requires us to zero the stuff ourself. */
memset(spritegroup, 0, sizeof(spritegroup));
}
uint16_t local_id; ///< id defined by the grf file for this entity
const struct GRFFile *grffile; ///< grf file that introduced this entity
const struct SpriteGroup *spritegroup[Tcnt]; ///< pointer to the different sprites of the entity
uint16_t local_id = 0; ///< id defined by the grf file for this entity
const struct GRFFile *grffile = nullptr; ///< grf file that introduced this entity
std::array<const struct SpriteGroup *, Tcnt> spritegroup{}; ///< pointers to the different sprites of the entity
};
/** Data related to the handling of grf files. */
struct GRFFileProps : GRFFilePropsBase<1> {
/** Set all default data constructor for the props. */
GRFFileProps(uint16_t subst_id = 0) :
GRFFilePropsBase<1>(), subst_id(subst_id), override(subst_id)
{
}
constexpr GRFFileProps(uint16_t subst_id = 0) : subst_id(subst_id), override(subst_id) {}
uint16_t subst_id;
uint16_t override; ///< id of the entity been replaced by

View File

@ -53,7 +53,7 @@ const SpriteGroup *GetWagonOverrideSpriteSet(EngineID engine, CargoID cargo, Eng
void SetCustomEngineSprites(EngineID engine, uint8_t cargo, const SpriteGroup *group)
{
Engine *e = Engine::Get(engine);
assert(cargo < lengthof(e->grf_prop.spritegroup));
assert(cargo < std::size(e->grf_prop.spritegroup));
if (e->grf_prop.spritegroup[cargo] != nullptr) {
GrfMsg(6, "SetCustomEngineSprites: engine {} cargo {} already has group -- replacing", engine, cargo);
@ -1062,7 +1062,7 @@ VehicleResolverObject::VehicleResolverObject(EngineID engine_type, const Vehicle
if (this->root_spritegroup == nullptr) {
const Engine *e = Engine::Get(engine_type);
CargoID cargo = v != nullptr ? v->cargo_type : SpriteGroupCargo::SG_PURCHASE;
assert(cargo < lengthof(e->grf_prop.spritegroup));
assert(cargo < std::size(e->grf_prop.spritegroup));
this->root_spritegroup = e->grf_prop.spritegroup[cargo] != nullptr ? e->grf_prop.spritegroup[cargo] : e->grf_prop.spritegroup[SpriteGroupCargo::SG_DEFAULT];
}
}

View File

@ -127,7 +127,6 @@ struct RoadStopSpec {
*/
GRFFilePropsBase<NUM_CARGO + 3> grf_prop;
RoadStopClassID cls_id; ///< The class to which this spec belongs.
int spec_id; ///< The ID of this spec inside the class.
StringID name; ///< Name of this stop
RoadStopAvailabilityType stop_type = ROADSTOPTYPE_ALL;

View File

@ -504,7 +504,7 @@ uint32_t Waypoint::GetNewGRFVariable(const ResolverObject &, uint8_t variable, [
/* virtual */ const SpriteGroup *StationResolverObject::ResolveReal(const RealSpriteGroup *group) const
{
if (this->station_scope.st == nullptr || this->station_scope.statspec->cls_id == STAT_CLASS_WAYP) {
if (this->station_scope.st == nullptr || !Station::IsExpected(this->station_scope.st)) {
return group->loading[0];
}
@ -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;
}

View File

@ -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> tileflags; ///< List of tile flags.
AnimationInfo animation;
@ -173,6 +178,7 @@ struct StationSpec {
*/
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>;

View File

@ -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);
}
/**
@ -1461,12 +1464,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);
}
}
@ -2885,7 +2888,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();
}
/**
@ -2978,8 +2983,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);
}

View File

@ -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,
@ -37,6 +37,7 @@ enum StationType {
STATION_DOCK,
STATION_BUOY,
STATION_WAYPOINT,
STATION_END,
};
/** Types of RoadStops */

View File

@ -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),

View File

@ -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<std::span<const DrawTileSprites>, 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,
};
}};