changeset 14487:338f7f48a0e7 draft

(svn r19056) -Add: [NewGRF] Action 3/2/1 (i.e. new graphics) support for rail types. (To be documented...)
author peter1138 <peter1138@openttd.org>
date Sun, 07 Feb 2010 22:22:54 +0000
parents 9faccf9e7894
children 9f9568145c7a
files source.list src/elrail.cpp src/newgrf.cpp src/newgrf_railtype.cpp src/newgrf_railtype.h src/newgrf_spritegroup.h src/newgrf_station.cpp src/rail.h src/rail_cmd.cpp src/road_cmd.cpp src/station_cmd.cpp src/table/railtypes.h src/table/road_land.h src/tunnelbridge_cmd.cpp
diffstat 14 files changed, 608 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/source.list
+++ b/source.list
@@ -206,6 +206,7 @@
 newgrf_industries.h
 newgrf_industrytiles.h
 newgrf_properties.h
+newgrf_railtype.h
 newgrf_sound.h
 newgrf_spritegroup.h
 newgrf_station.h
@@ -745,6 +746,7 @@
 newgrf_house.cpp
 newgrf_industries.cpp
 newgrf_industrytiles.cpp
+newgrf_railtype.cpp
 newgrf_sound.cpp
 newgrf_spritegroup.cpp
 newgrf_station.cpp
--- a/src/elrail.cpp
+++ b/src/elrail.cpp
@@ -64,6 +64,8 @@
 #include "elrail_func.h"
 #include "engine_base.h"
 #include "company_base.h"
+#include "rail.h"
+#include "newgrf_railtype.h"
 
 #include "table/sprites.h"
 #include "table/elrail_data.h"
@@ -165,7 +167,9 @@
  */
 static inline SpriteID GetWireBase(TileIndex tile)
 {
-	return SPR_WIRE_BASE;
+	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
+	SpriteID wires = GetCustomRailSprite(rti, tile, RTSG_WIRES);
+	return wires == 0 ? SPR_WIRE_BASE : wires;
 }
 
 /**
@@ -173,7 +177,9 @@
  */
 static inline SpriteID GetPylonBase(TileIndex tile)
 {
-	return SPR_PYLON_BASE;
+	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
+	SpriteID pylons = GetCustomRailSprite(rti, tile, RTSG_PYLONS);
+	return pylons == 0 ? SPR_PYLON_BASE : pylons;
 }
 
 /** Corrects the tileh for certain tile types. Returns an effective tileh for the track on the tile.
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -3037,6 +3037,7 @@
 				case GSF_STATION:
 				case GSF_CANAL:
 				case GSF_CARGOS:
+				case GSF_RAILTYPES:
 				{
 					byte sprites     = _cur_grffile->spriteset_numents;
 					byte num_loaded  = type;
@@ -3519,6 +3520,35 @@
 	}
 }
 
+static void RailTypeMapSpriteGroup(ByteReader *buf, uint8 idcount)
+{
+	uint8 *railtypes = AllocaM(uint8, idcount);
+	for (uint i = 0; i < idcount; i++) {
+		railtypes[i] = _cur_grffile->railtype_map[buf->ReadByte()];
+	}
+
+	uint8 cidcount = buf->ReadByte();
+	for (uint c = 0; c < cidcount; c++) {
+		uint8 ctype = buf->ReadByte();
+		uint16 groupid = buf->ReadWord();
+		if (!IsValidGroupID(groupid, "RailTypeMapSpriteGroup")) continue;
+
+		if (ctype >= RTSG_END) continue;
+
+		extern RailtypeInfo _railtypes[RAILTYPE_END];
+		for (uint i = 0; i < idcount; i++) {
+			if (railtypes[i] != INVALID_RAILTYPE) {
+				RailtypeInfo *rti = &_railtypes[railtypes[i]];
+
+				rti->group[ctype] = _cur_grffile->spritegroups[groupid];
+			}
+		}
+	}
+
+	/* Railtypes do not use the default group. */
+	buf->ReadWord();
+}
+
 
 /* Action 0x03 */
 static void FeatureMapSpriteGroup(ByteReader *buf)
@@ -3594,6 +3624,10 @@
 			CargoMapSpriteGroup(buf, idcount);
 			return;
 
+		case GSF_RAILTYPES:
+			RailTypeMapSpriteGroup(buf, idcount);
+			break;
+
 		default:
 			grfmsg(1, "FeatureMapSpriteGroup: Unsupported feature %d, skipping", feature);
 			return;
@@ -6446,6 +6480,9 @@
 	/* Load old shore sprites in new position, if they were replaced by ActionA */
 	ActivateOldShore();
 
