1
0
Fork 0

Compare commits

...

4 Commits

Author SHA1 Message Date
SamuXarick 8dc545ab46
Merge f0382c37ee into 10eeba86a6 2025-07-23 19:31:25 +00:00
Peter Nelson 10eeba86a6 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.
2025-07-23 20:31:15 +01:00
Peter Nelson d99dad9e9e Codechange: Pass bridge pillar palette/sprite by reference. 2025-07-23 20:31:15 +01:00
SamuXarick f0382c37ee Fix #12193: [YAPF] Don't use the entire docking area when computing closest station tile
Don't use CalcClosestStationTile for ships. Instead, use a specialized GetShipDestinationTiles which creates a list of possible destination tiles. For docks, it only takes into consideration the tiles that pass both IsDockingTile and IsShipDestinationTile tests. For the other cases it just takes the ship's current dest_tile.

Modified CYapfDestinationTileWaterT class to accept multiple destinations. In this manner, the estimate cost is calculated for each of the destination tiles and the shortest estimate is returned. Some adaptation was necessary to make this possible to work for both the high- and low-level pathfinders and the existing functions. ChooseShipTrack and its FindWaterRegionPath brother both will take the same destination tiles which are calculated at either YapfShipChooseTrack or YapfShipCheckReverse.
2025-02-16 20:54:32 +00:00
6 changed files with 168 additions and 113 deletions

View File

