4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
11 * @file bridge_cmd.cpp
12 * This file deals with bridges (non-gui stuff)
17 #include "tunnelbridge.h"
18 #include "landscape.h"
19 #include "slope_func.h"
20 #include "command_func.h"
21 #include "date_func.h"
22 #include "economy_func.h"
23 #include "map/ground.h"
24 #include "map/water.h"
27 #include "map/slope.h"
28 #include "map/tunnelbridge.h"
29 #include "viewport_func.h"
30 #include "transparency.h"
32 #include "elrail_func.h"
33 #include "clear_func.h"
36 #include "newgrf_commons.h"
37 #include "newgrf_railtype.h"
38 #include "newgrf_object.h"
39 #include "newgrf_station.h"
41 #include "table/sprites.h"
42 #include "table/strings.h"
43 #include "table/bridge_land.h"
46 /** Data for CheckExtendedBridgeHead; see the function for details */
47 extern const uint32 bridgehead_valid_slopes
[DIAGDIR_END
][2] = {
48 { (1 << SLOPE_W
) | (1 << SLOPE_STEEP_W
),
49 (1 << SLOPE_S
) | (1 << SLOPE_STEEP_S
) },
50 { (1 << SLOPE_N
) | (1 << SLOPE_STEEP_N
),
51 (1 << SLOPE_W
) | (1 << SLOPE_STEEP_W
) },
52 { (1 << SLOPE_E
) | (1 << SLOPE_STEEP_E
),
53 (1 << SLOPE_N
) | (1 << SLOPE_STEEP_N
) },
54 { (1 << SLOPE_S
) | (1 << SLOPE_STEEP_S
),
55 (1 << SLOPE_E
) | (1 << SLOPE_STEEP_E
) },
59 /** Z position of the bridge sprites relative to bridge height (downwards) */
60 static const int BRIDGE_Z_START
= 3;
62 BridgeSpec _bridge
[MAX_BRIDGES
]; ///< The specification of all bridges.
64 /** Reset the data been eventually changed by the grf loaded. */
67 /* First, free sprite table data */
68 for (BridgeType i
= 0; i
< MAX_BRIDGES
; i
++) {
69 if (_bridge
[i
].sprite_table
!= NULL
) {
70 for (BridgePieces j
= BRIDGE_PIECE_NORTH
; j
< BRIDGE_PIECE_INVALID
; j
++) free(_bridge
[i
].sprite_table
[j
]);
71 free(_bridge
[i
].sprite_table
);
75 /* Then, wipe out current bridges */
76 memset(&_bridge
, 0, sizeof(_bridge
));
77 /* And finally, reinstall default data */
78 memcpy(&_bridge
, &_orig_bridge
, sizeof(_orig_bridge
));
82 * Calculate the price factor for building a long bridge.
83 * Basically the cost delta is 1,1, 1, 2,2, 3,3,3, 4,4,4,4, 5,5,5,5,5, 6,6,6,6,6,6, 7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8,
84 * @param length Length of the bridge.
85 * @return Price factor for the bridge.
87 int CalcBridgeLenCostFactor(int length
)
89 if (length
<= 2) return length
;
95 for (delta
= 1; delta
< length
; delta
++) {
99 sum
+= delta
* length
;
104 * Get the foundation for a bridge.
105 * @param tileh The slope to build the bridge on.
106 * @param axis The axis of the bridge entrance.
107 * @return The foundation required.
109 Foundation
GetBridgeFoundation(Slope tileh
, Axis axis
)
111 if (tileh
== SLOPE_FLAT
||
112 ((tileh
== SLOPE_NE
|| tileh
== SLOPE_SW
) && axis
== AXIS_X
) ||
113 ((tileh
== SLOPE_NW
|| tileh
== SLOPE_SE
) && axis
== AXIS_Y
)) return FOUNDATION_NONE
;
115 return (HasSlopeHighestCorner(tileh
) ? InclinedFoundation(axis
) : FlatteningFoundation(tileh
));
119 * Get the height ('z') of a bridge.
120 * @param tile the bridge ramp tile to get the bridge height from
121 * @return the height of the bridge.
123 int GetBridgeHeight(TileIndex t
)
126 Slope tileh
= GetTileSlope(t
, &h
);
127 Foundation f
= GetBridgeFoundation(tileh
, DiagDirToAxis(GetTunnelBridgeDirection(t
)));
129 /* one height level extra for the ramp */
130 return h
+ 1 + ApplyFoundationToSlope(f
, &tileh
);
134 * Determines if the track on a bridge ramp is flat or goes up/down.
136 * @param tileh Slope of the tile under the bridge head
137 * @param axis Orientation of bridge
138 * @return true iff the track is flat.
140 bool HasBridgeFlatRamp(Slope tileh
, Axis axis
)
142 ApplyFoundationToSlope(GetBridgeFoundation(tileh
, axis
), &tileh
);
143 /* If the foundation slope is flat the bridge has a non-flat ramp and vice versa. */
144 return (tileh
!= SLOPE_FLAT
);
148 * Check tiles validity for a bridge.
150 * @param tile1 Start tile
151 * @param tile2 End tile
152 * @param axis Pointer to receive bridge axis, or NULL
153 * @return Null cost for success or an error
155 CommandCost
CheckBridgeTiles(TileIndex tile1
, TileIndex tile2
, Axis
*axis
)
157 if (!IsValidTile(tile1
) || !IsValidTile(tile2
)) return_cmd_error(STR_ERROR_BRIDGE_THROUGH_MAP_BORDER
);
159 if (tile1
== tile2
) {
160 return_cmd_error(STR_ERROR_CAN_T_START_AND_END_ON
);
161 } else if (TileX(tile1
) == TileX(tile2
)) {
162 if (axis
!= NULL
) *axis
= AXIS_Y
;
163 } else if (TileY(tile1
) == TileY(tile2
)) {
164 if (axis
!= NULL
) *axis
= AXIS_X
;
166 return_cmd_error(STR_ERROR_START_AND_END_MUST_BE_IN
);
169 return CommandCost();
173 * Check if a bridge can be built over a tile.
174 * @param tile The tile to check.
175 * @param dir The direction of the bridge to be built.
176 * @param z The height at which the bridge will be built.
177 * @param Whether a bridge is buildable over the tile.
179 static bool CheckBridgeTileBuildable (TileIndex tile
, DiagDirection dir
, int z
)
181 switch (GetTileType (tile
)) {
183 return IsPlainWater (tile
) || IsCoast (tile
);
186 if (IsTileSubtype (tile
, TT_MISC_TUNNEL
)) return true;
187 if (IsTileSubtype (tile
, TT_MISC_DEPOT
)) return false;
188 assert_compile (TT_BRIDGE
== TT_MISC_AQUEDUCT
);
192 if (!IsTileSubtype (tile
, TT_BRIDGE
)) return true;
193 if (DiagDirToAxis (dir
) == DiagDirToAxis (GetTunnelBridgeDirection (tile
))) return false;
194 return z
>= GetBridgeHeight (tile
);
197 const ObjectSpec
*spec
= ObjectSpec::GetByTile(tile
);
198 if ((spec
->flags
& OBJECT_FLAG_ALLOW_UNDER_BRIDGE
) == 0) return false;
199 return z
>= GetTileMaxZ (tile
) + spec
->height
;
203 assert (IsGroundTile (tile
));
204 return !IsTileSubtype (tile
, TT_GROUND_TREES
);
207 switch (GetStationType(tile
)) {
209 if (GetStationSpec (tile
) != NULL
) return false;
210 uint gfx
= GetStationGfx (tile
);
211 int h
= (gfx
< 2 ? 0 : gfx
< 4 ? 1 : 3);
212 return z
>= (GetTileMaxZ (tile
) + h
);
215 case STATION_WAYPOINT
:
216 if (GetStationSpec (tile
) != NULL
) return false;
217 return z
>= (GetTileMaxZ (tile
) + 2);
220 if (IsInsideMM (GetStationGfx (tile
),
221 GFX_DOCK_BASE_WATER_PART
,
224 assert (IsTileFlat (tile
));
225 return z
> GetTileZ (tile
);
227 return z
>= GetTileMaxZ (tile
);
232 return z
> GetTileMaxZ (tile
);
235 assert (IsTileFlat (tile
));
236 return z
>= GetTileZ (tile
);
248 * Check if a bridge can be built.
250 * @param tile1 Start tile
251 * @param tile2 End tile
252 * @param flags type of operation
253 * @param clear1 Try to clear start tile
254 * @param clear2 Try to clear end tile
255 * @param height Pointer to store the height at which the bridge will be
256 * @param restricted Force flat ramp (for aqueducts)
257 * @return Terraforming cost for success or an error
259 CommandCost
CheckBridgeBuildable (TileIndex tile1
, TileIndex tile2
,
260 DoCommandFlag flags
, bool clear1
, bool clear2
, int *height
,
263 DiagDirection dir
= DiagdirBetweenTiles(tile1
, tile2
);
264 assert(IsValidDiagDirection(dir
));
268 Slope tileh1
= GetTileSlope(tile1
, &z1
);
269 Slope tileh2
= GetTileSlope(tile2
, &z2
);
271 CommandCost terraform1
= CheckBridgeSlope(dir
, &tileh1
, &z1
);
272 CommandCost terraform2
= CheckBridgeSlope(ReverseDiagDir(dir
), &tileh2
, &z2
);
274 if (restricted
&& (tileh1
== SLOPE_FLAT
|| tileh2
== SLOPE_FLAT
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
275 if (z1
!= z2
) return_cmd_error(STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT
);
277 bool allow_on_slopes
= (_settings_game
.construction
.build_on_slopes
&& !restricted
);
282 /* Try and clear the start landscape */
283 CommandCost ret
= DoCommand(tile1
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
284 if (ret
.Failed()) return ret
;
287 if (terraform1
.Failed() || (terraform1
.GetCost() != 0 && !allow_on_slopes
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
288 cost
.AddCost(terraform1
);
290 assert(terraform1
.Succeeded());
294 /* Try and clear the end landscape */
295 CommandCost ret
= DoCommand(tile2
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
296 if (ret
.Failed()) return ret
;
299 if (terraform2
.Failed() || (terraform2
.GetCost() != 0 && !allow_on_slopes
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
300 cost
.AddCost(terraform2
);
302 assert(terraform2
.Succeeded());
305 const TileIndex heads
[] = {tile1
, tile2
};
306 for (int i
= 0; i
< 2; i
++) {
307 if (HasBridgeAbove(heads
[i
])) {
308 if (DiagDirToAxis(dir
) == GetBridgeAxis(heads
[i
])) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
310 if (z1
+ 1 == GetBridgeHeight(GetNorthernBridgeEnd(heads
[i
]))) {
311 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
316 TileIndexDiff delta
= TileOffsByDiagDir(dir
);
318 for (TileIndex tile
= tile1
+ delta
; tile
!= tile2
; tile
+= delta
) {
319 if (GetTileMaxZ(tile
) > z1
) return_cmd_error(STR_ERROR_BRIDGE_TOO_LOW_FOR_TERRAIN
);
321 if (z1
>= (GetTileZ(tile
) + _settings_game
.construction
.max_bridge_height
)) {
323 * Disallow too high bridges.
324 * Properly rendering a map where very high bridges (might) exist is expensive.
325 * See http://www.tt-forums.net/viewtopic.php?f=33&t=40844&start=980#p1131762
326 * for a detailed discussion. z1 here is one heightlevel below the bridge level.
328 return_cmd_error(STR_ERROR_BRIDGE_TOO_HIGH_FOR_TERRAIN
);
331 if (HasBridgeAbove(tile
)) {
332 /* Disallow crossing bridges for the time being */
333 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
336 if (!CheckBridgeTileBuildable (tile
, dir
, z1
)) {
337 /* try and clear the middle landscape */
338 CommandCost ret
= DoCommand (tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
339 if (ret
.Failed()) return ret
;
349 * Is a bridge of the specified type and length available?
350 * @param bridge_type Wanted type of bridge.
351 * @param bridge_len Wanted length of the bridge.
352 * @return A succeeded (the requested bridge is available) or failed (it cannot be built) command.
354 CommandCost
CheckBridgeAvailability(BridgeType bridge_type
, uint bridge_len
, DoCommandFlag flags
)
356 if (flags
& DC_QUERY_COST
) {
357 if (bridge_len
<= _settings_game
.construction
.max_bridge_length
) return CommandCost();
358 return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG
);
361 if (bridge_type
>= MAX_BRIDGES
) return CMD_ERROR
;
363 const BridgeSpec
*b
= GetBridgeSpec(bridge_type
);
364 if (b
->avail_year
> _cur_year
) return CMD_ERROR
;
366 uint max
= min(b
->max_length
, _settings_game
.construction
.max_bridge_length
);
368 if (b
->min_length
> bridge_len
) return CMD_ERROR
;
369 if (bridge_len
<= max
) return CommandCost();
370 return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG
);
374 * Determines the foundation for a bridge head, and tests if the resulting slope is valid.
376 * @param dir Diagonal direction the bridge ramp will be facing
377 * @param tileh Slope of the tile under the bridge head; returns slope on top of foundation
378 * @param z TileZ corresponding to tileh, gets modified as well
379 * @return Error or cost for bridge foundation
381 CommandCost
CheckBridgeSlope(DiagDirection dir
, Slope
*tileh
, int *z
)
383 static const Slope inclined
[DIAGDIR_END
] = {
384 SLOPE_SW
, ///< DIAGDIR_NE
385 SLOPE_NW
, ///< DIAGDIR_SE
386 SLOPE_NE
, ///< DIAGDIR_SW
387 SLOPE_SE
, ///< DIAGDIR_NW
390 Foundation f
= GetBridgeFoundation(*tileh
, DiagDirToAxis(dir
));
391 *z
+= ApplyFoundationToSlope(f
, tileh
);
393 if ((*tileh
!= SLOPE_FLAT
) && (*tileh
!= inclined
[dir
])) return CMD_ERROR
;
395 if (f
== FOUNDATION_NONE
) return CommandCost();
397 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
401 * Mark the tiles that a bridge spans dirty.
402 * @param start First tile of bridge.
403 * @param end Last tile of bridge.
404 * @param dir Direction from start to end.
405 * @param first Do mark the first tile dirty, else skip it.
407 void MarkBridgeTilesDirty (TileIndex start
, TileIndex end
, DiagDirection dir
,
410 assert (DiagdirBetweenTiles (start
, end
) == dir
);
412 if (first
) MarkTileDirtyByTile (start
);
414 uint height
= GetBridgeHeight (start
);
415 TileIndexDiff delta
= TileOffsByDiagDir (dir
);
416 for (TileIndex tile
= start
+ delta
; tile
!= end
; tile
+= delta
) {
417 MarkBridgeTileDirtyByTile (tile
, height
);
420 MarkTileDirtyByTile (end
);
424 * Set bridge axis on a new bridge middle tiles, and mark them dirty
426 * @param tile1 Bridge start tile
427 * @param tile2 Bridge end tile
428 * @param direction Bridge axis
429 * @param height Height of the bridge
431 void SetBridgeMiddleTiles (TileIndex tile1
, TileIndex tile2
, Axis direction
,
434 assert(tile1
< tile2
);
435 assert (height
== GetBridgeHeight (tile1
));
437 MarkTileDirtyByTile(tile1
);
438 MarkTileDirtyByTile(tile2
);
440 TileIndexDiff delta
= TileOffsByDiagDir(AxisToDiagDir(direction
));
441 for (TileIndex tile
= tile1
+ delta
; tile
< tile2
; tile
+= delta
) {
442 SetBridgeMiddle(tile
, direction
);
443 MarkBridgeTileDirtyByTile (tile
, height
);
448 * Clear middle bridge tiles
450 * @param tile1 Bridge start tile
451 * @param tile2 Bridge end tile
453 void RemoveBridgeMiddleTiles(TileIndex tile1
, TileIndex tile2
)
455 /* Call this function before clearing the endpoints. */
456 assert(IsBridgeHeadTile(tile1
));
457 assert(IsBridgeHeadTile(tile2
));
459 TileIndexDiff delta
= TileOffsByDiagDir(GetTunnelBridgeDirection(tile1
));
460 int height
= GetBridgeHeight(tile1
);
462 for (TileIndex t
= tile1
+ delta
; t
!= tile2
; t
+= delta
) {
463 /* do not let trees appear from 'nowhere' after removing bridge */
464 if (IsNormalRoadTile(t
) && GetRoadside(t
) == ROADSIDE_TREES
) {
465 int minz
= GetTileMaxZ(t
) + 3;
466 if (height
< minz
) SetRoadside(t
, ROADSIDE_PAVED
);
468 ClearBridgeMiddle(t
);
469 MarkBridgeTileDirtyByTile (t
, height
);
474 static inline const PalSpriteID
*GetBridgeSpriteTable(int index
, BridgePieces table
)
476 const BridgeSpec
*bridge
= GetBridgeSpec(index
);
477 assert(table
< BRIDGE_PIECE_INVALID
);
478 if (bridge
->sprite_table
== NULL
|| bridge
->sprite_table
[table
] == NULL
) {
479 return _bridge_sprite_table
[index
][table
];
481 return bridge
->sprite_table
[table
];
486 * Draw a single pillar sprite.
487 * @param vd The viewport drawer to use
488 * @param psid Pillarsprite
492 * @param w Bounding box size in X direction
493 * @param h Bounding box size in Y direction
494 * @param subsprite Optional subsprite for drawing halfpillars
496 static inline void DrawPillar (ViewportDrawer
*vd
, const PalSpriteID
*psid
,
497 int x
, int y
, int z
, int w
, int h
, const SubSprite
*subsprite
)
499 static const int PILLAR_Z_OFFSET
= TILE_HEIGHT
- BRIDGE_Z_START
; ///< Start offset of pillar wrt. bridge (downwards)
500 AddSortableSpriteToDraw (vd
, psid
->sprite
, psid
->pal
, x
, y
, w
, h
, BB_HEIGHT_UNDER_BRIDGE
- PILLAR_Z_OFFSET
, z
, IsTransparencySet(TO_BRIDGES
), 0, 0, -PILLAR_Z_OFFSET
, subsprite
);
504 * Draw two bridge pillars (north and south).
505 * @param z_bottom Bottom Z
507 * @param psid Pillarsprite
510 * @param w Bounding box size in X direction
511 * @param h Bounding box size in Y direction
512 * @return Reached Z at the bottom
514 static int DrawPillarColumn (ViewportDrawer
*vd
, int z_bottom
, int z_top
,
515 const PalSpriteID
*psid
, int x
, int y
, int w
, int h
)
518 for (cur_z
= z_top
; cur_z
>= z_bottom
; cur_z
-= TILE_HEIGHT
) {
519 DrawPillar (vd
, psid
, x
, y
, cur_z
, w
, h
, NULL
);
525 * Draws the pillars under high bridges.
527 * @param psid Image and palette of a bridge pillar.
528 * @param ti #TileInfo of current bridge-middle-tile.
529 * @param axis Orientation of bridge.
530 * @param drawfarpillar Whether to draw the pillar at the back
531 * @param x Sprite X position of front pillar.
532 * @param y Sprite Y position of front pillar.
533 * @param z_bridge Absolute height of bridge bottom.
535 static void DrawBridgePillars(const PalSpriteID
*psid
, const TileInfo
*ti
, Axis axis
, bool drawfarpillar
, int x
, int y
, int z_bridge
)
537 static const int bounding_box_size
[2] = {16, 2}; ///< bounding box size of pillars along bridge direction
538 static const int back_pillar_offset
[2] = { 0, 9}; ///< sprite position offset of back facing pillar
540 static const int INF
= 1000; ///< big number compared to sprite size
541 static const SubSprite half_pillar_sub_sprite
[2][2] = {
542 { { -14, -INF
, INF
, INF
}, { -INF
, -INF
, -15, INF
} }, // X axis, north and south
543 { { -INF
, -INF
, 15, INF
}, { 16, -INF
, INF
, INF
} }, // Y axis, north and south
546 if (psid
->sprite
== 0) return;
548 /* Determine ground height under pillars */
549 DiagDirection south_dir
= AxisToDiagDir(axis
);
550 int z_front_north
= ti
->z
;
551 int z_back_north
= ti
->z
;
552 int z_front_south
= ti
->z
;
553 int z_back_south
= ti
->z
;
554 GetSlopePixelZOnEdge(ti
->tileh
, south_dir
, &z_front_south
, &z_back_south
);
555 GetSlopePixelZOnEdge(ti
->tileh
, ReverseDiagDir(south_dir
), &z_front_north
, &z_back_north
);
557 /* Shared height of pillars */
558 int z_front
= max(z_front_north
, z_front_south
);
559 int z_back
= max(z_back_north
, z_back_south
);
561 /* x and y size of bounding-box of pillars */
562 int w
= bounding_box_size
[axis
];
563 int h
= bounding_box_size
[OtherAxis(axis
)];
564 /* sprite position of back facing pillar */
565 int x_back
= x
- back_pillar_offset
[axis
];
566 int y_back
= y
- back_pillar_offset
[OtherAxis(axis
)];
568 /* Draw front pillars */
569 int bottom_z
= DrawPillarColumn (ti
->vd
, z_front
, z_bridge
, psid
, x
, y
, w
, h
);
570 if (z_front_north
< z_front
) DrawPillar (ti
->vd
, psid
, x
, y
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][0]);
571 if (z_front_south
< z_front
) DrawPillar (ti
->vd
, psid
, x
, y
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][1]);
573 /* Draw back pillars, skip top two parts, which are hidden by the bridge */
574 int z_bridge_back
= z_bridge
- 2 * (int)TILE_HEIGHT
;
575 if (drawfarpillar
&& (z_back_north
<= z_bridge_back
|| z_back_south
<= z_bridge_back
)) {
576 bottom_z
= DrawPillarColumn (ti
->vd
, z_back
, z_bridge_back
, psid
, x_back
, y_back
, w
, h
);
577 if (z_back_north
< z_back
) DrawPillar (ti
->vd
, psid
, x_back
, y_back
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][0]);
578 if (z_back_south
< z_back
) DrawPillar (ti
->vd
, psid
, x_back
, y_back
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][1]);
583 * Compute bridge piece. Computes the bridge piece to display depending on the position inside the bridge.
584 * bridges pieces sequence (middle parts).
585 * Note that it is not covering the bridge heads, which are always referenced by the same sprite table.
586 * bridge len 1: BRIDGE_PIECE_NORTH
587 * bridge len 2: BRIDGE_PIECE_NORTH BRIDGE_PIECE_SOUTH
588 * bridge len 3: BRIDGE_PIECE_NORTH BRIDGE_PIECE_MIDDLE_ODD BRIDGE_PIECE_SOUTH
589 * bridge len 4: BRIDGE_PIECE_NORTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_SOUTH
590 * bridge len 5: BRIDGE_PIECE_NORTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_MIDDLE_EVEN BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_SOUTH
591 * bridge len 6: BRIDGE_PIECE_NORTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_SOUTH
592 * bridge len 7: BRIDGE_PIECE_NORTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_MIDDLE_ODD BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_SOUTH
593 * #0 - always as first, #1 - always as last (if len>1)
594 * #2,#3 are to pair in order
595 * for odd bridges: #5 is going in the bridge middle if on even position, #4 on odd (counting from 0)
596 * @param north Northernmost tile of bridge
597 * @param south Southernmost tile of bridge
598 * @return Index of bridge piece
600 static BridgePieces
CalcBridgePiece(uint north
, uint south
)
603 return BRIDGE_PIECE_NORTH
;
604 } else if (south
== 1) {
605 return BRIDGE_PIECE_SOUTH
;
606 } else if (north
< south
) {
607 return north
& 1 ? BRIDGE_PIECE_INNER_SOUTH
: BRIDGE_PIECE_INNER_NORTH
;
608 } else if (north
> south
) {
609 return south
& 1 ? BRIDGE_PIECE_INNER_NORTH
: BRIDGE_PIECE_INNER_SOUTH
;
611 return north
& 1 ? BRIDGE_PIECE_MIDDLE_EVEN
: BRIDGE_PIECE_MIDDLE_ODD
;
616 * Draws the trambits over an already drawn (lower end) of a bridge.
617 * @param vd the viewport drawer to use
618 * @param x the x of the bridge
619 * @param y the y of the bridge
620 * @param z the z of the bridge
621 * @param offset number representing whether to level or sloped and the direction
622 * @param overlay do we want to still see the road?
623 * @param head are we drawing bridge head?
625 void DrawBridgeTramBits (ViewportDrawer
*vd
, int x
, int y
, int z
, int offset
,
626 bool overlay
, bool head
)
629 SpriteID tram
[2], back
, front
;
630 uint size_x
, size_y
, front_bb_offset_x
, front_bb_offset_y
;
633 #define S(x) (SPR_TRAMWAY_BASE + (x))
634 static const SpriteData sprite_data
[6] = {
635 { { S(107), S( 4) }, S( 95), S( 97), 1, 16, 15, 0 },
636 { { S(108), S( 5) }, S( 96), S( 98), 16, 1, 0, 15 },
637 { { S(109), S(15) }, S( 99), S(103), 16, 1, 0, 15 },
638 { { S(110), S(16) }, S(102), S(106), 1, 16, 15, 0 },
639 { { S(111), S(17) }, S(100), S(104), 16, 1, 0, 15 },
640 { { S(112), S(18) }, S(101), S(105), 1, 16, 15, 0 },
644 const SpriteData
*data
= &sprite_data
[offset
];
646 /* The sprites under the vehicles are drawn as SpriteCombine. StartSpriteCombine() has already been called
647 * The bounding boxes here are the same as for bridge front/roof */
648 if (head
|| !IsInvisibilitySet(TO_BRIDGES
)) {
649 AddSortableSpriteToDraw (vd
, data
->tram
[overlay
], PAL_NONE
,
650 x
, y
, data
->size_x
, data
->size_y
, 0x28, z
,
651 !head
&& IsTransparencySet(TO_BRIDGES
));
654 /* Do not draw catenary if it is set invisible */
655 if (!IsInvisibilitySet(TO_CATENARY
)) {
656 AddSortableSpriteToDraw (vd
, data
->back
, PAL_NONE
,
657 x
, y
, data
->size_x
, data
->size_y
, 0x28, z
,
658 IsTransparencySet(TO_CATENARY
));
661 /* Start a new SpriteCombine for the front part */
662 EndSpriteCombine (vd
);
663 StartSpriteCombine (vd
);
665 /* For sloped sprites the bounding box needs to be higher, as the pylons stop on a higher point */
666 if (!IsInvisibilitySet(TO_CATENARY
)) {
667 AddSortableSpriteToDraw (vd
, data
->front
, PAL_NONE
,
668 x
, y
, data
->size_x
+ data
->front_bb_offset_x
, data
->size_y
+ data
->front_bb_offset_y
, 0x28, z
,
669 IsTransparencySet(TO_CATENARY
), data
->front_bb_offset_x
, data
->front_bb_offset_y
);
674 * Draw the middle bits of a bridge.
675 * @param ti Tile information of the tile to draw it on.
677 void DrawBridgeMiddle(const TileInfo
*ti
)
679 /* Sectional view of bridge bounding boxes:
681 * 1 2 1,2 = SpriteCombine of Bridge front/(back&floor) and TramCatenary
682 * 1 2 3 = empty helper BB
683 * 1 7 2 4,5 = pillars under higher bridges
684 * 1 6 88888 6 2 6 = elrail-pylons
685 * 1 6 88888 6 2 7 = elrail-wire
686 * 1 6 88888 6 2 <- TILE_HEIGHT 8 = rail-vehicle on bridge
687 * 3333333333333 <- BB_Z_SEPARATOR
689 * 4 5 <- BB_HEIGHT_UNDER_BRIDGE
695 if (!HasBridgeAbove(ti
->tile
)) return;
697 TileIndex rampnorth
= GetNorthernBridgeEnd(ti
->tile
);
698 TileIndex rampsouth
= GetSouthernBridgeEnd(ti
->tile
);
700 Axis axis
= GetBridgeAxis(ti
->tile
);
701 BridgePieces piece
= CalcBridgePiece(
702 GetTunnelBridgeLength(ti
->tile
, rampnorth
) + 1,
703 GetTunnelBridgeLength(ti
->tile
, rampsouth
) + 1
706 TransportType transport_type
;
707 const PalSpriteID
*psid
;
710 if (IsTileType(rampsouth
, TT_MISC
)) {
711 assert(IsAqueductTile(rampsouth
));
712 transport_type
= TRANSPORT_WATER
;
713 psid
= _aqueduct_sprites
;
714 drawfarpillar
= true;
716 assert(IsTileSubtype(rampsouth
, TT_BRIDGE
));
721 if (IsRailwayTile(rampsouth
)) {
722 transport_type
= TRANSPORT_RAIL
;
723 type
= GetRailBridgeType(rampsouth
);
724 base_offset
= GetRailTypeInfo(GetBridgeRailType(rampsouth
))->bridge_offset
;
726 transport_type
= TRANSPORT_ROAD
;
727 type
= GetRoadBridgeType(rampsouth
);
731 psid
= base_offset
+ GetBridgeSpriteTable(type
, piece
);
732 drawfarpillar
= !HasBit(GetBridgeSpec(type
)->flags
, 0);
735 if (axis
!= AXIS_X
) psid
+= 4;
739 uint bridge_z
= GetBridgePixelHeight(rampsouth
);
740 int z
= bridge_z
- BRIDGE_Z_START
;
742 /* Add a bounding box that separates the bridge from things below it. */
743 AddSortableSpriteToDraw (ti
->vd
, SPR_EMPTY_BOUNDING_BOX
, PAL_NONE
, x
, y
, 16, 16, 1, bridge_z
- TILE_HEIGHT
+ BB_Z_SEPARATOR
);
745 /* Draw Trambits as SpriteCombine */
746 if (transport_type
== TRANSPORT_ROAD
|| transport_type
== TRANSPORT_RAIL
) StartSpriteCombine (ti
->vd
);
748 /* Draw floor and far part of bridge*/
749 if (!IsInvisibilitySet(TO_BRIDGES
)) {
750 if (axis
== AXIS_X
) {
751 AddSortableSpriteToDraw (ti
->vd
, psid
->sprite
, psid
->pal
, x
, y
, 16, 1, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 0, BRIDGE_Z_START
);
753 AddSortableSpriteToDraw (ti
->vd
, psid
->sprite
, psid
->pal
, x
, y
, 1, 16, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 0, BRIDGE_Z_START
);
759 if (transport_type
== TRANSPORT_ROAD
) {
760 RoadBits bits
= DiagDirToRoadBits(axis
== AXIS_X
? DIAGDIR_NE
: DIAGDIR_NW
);
762 if ((GetRoadBits(rampsouth
, ROADTYPE_TRAM
) & bits
) != 0) {
763 /* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */
764 DrawBridgeTramBits (ti
->vd
, x
, y
, bridge_z
, axis
^ 1, (GetRoadBits(rampsouth
, ROADTYPE_ROAD
) & bits
) != 0, false);
766 EndSpriteCombine (ti
->vd
);
767 StartSpriteCombine (ti
->vd
);
769 } else if (transport_type
== TRANSPORT_RAIL
) {
770 const RailtypeInfo
*rti
= GetRailTypeInfo(GetBridgeRailType(rampsouth
));
771 if (rti
->UsesOverlay() && !IsInvisibilitySet(TO_BRIDGES
)) {
772 SpriteID surface
= GetCustomRailSprite(rti
, rampsouth
, RTSG_BRIDGE
, TCX_ON_BRIDGE
);
774 AddSortableSpriteToDraw (ti
->vd
, surface
+ axis
, PAL_NONE
, x
, y
, 16, 16, 0, bridge_z
, IsTransparencySet(TO_BRIDGES
));
778 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& !IsInvisibilitySet(TO_BRIDGES
) && HasBridgeMiddleReservation(rampnorth
)) {
779 SpriteID image
= rti
->UsesOverlay() ?
780 GetCustomRailSprite (rti
, ti
->tile
, RTSG_OVERLAY
) + RTO_X
+ axis
:
781 rti
->base_sprites
.single
[AxisToTrack(axis
)];
782 AddSortableSpriteToDraw (ti
->vd
, image
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 0, bridge_z
, IsTransparencySet (TO_BRIDGES
));
785 EndSpriteCombine (ti
->vd
);
787 if (HasRailCatenaryDrawn (rti
)) {
788 DrawRailCatenaryOnBridge(ti
);
792 /* draw roof, the component of the bridge which is logically between the vehicle and the camera */
793 if (!IsInvisibilitySet(TO_BRIDGES
)) {
794 if (axis
== AXIS_X
) {
796 if (psid
->sprite
& SPRITE_MASK
) AddSortableSpriteToDraw (ti
->vd
, psid
->sprite
, psid
->pal
, x
, y
, 16, 4, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 3, BRIDGE_Z_START
);
799 if (psid
->sprite
& SPRITE_MASK
) AddSortableSpriteToDraw (ti
->vd
, psid
->sprite
, psid
->pal
, x
, y
, 4, 16, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 3, 0, BRIDGE_Z_START
);
803 /* Draw TramFront as SpriteCombine */
804 if (transport_type
== TRANSPORT_ROAD
) EndSpriteCombine (ti
->vd
);
806 /* Do not draw anything more if bridges are invisible */
807 if (IsInvisibilitySet(TO_BRIDGES
)) return;
810 if (ti
->z
+ 5 == z
) {
811 /* draw poles below for small bridges */
812 if (psid
->sprite
!= 0) {
813 SpriteID image
= psid
->sprite
;
814 SpriteID pal
= psid
->pal
;
815 if (IsTransparencySet(TO_BRIDGES
)) {
816 SetBit(image
, PALETTE_MODIFIER_TRANSPARENT
);
817 pal
= PALETTE_TO_TRANSPARENT
;
820 DrawGroundSpriteAt (ti
, image
, pal
, x
- ti
->x
, y
- ti
->y
, z
- ti
->z
);
823 /* draw pillars below for high bridges */
824 DrawBridgePillars(psid
, ti
, axis
, drawfarpillar
, x
, y
, z
);
828 void DrawBridgeGround(TileInfo
*ti
)
830 DiagDirection dir
= GetTunnelBridgeDirection(ti
->tile
);
832 DrawFoundation(ti
, GetBridgeFoundation(ti
->tileh
, DiagDirToAxis(dir
)));
834 if (IsOnSnow(ti
->tile
)) {
835 DrawGroundSprite (ti
, SPR_FLAT_SNOW_DESERT_TILE
+ SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
837 TileIndex next
= ti
->tile
+ TileOffsByDiagDir(dir
);
838 if (ti
->tileh
!= SLOPE_FLAT
&& ti
->z
== 0 && HasTileWaterClass(next
) && GetWaterClass(next
) == WATER_CLASS_SEA
) {
841 DrawClearLandTile(ti
, 3);
846 const PalSpriteID
*GetBridgeRampSprite(int index
, int offset
, Slope slope
, DiagDirection dir
)
848 /* as the lower 3 bits are used for other stuff, make sure they are clear */
849 assert((offset
& 0x07) == 0x00);
851 if (slope
== SLOPE_FLAT
) offset
+= 4; // sloped bridge head
853 /* HACK Wizardry to convert the bridge ramp direction into a sprite offset */
854 offset
+= (6 - dir
) % 4;
856 /* Table number BRIDGE_PIECE_HEAD always refers to the bridge heads for any bridge type */
857 return offset
+ GetBridgeSpriteTable(index
, BRIDGE_PIECE_HEAD
);
860 void DrawAqueductRamp(TileInfo
*ti
)
862 DrawBridgeGround(ti
);
864 assert(ti
->tileh
!= SLOPE_FLAT
);
866 DiagDirection dir
= GetTunnelBridgeDirection(ti
->tile
);
868 /* HACK Wizardry to convert the bridge ramp direction into a sprite offset */
869 const PalSpriteID
*psid
= _aqueduct_sprites
+ 8 + (6 - dir
) % 4;
871 /* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
872 * it doesn't disappear behind it
874 /* Bridge heads are drawn solid no matter how invisibility/transparency is set */
875 AddSortableSpriteToDraw (ti
->vd
, psid
->sprite
, psid
->pal
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);