+	/* Set up custom rail types */
+	InitRailTypes();
+
 	Engine *e;
 	FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
 		if (_gted[e->index].rv_max_speed != 0) {
new file mode 100644
--- /dev/null
+++ b/src/newgrf_railtype.cpp
@@ -0,0 +1,112 @@
+/* $Id$ */
+
+/** @file newgrf_railtype.cpp NewGRF handling of rail types. */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "variables.h"
+#include "debug.h"
+#include "strings_type.h"
+#include "rail.h"
+#include "road_map.h"
+#include "newgrf.h"
+#include "newgrf_callbacks.h"
+#include "newgrf_commons.h"
+#include "newgrf_railtype.h"
+#include "newgrf_spritegroup.h"
+
+static uint32 RailTypeGetRandomBits(const ResolverObject *object)
+{
+	return 0;
+}
+
+static uint32 RailTypeGetTriggers(const ResolverObject *object)
+{
+	return 0;
+}
+
+static void RailTypeSetTriggers(const ResolverObject *object, int triggers)
+{
+}
+
+static uint32 RailTypeGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
+{
+	TileIndex tile = object->u.routes.tile;
+
+	if (tile == INVALID_TILE) {
+		switch (variable) {
+			case 0x40: return 0;
+			case 0x41: return 0;
+			case 0x42: return 0;
+		}
+	}
+
+	switch (variable) {
+		case 0x40: return GetTerrainType(tile);
+		case 0x41: return 0;
+		case 0x42: return IsLevelCrossingTile(tile) && IsCrossingBarred(tile);
+	}
+
+	DEBUG(grf, 1, "Unhandled rail type tile property 0x%X", variable);
+
+	*available = false;
+	return UINT_MAX;
+}
+
+static const SpriteGroup *RailTypeResolveReal(const ResolverObject *object, const RealSpriteGroup *group)
+{
+	if (group->num_loading > 0) return group->loading[0];
+	if (group->num_loaded  > 0) return group->loaded[0];
+	return NULL;
+}
+
+static inline void NewRailTypeResolver(ResolverObject *res, TileIndex tile)
+{
+	res->GetRandomBits = &RailTypeGetRandomBits;
+	res->GetTriggers   = &RailTypeGetTriggers;
+	res->SetTriggers   = &RailTypeSetTriggers;
+	res->GetVariable   = &RailTypeGetVariable;
+	res->ResolveReal   = &RailTypeResolveReal;
+
+	res->u.routes.tile = tile;
+
+	res->callback        = CBID_NO_CALLBACK;
+	res->callback_param1 = 0;
+	res->callback_param2 = 0;
+	res->last_value      = 0;
+	res->trigger         = 0;
+	res->reseed          = 0;
+	res->count           = 0;
+}
+
+SpriteID GetCustomRailSprite(const RailtypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg)
+{
+	assert(rtsg < RTSG_END);
+
+	if (rti->group[rtsg] == NULL) return 0;
+
+	const SpriteGroup *group;
+	ResolverObject object;
+
+	NewRailTypeResolver(&object, tile);
+
+	group = SpriteGroup::Resolve(rti->group[rtsg], &object);
+	if (group == NULL || group->GetNumResults() == 0) return 0;
+
+	return group->GetResult();
+}
+
+uint8 GetReverseRailTypeTranslation(RailType railtype, const GRFFile *grffile)
+{
+	/* No rail type table present, return rail type as-is */
+	if (grffile->railtype_max == 0) return railtype;
+
+	/* Look for a matching rail type label in the table */
+	RailTypeLabel label = GetRailTypeInfo(railtype)->label;
+	for (uint i = 0; i < grffile->railtype_max; i++) {
+		if (label == grffile->railtype_list[i]) return i;
+	}
+
+	/* If not found, return as invalid */
+	return 0xFF;
+}
new file mode 100644
--- /dev/null
+++ b/src/newgrf_railtype.h
@@ -0,0 +1,12 @@
+/* $Id$ */
+
+#ifndef NEWGRF_RAILTYPE_H
+#define NEWGRF_RAILTYPE_H
+
+#include "rail.h"
+
+SpriteID GetCustomRailSprite(const RailtypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg);
+
+uint8 GetReverseRailTypeTranslation(RailType railtype, const GRFFile *grffile);
+
+#endif /* NEWGRF_RAILTYPE_H */
--- a/src/newgrf_spritegroup.h
+++ b/src/newgrf_spritegroup.h
@@ -339,6 +339,9 @@
 			uint8 count;
 			uint8 station_size;
 		} generic;
+		struct {
+			TileIndex tile;
+		} routes;
 	} u;
 
 	uint32 (*GetRandomBits)(const struct ResolverObject*);
--- a/src/newgrf_station.cpp
+++ b/src/newgrf_station.cpp
@@ -20,6 +20,7 @@
 #include "newgrf_station.h"
 #include "newgrf_spritegroup.h"
 #include "newgrf_sound.h"
+#include "newgrf_railtype.h"
 #include "town.h"
 #include "newgrf_town.h"
 #include "date_func.h"
