Fix old map array tunnel head conversion
[openttd/fttd.git] / src / bridge_cmd.cpp
blobbf599145d4418d4ab340a77e419972bdbac19375
1 /* $Id$ */
3 /*
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/>.
8 */
10 /**
11 * @file bridge_cmd.cpp
12 * This file deals with bridges (non-gui stuff)
15 #include "stdafx.h"
16 #include "bridge.h"
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"
25 #include "map/rail.h"
26 #include "map/road.h"
27 #include "map/slope.h"
28 #include "map/tunnelbridge.h"
29 #include "viewport_func.h"
30 #include "transparency.h"
31 #include "rail.h"
32 #include "elrail_func.h"
33 #include "clear_func.h"
34 #include "water.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. */
65 void ResetBridges()
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));
81 /**
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;
91 length -= 2;
92 int sum = 2;
94 int delta;
95 for (delta = 1; delta < length; delta++) {
96 sum += delta * delta;
97 length -= delta;
99 sum += delta * length;
100 return sum;
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)
125 int h;
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;
165 } else {
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)) {
182 case TT_WATER:
183 return IsPlainWater (tile) || IsCoast (tile);
185 case TT_MISC:
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);
189 /* fall through */
190 case TT_RAILWAY:
191 case TT_ROAD:
192 if (!IsTileSubtype (tile, TT_BRIDGE)) return true;
193 if (DiagDirToAxis (dir) == DiagDirToAxis (GetTunnelBridgeDirection (tile))) return false;
194 return z >= GetBridgeHeight (tile);
196 case TT_OBJECT: {
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;
202 case TT_GROUND:
203 assert (IsGroundTile (tile));
204 return !IsTileSubtype (tile, TT_GROUND_TREES);
206 case TT_STATION:
207 switch (GetStationType(tile)) {
208 case STATION_RAIL: {
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);
219 case STATION_DOCK:
220 if (IsInsideMM (GetStationGfx (tile),
221 GFX_DOCK_BASE_WATER_PART,
222 GFX_DOCK_BUOY)) {
223 /* water part */
224 assert (IsTileFlat (tile));
225 return z > GetTileZ (tile);
226 } else {
227 return z >= GetTileMaxZ (tile);
230 case STATION_TRUCK:
231 case STATION_BUS:
232 return z > GetTileMaxZ (tile);
234 case STATION_BUOY:
235 assert (IsTileFlat (tile));
236 return z >= GetTileZ (tile);
238 default:
239 return false;
242 default:
243 return false;
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,
261 bool restricted)
263 DiagDirection dir = DiagdirBetweenTiles(tile1, tile2);
264 assert(IsValidDiagDirection(dir));
266 int z1;
267 int z2;
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);
279 CommandCost cost;
281 if (clear1) {
282 /* Try and clear the start landscape */
283 CommandCost ret = DoCommand(tile1, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
284 if (ret.Failed()) return ret;
285 cost.AddCost(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);
289 } else {
290 assert(terraform1.Succeeded());
293 if (clear2) {
294 /* Try and clear the end landscape */
295 CommandCost ret = DoCommand(tile2, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
296 if (ret.Failed()) return ret;
297 cost.AddCost(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);
301 } else {
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;
340 cost.AddCost (ret);
344 *height = z1 + 1;
345 return cost;
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,
408 bool first)
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,
432 int height)
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];
480 } else {
481 return bridge->sprite_table[table];
486 * Draw a single pillar sprite.
487 * @param vd The viewport drawer to use
488 * @param psid Pillarsprite
489 * @param x Pillar X
490 * @param y Pillar Y
491 * @param z Pillar Z
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
506 * @param z_top Top Z
507 * @param psid Pillarsprite
508 * @param x Pillar X
509 * @param y Pillar Y
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)
517 int cur_z;
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);
521 return cur_z;
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)
602 if (north == 1) {
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;
610 } else {
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)
628 struct SpriteData {
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 },
642 #undef S
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
688 * <- unused
689 * 4 5 <- BB_HEIGHT_UNDER_BRIDGE
690 * 4 5
691 * 4 5
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;
708 bool drawfarpillar;
710 if (IsTileType(rampsouth, TT_MISC)) {
711 assert(IsAqueductTile(rampsouth));
712 transport_type = TRANSPORT_WATER;
713 psid = _aqueduct_sprites;
714 drawfarpillar = true;
715 } else {
716 assert(IsTileSubtype(rampsouth, TT_BRIDGE));
718 BridgeType type;
719 uint base_offset;
721 if (IsRailwayTile(rampsouth)) {
722 transport_type = TRANSPORT_RAIL;
723 type = GetRailBridgeType(rampsouth);
724 base_offset = GetRailTypeInfo(GetBridgeRailType(rampsouth))->bridge_offset;
725 } else {
726 transport_type = TRANSPORT_ROAD;
727 type = GetRoadBridgeType(rampsouth);
728 base_offset = 8;
731 psid = base_offset + GetBridgeSpriteTable(type, piece);
732 drawfarpillar = !HasBit(GetBridgeSpec(type)->flags, 0);
735 if (axis != AXIS_X) psid += 4;
737 int x = ti->x;
738 int y = ti->y;
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);
752 } else {
753 AddSortableSpriteToDraw (ti->vd, psid->sprite, psid->pal, x, y, 1, 16, 0x28, z, IsTransparencySet(TO_BRIDGES), 0, 0, BRIDGE_Z_START);
757 psid++;
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);
765 } else {
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);
773 if (surface != 0) {
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) {
795 y += 12;
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);
797 } else {
798 x += 12;
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;
809 psid++;
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);
822 } else {
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);
836 } else {
837 TileIndex next = ti->tile + TileOffsByDiagDir(dir);
838 if (ti->tileh != SLOPE_FLAT && ti->z == 0 && HasTileWaterClass(next) && GetWaterClass(next) == WATER_CLASS_SEA) {
839 DrawShoreTile (ti);
840 } else {
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);