From 10eeba86a6ea528f2f52b2c8e2144102bd82ca8c Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 23 Jul 2025 00:03:11 +0100 Subject: [PATCH] Codechange: Simplify/breakout logic for selecting bridge table sprites. Move various base offsets to separate functions where they can be reused and documented. No longer rely on coincidences to select the correct data between bridges and aqueducts. --- src/table/bridge_land.h | 13 ++-- src/tunnelbridge_cmd.cpp | 124 ++++++++++++++++++++++----------------- 2 files changed, 78 insertions(+), 59 deletions(-) diff --git a/src/table/bridge_land.h b/src/table/bridge_land.h index e6f4f7f185..0b72c7a42b 100644 --- a/src/table/bridge_land.h +++ b/src/table/bridge_land.h @@ -36,10 +36,15 @@ # define MW(a) {a, PALETTE_TO_STRUCT_WHITE} # define MC(a) {a, PALETTE_TO_STRUCT_CONCRETE} -static const PalSpriteID _aqueduct_sprites[] = { - { SPR_AQUEDUCT_MIDDLE_X, PAL_NONE }, { 0x0, PAL_NONE }, { SPR_AQUEDUCT_PILLAR_X, PAL_NONE }, { 0x0, PAL_NONE }, - { SPR_AQUEDUCT_MIDDLE_Y, PAL_NONE }, { 0x0, PAL_NONE }, { SPR_AQUEDUCT_PILLAR_Y, PAL_NONE }, { 0x0, PAL_NONE }, - { SPR_AQUEDUCT_RAMP_SW, PAL_NONE }, { SPR_AQUEDUCT_RAMP_SE, PAL_NONE }, { SPR_AQUEDUCT_RAMP_NE, PAL_NONE }, { SPR_AQUEDUCT_RAMP_NW, PAL_NONE }, +/* Sprite table for middle part of aqueduct. */ +static const PalSpriteID _aqueduct_sprite_table_middle[] = { + {SPR_AQUEDUCT_MIDDLE_X, PAL_NONE}, {0x0, PAL_NONE}, {SPR_AQUEDUCT_PILLAR_X, PAL_NONE}, {0x0, PAL_NONE}, // AXIS_X + {SPR_AQUEDUCT_MIDDLE_Y, PAL_NONE}, {0x0, PAL_NONE}, {SPR_AQUEDUCT_PILLAR_Y, PAL_NONE}, {0x0, PAL_NONE}, // AIXS_Y +}; + +/* Sprite table for head part of aqueduct. */ +static const PalSpriteID _aqueduct_sprite_table_heads[] = { + {SPR_AQUEDUCT_RAMP_SW, PAL_NONE}, {SPR_AQUEDUCT_RAMP_SE, PAL_NONE}, {SPR_AQUEDUCT_RAMP_NE, PAL_NONE}, {SPR_AQUEDUCT_RAMP_NW, PAL_NONE}, }; static const PalSpriteID _bridge_sprite_table_4_0[] = { diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index b90894777d..3184393fdd 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -138,15 +138,58 @@ bool HasBridgeFlatRamp(Slope tileh, Axis axis) return (tileh != SLOPE_FLAT); } -static inline std::span GetBridgeSpriteTable(int index, BridgePieces table) +/** + * Get the sprite table for a rail/road bridge piece. + * @param bridge_type Bridge type. + * @param piece Bridge piece. + * @return Sprite table for the bridge piece. + */ +static std::span GetBridgeSpriteTable(BridgeType bridge_type, BridgePieces piece) { - const BridgeSpec *bridge = GetBridgeSpec(index); - assert(table < NUM_BRIDGE_PIECES); - if (table < bridge->sprite_table.size() && !bridge->sprite_table[table].empty()) return bridge->sprite_table[table]; + assert(piece < NUM_BRIDGE_PIECES); - return _bridge_sprite_table[index][table]; + const BridgeSpec *bridge = GetBridgeSpec(bridge_type); + if (piece < bridge->sprite_table.size() && !bridge->sprite_table[piece].empty()) return bridge->sprite_table[piece]; + + return _bridge_sprite_table[bridge_type][piece]; } +/** + * Get the sprite table transport type base offset for a rail/road bridge. + * @param transport_type Transport type of bridge. + * @param ramp Tile of bridge ramp. + * @return Offset for transport type. + */ +static uint8_t GetBridgeSpriteTableBaseOffset(TransportType transport_type, TileIndex ramp) +{ + switch (transport_type) { + case TRANSPORT_RAIL: return GetRailTypeInfo(GetRailType(ramp))->bridge_offset; + case TRANSPORT_ROAD: return 8; + default: NOT_REACHED(); + } +} + +/** + * Get bridge sprite table base offset for the ramp part of bridge. + * @param diagdir Direction of ramp. + * @return Offset for direction. + */ +static uint8_t GetBridgeRampDirectionBaseOffset(DiagDirection diagdir) +{ + /* Bridge ramps are ordered SW, SE, NE, NW instead of NE, SE, SW, NW. */ + static constexpr uint8_t ramp_offsets[DIAGDIR_END] = {2, 1, 0, 3}; + return ramp_offsets[diagdir]; +} + +/** + * Get bridge sprite table base offset for the middle part of bridge. + * @param axis Axis of bridge. + * @return Offset for axis. + */ +static uint8_t GetBridgeMiddleAxisBaseOffset(Axis axis) +{ + return axis == AXIS_X ? 0 : 4; +} /** * Determines the foundation for the bridge head, and tests if the resulting slope is valid. @@ -1404,34 +1447,20 @@ static void DrawTile_TunnelBridge(TileInfo *ti) DrawBridgeMiddle(ti); } else { // IsBridge(ti->tile) - const PalSpriteID *psid; - int base_offset; - bool ice = HasTunnelBridgeSnowOrDesert(ti->tile); - - if (transport_type == TRANSPORT_RAIL) { - base_offset = GetRailTypeInfo(GetRailType(ti->tile))->bridge_offset; - assert(base_offset != 8); // This one is used for roads - } else { - base_offset = 8; - } - - /* as the lower 3 bits are used for other stuff, make sure they are clear */ - assert( (base_offset & 0x07) == 0x00); - DrawFoundation(ti, GetBridgeFoundation(ti->tileh, DiagDirToAxis(tunnelbridge_direction))); - /* HACK Wizardry to convert the bridge ramp direction into a sprite offset */ - base_offset += (6 - tunnelbridge_direction) % 4; - - /* Table number BRIDGE_PIECE_HEAD always refers to the bridge heads for any bridge type */ + uint base_offset = GetBridgeRampDirectionBaseOffset(tunnelbridge_direction); + std::span psid; if (transport_type != TRANSPORT_WATER) { if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head - psid = &GetBridgeSpriteTable(GetBridgeType(ti->tile), BRIDGE_PIECE_HEAD)[base_offset]; + base_offset += GetBridgeSpriteTableBaseOffset(transport_type, ti->tile); + psid = GetBridgeSpriteTable(GetBridgeType(ti->tile), BRIDGE_PIECE_HEAD); } else { - psid = _aqueduct_sprites + base_offset; + psid = _aqueduct_sprite_table_heads; } + psid = psid.subspan(base_offset, 1); - if (!ice) { + if (!HasTunnelBridgeSnowOrDesert(ti->tile)) { TileIndex next = ti->tile + TileOffsByDiagDir(tunnelbridge_direction); if (ti->tileh != SLOPE_FLAT && ti->z == 0 && HasTileWaterClass(next) && GetWaterClass(next) == WATER_CLASS_SEA) { DrawShoreTile(ti->tileh); @@ -1451,7 +1480,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti) * it doesn't disappear behind it */ /* Bridge heads are drawn solid no matter how invisibility/transparency is set */ - AddSortableSpriteToDraw(psid->sprite, psid->pal, *ti, {{}, {TILE_SIZE, TILE_SIZE, static_cast(ti->tileh == SLOPE_FLAT ? 0 : TILE_HEIGHT)}, {}}); + AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, *ti, {{}, {TILE_SIZE, TILE_SIZE, static_cast(ti->tileh == SLOPE_FLAT ? 0 : TILE_HEIGHT)}, {}}); if (transport_type == TRANSPORT_ROAD) { uint offset = tunnelbridge_direction; @@ -1572,33 +1601,21 @@ void DrawBridgeMiddle(const TileInfo *ti) TileIndex rampnorth = GetNorthernBridgeEnd(ti->tile); TileIndex rampsouth = GetSouthernBridgeEnd(ti->tile); TransportType transport_type = GetTunnelBridgeTransportType(rampsouth); - Axis axis = GetBridgeAxis(ti->tile); - BridgePieces piece = CalcBridgePiece( - GetTunnelBridgeLength(ti->tile, rampnorth) + 1, - GetTunnelBridgeLength(ti->tile, rampsouth) + 1 - ); - const PalSpriteID *psid; + uint base_offset = GetBridgeMiddleAxisBaseOffset(axis); + std::span psid; bool drawfarpillar; if (transport_type != TRANSPORT_WATER) { - BridgeType type = GetBridgeType(rampsouth); - drawfarpillar = !HasBit(GetBridgeSpec(type)->flags, 0); - - uint base_offset; - if (transport_type == TRANSPORT_RAIL) { - base_offset = GetRailTypeInfo(GetRailType(rampsouth))->bridge_offset; - } else { - base_offset = 8; - } - - psid = &GetBridgeSpriteTable(type, piece)[base_offset]; + BridgeType bridge_type = GetBridgeType(rampsouth); + drawfarpillar = !HasBit(GetBridgeSpec(bridge_type)->flags, 0); + base_offset += GetBridgeSpriteTableBaseOffset(transport_type, rampsouth); + psid = GetBridgeSpriteTable(bridge_type, CalcBridgePiece(GetTunnelBridgeLength(ti->tile, rampnorth) + 1, GetTunnelBridgeLength(ti->tile, rampsouth) + 1)); } else { drawfarpillar = true; - psid = _aqueduct_sprites; + psid = _aqueduct_sprite_table_middle; } - - if (axis != AXIS_X) psid += 4; + psid = psid.subspan(base_offset, 3); int x = ti->x; int y = ti->y; @@ -1614,14 +1631,12 @@ void DrawBridgeMiddle(const TileInfo *ti) /* Draw floor and far part of bridge*/ if (!IsInvisibilitySet(TO_BRIDGES)) { if (axis == AXIS_X) { - AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); + AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); } else { - AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); + AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); } } - psid++; - if (transport_type == TRANSPORT_ROAD) { /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */ DrawBridgeRoadBits(rampsouth, x, y, bridge_z, axis ^ 1, false); @@ -1654,10 +1669,10 @@ void DrawBridgeMiddle(const TileInfo *ti) if (!IsInvisibilitySet(TO_BRIDGES)) { if (axis == AXIS_X) { y += 12; - if (psid->sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{0, 3, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, -3, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); + if (psid[1].sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid[1].sprite, psid[1].pal, x, y, z, {{0, 3, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, -3, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); } else { x += 12; - if (psid->sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{3, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {-3, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); + if (psid[1].sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid[1].sprite, psid[1].pal, x, y, z, {{3, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {-3, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES)); } } @@ -1667,8 +1682,7 @@ void DrawBridgeMiddle(const TileInfo *ti) /* Do not draw anything more if bridges are invisible */ if (IsInvisibilitySet(TO_BRIDGES)) return; - psid++; - DrawBridgePillars(*psid, ti, axis, drawfarpillar, x, y, z); + DrawBridgePillars(psid[2], ti, axis, drawfarpillar, x, y, z); }