@@ -442,7 +443,7 @@
 			if (!HasBit(_svc.valid, 1)) { _svc.v41 = GetPlatformInfoHelper(tile, true,  false, false); SetBit(_svc.valid, 1); }
 			return _svc.v41;
 
-		case 0x42: return GetTerrainType(tile) | (GetRailType(tile) << 8);
+		case 0x42: return GetTerrainType(tile) | (GetReverseRailTypeTranslation(GetRailType(tile), object->u.station.statspec->grffile) << 8);
 		case 0x43: return st->owner; // Station owner
 		case 0x44: return HasStationReservation(tile) ? 7 : 4; // PBS status
 		case 0x45:
--- a/src/rail.h
+++ b/src/rail.h
@@ -30,6 +30,54 @@
 };
 DECLARE_ENUM_AS_BIT_SET(RailTypeFlags);
 
+struct SpriteGroup;
+
+enum RailTypeSpriteGroup {
+	RTSG_CURSORS,     ///< Cursor and toolbar icon images
+	RTSG_OVERLAY,     ///< Images for overlaying track
+	RTSG_GROUND,      ///< Main group of ground images
+	RTSG_TUNNEL,      ///< Main group of ground images for snow or desert
+	RTSG_WIRES,       ///< Catenary wires
+	RTSG_PYLONS,      ///< Catenary pylons
+	RTSG_BRIDGE,      ///< Bridge surface images
+	RTSG_CROSSING,    ///< Level crossing overlay images
+	RTSG_DEPOT,       ///< Depot images
+	RTSG_FENCES,      ///< Fence images
+	RTSG_END,
+};
+
+/**
+ * Offsets for sprites within an overlay/underlay set.
+ * These are the same for overlay and underlay sprites.
+ */
+enum RailTrackOffset {
+	RTO_X,            ///< Piece of rail in X direction
+	RTO_Y,            ///< Piece of rail in Y direction
+	RTO_N,            ///< Piece of rail in northern corner
+	RTO_S,            ///< Piece of rail in southern corner
+	RTO_E,            ///< Piece of rail in eastern corner
+	RTO_W,            ///< Piece of rail in western corner
+	RTO_SLOPE_NE,     ///< Piece of rail on slope with north-east raised
+	RTO_SLOPE_SE,     ///< Piece of rail on slope with south-east raised
+	RTO_SLOPE_SW,     ///< Piece of rail on slope with south-west raised
+	RTO_SLOPE_NW,     ///< Piece of rail on slope with north-west raised
+	RTO_CROSSING_XY,  ///< Crossing of X and Y rail, with ballast
+	RTO_JUNCTION_SW,  ///< Ballast for junction 'pointing' SW
+	RTO_JUNCTION_NE,  ///< Ballast for junction 'pointing' NE
+	RTO_JUNCTION_SE,  ///< Ballast for junction 'pointing' SE
+	RTO_JUNCTION_NW,  ///< Ballast for junction 'pointing' NW
+	RTO_JUNCTION_NSEW,///< Ballast for full junction
+};
+
+/**
+ * Offsets for spries within a bridge surface overlay set.
+ */
+enum RailTrackBridgeOffset {
+	RTBO_X,     ///< Piece of rail in X direction
+	RTBO_Y,     ///< Piece of rail in Y direction
+	RTBO_SLOPE, ///< Sloped rail pieces, in order NE, SE, SW, NW
+};
+
 /** Offsets from base sprite for fence sprites. These are in the order of
  *  the sprites in the original data files.
  */
@@ -154,6 +202,16 @@
 	 * Unique 32 bit rail type identifier
 	 */
 	RailTypeLabel label;
+
+	/**
+	 * Sprite groups for resolving sprites
+	 */
+	const SpriteGroup *group[RTSG_END];
+
+	inline bool UsesOverlay() const
+	{
+		return this->group[RTSG_GROUND] != NULL;
+	}
 };
 
 
@@ -287,6 +345,11 @@
 void ResetRailTypes();
 
 /**
+ * Resolve sprites of custom rail types
+ */
+void InitRailTypes();
+
+/**
  * Allocate a new rail type label
  */
 RailType AllocateRailType(RailTypeLabel label);
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -19,6 +19,8 @@
 #include "pathfinder/yapf/yapf_cache.h"
 #include "newgrf_engine.h"
 #include "landscape_type.h"
+#include "newgrf_railtype.h"
+#include "newgrf_commons.h"
 #include "train.h"
 #include "variables.h"
 #include "autoslope.h"
@@ -52,6 +54,37 @@
 	memcpy(_railtypes, _original_railtypes, sizeof(_original_railtypes));
 }
 
