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 tunnelbridge_cmd.cpp
12 * This file deals with tunnels and bridges (non-gui stuff)
13 * @todo separate this file into two
17 #include "newgrf_object.h"
18 #include "viewport_func.h"
19 #include "cmd_helper.h"
20 #include "command_func.h"
25 #include "pathfinder/yapf/yapf_cache.h"
26 #include "newgrf_sound.h"
27 #include "autoslope.h"
28 #include "tunnelbridge_map.h"
29 #include "strings_func.h"
30 #include "date_func.h"
31 #include "clear_func.h"
32 #include "vehicle_func.h"
33 #include "sound_func.h"
34 #include "tunnelbridge.h"
35 #include "cheat_type.h"
36 #include "elrail_func.h"
38 #include "company_base.h"
39 #include "newgrf_railtype.h"
40 #include "object_base.h"
42 #include "company_gui.h"
44 #include "table/strings.h"
45 #include "table/bridge_land.h"
47 #include "safeguards.h"
49 BridgeSpec _bridge
[MAX_BRIDGES
]; ///< The specification of all bridges.
50 TileIndex _build_tunnel_endtile
; ///< The end of a tunnel; as hidden return from the tunnel build command for GUI purposes.
52 /** Z position of the bridge sprites relative to bridge height (downwards) */
53 static const int BRIDGE_Z_START
= 3;
57 * Mark bridge tiles dirty.
58 * Note: The bridge does not need to exist, everything is passed via parameters.
59 * @param begin Start tile.
60 * @param end End tile.
61 * @param direction Direction from \a begin to \a end.
62 * @param bridge_height Bridge height level.
64 void MarkBridgeDirty(TileIndex begin
, TileIndex end
, DiagDirection direction
, uint bridge_height
)
66 TileIndexDiff delta
= TileOffsByDiagDir(direction
);
67 for (TileIndex t
= begin
; t
!= end
; t
+= delta
) {
68 MarkTileDirtyByTile(t
, bridge_height
- TileHeight(t
));
70 MarkTileDirtyByTile(end
);
74 * Mark bridge tiles dirty.
75 * @param tile Bridge head.
77 void MarkBridgeDirty(TileIndex tile
)
79 MarkBridgeDirty(tile
, GetOtherTunnelBridgeEnd(tile
), GetTunnelBridgeDirection(tile
), GetBridgeHeight(tile
));
82 /** Reset the data been eventually changed by the grf loaded. */
85 /* First, free sprite table data */
86 for (BridgeType i
= 0; i
< MAX_BRIDGES
; i
++) {
87 if (_bridge
[i
].sprite_table
!= NULL
) {
88 for (BridgePieces j
= BRIDGE_PIECE_NORTH
; j
< BRIDGE_PIECE_INVALID
; j
++) free(_bridge
[i
].sprite_table
[j
]);
89 free(_bridge
[i
].sprite_table
);
93 /* Then, wipe out current bridges */
94 memset(&_bridge
, 0, sizeof(_bridge
));
95 /* And finally, reinstall default data */
96 memcpy(&_bridge
, &_orig_bridge
, sizeof(_orig_bridge
));
100 * Calculate the price factor for building a long bridge.
101 * 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,
102 * @param length Length of the bridge.
103 * @return Price factor for the bridge.
105 int CalcBridgeLenCostFactor(int length
)
107 if (length
< 2) return length
;
111 for (int delta
= 1;; delta
++) {
112 for (int count
= 0; count
< delta
; count
++) {
113 if (length
== 0) return sum
;
121 * Get the foundation for a bridge.
122 * @param tileh The slope to build the bridge on.
123 * @param axis The axis of the bridge entrance.
124 * @return The foundation required.
126 Foundation
GetBridgeFoundation(Slope tileh
, Axis axis
)
128 if (tileh
== SLOPE_FLAT
||
129 ((tileh
== SLOPE_NE
|| tileh
== SLOPE_SW
) && axis
== AXIS_X
) ||
130 ((tileh
== SLOPE_NW
|| tileh
== SLOPE_SE
) && axis
== AXIS_Y
)) return FOUNDATION_NONE
;
132 return (HasSlopeHighestCorner(tileh
) ? InclinedFoundation(axis
) : FlatteningFoundation(tileh
));
136 * Determines if the track on a bridge ramp is flat or goes up/down.
138 * @param tileh Slope of the tile under the bridge head
139 * @param axis Orientation of bridge
140 * @return true iff the track is flat.
142 bool HasBridgeFlatRamp(Slope tileh
, Axis axis
)
144 ApplyFoundationToSlope(GetBridgeFoundation(tileh
, axis
), &tileh
);
145 /* If the foundation slope is flat the bridge has a non-flat ramp and vice versa. */
146 return (tileh
!= SLOPE_FLAT
);
149 static inline const PalSpriteID
*GetBridgeSpriteTable(int index
, BridgePieces table
)
151 const BridgeSpec
*bridge
= GetBridgeSpec(index
);
152 assert(table
< BRIDGE_PIECE_INVALID
);
153 if (bridge
->sprite_table
== NULL
|| bridge
->sprite_table
[table
] == NULL
) {
154 return _bridge_sprite_table
[index
][table
];
156 return bridge
->sprite_table
[table
];
162 * Determines the foundation for the north bridge head, and tests if the resulting slope is valid.
164 * @param axis Axis of the bridge
165 * @param tileh Slope of the tile under the north bridge head; returns slope on top of foundation
166 * @param z TileZ corresponding to tileh, gets modified as well
167 * @return Error or cost for bridge foundation
169 static CommandCost
CheckBridgeSlopeNorth(Axis axis
, Slope
*tileh
, int *z
)
171 Foundation f
= GetBridgeFoundation(*tileh
, axis
);
172 *z
+= ApplyFoundationToSlope(f
, tileh
);
174 Slope valid_inclined
= (axis
== AXIS_X
? SLOPE_NE
: SLOPE_NW
);
175 if ((*tileh
!= SLOPE_FLAT
) && (*tileh
!= valid_inclined
)) return CMD_ERROR
;
177 if (f
== FOUNDATION_NONE
) return CommandCost();
179 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
183 * Determines the foundation for the south bridge head, and tests if the resulting slope is valid.
185 * @param axis Axis of the bridge
186 * @param tileh Slope of the tile under the south bridge head; returns slope on top of foundation
187 * @param z TileZ corresponding to tileh, gets modified as well
188 * @return Error or cost for bridge foundation
190 static CommandCost
CheckBridgeSlopeSouth(Axis axis
, Slope
*tileh
, int *z
)
192 Foundation f
= GetBridgeFoundation(*tileh
, axis
);
193 *z
+= ApplyFoundationToSlope(f
, tileh
);
195 Slope valid_inclined
= (axis
== AXIS_X
? SLOPE_SW
: SLOPE_SE
);
196 if ((*tileh
!= SLOPE_FLAT
) && (*tileh
!= valid_inclined
)) return CMD_ERROR
;
198 if (f
== FOUNDATION_NONE
) return CommandCost();
200 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
204 * Is a bridge of the specified type and length available?
205 * @param bridge_type Wanted type of bridge.
206 * @param bridge_len Wanted length of the bridge.
207 * @return A succeeded (the requested bridge is available) or failed (it cannot be built) command.
209 CommandCost
CheckBridgeAvailability(BridgeType bridge_type
, uint bridge_len
, DoCommandFlag flags
)
211 if (flags
& DC_QUERY_COST
) {
212 if (bridge_len
<= _settings_game
.construction
.max_bridge_length
) return CommandCost();
213 return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG
);
216 if (bridge_type
>= MAX_BRIDGES
) return CMD_ERROR
;
218 const BridgeSpec
*b
= GetBridgeSpec(bridge_type
);
219 if (b
->avail_year
> _cur_year
) return CMD_ERROR
;
221 uint max
= min(b
->max_length
, _settings_game
.construction
.max_bridge_length
);
223 if (b
->min_length
> bridge_len
) return CMD_ERROR
;
224 if (bridge_len
<= max
) return CommandCost();
225 return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG
);
230 * @param end_tile end tile
231 * @param flags type of operation
232 * @param p1 packed start tile coords (~ dx)
233 * @param p2 various bitstuffed elements
234 * - p2 = (bit 0- 7) - bridge type (hi bh)
235 * - p2 = (bit 8-11) - rail type or road types.
236 * - p2 = (bit 15-16) - transport type.
238 * @return the cost of this operation or an error
240 CommandCost
CmdBuildBridge(TileIndex end_tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
242 CompanyID company
= _current_company
;
244 RailType railtype
= INVALID_RAILTYPE
;
245 RoadTypes roadtypes
= ROADTYPES_NONE
;
247 /* unpack parameters */
248 BridgeType bridge_type
= GB(p2
, 0, 8);
250 if (!IsValidTile(p1
)) return_cmd_error(STR_ERROR_BRIDGE_THROUGH_MAP_BORDER
);
252 TransportType transport_type
= Extract
<TransportType
, 15, 2>(p2
);
255 switch (transport_type
) {
257 roadtypes
= Extract
<RoadTypes
, 8, 2>(p2
);
258 if (!HasExactlyOneBit(roadtypes
) || !HasRoadTypesAvail(company
, roadtypes
)) return CMD_ERROR
;
262 railtype
= Extract
<RailType
, 8, 4>(p2
);
263 if (!ValParamRailtype(railtype
)) return CMD_ERROR
;
266 case TRANSPORT_WATER
:
270 /* Airports don't have bridges. */
273 TileIndex tile_start
= p1
;
274 TileIndex tile_end
= end_tile
;
276 if (company
== OWNER_DEITY
) {
277 if (transport_type
!= TRANSPORT_ROAD
) return CMD_ERROR
;
278 const Town
*town
= CalcClosestTownFromTile(tile_start
);
280 company
= OWNER_TOWN
;
282 /* If we are not within a town, we are not owned by the town */
283 if (town
== NULL
|| DistanceSquare(tile_start
, town
->xy
) > town
->cache
.squared_town_zone_radius
[HZB_TOWN_EDGE
]) {
284 company
= OWNER_NONE
;
288 if (tile_start
== tile_end
) {
289 return_cmd_error(STR_ERROR_CAN_T_START_AND_END_ON
);
293 if (TileX(tile_start
) == TileX(tile_end
)) {
295 } else if (TileY(tile_start
) == TileY(tile_end
)) {
298 return_cmd_error(STR_ERROR_START_AND_END_MUST_BE_IN
);
301 if (tile_end
< tile_start
) Swap(tile_start
, tile_end
);
303 uint bridge_len
= GetTunnelBridgeLength(tile_start
, tile_end
);
304 if (transport_type
!= TRANSPORT_WATER
) {
305 /* set and test bridge length, availability */
306 CommandCost ret
= CheckBridgeAvailability(bridge_type
, bridge_len
, flags
);
307 if (ret
.Failed()) return ret
;
309 if (bridge_len
> _settings_game
.construction
.max_bridge_length
) return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG
);
314 Slope tileh_start
= GetTileSlope(tile_start
, &z_start
);
315 Slope tileh_end
= GetTileSlope(tile_end
, &z_end
);
316 bool pbs_reservation
= false;
318 CommandCost terraform_cost_north
= CheckBridgeSlopeNorth(direction
, &tileh_start
, &z_start
);
319 CommandCost terraform_cost_south
= CheckBridgeSlopeSouth(direction
, &tileh_end
, &z_end
);
321 /* Aqueducts can't be built of flat land. */
322 if (transport_type
== TRANSPORT_WATER
&& (tileh_start
== SLOPE_FLAT
|| tileh_end
== SLOPE_FLAT
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
323 if (z_start
!= z_end
) return_cmd_error(STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT
);
325 CommandCost
cost(EXPENSES_CONSTRUCTION
);
328 if (IsBridgeTile(tile_start
) && IsBridgeTile(tile_end
) &&
329 GetOtherBridgeEnd(tile_start
) == tile_end
&&
330 GetTunnelBridgeTransportType(tile_start
) == transport_type
) {
331 /* Replace a current bridge. */
333 /* If this is a railway bridge, make sure the railtypes match. */
334 if (transport_type
== TRANSPORT_RAIL
&& GetRailType(tile_start
) != railtype
) {
335 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
338 /* Do not replace town bridges with lower speed bridges, unless in scenario editor. */
339 if (!(flags
& DC_QUERY_COST
) && IsTileOwner(tile_start
, OWNER_TOWN
) &&
340 GetBridgeSpec(bridge_type
)->speed
< GetBridgeSpec(GetBridgeType(tile_start
))->speed
&&
341 _game_mode
!= GM_EDITOR
) {
342 Town
*t
= ClosestTownFromTile(tile_start
, UINT_MAX
);
347 SetDParam(0, t
->index
);
348 return_cmd_error(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS
);
352 /* Do not replace the bridge with the same bridge type. */
353 if (!(flags
& DC_QUERY_COST
) && (bridge_type
== GetBridgeType(tile_start
)) && (transport_type
!= TRANSPORT_ROAD
|| (roadtypes
& ~GetRoadTypes(tile_start
)) == 0)) {
354 return_cmd_error(STR_ERROR_ALREADY_BUILT
);
357 /* Do not allow replacing another company's bridges. */
358 if (!IsTileOwner(tile_start
, company
) && !IsTileOwner(tile_start
, OWNER_TOWN
) && !IsTileOwner(tile_start
, OWNER_NONE
)) {
359 return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER
);
362 cost
.AddCost((bridge_len
+ 1) * _price
[PR_CLEAR_BRIDGE
]); // The cost of clearing the current bridge.
363 owner
= GetTileOwner(tile_start
);
365 /* If bridge belonged to bankrupt company, it has a new owner now */
366 is_new_owner
= (owner
== OWNER_NONE
);
367 if (is_new_owner
) owner
= company
;
369 switch (transport_type
) {
371 /* Keep the reservation, the path stays valid. */
372 pbs_reservation
= HasTunnelBridgeReservation(tile_start
);
376 /* Do not remove road types when upgrading a bridge */
377 roadtypes
|= GetRoadTypes(tile_start
);
383 /* Build a new bridge. */
385 bool allow_on_slopes
= (_settings_game
.construction
.build_on_slopes
&& transport_type
!= TRANSPORT_WATER
);
387 /* Try and clear the start landscape */
388 CommandCost ret
= DoCommand(tile_start
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
389 if (ret
.Failed()) return ret
;
392 if (terraform_cost_north
.Failed() || (terraform_cost_north
.GetCost() != 0 && !allow_on_slopes
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
393 cost
.AddCost(terraform_cost_north
);
395 /* Try and clear the end landscape */
396 ret
= DoCommand(tile_end
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
397 if (ret
.Failed()) return ret
;
400 /* false - end tile slope check */
401 if (terraform_cost_south
.Failed() || (terraform_cost_south
.GetCost() != 0 && !allow_on_slopes
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
402 cost
.AddCost(terraform_cost_south
);
404 const TileIndex heads
[] = {tile_start
, tile_end
};
405 for (int i
= 0; i
< 2; i
++) {
406 if (IsBridgeAbove(heads
[i
])) {
407 TileIndex north_head
= GetNorthernBridgeEnd(heads
[i
]);
409 if (direction
== GetBridgeAxis(heads
[i
])) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
411 if (z_start
+ 1 == GetBridgeHeight(north_head
)) {
412 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
417 TileIndexDiff delta
= (direction
== AXIS_X
? TileDiffXY(1, 0) : TileDiffXY(0, 1));
418 for (TileIndex tile
= tile_start
+ delta
; tile
!= tile_end
; tile
+= delta
) {
419 if (GetTileMaxZ(tile
) > z_start
) return_cmd_error(STR_ERROR_BRIDGE_TOO_LOW_FOR_TERRAIN
);
421 if (z_start
>= (GetTileZ(tile
) + _settings_game
.construction
.max_bridge_height
)) {
423 * Disallow too high bridges.
424 * Properly rendering a map where very high bridges (might) exist is expensive.
425 * See http://www.tt-forums.net/viewtopic.php?f=33&t=40844&start=980#p1131762
426 * for a detailed discussion. z_start here is one heightlevel below the bridge level.
428 return_cmd_error(STR_ERROR_BRIDGE_TOO_HIGH_FOR_TERRAIN
);
431 if (IsBridgeAbove(tile
)) {
432 /* Disallow crossing bridges for the time being */
433 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
436 switch (GetTileType(tile
)) {
438 if (!IsWater(tile
) && !IsCoast(tile
)) goto not_valid_below
;
442 if (!IsPlainRail(tile
)) goto not_valid_below
;
446 if (IsRoadDepot(tile
)) goto not_valid_below
;
449 case MP_TUNNELBRIDGE
:
450 if (IsTunnel(tile
)) break;
451 if (direction
== DiagDirToAxis(GetTunnelBridgeDirection(tile
))) goto not_valid_below
;
452 if (z_start
< GetBridgeHeight(tile
)) goto not_valid_below
;
456 const ObjectSpec
*spec
= ObjectSpec::GetByTile(tile
);
457 if ((spec
->flags
& OBJECT_FLAG_ALLOW_UNDER_BRIDGE
) == 0) goto not_valid_below
;
458 if (GetTileMaxZ(tile
) + spec
->height
> z_start
) goto not_valid_below
;
467 /* try and clear the middle landscape */
468 ret
= DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
469 if (ret
.Failed()) return ret
;
474 if (flags
& DC_EXEC
) {
475 /* We do this here because when replacing a bridge with another
476 * type calling SetBridgeMiddle isn't needed. After all, the
477 * tile already has the has_bridge_above bits set. */
478 SetBridgeMiddle(tile
, direction
);
487 if (flags
& DC_EXEC
) {
488 DiagDirection dir
= AxisToDiagDir(direction
);
490 Company
*c
= Company::GetIfValid(company
);
491 switch (transport_type
) {
493 /* Add to company infrastructure count if required. */
494 if (is_new_owner
&& c
!= NULL
) c
->infrastructure
.rail
[railtype
] += (bridge_len
+ 2) * TUNNELBRIDGE_TRACKBIT_FACTOR
;
495 MakeRailBridgeRamp(tile_start
, owner
, bridge_type
, dir
, railtype
);
496 MakeRailBridgeRamp(tile_end
, owner
, bridge_type
, ReverseDiagDir(dir
), railtype
);
497 SetTunnelBridgeReservation(tile_start
, pbs_reservation
);
498 SetTunnelBridgeReservation(tile_end
, pbs_reservation
);
501 case TRANSPORT_ROAD
: {
502 RoadTypes prev_roadtypes
= IsBridgeTile(tile_start
) ? GetRoadTypes(tile_start
) : ROADTYPES_NONE
;
504 /* Also give unowned present roadtypes to new owner */
505 if (HasBit(prev_roadtypes
, ROADTYPE_ROAD
) && GetRoadOwner(tile_start
, ROADTYPE_ROAD
) == OWNER_NONE
) ClrBit(prev_roadtypes
, ROADTYPE_ROAD
);
506 if (HasBit(prev_roadtypes
, ROADTYPE_TRAM
) && GetRoadOwner(tile_start
, ROADTYPE_TRAM
) == OWNER_NONE
) ClrBit(prev_roadtypes
, ROADTYPE_TRAM
);
509 /* Add all new road types to the company infrastructure counter. */
511 FOR_EACH_SET_ROADTYPE(new_rt
, roadtypes
^ prev_roadtypes
) {
512 /* A full diagonal road tile has two road bits. */
513 c
->infrastructure
.road
[new_rt
] += (bridge_len
+ 2) * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
;
516 Owner owner_road
= HasBit(prev_roadtypes
, ROADTYPE_ROAD
) ? GetRoadOwner(tile_start
, ROADTYPE_ROAD
) : company
;
517 Owner owner_tram
= HasBit(prev_roadtypes
, ROADTYPE_TRAM
) ? GetRoadOwner(tile_start
, ROADTYPE_TRAM
) : company
;
518 MakeRoadBridgeRamp(tile_start
, owner
, owner_road
, owner_tram
, bridge_type
, dir
, roadtypes
);
519 MakeRoadBridgeRamp(tile_end
, owner
, owner_road
, owner_tram
, bridge_type
, ReverseDiagDir(dir
), roadtypes
);
523 case TRANSPORT_WATER
:
524 if (is_new_owner
&& c
!= NULL
) c
->infrastructure
.water
+= (bridge_len
+ 2) * TUNNELBRIDGE_TRACKBIT_FACTOR
;
525 MakeAqueductBridgeRamp(tile_start
, owner
, dir
);
526 MakeAqueductBridgeRamp(tile_end
, owner
, ReverseDiagDir(dir
));
533 /* Mark all tiles dirty */
534 MarkBridgeDirty(tile_start
, tile_end
, AxisToDiagDir(direction
), z_start
);
535 DirtyCompanyInfrastructureWindows(company
);
538 if ((flags
& DC_EXEC
) && transport_type
== TRANSPORT_RAIL
) {
539 Track track
= AxisToTrack(direction
);
540 AddSideToSignalBuffer(tile_start
, INVALID_DIAGDIR
, company
);
541 YapfNotifyTrackLayoutChange(tile_start
, track
);
544 /* for human player that builds the bridge he gets a selection to choose from bridges (DC_QUERY_COST)
545 * It's unnecessary to execute this command every time for every bridge. So it is done only
546 * and cost is computed in "bridge_gui.c". For AI, Towns this has to be of course calculated
548 Company
*c
= Company::GetIfValid(company
);
549 if (!(flags
& DC_QUERY_COST
) || (c
!= NULL
&& c
->is_ai
)) {
550 bridge_len
+= 2; // begin and end tiles/ramps
552 switch (transport_type
) {
553 case TRANSPORT_ROAD
: cost
.AddCost(bridge_len
* _price
[PR_BUILD_ROAD
] * 2 * CountBits(roadtypes
)); break;
554 case TRANSPORT_RAIL
: cost
.AddCost(bridge_len
* RailBuildCost(railtype
)); break;
558 if (c
!= NULL
) bridge_len
= CalcBridgeLenCostFactor(bridge_len
);
560 if (transport_type
!= TRANSPORT_WATER
) {
561 cost
.AddCost((int64
)bridge_len
* _price
[PR_BUILD_BRIDGE
] * GetBridgeSpec(bridge_type
)->price
>> 8);
563 /* Aqueducts use a separate base cost. */
564 cost
.AddCost((int64
)bridge_len
* _price
[PR_BUILD_AQUEDUCT
]);
575 * @param start_tile start tile of tunnel
576 * @param flags type of operation
577 * @param p1 bit 0-3 railtype or roadtypes
578 * bit 8-9 transport type
581 * @return the cost of this operation or an error
583 CommandCost
CmdBuildTunnel(TileIndex start_tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
585 CompanyID company
= _current_company
;
587 TransportType transport_type
= Extract
<TransportType
, 8, 2>(p1
);
589 RailType railtype
= INVALID_RAILTYPE
;
590 RoadTypes rts
= ROADTYPES_NONE
;
591 _build_tunnel_endtile
= 0;
592 switch (transport_type
) {
594 railtype
= Extract
<RailType
, 0, 4>(p1
);
595 if (!ValParamRailtype(railtype
)) return CMD_ERROR
;
599 rts
= Extract
<RoadTypes
, 0, 2>(p1
);
600 if (!HasExactlyOneBit(rts
) || !HasRoadTypesAvail(company
, rts
)) return CMD_ERROR
;
603 default: return CMD_ERROR
;
606 if (company
== OWNER_DEITY
) {
607 if (transport_type
!= TRANSPORT_ROAD
) return CMD_ERROR
;
608 const Town
*town
= CalcClosestTownFromTile(start_tile
);
610 company
= OWNER_TOWN
;
612 /* If we are not within a town, we are not owned by the town */
613 if (town
== NULL
|| DistanceSquare(start_tile
, town
->xy
) > town
->cache
.squared_town_zone_radius
[HZB_TOWN_EDGE
]) {
614 company
= OWNER_NONE
;
620 Slope start_tileh
= GetTileSlope(start_tile
, &start_z
);
621 DiagDirection direction
= GetInclinedSlopeDirection(start_tileh
);
622 if (direction
== INVALID_DIAGDIR
) return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL
);
624 if (HasTileWaterGround(start_tile
)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER
);
626 CommandCost ret
= DoCommand(start_tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
627 if (ret
.Failed()) return ret
;
629 /* XXX - do NOT change 'ret' in the loop, as it is used as the price
630 * for the clearing of the entrance of the tunnel. Assigning it to
631 * cost before the loop will yield different costs depending on start-
632 * position, because of increased-cost-by-length: 'cost += cost >> 3' */
634 TileIndexDiff delta
= TileOffsByDiagDir(direction
);
635 DiagDirection tunnel_in_way_dir
;
636 if (DiagDirToAxis(direction
) == AXIS_Y
) {
637 tunnel_in_way_dir
= (TileX(start_tile
) < (MapMaxX() / 2)) ? DIAGDIR_SW
: DIAGDIR_NE
;
639 tunnel_in_way_dir
= (TileY(start_tile
) < (MapMaxX() / 2)) ? DIAGDIR_SE
: DIAGDIR_NW
;
642 TileIndex end_tile
= start_tile
;
644 /* Tile shift coefficient. Will decrease for very long tunnels to avoid exponential growth of price*/
646 /* Number of tiles from start of tunnel */
648 /* Number of tiles at which the cost increase coefficient per tile is halved */
651 CommandCost
cost(EXPENSES_CONSTRUCTION
);
655 if (!IsValidTile(end_tile
)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER
);
656 end_tileh
= GetTileSlope(end_tile
, &end_z
);
658 if (start_z
== end_z
) break;
660 if (!_cheats
.crossing_tunnels
.value
&& IsTunnelInWayDir(end_tile
, start_z
, tunnel_in_way_dir
)) {
661 return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY
);
665 if (tiles
== tiles_bump
) {
670 cost
.AddCost(_price
[PR_BUILD_TUNNEL
]);
671 cost
.AddCost(cost
.GetCost() >> tiles_coef
); // add a multiplier for longer tunnels
674 /* Add the cost of the entrance */
675 cost
.AddCost(_price
[PR_BUILD_TUNNEL
]);
678 /* if the command fails from here on we want the end tile to be highlighted */
679 _build_tunnel_endtile
= end_tile
;
681 if (tiles
> _settings_game
.construction
.max_tunnel_length
) return_cmd_error(STR_ERROR_TUNNEL_TOO_LONG
);
683 if (HasTileWaterGround(end_tile
)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER
);
685 /* Clear the tile in any case */
686 ret
= DoCommand(end_tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
687 if (ret
.Failed()) return_cmd_error(STR_ERROR_UNABLE_TO_EXCAVATE_LAND
);
690 /* slope of end tile must be complementary to the slope of the start tile */
691 if (end_tileh
!= ComplementSlope(start_tileh
)) {
692 /* Mark the tile as already cleared for the terraform command.
693 * Do this for all tiles (like trees), not only objects. */
694 ClearedObjectArea
*coa
= FindClearedObject(end_tile
);
696 coa
= _cleared_object_areas
.Append();
697 coa
->first_tile
= end_tile
;
698 coa
->area
= TileArea(end_tile
, 1, 1);
701 /* Hide the tile from the terraforming command */
702 TileIndex old_first_tile
= coa
->first_tile
;
703 coa
->first_tile
= INVALID_TILE
;
704 ret
= DoCommand(end_tile
, end_tileh
& start_tileh
, 0, flags
, CMD_TERRAFORM_LAND
);
705 coa
->first_tile
= old_first_tile
;
706 if (ret
.Failed()) return_cmd_error(STR_ERROR_UNABLE_TO_EXCAVATE_LAND
);
709 cost
.AddCost(_price
[PR_BUILD_TUNNEL
]);
711 /* Pay for the rail/road in the tunnel including entrances */
712 switch (transport_type
) {
713 case TRANSPORT_ROAD
: cost
.AddCost((tiles
+ 2) * _price
[PR_BUILD_ROAD
] * 2); break;
714 case TRANSPORT_RAIL
: cost
.AddCost((tiles
+ 2) * RailBuildCost(railtype
)); break;
715 default: NOT_REACHED();
718 if (flags
& DC_EXEC
) {
719 Company
*c
= Company::GetIfValid(company
);
720 uint num_pieces
= (tiles
+ 2) * TUNNELBRIDGE_TRACKBIT_FACTOR
;
721 if (transport_type
== TRANSPORT_RAIL
) {
722 if (!IsTunnelTile(start_tile
) && c
!= NULL
) c
->infrastructure
.rail
[railtype
] += num_pieces
;
723 MakeRailTunnel(start_tile
, company
, direction
, railtype
);
724 MakeRailTunnel(end_tile
, company
, ReverseDiagDir(direction
), railtype
);
725 AddSideToSignalBuffer(start_tile
, INVALID_DIAGDIR
, company
);
726 YapfNotifyTrackLayoutChange(start_tile
, DiagDirToDiagTrack(direction
));
730 FOR_EACH_SET_ROADTYPE(rt
, rts
^ (IsTunnelTile(start_tile
) ? GetRoadTypes(start_tile
) : ROADTYPES_NONE
)) {
731 c
->infrastructure
.road
[rt
] += num_pieces
* 2; // A full diagonal road has two road bits.
734 MakeRoadTunnel(start_tile
, company
, direction
, rts
);
735 MakeRoadTunnel(end_tile
, company
, ReverseDiagDir(direction
), rts
);
737 DirtyCompanyInfrastructureWindows(company
);
745 * Are we allowed to remove the tunnel or bridge at \a tile?
746 * @param tile End point of the tunnel or bridge.
747 * @return A succeeded command if the tunnel or bridge may be removed, a failed command otherwise.
749 static inline CommandCost
CheckAllowRemoveTunnelBridge(TileIndex tile
)
751 /* Floods can remove anything as well as the scenario editor */
752 if (_current_company
== OWNER_WATER
|| _game_mode
== GM_EDITOR
) return CommandCost();
754 switch (GetTunnelBridgeTransportType(tile
)) {
755 case TRANSPORT_ROAD
: {
756 RoadTypes rts
= GetRoadTypes(tile
);
757 Owner road_owner
= _current_company
;
758 Owner tram_owner
= _current_company
;
760 if (HasBit(rts
, ROADTYPE_ROAD
)) road_owner
= GetRoadOwner(tile
, ROADTYPE_ROAD
);
761 if (HasBit(rts
, ROADTYPE_TRAM
)) tram_owner
= GetRoadOwner(tile
, ROADTYPE_TRAM
);
763 /* We can remove unowned road and if the town allows it */
764 if (road_owner
== OWNER_TOWN
&& _current_company
!= OWNER_TOWN
&& !(_settings_game
.construction
.extra_dynamite
|| _cheats
.magic_bulldozer
.value
)) {
765 /* Town does not allow */
766 return CheckTileOwnership(tile
);
768 if (road_owner
== OWNER_NONE
|| road_owner
== OWNER_TOWN
) road_owner
= _current_company
;
769 if (tram_owner
== OWNER_NONE
) tram_owner
= _current_company
;
771 CommandCost ret
= CheckOwnership(road_owner
, tile
);
772 if (ret
.Succeeded()) ret
= CheckOwnership(tram_owner
, tile
);
777 return CheckOwnership(GetTileOwner(tile
));
779 case TRANSPORT_WATER
: {
780 /* Always allow to remove aqueducts without owner. */
781 Owner aqueduct_owner
= GetTileOwner(tile
);
782 if (aqueduct_owner
== OWNER_NONE
) aqueduct_owner
= _current_company
;
783 return CheckOwnership(aqueduct_owner
);
786 default: NOT_REACHED();
791 * Remove a tunnel from the game, update town rating, etc.
792 * @param tile Tile containing one of the endpoints of the tunnel.
793 * @param flags Command flags.
794 * @return Succeeded or failed command.
796 static CommandCost
DoClearTunnel(TileIndex tile
, DoCommandFlag flags
)
798 CommandCost ret
= CheckAllowRemoveTunnelBridge(tile
);
799 if (ret
.Failed()) return ret
;
801 TileIndex endtile
= GetOtherTunnelEnd(tile
);
803 ret
= TunnelBridgeIsFree(tile
, endtile
);
804 if (ret
.Failed()) return ret
;
806 _build_tunnel_endtile
= endtile
;
809 if (IsTileOwner(tile
, OWNER_TOWN
) && _game_mode
!= GM_EDITOR
) {
810 t
= ClosestTownFromTile(tile
, UINT_MAX
); // town penalty rating
812 /* Check if you are allowed to remove the tunnel owned by a town
813 * Removal depends on difficulty settings */
814 CommandCost ret
= CheckforTownRating(flags
, t
, TUNNELBRIDGE_REMOVE
);
815 if (ret
.Failed()) return ret
;
818 /* checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
819 * you have a "Poor" (0) town rating */
820 if (IsTileOwner(tile
, OWNER_TOWN
) && _game_mode
!= GM_EDITOR
) {
821 ChangeTownRating(t
, RATING_TUNNEL_BRIDGE_DOWN_STEP
, RATING_TUNNEL_BRIDGE_MINIMUM
, flags
);
824 uint len
= GetTunnelBridgeLength(tile
, endtile
) + 2; // Don't forget the end tiles.
826 if (flags
& DC_EXEC
) {
827 if (GetTunnelBridgeTransportType(tile
) == TRANSPORT_RAIL
) {
828 /* We first need to request values before calling DoClearSquare */
829 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
830 Track track
= DiagDirToDiagTrack(dir
);
831 Owner owner
= GetTileOwner(tile
);
834 if (HasTunnelBridgeReservation(tile
)) {
835 v
= GetTrainForReservation(tile
, track
);
836 if (v
!= NULL
) FreeTrainTrackReservation(v
);
839 if (Company::IsValidID(owner
)) {
840 Company::Get(owner
)->infrastructure
.rail
[GetRailType(tile
)] -= len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
841 DirtyCompanyInfrastructureWindows(owner
);
845 DoClearSquare(endtile
);
847 /* cannot use INVALID_DIAGDIR for signal update because the tunnel doesn't exist anymore */
848 AddSideToSignalBuffer(tile
, ReverseDiagDir(dir
), owner
);
849 AddSideToSignalBuffer(endtile
, dir
, owner
);
851 YapfNotifyTrackLayoutChange(tile
, track
);
852 YapfNotifyTrackLayoutChange(endtile
, track
);
854 if (v
!= NULL
) TryPathReserve(v
);
857 FOR_EACH_SET_ROADTYPE(rt
, GetRoadTypes(tile
)) {
858 /* A full diagonal road tile has two road bits. */
859 Company
*c
= Company::GetIfValid(GetRoadOwner(tile
, rt
));
861 c
->infrastructure
.road
[rt
] -= len
* 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
;
862 DirtyCompanyInfrastructureWindows(c
->index
);
867 DoClearSquare(endtile
);
870 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_TUNNEL
] * len
);
875 * Remove a bridge from the game, update town rating, etc.
876 * @param tile Tile containing one of the endpoints of the bridge.
877 * @param flags Command flags.
878 * @return Succeeded or failed command.
880 static CommandCost
DoClearBridge(TileIndex tile
, DoCommandFlag flags
)
882 CommandCost ret
= CheckAllowRemoveTunnelBridge(tile
);
883 if (ret
.Failed()) return ret
;
885 TileIndex endtile
= GetOtherBridgeEnd(tile
);
887 ret
= TunnelBridgeIsFree(tile
, endtile
);
888 if (ret
.Failed()) return ret
;
890 DiagDirection direction
= GetTunnelBridgeDirection(tile
);
891 TileIndexDiff delta
= TileOffsByDiagDir(direction
);
894 if (IsTileOwner(tile
, OWNER_TOWN
) && _game_mode
!= GM_EDITOR
) {
895 t
= ClosestTownFromTile(tile
, UINT_MAX
); // town penalty rating
897 /* Check if you are allowed to remove the bridge owned by a town
898 * Removal depends on difficulty settings */
899 CommandCost ret
= CheckforTownRating(flags
, t
, TUNNELBRIDGE_REMOVE
);
900 if (ret
.Failed()) return ret
;
903 /* checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
904 * you have a "Poor" (0) town rating */
905 if (IsTileOwner(tile
, OWNER_TOWN
) && _game_mode
!= GM_EDITOR
) {
906 ChangeTownRating(t
, RATING_TUNNEL_BRIDGE_DOWN_STEP
, RATING_TUNNEL_BRIDGE_MINIMUM
, flags
);
909 Money base_cost
= (GetTunnelBridgeTransportType(tile
) != TRANSPORT_WATER
) ? _price
[PR_CLEAR_BRIDGE
] : _price
[PR_CLEAR_AQUEDUCT
];
910 uint len
= GetTunnelBridgeLength(tile
, endtile
) + 2; // Don't forget the end tiles.
912 if (flags
& DC_EXEC
) {
913 /* read this value before actual removal of bridge */
914 bool rail
= GetTunnelBridgeTransportType(tile
) == TRANSPORT_RAIL
;
915 Owner owner
= GetTileOwner(tile
);
916 int height
= GetBridgeHeight(tile
);
919 if (rail
&& HasTunnelBridgeReservation(tile
)) {
920 v
= GetTrainForReservation(tile
, DiagDirToDiagTrack(direction
));
921 if (v
!= NULL
) FreeTrainTrackReservation(v
);
924 /* Update company infrastructure counts. */
926 if (Company::IsValidID(owner
)) Company::Get(owner
)->infrastructure
.rail
[GetRailType(tile
)] -= len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
927 } else if (GetTunnelBridgeTransportType(tile
) == TRANSPORT_ROAD
) {
929 FOR_EACH_SET_ROADTYPE(rt
, GetRoadTypes(tile
)) {
930 Company
*c
= Company::GetIfValid(GetRoadOwner(tile
, rt
));
932 /* A full diagonal road tile has two road bits. */
933 c
->infrastructure
.road
[rt
] -= len
* 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
;
934 DirtyCompanyInfrastructureWindows(c
->index
);
938 if (Company::IsValidID(owner
)) Company::Get(owner
)->infrastructure
.water
-= len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
940 DirtyCompanyInfrastructureWindows(owner
);
943 DoClearSquare(endtile
);
944 for (TileIndex c
= tile
+ delta
; c
!= endtile
; c
+= delta
) {
945 /* do not let trees appear from 'nowhere' after removing bridge */
946 if (IsNormalRoadTile(c
) && GetRoadside(c
) == ROADSIDE_TREES
) {
947 int minz
= GetTileMaxZ(c
) + 3;
948 if (height
< minz
) SetRoadside(c
, ROADSIDE_PAVED
);
950 ClearBridgeMiddle(c
);
951 MarkTileDirtyByTile(c
, height
- TileHeight(c
));
955 /* cannot use INVALID_DIAGDIR for signal update because the bridge doesn't exist anymore */
956 AddSideToSignalBuffer(tile
, ReverseDiagDir(direction
), owner
);
957 AddSideToSignalBuffer(endtile
, direction
, owner
);
959 Track track
= DiagDirToDiagTrack(direction
);
960 YapfNotifyTrackLayoutChange(tile
, track
);
961 YapfNotifyTrackLayoutChange(endtile
, track
);
963 if (v
!= NULL
) TryPathReserve(v
, true);
967 return CommandCost(EXPENSES_CONSTRUCTION
, len
* base_cost
);
971 * Remove a tunnel or a bridge from the game.
972 * @param tile Tile containing one of the endpoints.
973 * @param flags Command flags.
974 * @return Succeeded or failed command.
976 static CommandCost
ClearTile_TunnelBridge(TileIndex tile
, DoCommandFlag flags
)
978 if (IsTunnel(tile
)) {
979 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST
);
980 return DoClearTunnel(tile
, flags
);
981 } else { // IsBridge(tile)
982 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
983 return DoClearBridge(tile
, flags
);
988 * Draw a single pillar sprite.
989 * @param psid Pillarsprite
993 * @param w Bounding box size in X direction
994 * @param h Bounding box size in Y direction
995 * @param subsprite Optional subsprite for drawing halfpillars
997 static inline void DrawPillar(const PalSpriteID
*psid
, int x
, int y
, int z
, int w
, int h
, const SubSprite
*subsprite
)
999 static const int PILLAR_Z_OFFSET
= TILE_HEIGHT
- BRIDGE_Z_START
; ///< Start offset of pillar wrt. bridge (downwards)
1000 AddSortableSpriteToDraw(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
);
1004 * Draw two bridge pillars (north and south).
1005 * @param z_bottom Bottom Z
1006 * @param z_top Top Z
1007 * @param psid Pillarsprite
1010 * @param w Bounding box size in X direction
1011 * @param h Bounding box size in Y direction
1012 * @return Reached Z at the bottom
1014 static int DrawPillarColumn(int z_bottom
, int z_top
, const PalSpriteID
*psid
, int x
, int y
, int w
, int h
)
1017 for (cur_z
= z_top
; cur_z
>= z_bottom
; cur_z
-= TILE_HEIGHT
) {
1018 DrawPillar(psid
, x
, y
, cur_z
, w
, h
, NULL
);
1024 * Draws the pillars under high bridges.
1026 * @param psid Image and palette of a bridge pillar.
1027 * @param ti #TileInfo of current bridge-middle-tile.
1028 * @param axis Orientation of bridge.
1029 * @param drawfarpillar Whether to draw the pillar at the back
1030 * @param x Sprite X position of front pillar.
1031 * @param y Sprite Y position of front pillar.
1032 * @param z_bridge Absolute height of bridge bottom.
1034 static void DrawBridgePillars(const PalSpriteID
*psid
, const TileInfo
*ti
, Axis axis
, bool drawfarpillar
, int x
, int y
, int z_bridge
)
1036 static const int bounding_box_size
[2] = {16, 2}; ///< bounding box size of pillars along bridge direction
1037 static const int back_pillar_offset
[2] = { 0, 9}; ///< sprite position offset of back facing pillar
1039 static const int INF
= 1000; ///< big number compared to sprite size
1040 static const SubSprite half_pillar_sub_sprite
[2][2] = {
1041 { { -14, -INF
, INF
, INF
}, { -INF
, -INF
, -15, INF
} }, // X axis, north and south
1042 { { -INF
, -INF
, 15, INF
}, { 16, -INF
, INF
, INF
} }, // Y axis, north and south
1045 if (psid
->sprite
== 0) return;
1047 /* Determine ground height under pillars */
1048 DiagDirection south_dir
= AxisToDiagDir(axis
);
1049 int z_front_north
= ti
->z
;
1050 int z_back_north
= ti
->z
;
1051 int z_front_south
= ti
->z
;
1052 int z_back_south
= ti
->z
;
1053 GetSlopePixelZOnEdge(ti
->tileh
, south_dir
, &z_front_south
, &z_back_south
);
1054 GetSlopePixelZOnEdge(ti
->tileh
, ReverseDiagDir(south_dir
), &z_front_north
, &z_back_north
);
1056 /* Shared height of pillars */
1057 int z_front
= max(z_front_north
, z_front_south
);
1058 int z_back
= max(z_back_north
, z_back_south
);
1060 /* x and y size of bounding-box of pillars */
1061 int w
= bounding_box_size
[axis
];
1062 int h
= bounding_box_size
[OtherAxis(axis
)];
1063 /* sprite position of back facing pillar */
1064 int x_back
= x
- back_pillar_offset
[axis
];
1065 int y_back
= y
- back_pillar_offset
[OtherAxis(axis
)];
1067 /* Draw front pillars */
1068 int bottom_z
= DrawPillarColumn(z_front
, z_bridge
, psid
, x
, y
, w
, h
);
1069 if (z_front_north
< z_front
) DrawPillar(psid
, x
, y
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][0]);
1070 if (z_front_south
< z_front
) DrawPillar(psid
, x
, y
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][1]);
1072 /* Draw back pillars, skip top two parts, which are hidden by the bridge */
1073 int z_bridge_back
= z_bridge
- 2 * (int)TILE_HEIGHT
;
1074 if (drawfarpillar
&& (z_back_north
<= z_bridge_back
|| z_back_south
<= z_bridge_back
)) {
1075 bottom_z
= DrawPillarColumn(z_back
, z_bridge_back
, psid
, x_back
, y_back
, w
, h
);
1076 if (z_back_north
< z_back
) DrawPillar(psid
, x_back
, y_back
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][0]);
1077 if (z_back_south
< z_back
) DrawPillar(psid
, x_back
, y_back
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][1]);
1082 * Draws the trambits over an already drawn (lower end) of a bridge.
1083 * @param x the x of the bridge
1084 * @param y the y of the bridge
1085 * @param z the z of the bridge
1086 * @param offset number representing whether to level or sloped and the direction
1087 * @param overlay do we want to still see the road?
1088 * @param head are we drawing bridge head?
1090 static void DrawBridgeTramBits(int x
, int y
, int z
, int offset
, bool overlay
, bool head
)
1092 static const SpriteID tram_offsets
[2][6] = { { 107, 108, 109, 110, 111, 112 }, { 4, 5, 15, 16, 17, 18 } };
1093 static const SpriteID back_offsets
[6] = { 95, 96, 99, 102, 100, 101 };
1094 static const SpriteID front_offsets
[6] = { 97, 98, 103, 106, 104, 105 };
1096 static const uint size_x
[6] = { 1, 16, 16, 1, 16, 1 };
1097 static const uint size_y
[6] = { 16, 1, 1, 16, 1, 16 };
1098 static const uint front_bb_offset_x
[6] = { 15, 0, 0, 15, 0, 15 };
1099 static const uint front_bb_offset_y
[6] = { 0, 15, 15, 0, 15, 0 };
1101 /* The sprites under the vehicles are drawn as SpriteCombine. StartSpriteCombine() has already been called
1102 * The bounding boxes here are the same as for bridge front/roof */
1103 if (head
|| !IsInvisibilitySet(TO_BRIDGES
)) {
1104 AddSortableSpriteToDraw(SPR_TRAMWAY_BASE
+ tram_offsets
[overlay
][offset
], PAL_NONE
,
1105 x
, y
, size_x
[offset
], size_y
[offset
], 0x28, z
,
1106 !head
&& IsTransparencySet(TO_BRIDGES
));
1109 /* Do not draw catenary if it is set invisible */
1110 if (!IsInvisibilitySet(TO_CATENARY
)) {
1111 AddSortableSpriteToDraw(SPR_TRAMWAY_BASE
+ back_offsets
[offset
], PAL_NONE
,
1112 x
, y
, size_x
[offset
], size_y
[offset
], 0x28, z
,
1113 IsTransparencySet(TO_CATENARY
));
1116 /* Start a new SpriteCombine for the front part */
1118 StartSpriteCombine();
1120 /* For sloped sprites the bounding box needs to be higher, as the pylons stop on a higher point */
1121 if (!IsInvisibilitySet(TO_CATENARY
)) {
1122 AddSortableSpriteToDraw(SPR_TRAMWAY_BASE
+ front_offsets
[offset
], PAL_NONE
,
1123 x
, y
, size_x
[offset
] + front_bb_offset_x
[offset
], size_y
[offset
] + front_bb_offset_y
[offset
], 0x28, z
,
1124 IsTransparencySet(TO_CATENARY
), front_bb_offset_x
[offset
], front_bb_offset_y
[offset
]);
1129 * Draws a tunnel of bridge tile.
1130 * For tunnels, this is rather simple, as you only need to draw the entrance.
1131 * Bridges are a bit more complex. base_offset is where the sprite selection comes into play
1132 * and it works a bit like a bitmask.<p> For bridge heads:
1133 * @param ti TileInfo of the structure to draw
1134 * <ul><li>Bit 0: direction</li>
1135 * <li>Bit 1: northern or southern heads</li>
1136 * <li>Bit 2: Set if the bridge head is sloped</li>
1137 * <li>Bit 3 and more: Railtype Specific subset</li>
1139 * Please note that in this code, "roads" are treated as railtype 1, whilst the real railtypes are 0, 2 and 3
1141 static void DrawTile_TunnelBridge(TileInfo
*ti
)
1143 TransportType transport_type
= GetTunnelBridgeTransportType(ti
->tile
);
1144 DiagDirection tunnelbridge_direction
= GetTunnelBridgeDirection(ti
->tile
);
1146 if (IsTunnel(ti
->tile
)) {
1147 /* Front view of tunnel bounding boxes:
1149 * 122223 <- BB_Z_SEPARATOR
1151 * 1 3 1,3 = empty helper BB
1152 * 1 3 2 = SpriteCombine of tunnel-roof and catenary (tram & elrail)
1156 static const int _tunnel_BB
[4][12] = {
1157 /* tunnnel-roof | Z-separator | tram-catenary
1158 * w h bb_x bb_y| x y w h |bb_x bb_y w h */
1159 { 1, 0, -15, -14, 0, 15, 16, 1, 0, 1, 16, 15 }, // NE
1160 { 0, 1, -14, -15, 15, 0, 1, 16, 1, 0, 15, 16 }, // SE
1161 { 1, 0, -15, -14, 0, 15, 16, 1, 0, 1, 16, 15 }, // SW
1162 { 0, 1, -14, -15, 15, 0, 1, 16, 1, 0, 15, 16 }, // NW
1164 const int *BB_data
= _tunnel_BB
[tunnelbridge_direction
];
1166 bool catenary
= false;
1169 SpriteID railtype_overlay
= 0;
1170 if (transport_type
== TRANSPORT_RAIL
) {
1171 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(ti
->tile
));
1172 image
= rti
->base_sprites
.tunnel
;
1173 if (rti
->UsesOverlay()) {
1174 /* Check if the railtype has custom tunnel portals. */
1175 railtype_overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_TUNNEL_PORTAL
);
1176 if (railtype_overlay
!= 0) image
= SPR_RAILTYPE_TUNNEL_BASE
; // Draw blank grass tunnel base.
1179 image
= SPR_TUNNEL_ENTRY_REAR_ROAD
;
1182 if (HasTunnelBridgeSnowOrDesert(ti
->tile
)) image
+= railtype_overlay
!= 0 ? 8 : 32;
1184 image
+= tunnelbridge_direction
* 2;
1185 DrawGroundSprite(image
, PAL_NONE
);
1187 if (transport_type
== TRANSPORT_ROAD
) {
1188 RoadTypes rts
= GetRoadTypes(ti
->tile
);
1190 if (HasBit(rts
, ROADTYPE_TRAM
)) {
1191 static const SpriteID tunnel_sprites
[2][4] = { { 28, 78, 79, 27 }, { 5, 76, 77, 4 } };
1193 DrawGroundSprite(SPR_TRAMWAY_BASE
+ tunnel_sprites
[rts
- ROADTYPES_TRAM
][tunnelbridge_direction
], PAL_NONE
);
1195 /* Do not draw wires if they are invisible */
1196 if (!IsInvisibilitySet(TO_CATENARY
)) {
1198 StartSpriteCombine();
1199 AddSortableSpriteToDraw(SPR_TRAMWAY_TUNNEL_WIRES
+ tunnelbridge_direction
, PAL_NONE
, ti
->x
, ti
->y
, BB_data
[10], BB_data
[11], TILE_HEIGHT
, ti
->z
, IsTransparencySet(TO_CATENARY
), BB_data
[8], BB_data
[9], BB_Z_SEPARATOR
);
1203 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(ti
->tile
));
1204 if (rti
->UsesOverlay()) {
1205 SpriteID surface
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_TUNNEL
);
1206 if (surface
!= 0) DrawGroundSprite(surface
+ tunnelbridge_direction
, PAL_NONE
);
1209 /* PBS debugging, draw reserved tracks darker */
1210 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasTunnelBridgeReservation(ti
->tile
)) {
1211 if (rti
->UsesOverlay()) {
1212 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
);
1213 DrawGroundSprite(overlay
+ RTO_X
+ DiagDirToAxis(tunnelbridge_direction
), PALETTE_CRASH
);
1215 DrawGroundSprite(DiagDirToAxis(tunnelbridge_direction
) == AXIS_X
? rti
->base_sprites
.single_x
: rti
->base_sprites
.single_y
, PALETTE_CRASH
);
1219 if (HasRailCatenaryDrawn(GetRailType(ti
->tile
))) {
1220 /* Maybe draw pylons on the entry side */
1221 DrawRailCatenary(ti
);
1224 StartSpriteCombine();
1225 /* Draw wire above the ramp */
1226 DrawRailCatenaryOnTunnel(ti
);
1230 if (railtype_overlay
!= 0 && !catenary
) StartSpriteCombine();
1232 AddSortableSpriteToDraw(image
+ 1, PAL_NONE
, ti
->x
+ TILE_SIZE
- 1, ti
->y
+ TILE_SIZE
- 1, BB_data
[0], BB_data
[1], TILE_HEIGHT
, ti
->z
, false, BB_data
[2], BB_data
[3], BB_Z_SEPARATOR
);
1233 /* Draw railtype tunnel portal overlay if defined. */
1234 if (railtype_overlay
!= 0) AddSortableSpriteToDraw(railtype_overlay
+ tunnelbridge_direction
, PAL_NONE
, ti
->x
+ TILE_SIZE
- 1, ti
->y
+ TILE_SIZE
- 1, BB_data
[0], BB_data
[1], TILE_HEIGHT
, ti
->z
, false, BB_data
[2], BB_data
[3], BB_Z_SEPARATOR
);
1236 if (catenary
|| railtype_overlay
!= 0) EndSpriteCombine();
1238 /* Add helper BB for sprite sorting that separates the tunnel from things beside of it. */
1239 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX
, PAL_NONE
, ti
->x
, ti
->y
, BB_data
[6], BB_data
[7], TILE_HEIGHT
, ti
->z
);
1240 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX
, PAL_NONE
, ti
->x
+ BB_data
[4], ti
->y
+ BB_data
[5], BB_data
[6], BB_data
[7], TILE_HEIGHT
, ti
->z
);
1242 DrawBridgeMiddle(ti
);
1243 } else { // IsBridge(ti->tile)
1244 const PalSpriteID
*psid
;
1246 bool ice
= HasTunnelBridgeSnowOrDesert(ti
->tile
);
1248 if (transport_type
== TRANSPORT_RAIL
) {
1249 base_offset
= GetRailTypeInfo(GetRailType(ti
->tile
))->bridge_offset
;
1250 assert(base_offset
!= 8); // This one is used for roads
1255 /* as the lower 3 bits are used for other stuff, make sure they are clear */
1256 assert( (base_offset
& 0x07) == 0x00);
1258 DrawFoundation(ti
, GetBridgeFoundation(ti
->tileh
, DiagDirToAxis(tunnelbridge_direction
)));
1260 /* HACK Wizardry to convert the bridge ramp direction into a sprite offset */
1261 base_offset
+= (6 - tunnelbridge_direction
) % 4;
1263 /* Table number BRIDGE_PIECE_HEAD always refers to the bridge heads for any bridge type */
1264 if (transport_type
!= TRANSPORT_WATER
) {
1265 if (ti
->tileh
== SLOPE_FLAT
) base_offset
+= 4; // sloped bridge head
1266 psid
= &GetBridgeSpriteTable(GetBridgeType(ti
->tile
), BRIDGE_PIECE_HEAD
)[base_offset
];
1268 psid
= _aqueduct_sprites
+ base_offset
;
1272 TileIndex next
= ti
->tile
+ TileOffsByDiagDir(tunnelbridge_direction
);
1273 if (ti
->tileh
!= SLOPE_FLAT
&& ti
->z
== 0 && HasTileWaterClass(next
) && GetWaterClass(next
) == WATER_CLASS_SEA
) {
1274 DrawShoreTile(ti
->tileh
);
1276 DrawClearLandTile(ti
, 3);
1279 DrawGroundSprite(SPR_FLAT_SNOW_DESERT_TILE
+ SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
1284 /* Draw Trambits and PBS Reservation as SpriteCombine */
1285 if (transport_type
== TRANSPORT_ROAD
|| transport_type
== TRANSPORT_RAIL
) StartSpriteCombine();
1287 /* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
1288 * it doesn't disappear behind it
1290 /* Bridge heads are drawn solid no matter how invisibility/transparency is set */
1291 AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, ti
->x
, ti
->y
, 16, 16, ti
->tileh
== SLOPE_FLAT
? 0 : 8, ti
->z
);
1293 if (transport_type
== TRANSPORT_ROAD
) {
1294 RoadTypes rts
= GetRoadTypes(ti
->tile
);
1296 if (HasBit(rts
, ROADTYPE_TRAM
)) {
1297 uint offset
= tunnelbridge_direction
;
1299 if (ti
->tileh
!= SLOPE_FLAT
) {
1300 offset
= (offset
+ 1) & 1;
1305 /* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */
1306 DrawBridgeTramBits(ti
->x
, ti
->y
, z
, offset
, HasBit(rts
, ROADTYPE_ROAD
), true);
1309 } else if (transport_type
== TRANSPORT_RAIL
) {
1310 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(ti
->tile
));
1311 if (rti
->UsesOverlay()) {
1312 SpriteID surface
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_BRIDGE
);
1314 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(tunnelbridge_direction
))) {
1315 AddSortableSpriteToDraw(surface
+ ((DiagDirToAxis(tunnelbridge_direction
) == AXIS_X
) ? RTBO_X
: RTBO_Y
), PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 0, ti
->z
+ 8);
1317 AddSortableSpriteToDraw(surface
+ RTBO_SLOPE
+ tunnelbridge_direction
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
1320 /* Don't fallback to non-overlay sprite -- the spec states that
1321 * if an overlay is present then the bridge surface must be
1325 /* PBS debugging, draw reserved tracks darker */
1326 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasTunnelBridgeReservation(ti
->tile
)) {
1327 if (rti
->UsesOverlay()) {
1328 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
);
1329 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(tunnelbridge_direction
))) {
1330 AddSortableSpriteToDraw(overlay
+ RTO_X
+ DiagDirToAxis(tunnelbridge_direction
), PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 0, ti
->z
+ 8);
1332 AddSortableSpriteToDraw(overlay
+ RTO_SLOPE_NE
+ tunnelbridge_direction
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
1335 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(tunnelbridge_direction
))) {
1336 AddSortableSpriteToDraw(DiagDirToAxis(tunnelbridge_direction
) == AXIS_X
? rti
->base_sprites
.single_x
: rti
->base_sprites
.single_y
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 0, ti
->z
+ 8);
1338 AddSortableSpriteToDraw(rti
->base_sprites
.single_sloped
+ tunnelbridge_direction
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
1344 if (HasRailCatenaryDrawn(GetRailType(ti
->tile
))) {
1345 DrawRailCatenary(ti
);
1349 DrawBridgeMiddle(ti
);
1355 * Compute bridge piece. Computes the bridge piece to display depending on the position inside the bridge.
1356 * bridges pieces sequence (middle parts).
1357 * Note that it is not covering the bridge heads, which are always referenced by the same sprite table.
1358 * bridge len 1: BRIDGE_PIECE_NORTH
1359 * bridge len 2: BRIDGE_PIECE_NORTH BRIDGE_PIECE_SOUTH
1360 * bridge len 3: BRIDGE_PIECE_NORTH BRIDGE_PIECE_MIDDLE_ODD BRIDGE_PIECE_SOUTH
1361 * bridge len 4: BRIDGE_PIECE_NORTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_SOUTH
1362 * bridge len 5: BRIDGE_PIECE_NORTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_MIDDLE_EVEN BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_SOUTH
1363 * 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
1364 * 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
1365 * #0 - always as first, #1 - always as last (if len>1)
1366 * #2,#3 are to pair in order
1367 * for odd bridges: #5 is going in the bridge middle if on even position, #4 on odd (counting from 0)
1368 * @param north Northernmost tile of bridge
1369 * @param south Southernmost tile of bridge
1370 * @return Index of bridge piece
1372 static BridgePieces
CalcBridgePiece(uint north
, uint south
)
1375 return BRIDGE_PIECE_NORTH
;
1376 } else if (south
== 1) {
1377 return BRIDGE_PIECE_SOUTH
;
1378 } else if (north
< south
) {
1379 return north
& 1 ? BRIDGE_PIECE_INNER_SOUTH
: BRIDGE_PIECE_INNER_NORTH
;
1380 } else if (north
> south
) {
1381 return south
& 1 ? BRIDGE_PIECE_INNER_NORTH
: BRIDGE_PIECE_INNER_SOUTH
;
1383 return north
& 1 ? BRIDGE_PIECE_MIDDLE_EVEN
: BRIDGE_PIECE_MIDDLE_ODD
;
1388 * Draw the middle bits of a bridge.
1389 * @param ti Tile information of the tile to draw it on.
1391 void DrawBridgeMiddle(const TileInfo
*ti
)
1393 /* Sectional view of bridge bounding boxes:
1395 * 1 2 1,2 = SpriteCombine of Bridge front/(back&floor) and RoadCatenary
1396 * 1 2 3 = empty helper BB
1397 * 1 7 2 4,5 = pillars under higher bridges
1398 * 1 6 88888 6 2 6 = elrail-pylons
1399 * 1 6 88888 6 2 7 = elrail-wire
1400 * 1 6 88888 6 2 <- TILE_HEIGHT 8 = rail-vehicle on bridge
1401 * 3333333333333 <- BB_Z_SEPARATOR
1403 * 4 5 <- BB_HEIGHT_UNDER_BRIDGE
1409 if (!IsBridgeAbove(ti
->tile
)) return;
1411 TileIndex rampnorth
= GetNorthernBridgeEnd(ti
->tile
);
1412 TileIndex rampsouth
= GetSouthernBridgeEnd(ti
->tile
);
1413 TransportType transport_type
= GetTunnelBridgeTransportType(rampsouth
);
1415 Axis axis
= GetBridgeAxis(ti
->tile
);
1416 BridgePieces piece
= CalcBridgePiece(
1417 GetTunnelBridgeLength(ti
->tile
, rampnorth
) + 1,
1418 GetTunnelBridgeLength(ti
->tile
, rampsouth
) + 1
1421 const PalSpriteID
*psid
;
1423 if (transport_type
!= TRANSPORT_WATER
) {
1424 BridgeType type
= GetBridgeType(rampsouth
);
1425 drawfarpillar
= !HasBit(GetBridgeSpec(type
)->flags
, 0);
1428 if (transport_type
== TRANSPORT_RAIL
) {
1429 base_offset
= GetRailTypeInfo(GetRailType(rampsouth
))->bridge_offset
;
1434 psid
= base_offset
+ GetBridgeSpriteTable(type
, piece
);
1436 drawfarpillar
= true;
1437 psid
= _aqueduct_sprites
;
1440 if (axis
!= AXIS_X
) psid
+= 4;
1444 uint bridge_z
= GetBridgePixelHeight(rampsouth
);
1445 int z
= bridge_z
- BRIDGE_Z_START
;
1447 /* Add a bounding box that separates the bridge from things below it. */
1448 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX
, PAL_NONE
, x
, y
, 16, 16, 1, bridge_z
- TILE_HEIGHT
+ BB_Z_SEPARATOR
);
1450 /* Draw Trambits as SpriteCombine */
1451 if (transport_type
== TRANSPORT_ROAD
|| transport_type
== TRANSPORT_RAIL
) StartSpriteCombine();
1453 /* Draw floor and far part of bridge*/
1454 if (!IsInvisibilitySet(TO_BRIDGES
)) {
1455 if (axis
== AXIS_X
) {
1456 AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, x
, y
, 16, 1, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 0, BRIDGE_Z_START
);
1458 AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, x
, y
, 1, 16, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 0, BRIDGE_Z_START
);
1464 if (transport_type
== TRANSPORT_ROAD
) {
1465 RoadTypes rts
= GetRoadTypes(rampsouth
);
1467 if (HasBit(rts
, ROADTYPE_TRAM
)) {
1468 /* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */
1469 DrawBridgeTramBits(x
, y
, bridge_z
, axis
^ 1, HasBit(rts
, ROADTYPE_ROAD
), false);
1472 StartSpriteCombine();
1474 } else if (transport_type
== TRANSPORT_RAIL
) {
1475 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(rampsouth
));
1476 if (rti
->UsesOverlay() && !IsInvisibilitySet(TO_BRIDGES
)) {
1477 SpriteID surface
= GetCustomRailSprite(rti
, rampsouth
, RTSG_BRIDGE
, TCX_ON_BRIDGE
);
1479 AddSortableSpriteToDraw(surface
+ axis
, PAL_NONE
, x
, y
, 16, 16, 0, bridge_z
, IsTransparencySet(TO_BRIDGES
));
1483 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& !IsInvisibilitySet(TO_BRIDGES
) && HasTunnelBridgeReservation(rampnorth
)) {
1484 if (rti
->UsesOverlay()) {
1485 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
);
1486 AddSortableSpriteToDraw(overlay
+ RTO_X
+ axis
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 0, bridge_z
, IsTransparencySet(TO_BRIDGES
));
1488 AddSortableSpriteToDraw(axis
== AXIS_X
? rti
->base_sprites
.single_x
: rti
->base_sprites
.single_y
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 0, bridge_z
, IsTransparencySet(TO_BRIDGES
));
1494 if (HasRailCatenaryDrawn(GetRailType(rampsouth
))) {
1495 DrawRailCatenaryOnBridge(ti
);
1499 /* draw roof, the component of the bridge which is logically between the vehicle and the camera */
1500 if (!IsInvisibilitySet(TO_BRIDGES
)) {
1501 if (axis
== AXIS_X
) {
1503 if (psid
->sprite
& SPRITE_MASK
) AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, x
, y
, 16, 4, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 3, BRIDGE_Z_START
);
1506 if (psid
->sprite
& SPRITE_MASK
) AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, x
, y
, 4, 16, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 3, 0, BRIDGE_Z_START
);
1510 /* Draw TramFront as SpriteCombine */
1511 if (transport_type
== TRANSPORT_ROAD
) EndSpriteCombine();
1513 /* Do not draw anything more if bridges are invisible */
1514 if (IsInvisibilitySet(TO_BRIDGES
)) return;
1517 if (ti
->z
+ 5 == z
) {
1518 /* draw poles below for small bridges */
1519 if (psid
->sprite
!= 0) {
1520 SpriteID image
= psid
->sprite
;
1521 SpriteID pal
= psid
->pal
;
1522 if (IsTransparencySet(TO_BRIDGES
)) {
1523 SetBit(image
, PALETTE_MODIFIER_TRANSPARENT
);
1524 pal
= PALETTE_TO_TRANSPARENT
;
1527 DrawGroundSpriteAt(image
, pal
, x
- ti
->x
, y
- ti
->y
, z
- ti
->z
);
1530 /* draw pillars below for high bridges */
1531 DrawBridgePillars(psid
, ti
, axis
, drawfarpillar
, x
, y
, z
);
1536 static int GetSlopePixelZ_TunnelBridge(TileIndex tile
, uint x
, uint y
)
1539 Slope tileh
= GetTilePixelSlope(tile
, &z
);
1544 if (IsTunnel(tile
)) {
1545 uint pos
= (DiagDirToAxis(GetTunnelBridgeDirection(tile
)) == AXIS_X
? y
: x
);
1547 /* In the tunnel entrance? */
1548 if (5 <= pos
&& pos
<= 10) return z
;
1549 } else { // IsBridge(tile)
1550 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
1551 uint pos
= (DiagDirToAxis(dir
) == AXIS_X
? y
: x
);
1553 z
+= ApplyPixelFoundationToSlope(GetBridgeFoundation(tileh
, DiagDirToAxis(dir
)), &tileh
);
1555 /* On the bridge ramp? */
1556 if (5 <= pos
&& pos
<= 10) {
1559 if (tileh
!= SLOPE_FLAT
) return z
+ TILE_HEIGHT
;
1562 default: NOT_REACHED();
1563 case DIAGDIR_NE
: delta
= (TILE_SIZE
- 1 - x
) / 2; break;
1564 case DIAGDIR_SE
: delta
= y
/ 2; break;
1565 case DIAGDIR_SW
: delta
= x
/ 2; break;
1566 case DIAGDIR_NW
: delta
= (TILE_SIZE
- 1 - y
) / 2; break;
1568 return z
+ 1 + delta
;
1572 return z
+ GetPartialPixelZ(x
, y
, tileh
);
1575 static Foundation
GetFoundation_TunnelBridge(TileIndex tile
, Slope tileh
)
1577 return IsTunnel(tile
) ? FOUNDATION_NONE
: GetBridgeFoundation(tileh
, DiagDirToAxis(GetTunnelBridgeDirection(tile
)));
1580 static void GetTileDesc_TunnelBridge(TileIndex tile
, TileDesc
*td
)
1582 TransportType tt
= GetTunnelBridgeTransportType(tile
);
1584 if (IsTunnel(tile
)) {
1585 td
->str
= (tt
== TRANSPORT_RAIL
) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD
: STR_LAI_TUNNEL_DESCRIPTION_ROAD
;
1586 } else { // IsBridge(tile)
1587 td
->str
= (tt
== TRANSPORT_WATER
) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT
: GetBridgeSpec(GetBridgeType(tile
))->transport_name
[tt
];
1589 td
->owner
[0] = GetTileOwner(tile
);
1591 Owner road_owner
= INVALID_OWNER
;
1592 Owner tram_owner
= INVALID_OWNER
;
1593 RoadTypes rts
= GetRoadTypes(tile
);
1594 if (HasBit(rts
, ROADTYPE_ROAD
)) road_owner
= GetRoadOwner(tile
, ROADTYPE_ROAD
);
1595 if (HasBit(rts
, ROADTYPE_TRAM
)) tram_owner
= GetRoadOwner(tile
, ROADTYPE_TRAM
);
1597 /* Is there a mix of owners? */
1598 if ((tram_owner
!= INVALID_OWNER
&& tram_owner
!= td
->owner
[0]) ||
1599 (road_owner
!= INVALID_OWNER
&& road_owner
!= td
->owner
[0])) {
1601 if (road_owner
!= INVALID_OWNER
) {
1602 td
->owner_type
[i
] = STR_LAND_AREA_INFORMATION_ROAD_OWNER
;
1603 td
->owner
[i
] = road_owner
;
1606 if (tram_owner
!= INVALID_OWNER
) {
1607 td
->owner_type
[i
] = STR_LAND_AREA_INFORMATION_TRAM_OWNER
;
1608 td
->owner
[i
] = tram_owner
;
1612 if (tt
== TRANSPORT_RAIL
) {
1613 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(tile
));
1614 td
->rail_speed
= rti
->max_speed
;
1615 td
->railtype
= rti
->strings
.name
;
1617 if (!IsTunnel(tile
)) {
1618 uint16 spd
= GetBridgeSpec(GetBridgeType(tile
))->speed
;
1619 if (td
->rail_speed
== 0 || spd
< td
->rail_speed
) {
1620 td
->rail_speed
= spd
;
1623 } else if (tt
== TRANSPORT_ROAD
&& !IsTunnel(tile
)) {
1624 td
->road_speed
= GetBridgeSpec(GetBridgeType(tile
))->speed
;
1629 static void TileLoop_TunnelBridge(TileIndex tile
)
1631 bool snow_or_desert
= HasTunnelBridgeSnowOrDesert(tile
);
1632 switch (_settings_game
.game_creation
.landscape
) {
1634 /* As long as we do not have a snow density, we want to use the density
1635 * from the entry edge. For tunnels this is the lowest point for bridges the highest point.
1636 * (Independent of foundations) */
1637 int z
= IsBridge(tile
) ? GetTileMaxZ(tile
) : GetTileZ(tile
);
1638 if (snow_or_desert
!= (z
> GetSnowLine())) {
1639 SetTunnelBridgeSnowOrDesert(tile
, !snow_or_desert
);
1640 MarkTileDirtyByTile(tile
);
1646 if (GetTropicZone(tile
) == TROPICZONE_DESERT
&& !snow_or_desert
) {
1647 SetTunnelBridgeSnowOrDesert(tile
, true);
1648 MarkTileDirtyByTile(tile
);
1657 static TrackStatus
GetTileTrackStatus_TunnelBridge(TileIndex tile
, TransportType mode
, uint sub_mode
, DiagDirection side
)
1659 TransportType transport_type
= GetTunnelBridgeTransportType(tile
);
1660 if (transport_type
!= mode
|| (transport_type
== TRANSPORT_ROAD
&& (GetRoadTypes(tile
) & sub_mode
) == 0)) return 0;
1662 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
1663 if (side
!= INVALID_DIAGDIR
&& side
!= ReverseDiagDir(dir
)) return 0;
1664 return CombineTrackStatus(TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir
)), TRACKDIR_BIT_NONE
);
1667 static void ChangeTileOwner_TunnelBridge(TileIndex tile
, Owner old_owner
, Owner new_owner
)
1669 TileIndex other_end
= GetOtherTunnelBridgeEnd(tile
);
1670 /* Set number of pieces to zero if it's the southern tile as we
1671 * don't want to update the infrastructure counts twice. */
1672 uint num_pieces
= tile
< other_end
? (GetTunnelBridgeLength(tile
, other_end
) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR
: 0;
1674 for (RoadType rt
= ROADTYPE_ROAD
; rt
< ROADTYPE_END
; rt
++) {
1675 /* Update all roadtypes, no matter if they are present */
1676 if (GetRoadOwner(tile
, rt
) == old_owner
) {
1677 if (HasBit(GetRoadTypes(tile
), rt
)) {
1678 /* Update company infrastructure counts. A full diagonal road tile has two road bits.
1679 * No need to dirty windows here, we'll redraw the whole screen anyway. */
1680 Company::Get(old_owner
)->infrastructure
.road
[rt
] -= num_pieces
* 2;
1681 if (new_owner
!= INVALID_OWNER
) Company::Get(new_owner
)->infrastructure
.road
[rt
] += num_pieces
* 2;
1684 SetRoadOwner(tile
, rt
, new_owner
== INVALID_OWNER
? OWNER_NONE
: new_owner
);
1688 if (!IsTileOwner(tile
, old_owner
)) return;
1690 /* Update company infrastructure counts for rail and water as well.
1691 * No need to dirty windows here, we'll redraw the whole screen anyway. */
1692 TransportType tt
= GetTunnelBridgeTransportType(tile
);
1693 Company
*old
= Company::Get(old_owner
);
1694 if (tt
== TRANSPORT_RAIL
) {
1695 old
->infrastructure
.rail
[GetRailType(tile
)] -= num_pieces
;
1696 if (new_owner
!= INVALID_OWNER
) Company::Get(new_owner
)->infrastructure
.rail
[GetRailType(tile
)] += num_pieces
;
1697 } else if (tt
== TRANSPORT_WATER
) {
1698 old
->infrastructure
.water
-= num_pieces
;
1699 if (new_owner
!= INVALID_OWNER
) Company::Get(new_owner
)->infrastructure
.water
+= num_pieces
;
1702 if (new_owner
!= INVALID_OWNER
) {
1703 SetTileOwner(tile
, new_owner
);
1705 if (tt
== TRANSPORT_RAIL
) {
1706 /* Since all of our vehicles have been removed, it is safe to remove the rail
1707 * bridge / tunnel. */
1708 CommandCost ret
= DoCommand(tile
, 0, 0, DC_EXEC
| DC_BANKRUPT
, CMD_LANDSCAPE_CLEAR
);
1709 assert(ret
.Succeeded());
1711 /* In any other case, we can safely reassign the ownership to OWNER_NONE. */
1712 SetTileOwner(tile
, OWNER_NONE
);
1718 * Frame when the 'enter tunnel' sound should be played. This is the second
1719 * frame on a tile, so the sound is played shortly after entering the tunnel
1720 * tile, while the vehicle is still visible.
1722 static const byte TUNNEL_SOUND_FRAME
= 1;
1725 * Frame when a vehicle should be hidden in a tunnel with a certain direction.
1726 * This differs per direction, because of visibility / bounding box issues.
1727 * Note that direction, in this case, is the direction leading into the tunnel.
1728 * When entering a tunnel, hide the vehicle when it reaches the given frame.
1729 * When leaving a tunnel, show the vehicle when it is one frame further
1730 * to the 'outside', i.e. at (TILE_SIZE-1) - (frame) + 1
1732 extern const byte _tunnel_visibility_frame
[DIAGDIR_END
] = {12, 8, 8, 12};
1734 static VehicleEnterTileStatus
VehicleEnter_TunnelBridge(Vehicle
*v
, TileIndex tile
, int x
, int y
)
1736 int z
= GetSlopePixelZ(x
, y
) - v
->z_pos
;
1738 if (abs(z
) > 2) return VETSB_CANNOT_ENTER
;
1739 /* Direction into the wormhole */
1740 const DiagDirection dir
= GetTunnelBridgeDirection(tile
);
1741 /* Direction of the vehicle */
1742 const DiagDirection vdir
= DirToDiagDir(v
->direction
);
1743 /* New position of the vehicle on the tile */
1744 byte pos
= (DiagDirToAxis(vdir
) == AXIS_X
? x
: y
) & TILE_UNIT_MASK
;
1745 /* Number of units moved by the vehicle since entering the tile */
1746 byte frame
= (vdir
== DIAGDIR_NE
|| vdir
== DIAGDIR_NW
) ? TILE_SIZE
- 1 - pos
: pos
;
1748 if (IsTunnel(tile
)) {
1749 if (v
->type
== VEH_TRAIN
) {
1750 Train
*t
= Train::From(v
);
1752 if (t
->track
!= TRACK_BIT_WORMHOLE
&& dir
== vdir
) {
1753 if (t
->IsFrontEngine() && frame
== TUNNEL_SOUND_FRAME
) {
1754 if (!PlayVehicleSound(t
, VSE_TUNNEL
) && RailVehInfo(t
->engine_type
)->engclass
== 0) {
1755 SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL
, v
);
1757 return VETSB_CONTINUE
;
1759 if (frame
== _tunnel_visibility_frame
[dir
]) {
1761 t
->track
= TRACK_BIT_WORMHOLE
;
1762 t
->vehstatus
|= VS_HIDDEN
;
1763 return VETSB_ENTERED_WORMHOLE
;
1767 if (dir
== ReverseDiagDir(vdir
) && frame
== TILE_SIZE
- _tunnel_visibility_frame
[dir
] && z
== 0) {
1768 /* We're at the tunnel exit ?? */
1770 t
->track
= DiagDirToDiagTrackBits(vdir
);
1772 t
->vehstatus
&= ~VS_HIDDEN
;
1773 return VETSB_ENTERED_WORMHOLE
;
1775 } else if (v
->type
== VEH_ROAD
) {
1776 RoadVehicle
*rv
= RoadVehicle::From(v
);
1779 if (rv
->state
!= RVSB_WORMHOLE
&& dir
== vdir
) {
1780 if (frame
== _tunnel_visibility_frame
[dir
]) {
1781 /* Frame should be equal to the next frame number in the RV's movement */
1782 assert(frame
== rv
->frame
+ 1);
1784 rv
->state
= RVSB_WORMHOLE
;
1785 rv
->vehstatus
|= VS_HIDDEN
;
1786 return VETSB_ENTERED_WORMHOLE
;
1788 return VETSB_CONTINUE
;
1792 /* We're at the tunnel exit ?? */
1793 if (dir
== ReverseDiagDir(vdir
) && frame
== TILE_SIZE
- _tunnel_visibility_frame
[dir
] && z
== 0) {
1795 rv
->state
= DiagDirToDiagTrackdir(vdir
);
1797 rv
->vehstatus
&= ~VS_HIDDEN
;
1798 return VETSB_ENTERED_WORMHOLE
;
1801 } else { // IsBridge(tile)
1802 if (v
->type
!= VEH_SHIP
) {
1803 /* modify speed of vehicle */
1804 uint16 spd
= GetBridgeSpec(GetBridgeType(tile
))->speed
;
1806 if (v
->type
== VEH_ROAD
) spd
*= 2;
1807 Vehicle
*first
= v
->First();
1808 first
->cur_speed
= min(first
->cur_speed
, spd
);
1812 /* Vehicle enters bridge at the last frame inside this tile. */
1813 if (frame
!= TILE_SIZE
- 1) return VETSB_CONTINUE
;
1816 Train
*t
= Train::From(v
);
1817 t
->track
= TRACK_BIT_WORMHOLE
;
1818 ClrBit(t
->gv_flags
, GVF_GOINGUP_BIT
);
1819 ClrBit(t
->gv_flags
, GVF_GOINGDOWN_BIT
);
1824 RoadVehicle
*rv
= RoadVehicle::From(v
);
1825 rv
->state
= RVSB_WORMHOLE
;
1826 /* There are no slopes inside bridges / tunnels. */
1827 ClrBit(rv
->gv_flags
, GVF_GOINGUP_BIT
);
1828 ClrBit(rv
->gv_flags
, GVF_GOINGDOWN_BIT
);
1833 Ship::From(v
)->state
= TRACK_BIT_WORMHOLE
;
1836 default: NOT_REACHED();
1838 return VETSB_ENTERED_WORMHOLE
;
1839 } else if (vdir
== ReverseDiagDir(dir
)) {
1843 Train
*t
= Train::From(v
);
1844 if (t
->track
== TRACK_BIT_WORMHOLE
) {
1845 t
->track
= DiagDirToDiagTrackBits(vdir
);
1846 return VETSB_ENTERED_WORMHOLE
;
1852 RoadVehicle
*rv
= RoadVehicle::From(v
);
1853 if (rv
->state
== RVSB_WORMHOLE
) {
1854 rv
->state
= DiagDirToDiagTrackdir(vdir
);
1856 return VETSB_ENTERED_WORMHOLE
;
1862 Ship
*ship
= Ship::From(v
);
1863 if (ship
->state
== TRACK_BIT_WORMHOLE
) {
1864 ship
->state
= DiagDirToDiagTrackBits(vdir
);
1865 return VETSB_ENTERED_WORMHOLE
;
1870 default: NOT_REACHED();
1874 return VETSB_CONTINUE
;
1877 static CommandCost
TerraformTile_TunnelBridge(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
1879 if (_settings_game
.construction
.build_on_slopes
&& AutoslopeEnabled() && IsBridge(tile
) && GetTunnelBridgeTransportType(tile
) != TRANSPORT_WATER
) {
1880 DiagDirection direction
= GetTunnelBridgeDirection(tile
);
1881 Axis axis
= DiagDirToAxis(direction
);
1884 Slope tileh_old
= GetTileSlope(tile
, &z_old
);
1886 /* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
1887 if ((direction
== DIAGDIR_NW
) || (direction
== DIAGDIR_NE
)) {
1888 CheckBridgeSlopeSouth(axis
, &tileh_old
, &z_old
);
1889 res
= CheckBridgeSlopeSouth(axis
, &tileh_new
, &z_new
);
1891 CheckBridgeSlopeNorth(axis
, &tileh_old
, &z_old
);
1892 res
= CheckBridgeSlopeNorth(axis
, &tileh_new
, &z_new
);
1895 /* Surface slope is valid and remains unchanged? */
1896 if (res
.Succeeded() && (z_old
== z_new
) && (tileh_old
== tileh_new
)) return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
1899 return DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
1902 extern const TileTypeProcs _tile_type_tunnelbridge_procs
= {
1903 DrawTile_TunnelBridge
, // draw_tile_proc
1904 GetSlopePixelZ_TunnelBridge
, // get_slope_z_proc
1905 ClearTile_TunnelBridge
, // clear_tile_proc
1906 NULL
, // add_accepted_cargo_proc
1907 GetTileDesc_TunnelBridge
, // get_tile_desc_proc
1908 GetTileTrackStatus_TunnelBridge
, // get_tile_track_status_proc
1909 NULL
, // click_tile_proc
1910 NULL
, // animate_tile_proc
1911 TileLoop_TunnelBridge
, // tile_loop_proc
1912 ChangeTileOwner_TunnelBridge
, // change_tile_owner_proc
1913 NULL
, // add_produced_cargo_proc
1914 VehicleEnter_TunnelBridge
, // vehicle_enter_tile_proc
1915 GetFoundation_TunnelBridge
, // get_foundation_proc
1916 TerraformTile_TunnelBridge
, // terraform_tile_proc