@ -12,6 +12,40 @@
#include "../tile_cmd.h"
#include "../waypoint_base.h"
#include "../ship.h"
/**
* Creates a list containing possible destination tiles for a ship.
* @param v The ship
* return Vector of tiles filled with all possible destinations.
*/
inline std::vector<TileIndex> GetShipDestinationTiles(const Ship *v)
{
std::vector<TileIndex> dest_tiles;
if (v->current_order.IsType(OT_GOTO_STATION)) {
const StationID station = v->current_order.GetDestination().ToStationID();
const BaseStation *st = BaseStation::Get(station);
TileArea ta;
st->GetTileArea(&ta, StationType::Dock);
/* If the dock station is (temporarily) not present, use the station sign to drive near the station. */
if (ta.tile == INVALID_TILE) {
dest_tiles.push_back(st->xy);
} else {
for (const TileIndex &docking_tile : ta) {
if (!IsDockingTile(docking_tile) || !IsShipDestinationTile(docking_tile, station)) continue;
dest_tiles.push_back(docking_tile);
}
}
} else {
dest_tiles.push_back(v->dest_tile);
}
assert(!dest_tiles.empty());
return dest_tiles;
}
/**
* Calculates the tile of given station that is closest to a given tile

View File

@ -33,8 +33,7 @@ public:
typedef typename Node::Key Key; ///< key to hash tables.
protected:
TileIndex dest_tile;
TrackdirBits dest_trackdirs;
std::span<TileIndex> dest_tiles;
StationID dest_station;
bool has_intermediate_dest = false;
@ -42,16 +41,13 @@ protected:
WaterRegionPatchDesc intermediate_dest_region_patch;
public:
void SetDestination(const Ship *v)
void SetDestination(const Ship *v, const std::span<TileIndex> destination_tiles)
{
this->dest_tiles = destination_tiles;
if (v->current_order.IsType(OT_GOTO_STATION)) {
this->dest_station = v->current_order.GetDestination().ToStationID();
this->dest_tile = CalcClosestStationTile(this->dest_station, v->tile, StationType::Dock);
this->dest_trackdirs = INVALID_TRACKDIR_BIT;
} else {
this->dest_station = StationID::Invalid();
this->dest_tile = v->dest_tile;
this->dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0));
}
}
@ -73,11 +69,8 @@ public:
/** Called by YAPF to detect if node ends in the desired destination. */
inline bool PfDetectDestination(Node &n)
{
return this->PfDetectDestinationTile(n.segment_last_tile, n.segment_last_td);
}
const TileIndex tile = n.segment_last_tile;
inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir)
{
if (this->has_intermediate_dest) {
/* GetWaterRegionInfo is much faster than GetWaterRegionPatchInfo so we try that first. */
if (GetWaterRegionInfo(tile) != this->intermediate_dest_region_patch) return false;
@ -86,23 +79,14 @@ public:
if (this->dest_station != StationID::Invalid()) return IsDockingTile(tile) && IsShipDestinationTile(tile, this->dest_station);
return tile == this->dest_tile && ((this->dest_trackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
assert(this->dest_tiles.size() == 1);
return tile == this->dest_tiles.front();
}
/**
* Called by YAPF to calculate cost estimate. Calculates distance to the destination
* adds it to the actual cost from origin and stores the sum to the Node::estimate.
*/
inline bool PfCalcEstimate(Node &n)
static inline int CalcEstimate(Node &n, TileIndex destination_tile)
{
const TileIndex destination_tile = this->has_intermediate_dest ? this->intermediate_dest_tile : this->dest_tile;
static const int dg_dir_to_x_offs[] = { -1, 0, 1, 0 };
static const int dg_dir_to_y_offs[] = { 0, 1, 0, -1 };
if (this->PfDetectDestination(n)) {
n.estimate = n.cost;
return true;
}
TileIndex tile = n.segment_last_tile;
DiagDirection exitdir = TrackdirToExitdir(n.segment_last_td);
@ -115,8 +99,33 @@ public:
int dmin = std::min(dx, dy);
int dxy = abs(dx - dy);
int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
n.estimate = n.cost + d;
assert(n.estimate >= n.parent->estimate);
int estimate = n.cost + d;
assert(estimate >= n.parent->estimate);
return estimate;
}
/**
* Called by YAPF to calculate cost estimate. Calculates distance to the destination
* adds it to the actual cost from origin and stores the sum to the Node::estimate.
*/
inline bool PfCalcEstimate(Node &n)
{
if (this->PfDetectDestination(n)) {
n.estimate = n.cost;
return true;
}
int shortest_estimate = std::numeric_limits<int>::max();
if (this->has_intermediate_dest) {
shortest_estimate = this->CalcEstimate(n, this->intermediate_dest_tile);
} else {
for (const TileIndex &destination_tile : this->dest_tiles) {
int estimate = this->CalcEstimate(n, destination_tile);
if (estimate < shortest_estimate) shortest_estimate = estimate;
}
}
n.estimate = shortest_estimate;
return true;
}
};
@ -211,10 +220,10 @@ public:
return result;
}
static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs,
static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs, const std::span<TileIndex> dest_tiles,
bool &path_found, ShipPathCache &path_cache, Trackdir &best_origin_dir)
{
const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1);
const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1, dest_tiles);
if (high_level_path.empty()) {
path_found = false;
/* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
@ -229,7 +238,7 @@ public:
/* Set origin and destination nodes */
pf.SetOrigin(v->tile, forward_dirs | reverse_dirs);
pf.SetDestination(v);
pf.SetDestination(v, dest_tiles);
const bool is_intermediate_destination = static_cast<int>(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1;
if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back());
@ -297,9 +306,10 @@ public:
* Called when leaving depot.
* @param v Ship.
* @param trackdir [out] the best of all possible reversed trackdirs.
* @param dest_tiles list of destination tiles.
* @return true if the reverse direction is better.
*/
static bool CheckShipReverse(const Ship *v, Trackdir *trackdir)
static bool CheckShipReverse(const Ship *v, Trackdir *trackdir, const std::span<TileIndex> dest_tiles)
{
bool path_found = false;
ShipPathCache dummy_cache;
@ -310,13 +320,13 @@ public:
const Trackdir reverse_dir = ReverseTrackdir(v->GetVehicleTrackdir());
const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
const TrackdirBits reverse_dirs = TrackdirToTrackdirBits(reverse_dir);
(void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, path_found, dummy_cache, best_origin_dir);
(void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, dest_tiles, path_found, dummy_cache, best_origin_dir);
return path_found && best_origin_dir == reverse_dir;
} else {
/* This gets called when a ship suddenly can't move forward, e.g. due to terraforming. */
const DiagDirection entry = ReverseDiagDir(VehicleExitDir(v->direction, v->state));
const TrackdirBits reverse_dirs = DiagdirReachesTrackdirs(entry) & TrackStatusToTrackdirBits(GetTileTrackStatus(v->tile, TRANSPORT_WATER, 0, entry));
(void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, path_found, dummy_cache, best_origin_dir);
(void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, dest_tiles, path_found, dummy_cache, best_origin_dir);
*trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs);
return true;
}
@ -420,13 +430,15 @@ struct CYapfShip : CYapfT<CYapfShip_TypesT<CYapfShip, CFollowTrackWater, CShipNo
/** Ship controller helper - path finder invoker. */
Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipPathCache &path_cache)
{
std::vector<TileIndex> dest_tiles = GetShipDestinationTiles(v);
Trackdir best_origin_dir = INVALID_TRACKDIR;
const TrackdirBits origin_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, path_found, path_cache, best_origin_dir);
const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, dest_tiles, path_found, path_cache, best_origin_dir);
return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
}
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir)
{
return CYapfShip::CheckShipReverse(v, trackdir);
std::vector<TileIndex> dest_tiles = GetShipDestinationTiles(v);
return CYapfShip::CheckShipReverse(v, trackdir, dest_tiles);
}

View File