+void ResolveRailTypeGUISprites(RailtypeInfo *rti)
+{
+	SpriteID cursors_base = GetCustomRailSprite(rti, INVALID_TILE, RTSG_CURSORS);
+	if (cursors_base != 0) {
+		rti->gui_sprites.build_ns_rail = cursors_base +  0;
+		rti->gui_sprites.build_x_rail  = cursors_base +  1;
+		rti->gui_sprites.build_ew_rail = cursors_base +  2;
+		rti->gui_sprites.build_y_rail  = cursors_base +  3;
+		rti->gui_sprites.auto_rail     = cursors_base +  4;
+		rti->gui_sprites.build_depot   = cursors_base +  5;
+		rti->gui_sprites.build_tunnel  = cursors_base +  6;
+		rti->gui_sprites.convert_rail  = cursors_base +  7;
+		rti->cursor.rail_ns   = cursors_base +  8;
+		rti->cursor.rail_swne = cursors_base +  9;
+		rti->cursor.rail_ew   = cursors_base + 10;
+		rti->cursor.rail_nwse = cursors_base + 11;
+		rti->cursor.autorail  = cursors_base + 12;
+		rti->cursor.depot     = cursors_base + 13;
+		rti->cursor.tunnel    = cursors_base + 14;
+		rti->cursor.convert   = cursors_base + 15;
+	}
+}
+
+void InitRailTypes()
+{
+	for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
+		RailtypeInfo *rti = &_railtypes[rt];
+		ResolveRailTypeGUISprites(rti);
+	}
+}
+
 RailType AllocateRailType(RailTypeLabel label)
 {
 	for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
@@ -1709,10 +1742,11 @@
 }
 
 
-static void DrawTrackDetails(const TileInfo *ti)
+static void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti)
 {
 	/* Base sprite for track fences. */
-	SpriteID base_image = SPR_TRACK_FENCE_FLAT_X;
+	SpriteID base_image = GetCustomRailSprite(rti, ti->tile, RTSG_FENCES);
+	if (base_image == 0) base_image = SPR_TRACK_FENCE_FLAT_X;
 
 	switch (GetRailGroundType(ti->tile)) {
 		case RAIL_GROUND_FENCE_NW:     DrawTrackFence_NW(ti, base_image);    break;
@@ -1747,6 +1781,162 @@
 	}
 }
 
