diff --git a/docs/landscape.html b/docs/landscape.html index 104b8f3931..9ec6230520 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -1757,7 +1757,49 @@
  • m7 bits 4..0: owner of road
  • -
  • m7 bit 5 set = on snow or desert
  • +
  • m7 bits 7..5: ground type, values greater than 1 are only valid for rail custom bridge heads + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    0  on grass, no fences
    1  on snow or desert
    2  on bare land
    3  facing NE, SE, SW: fence on the NW side, facing NW: fence on the SE side
    4  facing NE, SW: fence on the SE side, facing SE, NW: fence on the NE side
    5  facing NE, SE, NW: fence on the SW side, facing SW: fence on the NE side
    6  facing NE, SE: fence on the W side (track in the E corner), facing: SW, NW: fence on the E side (track in the W corner)
    7  facing NE, NW: fence on the S side (track in the N corner), facing: SE, SW: fence on the N side (track in the S corner)
    +
  • m8 bits 11..6: Tramtype
  • m8 bits 5..0: track type for railway
  • m8 bits 11..6 = secondary track type for railway (used for bridge-bypassing track when two parallel tracks on custom bridge head)
  • diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index 901c4fd3c1..1b9b1c02a4 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -362,7 +362,7 @@ the array so you can quickly see what is used and what is not. OOXX XXXX XOOX XXXX PPOO OOPP - OOXX XXXX + PPXX XXXX OOOO XXXX XXXX XXXX diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 265539f7e8..3681b4a878 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -651,6 +651,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u if (flags & DC_EXEC) { SubtractRailTunnelBridgeInfrastructure(tile, other_end); SetCustomBridgeHeadTrackBits(tile, future); + SetTunnelBridgeGroundBits(tile, IsRailCustomBridgeHead(tile) ? 2 : 0); if (secondary_piece) { SetSecondaryRailType(tile, railtype); } @@ -939,6 +940,7 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, } SetCustomBridgeHeadTrackBits(tile, future); + SetTunnelBridgeGroundBits(tile, IsRailCustomBridgeHead(tile) ? 2 : 0); AddRailTunnelBridgeInfrastructure(tile, other_end); DirtyCompanyInfrastructureWindows(_current_company); } @@ -2628,7 +2630,7 @@ static void DrawTrackFence_SW(const TileInfo *ti, SpriteID base_image, uint num_ * @param ti Tile drawing information. * @param rti Rail type information. */ -static void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti) +void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti, const RailGroundType rgt) { /* Base sprite for track fences. * Note: Halftile slopes only have fences on the upper part. */ @@ -2641,7 +2643,7 @@ static void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti) assert(num_sprites > 0); - switch (GetRailGroundType(ti->tile)) { + switch (rgt) { case RAIL_GROUND_FENCE_NW: DrawTrackFence_NW(ti, base_image, num_sprites); break; case RAIL_GROUND_FENCE_SE: DrawTrackFence_SE(ti, base_image, num_sprites); break; case RAIL_GROUND_FENCE_SENW: DrawTrackFence_NW(ti, base_image, num_sprites); @@ -2698,7 +2700,7 @@ static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInf static RailGroundType GetRailOrBridgeGroundType(TileInfo *ti) { if (IsTileType(ti->tile, MP_TUNNELBRIDGE)) { - return HasTunnelBridgeSnowOrDesert(ti->tile) ? RAIL_GROUND_ICE_DESERT : RAIL_GROUND_GRASS; + return GetTunnelBridgeGroundType(ti->tile); } else { return GetRailGroundType(ti->tile); } @@ -3115,7 +3117,7 @@ static void DrawTile_Track(TileInfo *ti, DrawTileProcParams params) DrawTrackBits(ti, rails); - if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti); + if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti, GetRailGroundType(ti->tile)); if (HasRailCatenaryDrawn(GetRailType(ti->tile), GetTileSecondaryRailTypeIfValid(ti->tile))) DrawRailCatenary(ti); @@ -3271,6 +3273,44 @@ static Foundation GetFoundation_Track(TileIndex tile, Slope tileh) return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh); } +RailGroundType RailTrackToFence(TileIndex tile, TrackBits rail) +{ + Owner owner = GetTileOwner(tile); + byte fences = 0; + + for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) { + static const TrackBits dir_to_trackbits[DIAGDIR_END] = {TRACK_BIT_3WAY_NE, TRACK_BIT_3WAY_SE, TRACK_BIT_3WAY_SW, TRACK_BIT_3WAY_NW}; + + /* Track bit on this edge => no fence. */ + if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue; + + TileIndex tile2 = tile + TileOffsByDiagDir(d); + + /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */ + if (!IsValidTile(tile2) || IsTileType(tile2, MP_HOUSE) || IsTileType(tile2, MP_INDUSTRY) || + IsTileType(tile2, MP_ROAD) || (IsTileType(tile2, MP_OBJECT) && !IsObjectType(tile2, OBJECT_OWNED_LAND)) || IsTileType(tile2, MP_TUNNELBRIDGE) || !IsTileOwner(tile2, owner)) { + fences |= 1 << d; + } + } + + RailGroundType new_ground; + switch (fences) { + case 0: new_ground = RAIL_GROUND_GRASS; break; + case (1 << DIAGDIR_NE): new_ground = RAIL_GROUND_FENCE_NE; break; + case (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_SE; break; + case (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_SW; break; + case (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_NW; break; + case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_NESW; break; + case (1 << DIAGDIR_SE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_SENW; break; + case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_VERT1; break; + case (1 << DIAGDIR_NE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_HORIZ2; break; + case (1 << DIAGDIR_SE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_HORIZ1; break; + case (1 << DIAGDIR_SW) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_VERT2; break; + default: NOT_REACHED(); + } + return new_ground; +} + static void TileLoop_Track(TileIndex tile) { RailGroundType old_ground = GetRailGroundType(tile); @@ -3352,39 +3392,7 @@ static void TileLoop_Track(TileIndex tile) if (IsPlainRail(tile) && old_ground != RAIL_GROUND_BARREN) { // wait until bottom is green /* determine direction of fence */ TrackBits rail = GetTrackBits(tile); - - Owner owner = GetTileOwner(tile); - byte fences = 0; - - for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) { - static const TrackBits dir_to_trackbits[DIAGDIR_END] = {TRACK_BIT_3WAY_NE, TRACK_BIT_3WAY_SE, TRACK_BIT_3WAY_SW, TRACK_BIT_3WAY_NW}; - - /* Track bit on this edge => no fence. */ - if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue; - - TileIndex tile2 = tile + TileOffsByDiagDir(d); - - /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */ - if (!IsValidTile(tile2) || IsTileType(tile2, MP_HOUSE) || IsTileType(tile2, MP_INDUSTRY) || - IsTileType(tile2, MP_ROAD) || (IsTileType(tile2, MP_OBJECT) && !IsObjectType(tile2, OBJECT_OWNED_LAND)) || IsTileType(tile2, MP_TUNNELBRIDGE) || !IsTileOwner(tile2, owner)) { - fences |= 1 << d; - } - } - - switch (fences) { - case 0: break; - case (1 << DIAGDIR_NE): new_ground = RAIL_GROUND_FENCE_NE; break; - case (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_SE; break; - case (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_SW; break; - case (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_NW; break; - case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_NESW; break; - case (1 << DIAGDIR_SE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_SENW; break; - case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_VERT1; break; - case (1 << DIAGDIR_NE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_HORIZ2; break; - case (1 << DIAGDIR_SE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_HORIZ1; break; - case (1 << DIAGDIR_SW) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_VERT2; break; - default: NOT_REACHED(); - } + new_ground = RailTrackToFence(tile, rail); } set_ground: diff --git a/src/rail_map.h b/src/rail_map.h index 2b4cfabb1c..89ba7a12e8 100644 --- a/src/rail_map.h +++ b/src/rail_map.h @@ -588,6 +588,7 @@ static inline bool IsSnowRailGround(TileIndex t) return GetRailGroundType(t) == RAIL_GROUND_ICE_DESERT; } +RailGroundType GetTunnelBridgeGroundType(TileIndex tile); static inline void MakeRailNormal(TileIndex t, Owner o, TrackBits b, RailType r) { diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index b69e53d516..29353e8848 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -1523,6 +1523,13 @@ bool AfterLoadGame() } } + if (!SlXvIsFeaturePresent(XSLFI_CUSTOM_BRIDGE_HEADS, 3)) { + /* fence/ground type support for custom rail bridges */ + for (TileIndex t = 0; t < map_size; t++) { + if (IsTileType(t, MP_TUNNELBRIDGE)) SB(_me[t].m7, 6, 2, 0); + } + } + /* Elrails got added in rev 24 */ if (IsSavegameVersionBefore(SLV_24)) { RailType min_rail = RAILTYPE_ELECTRIC; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index c211f36b99..789c72304e 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -87,7 +87,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 3, 3, "cargo_type_orders", nullptr, nullptr, "ORDX,VEOX" }, { XSLFI_EXTENDED_GAMELOG, XSCF_NULL, 1, 1, "extended_gamelog", nullptr, nullptr, nullptr }, { XSLFI_STATION_CATCHMENT_INC, XSCF_NULL, 1, 1, "station_catchment_inc", nullptr, nullptr, nullptr }, - { XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 2, 2, "custom_bridge_heads", nullptr, nullptr, nullptr }, + { XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 3, 3, "custom_bridge_heads", nullptr, nullptr, nullptr }, { XSLFI_CHUNNEL, XSCF_NULL, 2, 2, "chunnel", nullptr, nullptr, "TUNN" }, { XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 2, 2, "scheduled_dispatch", nullptr, nullptr, nullptr }, { XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL, 1, 1, "more_town_growth_rates", nullptr, nullptr, nullptr }, diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index e832807e3d..54ac8d1a60 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -1805,7 +1805,12 @@ static void DrawTile_TunnelBridge(TileInfo *ti, DrawTileProcParams params) return; } if (transport_type == TRANSPORT_RAIL && IsRailCustomBridgeHead(ti->tile)) { + const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); DrawTrackBits(ti, GetCustomBridgeHeadTrackBits(ti->tile)); + if (HasBit(_display_opt, DO_FULL_DETAIL)) { + extern void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti, const RailGroundType rgt); + DrawTrackDetails(ti, rti, GetTunnelBridgeGroundType(ti->tile)); + } if (HasRailCatenaryDrawn(GetRailType(ti->tile), GetTileSecondaryRailTypeIfValid(ti->tile))) { DrawRailCatenary(ti); } @@ -1816,7 +1821,6 @@ static void DrawTile_TunnelBridge(TileInfo *ti, DrawTileProcParams params) DiagDirection dir = GetTunnelBridgeDirection(ti->tile); SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC; - const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); Track t = FindFirstTrack(GetAcrossTunnelBridgeTrackBits(ti->tile)); auto draw_signals = [&](uint position, SignalOffsets image, DiagDirection towards) { @@ -2293,33 +2297,133 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td) } } +static const RailGroundType _tunnel_bridge_fence_table[4][5] = { + { // DIAGDIR_NE + RAIL_GROUND_FENCE_NW, + RAIL_GROUND_FENCE_SE, + RAIL_GROUND_FENCE_SW, + RAIL_GROUND_FENCE_VERT2, + RAIL_GROUND_FENCE_HORIZ1, + }, + { // DIAGDIR_SE + RAIL_GROUND_FENCE_NW, + RAIL_GROUND_FENCE_NE, + RAIL_GROUND_FENCE_SW, + RAIL_GROUND_FENCE_VERT2, + RAIL_GROUND_FENCE_HORIZ2, + }, + { // DIAGDIR_SW + RAIL_GROUND_FENCE_NW, + RAIL_GROUND_FENCE_SE, + RAIL_GROUND_FENCE_NE, + RAIL_GROUND_FENCE_VERT1, + RAIL_GROUND_FENCE_HORIZ2, + }, + { // DIAGDIR_NW + RAIL_GROUND_FENCE_SE, + RAIL_GROUND_FENCE_NE, + RAIL_GROUND_FENCE_SW, + RAIL_GROUND_FENCE_VERT1, + RAIL_GROUND_FENCE_HORIZ1, + }, +}; + +RailGroundType GetTunnelBridgeGroundType(TileIndex tile) +{ + uint8 ground_bits = GetTunnelBridgeGroundBits(tile); + if (ground_bits == 0) return RAIL_GROUND_GRASS; + if (ground_bits == 1) return RAIL_GROUND_ICE_DESERT; + if (ground_bits == 2) return RAIL_GROUND_BARREN; + return _tunnel_bridge_fence_table[GetTunnelBridgeDirection(tile)][ground_bits - 3]; +} + +static uint8 MapTunnelBridgeGroundTypeBits(TileIndex tile, RailGroundType type) +{ + uint8 ground_bits; + switch (type) { + case RAIL_GROUND_BARREN: + ground_bits = 2; + break; + + case RAIL_GROUND_GRASS: + ground_bits = 0; + break; + + case RAIL_GROUND_FENCE_NW: + ground_bits = 3; + break; + + case RAIL_GROUND_FENCE_SE: + ground_bits = GetTunnelBridgeDirection(tile) == DIAGDIR_NW ? 3 : 4; + break; + + case RAIL_GROUND_FENCE_NE: + ground_bits = GetTunnelBridgeDirection(tile) == DIAGDIR_SW ? 5 : 4; + break; + + case RAIL_GROUND_FENCE_SW: + ground_bits = 5; + break; + + case RAIL_GROUND_FENCE_VERT1: + case RAIL_GROUND_FENCE_VERT2: + ground_bits = 6; + break; + + case RAIL_GROUND_FENCE_HORIZ1: + case RAIL_GROUND_FENCE_HORIZ2: + ground_bits = 7; + break; + + case RAIL_GROUND_ICE_DESERT: + ground_bits = 1; + break; + + default: + NOT_REACHED(); + } + return ground_bits; +} static void TileLoop_TunnelBridge(TileIndex tile) { - bool snow_or_desert = HasTunnelBridgeSnowOrDesert(tile); + const uint8 old_ground_bits = GetTunnelBridgeGroundBits(tile); + bool snow_or_desert = false; switch (_settings_game.game_creation.landscape) { case LT_ARCTIC: { /* As long as we do not have a snow density, we want to use the density * from the entry edge. For tunnels this is the lowest point for bridges the highest point. * (Independent of foundations) */ int z = IsBridge(tile) ? GetTileMaxZ(tile) : GetTileZ(tile); - if (snow_or_desert != (z > GetSnowLine())) { - SetTunnelBridgeSnowOrDesert(tile, !snow_or_desert); - MarkTileDirtyByTile(tile); - } + snow_or_desert = (z > GetSnowLine()); break; } case LT_TROPIC: - if (GetTropicZone(tile) == TROPICZONE_DESERT && !snow_or_desert) { - SetTunnelBridgeSnowOrDesert(tile, true); - MarkTileDirtyByTile(tile); - } + snow_or_desert = (GetTropicZone(tile) == TROPICZONE_DESERT); break; default: break; } + + RailGroundType new_ground; + if (snow_or_desert) { + new_ground = RAIL_GROUND_ICE_DESERT; + } else { + new_ground = RAIL_GROUND_GRASS; + if (IsRailCustomBridgeHeadTile(tile) && old_ground_bits != 2) { // wait until bottom is green + /* determine direction of fence */ + TrackBits rail = GetCustomBridgeHeadTrackBits(tile); + extern RailGroundType RailTrackToFence(TileIndex tile, TrackBits rail); + new_ground = RailTrackToFence(tile, rail); + } + } + uint8 ground_bits = MapTunnelBridgeGroundTypeBits(tile, new_ground); + if (ground_bits != old_ground_bits) { + SetTunnelBridgeGroundBits(tile, ground_bits); + MarkTileDirtyByTile(tile); + } } static bool ClickTile_TunnelBridge(TileIndex tile) diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h index 7049caf0f0..7b3b595ca2 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -47,6 +47,12 @@ static inline TransportType GetTunnelBridgeTransportType(TileIndex t) return (TransportType)GB(_m[t].m5, 2, 2); } +static inline uint8 GetTunnelBridgeGroundBits(TileIndex t) +{ + assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t); + return GB(_me[t].m7, 5, 3); +} + /** * Tunnel: Is this tunnel entrance in a snowy or desert area? * Bridge: Does the bridge ramp lie in a snow or desert area? @@ -56,11 +62,9 @@ static inline TransportType GetTunnelBridgeTransportType(TileIndex t) */ static inline bool HasTunnelBridgeSnowOrDesert(TileIndex t) { - assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t); - return HasBit(_me[t].m7, 5); + return GetTunnelBridgeGroundBits(t) == 1; } - /** * Is this a rail bridge or tunnel? * @param t the tile that might be a rail bridge or tunnel @@ -73,6 +77,12 @@ static inline bool IsRailTunnelBridgeTile(TileIndex t) return IsTileType(t, MP_TUNNELBRIDGE) && (tt == TRANSPORT_RAIL); } +static inline void SetTunnelBridgeGroundBits(TileIndex t, uint8 bits) +{ + assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t); + SB(_me[t].m7, 5, 3, bits); +} + /** * Tunnel: Places this tunnel entrance in a snowy or desert area, or takes it out of there. * Bridge: Sets whether the bridge ramp lies in a snow or desert area. @@ -83,8 +93,7 @@ static inline bool IsRailTunnelBridgeTile(TileIndex t) */ static inline void SetTunnelBridgeSnowOrDesert(TileIndex t, bool snow_or_desert) { - assert_tile(IsTileType(t, MP_TUNNELBRIDGE), t); - SB(_me[t].m7, 5, 1, snow_or_desert); + SetTunnelBridgeGroundBits(t, snow_or_desert ? 1 : 0); } /**