@ -175,7 +175,7 @@ public:
inline char TransportTypeChar() const { return '^'; }
static std::vector<WaterRegionPatchDesc> FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
static std::vector<WaterRegionPatchDesc> FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span<TileIndex> dest_tiles)
{
const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile);
@ -184,18 +184,7 @@ public:
Tpf pf(std::min(static_cast<int>(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES));
pf.SetDestination(start_water_region_patch);
if (v->current_order.IsType(OT_GOTO_STATION)) {
StationID station_id = v->current_order.GetDestination().ToStationID();
const BaseStation *station = BaseStation::Get(station_id);
TileArea tile_area;
station->GetTileArea(&tile_area, StationType::Dock);
for (const auto &tile : tile_area) {
if (IsDockingTile(tile) && IsShipDestinationTile(tile, station_id)) {
pf.AddOrigin(GetWaterRegionPatchInfo(tile));
}
}
} else {
TileIndex tile = v->dest_tile;
for (const TileIndex &tile : dest_tiles) {
pf.AddOrigin(GetWaterRegionPatchInfo(tile));
}
@ -292,9 +281,10 @@ struct CYapfRegionWater : CYapfT<CYapfRegion_TypesT<CYapfRegionWater, CRegionNod
* @param v The ship to find a path for.
* @param start_tile The tile to start searching from.
* @param max_returned_path_length The maximum length of the path that will be returned.
* @param dest_tiles List of destination tiles.
* @returns A path of water region patches, or an empty vector if no path was found.
*/
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span<TileIndex> dest_tiles)
{
return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length);
return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length, dest_tiles);
}

View File

@ -15,6 +15,6 @@
struct Ship;
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length);
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span<TileIndex> dest_tiles);
#endif /* YAPF_SHIP_REGIONS_H */

View File

@ -36,9 +36,14 @@
# 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 },
/* 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},
};

View File

@ -138,15 +138,58 @@ bool HasBridgeFlatRamp(Slope tileh, Axis axis)
return (tileh != SLOPE_FLAT);
}
static inline std::span<const PalSpriteID> 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<const PalSpriteID> 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.
@ -1017,10 +1060,10 @@ static CommandCost ClearTile_TunnelBridge(TileIndex tile, DoCommandFlags flags)
* @param h Bounding box size in Y direction
* @param subsprite Optional subsprite for drawing halfpillars
*/
static inline void DrawPillar(const PalSpriteID *psid, int x, int y, int z, uint8_t w, uint8_t h, const SubSprite *subsprite)
static inline void DrawPillar(const PalSpriteID &psid, int x, int y, int z, uint8_t w, uint8_t h, const SubSprite *subsprite)
{
static const int PILLAR_Z_OFFSET = TILE_HEIGHT - BRIDGE_Z_START; ///< Start offset of pillar wrt. bridge (downwards)
AddSortableSpriteToDraw(psid->sprite, psid->pal, x, y, z, {{0, 0, -PILLAR_Z_OFFSET}, {w, h, BB_HEIGHT_UNDER_BRIDGE}, {0, 0, PILLAR_Z_OFFSET}}, IsTransparencySet(TO_BRIDGES), subsprite);
AddSortableSpriteToDraw(psid.sprite, psid.pal, x, y, z, {{0, 0, -PILLAR_Z_OFFSET}, {w, h, BB_HEIGHT_UNDER_BRIDGE}, {0, 0, PILLAR_Z_OFFSET}}, IsTransparencySet(TO_BRIDGES), subsprite);
}
/**
@ -1034,7 +1077,7 @@ static inline void DrawPillar(const PalSpriteID *psid, int x, int y, int z, uint
* @param h Bounding box size in Y direction
* @return Reached Z at the bottom
*/
static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID *psid, int x, int y, int w, int h)
static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID &psid, int x, int y, int w, int h)
{
int cur_z;
for (cur_z = z_top; cur_z >= z_bottom; cur_z -= TILE_HEIGHT) {
@ -1054,7 +1097,7 @@ static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID *psid, in
* @param y Sprite Y position of front pillar.
* @param z_bridge Absolute height of bridge bottom.
*/
static void DrawBridgePillars(const PalSpriteID *psid, const TileInfo *ti, Axis axis, bool drawfarpillar, int x, int y, int z_bridge)
static void DrawBridgePillars(const PalSpriteID &psid, const TileInfo *ti, Axis axis, bool drawfarpillar, int x, int y, int z_bridge)
{
static const int bounding_box_size[2] = {16, 2}; ///< bounding box size of pillars along bridge direction
static const int back_pillar_offset[2] = { 0, 9}; ///< sprite position offset of back facing pillar
@ -1065,7 +1108,7 @@ static void DrawBridgePillars(const PalSpriteID *psid, const TileInfo *ti, Axis
{ { -INF, -INF, 15, INF }, { 16, -INF, INF, INF } }, // Y axis, north and south
};
if (psid->sprite == 0) return;
if (psid.sprite == 0) return;
/* Determine ground height under pillars */
DiagDirection south_dir = AxisToDiagDir(axis);
@ -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<const PalSpriteID> 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<uint8_t>(ti->tileh == SLOPE_FLAT ? 0 : TILE_HEIGHT)}, {}});
AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, *ti, {{}, {TILE_SIZE, TILE_SIZE, static_cast<uint8_t>(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<const PalSpriteID> 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);
}