+/* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */
+static const int INF = 1000; // big number compared to tilesprite size
+static const SubSprite _halftile_sub_sprite[4] = {
+	{ -INF    , -INF  , 32 - 33, INF     }, // CORNER_W, clip 33 pixels from right
+	{ -INF    ,  0 + 7, INF    , INF     }, // CORNER_S, clip 7 pixels from top
+	{ -31 + 33, -INF  , INF    , INF     }, // CORNER_E, clip 33 pixels from left
+	{ -INF    , -INF  , INF    , 30 - 23 }  // CORNER_N, clip 23 pixels from bottom
+};
+
+static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
+{
+	DrawGroundSprite(sprite, pal, NULL, 0, (ti->tileh & s) ? -8 : 0);
+}
+
+static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeInfo *rti)
+{
+	RailGroundType rgt = GetRailGroundType(ti->tile);
+	Foundation f = GetRailFoundation(ti->tileh, track);
+	Corner halftile_corner = CORNER_INVALID;
+
+	if (IsNonContinuousFoundation(f)) {
+		/* Save halftile corner */
+		halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f));
+		/* Draw lower part first */
+		track &= ~CornerToTrackBits(halftile_corner);
+		f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE);
+	}
+
+	DrawFoundation(ti, f);
+	/* DrawFoundation modifies ti */
+
+	/* Draw ground */
+	if (track == TRACK_BIT_NONE && rgt == RAIL_GROUND_WATER) {
+		if (IsSteepSlope(ti->tileh)) {
+			DrawShoreTile(ti->tileh);
+		} else {
+			DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
+		}
+	} else {
+		SpriteID image;
+
+		switch (rgt) {
+			case RAIL_GROUND_BARREN:     image = SPR_FLAT_BARE_LAND;  break;
+			case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOW_DESERT_TILE; break;
+			default:                     image = SPR_FLAT_GRASS_TILE; break;
+		}
+
+		image += _tileh_to_sprite[ti->tileh];
+
+		DrawGroundSprite(image, PAL_NONE);
+	}
+
+	SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
+	SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
+	TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE;
+
+	if (track == TRACK_BIT_NONE) {
+		/* Half-tile foundation, no track here? */
+	} else if (ti->tileh == SLOPE_NW && track == TRACK_BIT_Y) {
+		DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
+		if (pbs != TRACK_BIT_NONE) DrawGroundSprite(overlay + 9, PALETTE_CRASH);
+	} else if (ti->tileh == SLOPE_NE && track == TRACK_BIT_X) {
+		DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
+		if (pbs != TRACK_BIT_NONE) DrawGroundSprite(overlay + 6, PALETTE_CRASH);
+	} else if (ti->tileh == SLOPE_SE && track == TRACK_BIT_Y) {
+		DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
+		if (pbs != TRACK_BIT_NONE) DrawGroundSprite(overlay + 7, PALETTE_CRASH);
+	} else if (ti->tileh == SLOPE_SW && track == TRACK_BIT_X) {
+		DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
+		if (pbs != TRACK_BIT_NONE) DrawGroundSprite(overlay + 8, PALETTE_CRASH);
+	} else {
+		switch (track) {
+			/* Draw single ground sprite when not overlapping. No track overlay
+			 * is necessary for these sprites. */
+			case TRACK_BIT_X:     DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
+			case TRACK_BIT_Y:     DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
+			case TRACK_BIT_UPPER: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
+			case TRACK_BIT_LOWER: DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
+			case TRACK_BIT_RIGHT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
+			case TRACK_BIT_LEFT:  DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
+			case TRACK_BIT_CROSS: DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
+			case TRACK_BIT_HORZ:  DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
+			                      DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
+			case TRACK_BIT_VERT:  DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
+			                      DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
+
+			default:
+				/* We're drawing a junction tile */
+				if ((track & TRACK_BIT_3WAY_NE) == 0) {
+					DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
+				} else if ((track & TRACK_BIT_3WAY_SW) == 0) {
+					DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
+				} else if ((track & TRACK_BIT_3WAY_NW) == 0) {
+					DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
+				} else if ((track & TRACK_BIT_3WAY_SE) == 0) {
+					DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
+				} else {
+					DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
+				}
+
+				/* Mask out PBS bits as we shall draw them afterwards anyway. */
+				track &= ~pbs;
+
+				/* Draw regular track bits */
+				if (track & TRACK_BIT_X)     DrawGroundSprite(overlay + RTO_X, PAL_NONE);
+				if (track & TRACK_BIT_Y)     DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
+				if (track & TRACK_BIT_UPPER) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
+				if (track & TRACK_BIT_LOWER) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
+				if (track & TRACK_BIT_RIGHT) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
+				if (track & TRACK_BIT_LEFT)  DrawGroundSprite(overlay + RTO_W, PAL_NONE);
+		}
+
+		/* Draw reserved track bits */
+		if (pbs & TRACK_BIT_X)     DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
+		if (pbs & TRACK_BIT_Y)     DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
+		if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
+		if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
+		if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
+		if (pbs & TRACK_BIT_LEFT)  DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
+	}
+
+	if (IsValidCorner(halftile_corner)) {
+		DrawFoundation(ti, HalftileFoundation(halftile_corner));
+
+		/* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
+		Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
+
+		SpriteID image;
+		switch (rgt) {
+			case RAIL_GROUND_BARREN:     image = SPR_FLAT_BARE_LAND;  break;
+			case RAIL_GROUND_ICE_DESERT:
+			case RAIL_GROUND_HALF_SNOW:  image = SPR_FLAT_SNOW_DESERT_TILE; break;
+			default:                     image = SPR_FLAT_GRASS_TILE; break;
+		}
+
+		image += _tileh_to_sprite[fake_slope];
+
+		DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
+
+		track = CornerToTrackBits(halftile_corner);
+
+		int offset;
+		switch (track) {
+			default: NOT_REACHED();
+			case TRACK_BIT_UPPER: offset = RTO_N; break;
+			case TRACK_BIT_LOWER: offset = RTO_S; break;
+			case TRACK_BIT_RIGHT: offset = RTO_E; break;
+			case TRACK_BIT_LEFT:  offset = RTO_W; break;
+		}
+
+		DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
+		if (HasReservedTracks(ti->tile, track)) {
+			DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
+		}
+	}
+}
 
 /**
  * Draw ground sprite and track bits
@@ -1755,16 +1945,13 @@
  */
 static void DrawTrackBits(TileInfo *ti, TrackBits track)
 {
-	/* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */
-	static const int INF = 1000; // big number compared to tilesprite size
-	static const SubSprite _halftile_sub_sprite[4] = {
-		{ -INF    , -INF  , 32 - 33, INF     }, // CORNER_W, clip 33 pixels from right
-		{ -INF    ,  0 + 7, INF    , INF     }, // CORNER_S, clip 7 pixels from top
-		{ -31 + 33, -INF  , INF    , INF     }, // CORNER_E, clip 33 pixels from left
-		{ -INF    , -INF  , INF    , 30 - 23 }  // CORNER_N, clip 23 pixels from bottom
-	};
-
 	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+
+	if (rti->UsesOverlay()) {
+		DrawTrackBitsOverlay(ti, track, rti);
+		return;
+	}
+
 	RailGroundType rgt = GetRailGroundType(ti->tile);
 	Foundation f = GetRailFoundation(ti->tileh, track);
 	Corner halftile_corner = CORNER_INVALID;
@@ -1960,7 +2147,7 @@
 
 		DrawTrackBits(ti, rails);
 
-		if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti);
+		if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti);
 
 		if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti);
 
