diff --git a/source.list b/source.list index 8829cff564..19af997069 100644 --- a/source.list +++ b/source.list @@ -206,6 +206,7 @@ newgrf_house.h 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_generic.cpp newgrf_house.cpp newgrf_industries.cpp newgrf_industrytiles.cpp +newgrf_railtype.cpp newgrf_sound.cpp newgrf_spritegroup.cpp newgrf_station.cpp diff --git a/src/elrail.cpp b/src/elrail.cpp index 0cf7b905d2..41b0d5a740 100644 --- 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 TrackBits MaskWireBits(TileIndex t, TrackBits tracks) */ 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 GetWireBase(TileIndex tile) */ 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. diff --git a/src/newgrf.cpp b/src/newgrf.cpp index f8d9884890..d6db4c6f37 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -3037,6 +3037,7 @@ static void NewSpriteGroup(ByteReader *buf) 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 CargoMapSpriteGroup(ByteReader *buf, uint8 idcount) } } +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 @@ static void FeatureMapSpriteGroup(ByteReader *buf) 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 @@ static void AfterLoadGRFs() /* 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) { diff --git a/src/newgrf_railtype.cpp b/src/newgrf_railtype.cpp new file mode 100644 index 0000000000..b76eb2aef8 --- /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; +} diff --git a/src/newgrf_railtype.h b/src/newgrf_railtype.h new file mode 100644 index 0000000000..8dcd4fed07 --- /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 */ diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index a974056725..22c202aa68 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -339,6 +339,9 @@ struct ResolverObject { uint8 count; uint8 station_size; } generic; + struct { + TileIndex tile; + } routes; } u; uint32 (*GetRandomBits)(const struct ResolverObject*); diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp index d6dcba7b0f..52c7c1edb2 100644 --- 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 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by 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: diff --git a/src/rail.h b/src/rail.h index 3ea708db10..8caf2573dc 100644 --- a/src/rail.h +++ b/src/rail.h @@ -30,6 +30,54 @@ enum RailTypeFlags { }; 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 @@ struct RailtypeInfo { * 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; + } }; @@ -286,6 +344,11 @@ RailType GetRailTypeByLabel(RailTypeLabel label); */ void ResetRailTypes(); +/** + * Resolve sprites of custom rail types + */ +void InitRailTypes(); + /** * Allocate a new rail type label */ diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index c9e2c3916d..7de13a03a9 100644 --- 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 @@ void ResetRailTypes() 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 DrawTrackFence_WE_2(const TileInfo *ti, SpriteID base_image) } -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 @@ static void DrawTrackDetails(const TileInfo *ti) } } +/* 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 DrawTrackDetails(const TileInfo *ti) */ 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 @@ static void DrawTile_Track(TileInfo *ti) 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 @@ static void DrawTile_Track(TileInfo *ti) /* draw depot */ const DrawTileSprites *dts; PaletteID pal = PAL_NONE; + SpriteID relocation; if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); @@ -1979,8 +2167,12 @@ static void DrawTile_Track(TileInfo *ti) 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 @@ static void DrawTile_Track(TileInfo *ti) 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); } diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 8b7643cc17..c877bf0ef0 100644 --- 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 @@ static void DrawTile_Road(TileInfo *ti) 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++; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index b92adbd969..5ac5b7cac2 100644 --- 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 @@ static void DrawTile_Station(TileInfo *ti) 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 @@ static void DrawTile_Station(TileInfo *ti) } 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; - } else { - image += total_offset; - } - DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette)); + 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); - /* 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 (_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 { + 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 @@ void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, Ro 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); diff --git a/src/table/railtypes.h b/src/table/railtypes.h index a01200a057..75d69ee699 100644 --- a/src/table/railtypes.h +++ b/src/table/railtypes.h @@ -89,6 +89,8 @@ static const RailtypeInfo _original_railtypes[] = { /* rail type label */ 'RAIL', + + { NULL }, }, /** Electrified railway */ @@ -167,6 +169,8 @@ static const RailtypeInfo _original_railtypes[] = { /* rail type label */ 'ELRL', + + { NULL }, }, /** Monorail */ @@ -241,6 +245,8 @@ static const RailtypeInfo _original_railtypes[] = { /* rail type label */ 'MONO', + + { NULL }, }, /** Maglev */ @@ -315,6 +321,8 @@ static const RailtypeInfo _original_railtypes[] = { /* rail type label */ 'MGLV', + + { NULL }, }, }; diff --git a/src/table/road_land.h b/src/table/road_land.h index e6ec411bd8..19b8f57119 100644 --- a/src/table/road_land.h +++ b/src/table/road_land.h @@ -70,6 +70,20 @@ static const DrawTileSprites _tram_depot[] = { { {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 diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index a9d24c7a20..fdbfc8a67e 100644 --- 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 @@ static void DrawTile_TunnelBridge(TileInfo *ti) 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 @@ static void DrawTile_TunnelBridge(TileInfo *ti) /* 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 @@ static void DrawTile_TunnelBridge(TileInfo *ti) } 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 @@ void DrawBridgeMiddle(const TileInfo *ti) 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 @@ void DrawBridgeMiddle(const TileInfo *ti) 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); }