@@ -1969,6 +2156,7 @@
 		/* draw depot */
 		const DrawTileSprites *dts;
 		PaletteID pal = PAL_NONE;
+		SpriteID relocation;
 
 		if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
 
@@ -1979,8 +2167,12 @@
 			dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)];
 		}
 
-		image = dts->ground.sprite;
-		if (image != SPR_FLAT_GRASS_TILE) image += rti->total_offset;
+		if (rti->UsesOverlay()) {
+			image = SPR_FLAT_GRASS_TILE;
+		} else {
+			image = dts->ground.sprite;
+			if (image != SPR_FLAT_GRASS_TILE) image += rti->total_offset;
+		}
 
 		/* adjust ground tile for desert
 		 * don't adjust for snow, because snow in depots looks weird */
@@ -1994,19 +2186,43 @@
 
 		DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, _drawtile_track_palette));
 
-		/* PBS debugging, draw reserved tracks darker */
-		if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
+		if (rti->UsesOverlay()) {
+			SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
+
 			switch (GetRailDepotDirection(ti->tile)) {
-				case DIAGDIR_SW: DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH); break;
-				case DIAGDIR_SE: DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH); break;
+				case DIAGDIR_SW: DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
+				case DIAGDIR_SE: DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
 				default: break;
 			}
+
+			if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
+				SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
+
+				switch (GetRailDepotDirection(ti->tile)) {
+					case DIAGDIR_SW: DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH); break;
+					case DIAGDIR_SE: DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH); break;
+					default: break;
+				}
+			}
+
+			relocation  = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT);
+			relocation -= SPR_RAIL_DEPOT_SE_1;
+		} else {
+			/* PBS debugging, draw reserved tracks darker */
+			if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
+				switch (GetRailDepotDirection(ti->tile)) {
+					case DIAGDIR_SW: DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH); break;
+					case DIAGDIR_SE: DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH); break;
+					default: break;
+				}
+			}
+
+			relocation = rti->total_offset;
 		}
 
 		if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti);
 
-		/* No NewGRF depots, so no relocation */
-		DrawRailTileSeq(ti, dts, TO_BUILDINGS, rti->total_offset, 0, _drawtile_track_palette);
+		DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, _drawtile_track_palette);
 	}
 	DrawBridgeMiddle(ti);
 }
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -35,6 +35,7 @@
 #include "town.h"
 #include "company_base.h"
 #include "core/random_func.hpp"
+#include "newgrf_railtype.h"
 
 #include "table/strings.h"
 
@@ -1164,7 +1165,36 @@
 		case ROAD_TILE_CROSSING: {
 			if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
 
-			SpriteID image = GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.crossing;
+			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+
+			if (rti->UsesOverlay()) {
+				Axis axis = GetCrossingRailAxis(ti->tile);
+				SpriteID road = SPR_ROAD_Y + axis;
+				PaletteID pal = PAL_NONE;
+
+				Roadside roadside = GetRoadside(ti->tile);
+
+				if (AlwaysDrawUnpavedRoads(ti->tile, roadside)) {
+					road += 19;
+				} else {
+					switch (roadside) {
+						case ROADSIDE_BARREN: pal = PALETTE_TO_BARE_LAND; break;
+						case ROADSIDE_GRASS:  break;
+						default:              road -= 19; break; // Paved
+					}
+				}
+
+				DrawGroundSprite(road, pal);
+
+				SpriteID rail = GetCustomRailSprite(rti, ti->tile, RTSG_CROSSING) + axis;
+				DrawGroundSprite(rail, PAL_NONE);
+				DrawRailTileSeq(ti, &_crossing_layout, TO_CATENARY, rail, 0, PAL_NONE);
+
+				if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti);
+				break;
+			}
+
+			SpriteID image = rti->base_sprites.crossing;
 			PaletteID pal = PAL_NONE;
 
 			if (GetCrossingRoadAxis(ti->tile) == AXIS_X) image++;
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -39,6 +39,7 @@
 #include "elrail_func.h"
 #include "station_base.h"
 #include "roadstop_base.h"
+#include "newgrf_railtype.h"
 #include "waypoint_base.h"
 #include "waypoint_func.h"
 #include "pbs.h"
@@ -2303,12 +2304,13 @@
 	RoadTypes roadtypes;
 	int32 total_offset;
 	int32 custom_ground_offset;
+	const RailtypeInfo *rti = NULL;
 	uint32 relocation = 0;
 	const BaseStation *st = NULL;
 	const StationSpec *statspec = NULL;
 
 	if (HasStationRail(ti->tile)) {
-		const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+		rti = GetRailTypeInfo(GetRailType(ti->tile));
 		roadtypes = ROADTYPES_NONE;
 		total_offset = rti->total_offset;
 		custom_ground_offset = rti->custom_ground_offset;
@@ -2446,18 +2448,29 @@
 	} else {
 		SpriteID image = t->ground.sprite;
 		PaletteID pal  = t->ground.pal;
-		if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
-			image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
-			image += custom_ground_offset;
+		if (rti != NULL && rti->UsesOverlay() && (image == SPR_RAIL_TRACK_X || image == SPR_RAIL_TRACK_Y)) {
+			SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
+			DrawGroundSprite(SPR_FLAT_GRASS_TILE, PAL_NONE);
+			DrawGroundSprite(ground + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE);
+
+			if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
+				SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
+				DrawGroundSprite(overlay + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PALETTE_CRASH);
+			}
 		} else {
-			image += total_offset;
-		}
-		DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
-
-		/* PBS debugging, draw reserved tracks darker */
-		if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
-			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
-			DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
+			if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
+				image += GetCustomStationGroundRelocation(statspec, st, ti->tile);
+				image += custom_ground_offset;
+			} else {
+				image += total_offset;
+			}
+			DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
+
+			/* PBS debugging, draw reserved tracks darker */
+			if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
+				const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+				DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
+			}
 		}
 	}
 
@@ -2482,14 +2495,21 @@
 	int32 total_offset = 0;
 	PaletteID pal = COMPANY_SPRITE_COLOUR(_local_company);
 	const DrawTileSprites *t = &_station_display_datas[st][image];
+	const RailtypeInfo *rti = NULL;
 
 	if (railtype != INVALID_RAILTYPE) {
-		const RailtypeInfo *rti = GetRailTypeInfo(railtype);
+		rti = GetRailTypeInfo(railtype);
 		total_offset = rti->total_offset;
 	}
 
 	SpriteID img = t->ground.sprite;
-	DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
+	if ((img == SPR_RAIL_TRACK_X || img == SPR_RAIL_TRACK_Y) && rti->UsesOverlay()) {
+		SpriteID ground = GetCustomRailSprite(rti, INVALID_TILE, RTSG_GROUND);
+		DrawSprite(SPR_FLAT_GRASS_TILE, PAL_NONE, x, y);
+		DrawSprite(ground + (img == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE, x, y);
+	} else {
+		DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
+	}
 
 	if (roadtype == ROADTYPE_TRAM) {
 		DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y);
--- a/src/table/railtypes.h
+++ b/src/table/railtypes.h
@@ -89,6 +89,8 @@
 
 		/* rail type label */
 		'RAIL',
+
+		{ NULL },
 	},
 
 	/** Electrified railway */
@@ -167,6 +169,8 @@
 
 		/* rail type label */
 		'ELRL',
+
+		{ NULL },
 	},
 
 	/** Monorail */
@@ -241,6 +245,8 @@
 
 		/* rail type label */
 		'MONO',
+
+		{ NULL },
 	},
 
 	/** Maglev */
@@ -315,6 +321,8 @@
 
 		/* rail type label */
 		'MGLV',
+
+		{ NULL },
 	},
 };
 
--- a/src/table/road_land.h
+++ b/src/table/road_land.h
@@ -70,6 +70,20 @@
 	{ {0xA4A, PAL_NONE}, _tram_depot_NW }
 };
 
+/* Sprite layout for level crossings. The SpriteIDs are actually offsets
+ * from the base SpriteID returned from the NewGRF sprite resolver. */
+static const DrawTileSeqStruct _crossing_layout_ALL[] = {
+	TILE_SEQ_LINE(2, PAL_NONE,  0,  0, 3, 3)
+	TILE_SEQ_LINE(4, PAL_NONE,  0, 13, 3, 3)
+	TILE_SEQ_LINE(6, PAL_NONE, 13,  0, 3, 3)
+	TILE_SEQ_LINE(8, PAL_NONE, 13, 13, 3, 3)
+	TILE_SEQ_END()
+};
+
+static const DrawTileSprites _crossing_layout = {
+	{0, PAL_NONE}, _crossing_layout_ALL
+};
+
 #undef TILE_SEQ_LINE
 #undef TILE_SEQ_END
 
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -40,6 +40,7 @@
 #include "pbs.h"
 #include "company_base.h"
 #include "engine_base.h"
+#include "newgrf_railtype.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -930,14 +931,22 @@
 					AddSortableSpriteToDraw(SPR_TRAMWAY_TUNNEL_WIRES + tunnelbridge_direction, PAL_NONE, ti->x, ti->y, BB_data[10], BB_data[11], TILE_HEIGHT, ti->z, IsTransparencySet(TO_CATENARY), BB_data[8], BB_data[9], BB_Z_SEPARATOR);
 				}
 			}
-		} else if (HasCatenaryDrawn(GetRailType(ti->tile))) {
-			/* Maybe draw pylons on the entry side */
-			DrawCatenary(ti);
+		} else {
+			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+			if (rti->UsesOverlay()) {
+				SpriteID surface = GetCustomRailSprite(rti, ti->tile, RTSG_TUNNEL);
+				if (surface != 0) DrawGroundSprite(surface + tunnelbridge_direction, PAL_NONE);
+			}
 
-			catenary = true;
-			StartSpriteCombine();
-			/* Draw wire above the ramp */
-			DrawCatenaryOnTunnel(ti);
+			if (HasCatenaryDrawn(GetRailType(ti->tile))) {
+				/* Maybe draw pylons on the entry side */
+				DrawCatenary(ti);
+
+				catenary = true;
+				StartSpriteCombine();
+				/* Draw wire above the ramp */
+				DrawCatenaryOnTunnel(ti);
+			}
 		}
 
 		AddSortableSpriteToDraw(image + 1, PAL_NONE, ti->x + TILE_SIZE - 1, ti->y + TILE_SIZE - 1, BB_data[0], BB_data[1], TILE_HEIGHT, ti->z, false, BB_data[2], BB_data[3], BB_Z_SEPARATOR);
@@ -995,15 +1004,6 @@
 		/* Bridge heads are drawn solid no matter how invisibility/transparency is set */
 		AddSortableSpriteToDraw(psid->sprite, psid->pal, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 0 : 8, ti->z);
 
-		if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && transport_type == TRANSPORT_RAIL && HasTunnelBridgeReservation(ti->tile)) {
-			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
-			if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
-				AddSortableSpriteToDraw(DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + 8);
-			} else {
-				AddSortableSpriteToDraw(rti->base_sprites.single_sloped + tunnelbridge_direction, PALETTE_CRASH, ti->x, ti->y, 16, 16, 8, ti->z);
-			}
-		}
-
 		if (transport_type == TRANSPORT_ROAD) {
 			RoadTypes rts = GetRoadTypes(ti->tile);
 
@@ -1021,6 +1021,26 @@
 			}
 			EndSpriteCombine();
 		} else if (transport_type == TRANSPORT_RAIL) {
+			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+			if (rti->UsesOverlay()) {
+				SpriteID surface = GetCustomRailSprite(rti, ti->tile, RTSG_BRIDGE);
+				if (surface != 0) {
+					if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
+						AddSortableSpriteToDraw(surface + ((DiagDirToAxis(tunnelbridge_direction) == AXIS_X) ? RTBO_X : RTBO_Y), PAL_NONE, ti->x, ti->y, 16, 16, 0, ti->z + 8);
+					} else {
+						AddSortableSpriteToDraw(surface + RTBO_SLOPE + tunnelbridge_direction, PAL_NONE, ti->x, ti->y, 16, 16, 8, ti->z);
+					}
+				}
+				/* Don't fallback to non-overlay sprite -- the spec states that
+				 * if an overlay is present then the bridge surface must be
+				 * present. */
+			} else if (_game_mode != GM_MENU &&_settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) {
+				if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
+					AddSortableSpriteToDraw(DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + 8);
+				} else {
+					AddSortableSpriteToDraw(rti->base_sprites.single_sloped + tunnelbridge_direction, PALETTE_CRASH, ti->x, ti->y, 16, 16, 8, ti->z);
+				}
+			}
 			EndSpriteCombine();
 			if (HasCatenaryDrawn(GetRailType(ti->tile))) {
 				DrawCatenary(ti);
@@ -1128,7 +1148,7 @@
 	AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, x, y, 16, 16, 1, bridge_z - TILE_HEIGHT + BB_Z_SEPARATOR);
 
 	/* Draw Trambits as SpriteCombine */
-	if (transport_type == TRANSPORT_ROAD) StartSpriteCombine();
+	if (transport_type == TRANSPORT_ROAD || transport_type == TRANSPORT_RAIL) StartSpriteCombine();
 
 	/* Draw floor and far part of bridge*/
 	if (!IsInvisibilitySet(TO_BRIDGES)) {
@@ -1152,6 +1172,15 @@
 			StartSpriteCombine();
 		}
 	} else if (transport_type == TRANSPORT_RAIL) {
+		const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(rampsouth));
+		if (rti->UsesOverlay()) {
+			SpriteID surface = GetCustomRailSprite(rti, ti->tile, RTSG_BRIDGE);
+			if (surface != 0) {
+				AddSortableSpriteToDraw(surface + axis, PAL_NONE, x, y, 16, 16, 0, bridge_z, IsTransparencySet(TO_BRIDGES));
+			}
+		}
+		EndSpriteCombine();
+
 		if (HasCatenaryDrawn(GetRailType(rampsouth))) {
 			DrawCatenaryOnBridge(ti);
 		}