2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
9 * @file tunnelbridge_cmd.cpp
10 * This file deals with tunnels and bridges (non-gui stuff)
11 * @todo separate this file into two
15 #include "newgrf_object.h"
16 #include "viewport_func.h"
17 #include "cmd_helper.h"
18 #include "command_func.h"
23 #include "pathfinder/yapf/yapf_cache.h"
24 #include "newgrf_sound.h"
25 #include "autoslope.h"
26 #include "tunnelbridge_map.h"
27 #include "strings_func.h"
28 #include "date_func.h"
29 #include "clear_func.h"
30 #include "vehicle_func.h"
31 #include "sound_func.h"
32 #include "tunnelbridge.h"
33 #include "cheat_type.h"
34 #include "elrail_func.h"
36 #include "company_base.h"
37 #include "newgrf_railtype.h"
38 #include "newgrf_roadtype.h"
39 #include "object_base.h"
41 #include "company_gui.h"
42 #include "station_func.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
!= nullptr) {
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
== nullptr || bridge
->sprite_table
[table
] == nullptr) {
154 return _bridge_sprite_table
[index
][table
];
156 return bridge
->sprite_table
[table
];
162 * Determines the foundation for the bridge head, and tests if the resulting slope is valid.
164 * @param bridge_piece Direction of the bridge head.
165 * @param axis Axis of the bridge
166 * @param tileh Slope of the tile under the north bridge head; returns slope on top of foundation
167 * @param z TileZ corresponding to tileh, gets modified as well
168 * @return Error or cost for bridge foundation
170 static CommandCost
CheckBridgeSlope(BridgePieces bridge_piece
, Axis axis
, Slope
*tileh
, int *z
)
172 assert(bridge_piece
== BRIDGE_PIECE_NORTH
|| bridge_piece
== BRIDGE_PIECE_SOUTH
);
174 Foundation f
= GetBridgeFoundation(*tileh
, axis
);
175 *z
+= ApplyFoundationToSlope(f
, tileh
);
177 Slope valid_inclined
;
178 if (bridge_piece
== BRIDGE_PIECE_NORTH
) {
179 valid_inclined
= (axis
== AXIS_X
? SLOPE_NE
: SLOPE_NW
);
181 valid_inclined
= (axis
== AXIS_X
? SLOPE_SW
: SLOPE_SE
);
183 if ((*tileh
!= SLOPE_FLAT
) && (*tileh
!= valid_inclined
)) return CMD_ERROR
;
185 if (f
== FOUNDATION_NONE
) return CommandCost();
187 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
191 * Is a bridge of the specified type and length available?
192 * @param bridge_type Wanted type of bridge.
193 * @param bridge_len Wanted length of the bridge.
194 * @param flags Type of operation.
195 * @return A succeeded (the requested bridge is available) or failed (it cannot be built) command.
197 CommandCost
CheckBridgeAvailability(BridgeType bridge_type
, uint bridge_len
, DoCommandFlag flags
)
199 if (flags
& DC_QUERY_COST
) {
200 if (bridge_len
<= _settings_game
.construction
.max_bridge_length
) return CommandCost();
201 return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG
);
204 if (bridge_type
>= MAX_BRIDGES
) return CMD_ERROR
;
206 const BridgeSpec
*b
= GetBridgeSpec(bridge_type
);
207 if (b
->avail_year
> _cur_year
) return CMD_ERROR
;
209 uint max
= std::min(b
->max_length
, _settings_game
.construction
.max_bridge_length
);
211 if (b
->min_length
> bridge_len
) return CMD_ERROR
;
212 if (bridge_len
<= max
) return CommandCost();
213 return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG
);
217 * Calculate the base cost of clearing a tunnel/bridge per tile.
218 * @param tile Start tile of the tunnel/bridge.
219 * @return How much clearing this tunnel/bridge costs per tile.
221 static Money
TunnelBridgeClearCost(TileIndex tile
, Price base_price
)
223 Money base_cost
= _price
[base_price
];
225 /* Add the cost of the transport that is on the tunnel/bridge. */
226 switch (GetTunnelBridgeTransportType(tile
)) {
227 case TRANSPORT_ROAD
: {
228 RoadType road_rt
= GetRoadTypeRoad(tile
);
229 RoadType tram_rt
= GetRoadTypeTram(tile
);
231 if (road_rt
!= INVALID_ROADTYPE
) {
232 base_cost
+= 2 * RoadClearCost(road_rt
);
234 if (tram_rt
!= INVALID_ROADTYPE
) {
235 base_cost
+= 2 * RoadClearCost(tram_rt
);
239 case TRANSPORT_RAIL
: base_cost
+= RailClearCost(GetRailType(tile
)); break;
240 /* Aquaducts have their own clear price. */
241 case TRANSPORT_WATER
: base_cost
= _price
[PR_CLEAR_AQUEDUCT
]; break;
250 * @param end_tile end tile
251 * @param flags type of operation
252 * @param p1 packed start tile coords (~ dx)
253 * @param p2 various bitstuffed elements
254 * - p2 = (bit 0- 7) - bridge type (hi bh)
255 * - p2 = (bit 8-13) - rail type or road types.
256 * - p2 = (bit 15-16) - transport type.
258 * @return the cost of this operation or an error
260 CommandCost
CmdBuildBridge(TileIndex end_tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const std::string
&text
)
262 CompanyID company
= _current_company
;
264 RailType railtype
= INVALID_RAILTYPE
;
265 RoadType roadtype
= INVALID_ROADTYPE
;
267 /* unpack parameters */
268 BridgeType bridge_type
= GB(p2
, 0, 8);
270 if (!IsValidTile(p1
)) return_cmd_error(STR_ERROR_BRIDGE_THROUGH_MAP_BORDER
);
272 TransportType transport_type
= Extract
<TransportType
, 15, 2>(p2
);
275 switch (transport_type
) {
277 roadtype
= Extract
<RoadType
, 8, 6>(p2
);
278 if (!ValParamRoadType(roadtype
)) return CMD_ERROR
;
282 railtype
= Extract
<RailType
, 8, 6>(p2
);
283 if (!ValParamRailtype(railtype
)) return CMD_ERROR
;
286 case TRANSPORT_WATER
:
290 /* Airports don't have bridges. */
293 TileIndex tile_start
= p1
;
294 TileIndex tile_end
= end_tile
;
296 if (company
== OWNER_DEITY
) {
297 if (transport_type
!= TRANSPORT_ROAD
) return CMD_ERROR
;
298 const Town
*town
= CalcClosestTownFromTile(tile_start
);
300 company
= OWNER_TOWN
;
302 /* If we are not within a town, we are not owned by the town */
303 if (town
== nullptr || DistanceSquare(tile_start
, town
->xy
) > town
->cache
.squared_town_zone_radius
[HZB_TOWN_EDGE
]) {
304 company
= OWNER_NONE
;
308 if (tile_start
== tile_end
) {
309 return_cmd_error(STR_ERROR_CAN_T_START_AND_END_ON
);
313 if (TileX(tile_start
) == TileX(tile_end
)) {
315 } else if (TileY(tile_start
) == TileY(tile_end
)) {
318 return_cmd_error(STR_ERROR_START_AND_END_MUST_BE_IN
);
321 if (tile_end
< tile_start
) Swap(tile_start
, tile_end
);
323 uint bridge_len
= GetTunnelBridgeLength(tile_start
, tile_end
);
324 if (transport_type
!= TRANSPORT_WATER
) {
325 /* set and test bridge length, availability */
326 CommandCost ret
= CheckBridgeAvailability(bridge_type
, bridge_len
, flags
);
327 if (ret
.Failed()) return ret
;
329 if (bridge_len
> _settings_game
.construction
.max_bridge_length
) return_cmd_error(STR_ERROR_BRIDGE_TOO_LONG
);
331 bridge_len
+= 2; // begin and end tiles/ramps
335 Slope tileh_start
= GetTileSlope(tile_start
, &z_start
);
336 Slope tileh_end
= GetTileSlope(tile_end
, &z_end
);
337 bool pbs_reservation
= false;
339 CommandCost terraform_cost_north
= CheckBridgeSlope(BRIDGE_PIECE_NORTH
, direction
, &tileh_start
, &z_start
);
340 CommandCost terraform_cost_south
= CheckBridgeSlope(BRIDGE_PIECE_SOUTH
, direction
, &tileh_end
, &z_end
);
342 /* Aqueducts can't be built of flat land. */
343 if (transport_type
== TRANSPORT_WATER
&& (tileh_start
== SLOPE_FLAT
|| tileh_end
== SLOPE_FLAT
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
344 if (z_start
!= z_end
) return_cmd_error(STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT
);
346 CommandCost
cost(EXPENSES_CONSTRUCTION
);
349 RoadType road_rt
= INVALID_ROADTYPE
;
350 RoadType tram_rt
= INVALID_ROADTYPE
;
351 if (IsBridgeTile(tile_start
) && IsBridgeTile(tile_end
) &&
352 GetOtherBridgeEnd(tile_start
) == tile_end
&&
353 GetTunnelBridgeTransportType(tile_start
) == transport_type
) {
354 /* Replace a current bridge. */
356 switch (transport_type
) {
358 /* Keep the reservation, the path stays valid. */
359 pbs_reservation
= HasTunnelBridgeReservation(tile_start
);
363 /* Do not remove road types when upgrading a bridge */
364 road_rt
= GetRoadTypeRoad(tile_start
);
365 tram_rt
= GetRoadTypeTram(tile_start
);
371 /* If this is a railway bridge, make sure the railtypes match. */
372 if (transport_type
== TRANSPORT_RAIL
&& GetRailType(tile_start
) != railtype
) {
373 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
376 /* If this is a road bridge, make sure the roadtype matches. */
377 if (transport_type
== TRANSPORT_ROAD
) {
378 RoadType existing_rt
= RoadTypeIsRoad(roadtype
) ? road_rt
: tram_rt
;
379 if (existing_rt
!= roadtype
&& existing_rt
!= INVALID_ROADTYPE
) {
380 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
384 /* Do not replace town bridges with lower speed bridges, unless in scenario editor. */
385 if (!(flags
& DC_QUERY_COST
) && IsTileOwner(tile_start
, OWNER_TOWN
) &&
386 GetBridgeSpec(bridge_type
)->speed
< GetBridgeSpec(GetBridgeType(tile_start
))->speed
&&
387 _game_mode
!= GM_EDITOR
) {
388 Town
*t
= ClosestTownFromTile(tile_start
, UINT_MAX
);
393 SetDParam(0, t
->index
);
394 return_cmd_error(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS
);
398 /* Do not replace the bridge with the same bridge type. */
399 if (!(flags
& DC_QUERY_COST
) && (bridge_type
== GetBridgeType(tile_start
)) && (transport_type
!= TRANSPORT_ROAD
|| road_rt
== roadtype
|| tram_rt
== roadtype
)) {
400 return_cmd_error(STR_ERROR_ALREADY_BUILT
);
403 /* Do not allow replacing another company's bridges. */
404 if (!IsTileOwner(tile_start
, company
) && !IsTileOwner(tile_start
, OWNER_TOWN
) && !IsTileOwner(tile_start
, OWNER_NONE
)) {
405 return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER
);
408 /* The cost of clearing the current bridge. */
409 cost
.AddCost(bridge_len
* TunnelBridgeClearCost(tile_start
, PR_CLEAR_BRIDGE
));
410 owner
= GetTileOwner(tile_start
);
412 /* If bridge belonged to bankrupt company, it has a new owner now */
413 is_new_owner
= (owner
== OWNER_NONE
);
414 if (is_new_owner
) owner
= company
;
416 /* Build a new bridge. */
418 bool allow_on_slopes
= (_settings_game
.construction
.build_on_slopes
&& transport_type
!= TRANSPORT_WATER
);
420 /* Try and clear the start landscape */
421 CommandCost ret
= DoCommand(tile_start
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
422 if (ret
.Failed()) return ret
;
425 if (terraform_cost_north
.Failed() || (terraform_cost_north
.GetCost() != 0 && !allow_on_slopes
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
426 cost
.AddCost(terraform_cost_north
);
428 /* Try and clear the end landscape */
429 ret
= DoCommand(tile_end
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
430 if (ret
.Failed()) return ret
;
433 /* false - end tile slope check */
434 if (terraform_cost_south
.Failed() || (terraform_cost_south
.GetCost() != 0 && !allow_on_slopes
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
435 cost
.AddCost(terraform_cost_south
);
437 const TileIndex heads
[] = {tile_start
, tile_end
};
438 for (int i
= 0; i
< 2; i
++) {
439 if (IsBridgeAbove(heads
[i
])) {
440 TileIndex north_head
= GetNorthernBridgeEnd(heads
[i
]);
442 if (direction
== GetBridgeAxis(heads
[i
])) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
444 if (z_start
+ 1 == GetBridgeHeight(north_head
)) {
445 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
450 TileIndexDiff delta
= (direction
== AXIS_X
? TileDiffXY(1, 0) : TileDiffXY(0, 1));
451 for (TileIndex tile
= tile_start
+ delta
; tile
!= tile_end
; tile
+= delta
) {
452 if (GetTileMaxZ(tile
) > z_start
) return_cmd_error(STR_ERROR_BRIDGE_TOO_LOW_FOR_TERRAIN
);
454 if (z_start
>= (GetTileZ(tile
) + _settings_game
.construction
.max_bridge_height
)) {
456 * Disallow too high bridges.
457 * Properly rendering a map where very high bridges (might) exist is expensive.
458 * See http://www.tt-forums.net/viewtopic.php?f=33&t=40844&start=980#p1131762
459 * for a detailed discussion. z_start here is one heightlevel below the bridge level.
461 return_cmd_error(STR_ERROR_BRIDGE_TOO_HIGH_FOR_TERRAIN
);
464 if (IsBridgeAbove(tile
)) {
465 /* Disallow crossing bridges for the time being */
466 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
469 switch (GetTileType(tile
)) {
471 if (!IsWater(tile
) && !IsCoast(tile
)) goto not_valid_below
;
475 if (!IsPlainRail(tile
)) goto not_valid_below
;
479 if (IsRoadDepot(tile
)) goto not_valid_below
;
482 case MP_TUNNELBRIDGE
:
483 if (IsTunnel(tile
)) break;
484 if (direction
== DiagDirToAxis(GetTunnelBridgeDirection(tile
))) goto not_valid_below
;
485 if (z_start
< GetBridgeHeight(tile
)) goto not_valid_below
;
489 const ObjectSpec
*spec
= ObjectSpec::GetByTile(tile
);
490 if ((spec
->flags
& OBJECT_FLAG_ALLOW_UNDER_BRIDGE
) == 0) goto not_valid_below
;
491 if (GetTileMaxZ(tile
) + spec
->height
> z_start
) goto not_valid_below
;
500 /* try and clear the middle landscape */
501 ret
= DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
502 if (ret
.Failed()) return ret
;
507 if (flags
& DC_EXEC
) {
508 /* We do this here because when replacing a bridge with another
509 * type calling SetBridgeMiddle isn't needed. After all, the
510 * tile already has the has_bridge_above bits set. */
511 SetBridgeMiddle(tile
, direction
);
519 bool hasroad
= road_rt
!= INVALID_ROADTYPE
;
520 bool hastram
= tram_rt
!= INVALID_ROADTYPE
;
521 if (transport_type
== TRANSPORT_ROAD
) {
522 if (RoadTypeIsRoad(roadtype
)) road_rt
= roadtype
;
523 if (RoadTypeIsTram(roadtype
)) tram_rt
= roadtype
;
527 if (flags
& DC_EXEC
) {
528 DiagDirection dir
= AxisToDiagDir(direction
);
530 Company
*c
= Company::GetIfValid(company
);
531 switch (transport_type
) {
533 /* Add to company infrastructure count if required. */
534 if (is_new_owner
&& c
!= nullptr) c
->infrastructure
.rail
[railtype
] += bridge_len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
535 MakeRailBridgeRamp(tile_start
, owner
, bridge_type
, dir
, railtype
);
536 MakeRailBridgeRamp(tile_end
, owner
, bridge_type
, ReverseDiagDir(dir
), railtype
);
537 SetTunnelBridgeReservation(tile_start
, pbs_reservation
);
538 SetTunnelBridgeReservation(tile_end
, pbs_reservation
);
541 case TRANSPORT_ROAD
: {
543 /* Also give unowned present roadtypes to new owner */
544 if (hasroad
&& GetRoadOwner(tile_start
, RTT_ROAD
) == OWNER_NONE
) hasroad
= false;
545 if (hastram
&& GetRoadOwner(tile_start
, RTT_TRAM
) == OWNER_NONE
) hastram
= false;
548 /* Add all new road types to the company infrastructure counter. */
549 if (!hasroad
&& road_rt
!= INVALID_ROADTYPE
) {
550 /* A full diagonal road tile has two road bits. */
551 c
->infrastructure
.road
[road_rt
] += bridge_len
* 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
;
553 if (!hastram
&& tram_rt
!= INVALID_ROADTYPE
) {
554 /* A full diagonal road tile has two road bits. */
555 c
->infrastructure
.road
[tram_rt
] += bridge_len
* 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
;
558 Owner owner_road
= hasroad
? GetRoadOwner(tile_start
, RTT_ROAD
) : company
;
559 Owner owner_tram
= hastram
? GetRoadOwner(tile_start
, RTT_TRAM
) : company
;
560 MakeRoadBridgeRamp(tile_start
, owner
, owner_road
, owner_tram
, bridge_type
, dir
, road_rt
, tram_rt
);
561 MakeRoadBridgeRamp(tile_end
, owner
, owner_road
, owner_tram
, bridge_type
, ReverseDiagDir(dir
), road_rt
, tram_rt
);
565 case TRANSPORT_WATER
:
566 if (is_new_owner
&& c
!= nullptr) c
->infrastructure
.water
+= bridge_len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
567 MakeAqueductBridgeRamp(tile_start
, owner
, dir
);
568 MakeAqueductBridgeRamp(tile_end
, owner
, ReverseDiagDir(dir
));
569 CheckForDockingTile(tile_start
);
570 CheckForDockingTile(tile_end
);
577 /* Mark all tiles dirty */
578 MarkBridgeDirty(tile_start
, tile_end
, AxisToDiagDir(direction
), z_start
);
579 DirtyCompanyInfrastructureWindows(company
);
582 if ((flags
& DC_EXEC
) && transport_type
== TRANSPORT_RAIL
) {
583 Track track
= AxisToTrack(direction
);
584 AddSideToSignalBuffer(tile_start
, INVALID_DIAGDIR
, company
);
585 YapfNotifyTrackLayoutChange(tile_start
, track
);
588 /* Human players that build bridges get a selection to choose from (DC_QUERY_COST)
589 * It's unnecessary to execute this command every time for every bridge.
590 * So it is done only for humans and cost is computed in bridge_gui.cpp.
591 * For (non-spectated) AI, Towns this has to be of course calculated. */
592 Company
*c
= Company::GetIfValid(company
);
593 if (!(flags
& DC_QUERY_COST
) || (c
!= nullptr && c
->is_ai
&& company
!= _local_company
)) {
594 switch (transport_type
) {
596 if (road_rt
!= INVALID_ROADTYPE
) {
597 cost
.AddCost(bridge_len
* 2 * RoadBuildCost(road_rt
));
599 if (tram_rt
!= INVALID_ROADTYPE
) {
600 cost
.AddCost(bridge_len
* 2 * RoadBuildCost(tram_rt
));
604 case TRANSPORT_RAIL
: cost
.AddCost(bridge_len
* RailBuildCost(railtype
)); break;
608 if (c
!= nullptr) bridge_len
= CalcBridgeLenCostFactor(bridge_len
);
610 if (transport_type
!= TRANSPORT_WATER
) {
611 cost
.AddCost((int64
)bridge_len
* _price
[PR_BUILD_BRIDGE
] * GetBridgeSpec(bridge_type
)->price
>> 8);
613 /* Aqueducts use a separate base cost. */
614 cost
.AddCost((int64
)bridge_len
* _price
[PR_BUILD_AQUEDUCT
]);
625 * @param start_tile start tile of tunnel
626 * @param flags type of operation
627 * @param p1 bit 0-5 railtype or roadtype
628 * bit 8-9 transport type
631 * @return the cost of this operation or an error
633 CommandCost
CmdBuildTunnel(TileIndex start_tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const std::string
&text
)
635 CompanyID company
= _current_company
;
637 TransportType transport_type
= Extract
<TransportType
, 8, 2>(p1
);
638 RailType railtype
= INVALID_RAILTYPE
;
639 RoadType roadtype
= INVALID_ROADTYPE
;
640 _build_tunnel_endtile
= 0;
641 switch (transport_type
) {
643 railtype
= Extract
<RailType
, 0, 6>(p1
);
644 if (!ValParamRailtype(railtype
)) return CMD_ERROR
;
648 roadtype
= Extract
<RoadType
, 0, 6>(p1
);
649 if (!ValParamRoadType(roadtype
)) return CMD_ERROR
;
652 default: return CMD_ERROR
;
655 if (company
== OWNER_DEITY
) {
656 if (transport_type
!= TRANSPORT_ROAD
) return CMD_ERROR
;
657 const Town
*town
= CalcClosestTownFromTile(start_tile
);
659 company
= OWNER_TOWN
;
661 /* If we are not within a town, we are not owned by the town */
662 if (town
== nullptr || DistanceSquare(start_tile
, town
->xy
) > town
->cache
.squared_town_zone_radius
[HZB_TOWN_EDGE
]) {
663 company
= OWNER_NONE
;
669 Slope start_tileh
= GetTileSlope(start_tile
, &start_z
);
670 DiagDirection direction
= GetInclinedSlopeDirection(start_tileh
);
671 if (direction
== INVALID_DIAGDIR
) return_cmd_error(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL
);
673 if (HasTileWaterGround(start_tile
)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER
);
675 CommandCost ret
= DoCommand(start_tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
676 if (ret
.Failed()) return ret
;
678 /* XXX - do NOT change 'ret' in the loop, as it is used as the price
679 * for the clearing of the entrance of the tunnel. Assigning it to
680 * cost before the loop will yield different costs depending on start-
681 * position, because of increased-cost-by-length: 'cost += cost >> 3' */
683 TileIndexDiff delta
= TileOffsByDiagDir(direction
);
684 DiagDirection tunnel_in_way_dir
;
685 if (DiagDirToAxis(direction
) == AXIS_Y
) {
686 tunnel_in_way_dir
= (TileX(start_tile
) < (MapMaxX() / 2)) ? DIAGDIR_SW
: DIAGDIR_NE
;
688 tunnel_in_way_dir
= (TileY(start_tile
) < (MapMaxX() / 2)) ? DIAGDIR_SE
: DIAGDIR_NW
;
691 TileIndex end_tile
= start_tile
;
693 /* Tile shift coefficient. Will decrease for very long tunnels to avoid exponential growth of price*/
695 /* Number of tiles from start of tunnel */
697 /* Number of tiles at which the cost increase coefficient per tile is halved */
700 CommandCost
cost(EXPENSES_CONSTRUCTION
);
704 if (!IsValidTile(end_tile
)) return_cmd_error(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER
);
705 end_tileh
= GetTileSlope(end_tile
, &end_z
);
707 if (start_z
== end_z
) break;
709 if (!_cheats
.crossing_tunnels
.value
&& IsTunnelInWayDir(end_tile
, start_z
, tunnel_in_way_dir
)) {
710 return_cmd_error(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY
);
714 if (tiles
== tiles_bump
) {
719 cost
.AddCost(_price
[PR_BUILD_TUNNEL
]);
720 cost
.AddCost(cost
.GetCost() >> tiles_coef
); // add a multiplier for longer tunnels
723 /* Add the cost of the entrance */
724 cost
.AddCost(_price
[PR_BUILD_TUNNEL
]);
727 /* if the command fails from here on we want the end tile to be highlighted */
728 _build_tunnel_endtile
= end_tile
;
730 if (tiles
> _settings_game
.construction
.max_tunnel_length
) return_cmd_error(STR_ERROR_TUNNEL_TOO_LONG
);
732 if (HasTileWaterGround(end_tile
)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER
);
734 /* Clear the tile in any case */
735 ret
= DoCommand(end_tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
736 if (ret
.Failed()) return_cmd_error(STR_ERROR_UNABLE_TO_EXCAVATE_LAND
);
739 /* slope of end tile must be complementary to the slope of the start tile */
740 if (end_tileh
!= ComplementSlope(start_tileh
)) {
741 /* Mark the tile as already cleared for the terraform command.
742 * Do this for all tiles (like trees), not only objects. */
743 ClearedObjectArea
*coa
= FindClearedObject(end_tile
);
744 if (coa
== nullptr) {
745 coa
= &_cleared_object_areas
.emplace_back(ClearedObjectArea
{ end_tile
, TileArea(end_tile
, 1, 1) });
748 /* Hide the tile from the terraforming command */
749 TileIndex old_first_tile
= coa
->first_tile
;
750 coa
->first_tile
= INVALID_TILE
;
752 /* CMD_TERRAFORM_LAND may append further items to _cleared_object_areas,
753 * however it will never erase or re-order existing items.
754 * _cleared_object_areas is a value-type self-resizing vector, therefore appending items
755 * may result in a backing-store re-allocation, which would invalidate the coa pointer.
756 * The index of the coa pointer into the _cleared_object_areas vector remains valid,
757 * and can be used safely after the CMD_TERRAFORM_LAND operation.
758 * Deliberately clear the coa pointer to avoid leaving dangling pointers which could
759 * inadvertently be dereferenced.
761 ClearedObjectArea
*begin
= _cleared_object_areas
.data();
762 assert(coa
>= begin
&& coa
< begin
+ _cleared_object_areas
.size());
763 size_t coa_index
= coa
- begin
;
764 assert(coa_index
< UINT_MAX
); // more than 2**32 cleared areas would be a bug in itself
767 ret
= DoCommand(end_tile
, end_tileh
& start_tileh
, 0, flags
, CMD_TERRAFORM_LAND
);
768 _cleared_object_areas
[(uint
)coa_index
].first_tile
= old_first_tile
;
769 if (ret
.Failed()) return_cmd_error(STR_ERROR_UNABLE_TO_EXCAVATE_LAND
);
772 cost
.AddCost(_price
[PR_BUILD_TUNNEL
]);
774 /* Pay for the rail/road in the tunnel including entrances */
775 switch (transport_type
) {
776 case TRANSPORT_ROAD
: cost
.AddCost((tiles
+ 2) * RoadBuildCost(roadtype
) * 2); break;
777 case TRANSPORT_RAIL
: cost
.AddCost((tiles
+ 2) * RailBuildCost(railtype
)); break;
778 default: NOT_REACHED();
781 if (flags
& DC_EXEC
) {
782 Company
*c
= Company::GetIfValid(company
);
783 uint num_pieces
= (tiles
+ 2) * TUNNELBRIDGE_TRACKBIT_FACTOR
;
784 if (transport_type
== TRANSPORT_RAIL
) {
785 if (c
!= nullptr) c
->infrastructure
.rail
[railtype
] += num_pieces
;
786 MakeRailTunnel(start_tile
, company
, direction
, railtype
);
787 MakeRailTunnel(end_tile
, company
, ReverseDiagDir(direction
), railtype
);
788 AddSideToSignalBuffer(start_tile
, INVALID_DIAGDIR
, company
);
789 YapfNotifyTrackLayoutChange(start_tile
, DiagDirToDiagTrack(direction
));
791 if (c
!= nullptr) c
->infrastructure
.road
[roadtype
] += num_pieces
* 2; // A full diagonal road has two road bits.
792 RoadType road_rt
= RoadTypeIsRoad(roadtype
) ? roadtype
: INVALID_ROADTYPE
;
793 RoadType tram_rt
= RoadTypeIsTram(roadtype
) ? roadtype
: INVALID_ROADTYPE
;
794 MakeRoadTunnel(start_tile
, company
, direction
, road_rt
, tram_rt
);
795 MakeRoadTunnel(end_tile
, company
, ReverseDiagDir(direction
), road_rt
, tram_rt
);
797 DirtyCompanyInfrastructureWindows(company
);
805 * Are we allowed to remove the tunnel or bridge at \a tile?
806 * @param tile End point of the tunnel or bridge.
807 * @return A succeeded command if the tunnel or bridge may be removed, a failed command otherwise.
809 static inline CommandCost
CheckAllowRemoveTunnelBridge(TileIndex tile
)
811 /* Floods can remove anything as well as the scenario editor */
812 if (_current_company
== OWNER_WATER
|| _game_mode
== GM_EDITOR
) return CommandCost();
814 switch (GetTunnelBridgeTransportType(tile
)) {
815 case TRANSPORT_ROAD
: {
816 RoadType road_rt
= GetRoadTypeRoad(tile
);
817 RoadType tram_rt
= GetRoadTypeTram(tile
);
818 Owner road_owner
= _current_company
;
819 Owner tram_owner
= _current_company
;
821 if (road_rt
!= INVALID_ROADTYPE
) road_owner
= GetRoadOwner(tile
, RTT_ROAD
);
822 if (tram_rt
!= INVALID_ROADTYPE
) tram_owner
= GetRoadOwner(tile
, RTT_TRAM
);
824 /* We can remove unowned road and if the town allows it */
825 if (road_owner
== OWNER_TOWN
&& _current_company
!= OWNER_TOWN
&& !(_settings_game
.construction
.extra_dynamite
|| _cheats
.magic_bulldozer
.value
)) {
826 /* Town does not allow */
827 return CheckTileOwnership(tile
);
829 if (road_owner
== OWNER_NONE
|| road_owner
== OWNER_TOWN
) road_owner
= _current_company
;
830 if (tram_owner
== OWNER_NONE
) tram_owner
= _current_company
;
832 CommandCost ret
= CheckOwnership(road_owner
, tile
);
833 if (ret
.Succeeded()) ret
= CheckOwnership(tram_owner
, tile
);
838 return CheckOwnership(GetTileOwner(tile
));
840 case TRANSPORT_WATER
: {
841 /* Always allow to remove aqueducts without owner. */
842 Owner aqueduct_owner
= GetTileOwner(tile
);
843 if (aqueduct_owner
== OWNER_NONE
) aqueduct_owner
= _current_company
;
844 return CheckOwnership(aqueduct_owner
);
847 default: NOT_REACHED();
852 * Remove a tunnel from the game, update town rating, etc.
853 * @param tile Tile containing one of the endpoints of the tunnel.
854 * @param flags Command flags.
855 * @return Succeeded or failed command.
857 static CommandCost
DoClearTunnel(TileIndex tile
, DoCommandFlag flags
)
859 CommandCost ret
= CheckAllowRemoveTunnelBridge(tile
);
860 if (ret
.Failed()) return ret
;
862 TileIndex endtile
= GetOtherTunnelEnd(tile
);
864 ret
= TunnelBridgeIsFree(tile
, endtile
);
865 if (ret
.Failed()) return ret
;
867 _build_tunnel_endtile
= endtile
;
870 if (IsTileOwner(tile
, OWNER_TOWN
) && _game_mode
!= GM_EDITOR
) {
871 t
= ClosestTownFromTile(tile
, UINT_MAX
); // town penalty rating
873 /* Check if you are allowed to remove the tunnel owned by a town
874 * Removal depends on difficulty settings */
875 CommandCost ret
= CheckforTownRating(flags
, t
, TUNNELBRIDGE_REMOVE
);
876 if (ret
.Failed()) return ret
;
879 /* checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
880 * you have a "Poor" (0) town rating */
881 if (IsTileOwner(tile
, OWNER_TOWN
) && _game_mode
!= GM_EDITOR
) {
882 ChangeTownRating(t
, RATING_TUNNEL_BRIDGE_DOWN_STEP
, RATING_TUNNEL_BRIDGE_MINIMUM
, flags
);
885 Money base_cost
= TunnelBridgeClearCost(tile
, PR_CLEAR_TUNNEL
);
886 uint len
= GetTunnelBridgeLength(tile
, endtile
) + 2; // Don't forget the end tiles.
888 if (flags
& DC_EXEC
) {
889 if (GetTunnelBridgeTransportType(tile
) == TRANSPORT_RAIL
) {
890 /* We first need to request values before calling DoClearSquare */
891 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
892 Track track
= DiagDirToDiagTrack(dir
);
893 Owner owner
= GetTileOwner(tile
);
896 if (HasTunnelBridgeReservation(tile
)) {
897 v
= GetTrainForReservation(tile
, track
);
898 if (v
!= nullptr) FreeTrainTrackReservation(v
);
901 if (Company::IsValidID(owner
)) {
902 Company::Get(owner
)->infrastructure
.rail
[GetRailType(tile
)] -= len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
903 DirtyCompanyInfrastructureWindows(owner
);
907 DoClearSquare(endtile
);
909 /* cannot use INVALID_DIAGDIR for signal update because the tunnel doesn't exist anymore */
910 AddSideToSignalBuffer(tile
, ReverseDiagDir(dir
), owner
);
911 AddSideToSignalBuffer(endtile
, dir
, owner
);
913 YapfNotifyTrackLayoutChange(tile
, track
);
914 YapfNotifyTrackLayoutChange(endtile
, track
);
916 if (v
!= nullptr) TryPathReserve(v
);
918 /* A full diagonal road tile has two road bits. */
919 UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile
), GetRoadOwner(tile
, RTT_ROAD
), -(int)(len
* 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
));
920 UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile
), GetRoadOwner(tile
, RTT_TRAM
), -(int)(len
* 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
));
923 DoClearSquare(endtile
);
927 return CommandCost(EXPENSES_CONSTRUCTION
, len
* base_cost
);
932 * Remove a bridge from the game, update town rating, etc.
933 * @param tile Tile containing one of the endpoints of the bridge.
934 * @param flags Command flags.
935 * @return Succeeded or failed command.
937 static CommandCost
DoClearBridge(TileIndex tile
, DoCommandFlag flags
)
939 CommandCost ret
= CheckAllowRemoveTunnelBridge(tile
);
940 if (ret
.Failed()) return ret
;
942 TileIndex endtile
= GetOtherBridgeEnd(tile
);
944 ret
= TunnelBridgeIsFree(tile
, endtile
);
945 if (ret
.Failed()) return ret
;
947 DiagDirection direction
= GetTunnelBridgeDirection(tile
);
948 TileIndexDiff delta
= TileOffsByDiagDir(direction
);
951 if (IsTileOwner(tile
, OWNER_TOWN
) && _game_mode
!= GM_EDITOR
) {
952 t
= ClosestTownFromTile(tile
, UINT_MAX
); // town penalty rating
954 /* Check if you are allowed to remove the bridge owned by a town
955 * Removal depends on difficulty settings */
956 CommandCost ret
= CheckforTownRating(flags
, t
, TUNNELBRIDGE_REMOVE
);
957 if (ret
.Failed()) return ret
;
960 /* checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
961 * you have a "Poor" (0) town rating */
962 if (IsTileOwner(tile
, OWNER_TOWN
) && _game_mode
!= GM_EDITOR
) {
963 ChangeTownRating(t
, RATING_TUNNEL_BRIDGE_DOWN_STEP
, RATING_TUNNEL_BRIDGE_MINIMUM
, flags
);
966 Money base_cost
= TunnelBridgeClearCost(tile
, PR_CLEAR_BRIDGE
);
967 uint len
= GetTunnelBridgeLength(tile
, endtile
) + 2; // Don't forget the end tiles.
969 if (flags
& DC_EXEC
) {
970 /* read this value before actual removal of bridge */
971 bool rail
= GetTunnelBridgeTransportType(tile
) == TRANSPORT_RAIL
;
972 Owner owner
= GetTileOwner(tile
);
973 int height
= GetBridgeHeight(tile
);
976 if (rail
&& HasTunnelBridgeReservation(tile
)) {
977 v
= GetTrainForReservation(tile
, DiagDirToDiagTrack(direction
));
978 if (v
!= nullptr) FreeTrainTrackReservation(v
);
981 bool removetile
= false;
982 bool removeendtile
= false;
984 /* Update company infrastructure counts. */
986 if (Company::IsValidID(owner
)) Company::Get(owner
)->infrastructure
.rail
[GetRailType(tile
)] -= len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
987 } else if (GetTunnelBridgeTransportType(tile
) == TRANSPORT_ROAD
) {
988 /* A full diagonal road tile has two road bits. */
989 UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile
), GetRoadOwner(tile
, RTT_ROAD
), -(int)(len
* 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
));
990 UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile
), GetRoadOwner(tile
, RTT_TRAM
), -(int)(len
* 2 * TUNNELBRIDGE_TRACKBIT_FACTOR
));
992 if (Company::IsValidID(owner
)) Company::Get(owner
)->infrastructure
.water
-= len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
993 removetile
= IsDockingTile(tile
);
994 removeendtile
= IsDockingTile(endtile
);
996 DirtyCompanyInfrastructureWindows(owner
);
999 DoClearSquare(endtile
);
1001 if (removetile
) RemoveDockingTile(tile
);
1002 if (removeendtile
) RemoveDockingTile(endtile
);
1003 for (TileIndex c
= tile
+ delta
; c
!= endtile
; c
+= delta
) {
1004 /* do not let trees appear from 'nowhere' after removing bridge */
1005 if (IsNormalRoadTile(c
) && GetRoadside(c
) == ROADSIDE_TREES
) {
1006 int minz
= GetTileMaxZ(c
) + 3;
1007 if (height
< minz
) SetRoadside(c
, ROADSIDE_PAVED
);
1009 ClearBridgeMiddle(c
);
1010 MarkTileDirtyByTile(c
, height
- TileHeight(c
));
1014 /* cannot use INVALID_DIAGDIR for signal update because the bridge doesn't exist anymore */
1015 AddSideToSignalBuffer(tile
, ReverseDiagDir(direction
), owner
);
1016 AddSideToSignalBuffer(endtile
, direction
, owner
);
1018 Track track
= DiagDirToDiagTrack(direction
);
1019 YapfNotifyTrackLayoutChange(tile
, track
);
1020 YapfNotifyTrackLayoutChange(endtile
, track
);
1022 if (v
!= nullptr) TryPathReserve(v
, true);
1026 return CommandCost(EXPENSES_CONSTRUCTION
, len
* base_cost
);
1030 * Remove a tunnel or a bridge from the game.
1031 * @param tile Tile containing one of the endpoints.
1032 * @param flags Command flags.
1033 * @return Succeeded or failed command.
1035 static CommandCost
ClearTile_TunnelBridge(TileIndex tile
, DoCommandFlag flags
)
1037 if (IsTunnel(tile
)) {
1038 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST
);
1039 return DoClearTunnel(tile
, flags
);
1040 } else { // IsBridge(tile)
1041 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
1042 return DoClearBridge(tile
, flags
);
1047 * Draw a single pillar sprite.
1048 * @param psid Pillarsprite
1052 * @param w Bounding box size in X direction
1053 * @param h Bounding box size in Y direction
1054 * @param subsprite Optional subsprite for drawing halfpillars
1056 static inline void DrawPillar(const PalSpriteID
*psid
, int x
, int y
, int z
, int w
, int h
, const SubSprite
*subsprite
)
1058 static const int PILLAR_Z_OFFSET
= TILE_HEIGHT
- BRIDGE_Z_START
; ///< Start offset of pillar wrt. bridge (downwards)
1059 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
);
1063 * Draw two bridge pillars (north and south).
1064 * @param z_bottom Bottom Z
1065 * @param z_top Top Z
1066 * @param psid Pillarsprite
1069 * @param w Bounding box size in X direction
1070 * @param h Bounding box size in Y direction
1071 * @return Reached Z at the bottom
1073 static int DrawPillarColumn(int z_bottom
, int z_top
, const PalSpriteID
*psid
, int x
, int y
, int w
, int h
)
1076 for (cur_z
= z_top
; cur_z
>= z_bottom
; cur_z
-= TILE_HEIGHT
) {
1077 DrawPillar(psid
, x
, y
, cur_z
, w
, h
, nullptr);
1083 * Draws the pillars under high bridges.
1085 * @param psid Image and palette of a bridge pillar.
1086 * @param ti #TileInfo of current bridge-middle-tile.
1087 * @param axis Orientation of bridge.
1088 * @param drawfarpillar Whether to draw the pillar at the back
1089 * @param x Sprite X position of front pillar.
1090 * @param y Sprite Y position of front pillar.
1091 * @param z_bridge Absolute height of bridge bottom.
1093 static void DrawBridgePillars(const PalSpriteID
*psid
, const TileInfo
*ti
, Axis axis
, bool drawfarpillar
, int x
, int y
, int z_bridge
)
1095 static const int bounding_box_size
[2] = {16, 2}; ///< bounding box size of pillars along bridge direction
1096 static const int back_pillar_offset
[2] = { 0, 9}; ///< sprite position offset of back facing pillar
1098 static const int INF
= 1000; ///< big number compared to sprite size
1099 static const SubSprite half_pillar_sub_sprite
[2][2] = {
1100 { { -14, -INF
, INF
, INF
}, { -INF
, -INF
, -15, INF
} }, // X axis, north and south
1101 { { -INF
, -INF
, 15, INF
}, { 16, -INF
, INF
, INF
} }, // Y axis, north and south
1104 if (psid
->sprite
== 0) return;
1106 /* Determine ground height under pillars */
1107 DiagDirection south_dir
= AxisToDiagDir(axis
);
1108 int z_front_north
= ti
->z
;
1109 int z_back_north
= ti
->z
;
1110 int z_front_south
= ti
->z
;
1111 int z_back_south
= ti
->z
;
1112 GetSlopePixelZOnEdge(ti
->tileh
, south_dir
, &z_front_south
, &z_back_south
);
1113 GetSlopePixelZOnEdge(ti
->tileh
, ReverseDiagDir(south_dir
), &z_front_north
, &z_back_north
);
1115 /* Shared height of pillars */
1116 int z_front
= std::max(z_front_north
, z_front_south
);
1117 int z_back
= std::max(z_back_north
, z_back_south
);
1119 /* x and y size of bounding-box of pillars */
1120 int w
= bounding_box_size
[axis
];
1121 int h
= bounding_box_size
[OtherAxis(axis
)];
1122 /* sprite position of back facing pillar */
1123 int x_back
= x
- back_pillar_offset
[axis
];
1124 int y_back
= y
- back_pillar_offset
[OtherAxis(axis
)];
1126 /* Draw front pillars */
1127 int bottom_z
= DrawPillarColumn(z_front
, z_bridge
, psid
, x
, y
, w
, h
);
1128 if (z_front_north
< z_front
) DrawPillar(psid
, x
, y
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][0]);
1129 if (z_front_south
< z_front
) DrawPillar(psid
, x
, y
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][1]);
1131 /* Draw back pillars, skip top two parts, which are hidden by the bridge */
1132 int z_bridge_back
= z_bridge
- 2 * (int)TILE_HEIGHT
;
1133 if (drawfarpillar
&& (z_back_north
<= z_bridge_back
|| z_back_south
<= z_bridge_back
)) {
1134 bottom_z
= DrawPillarColumn(z_back
, z_bridge_back
, psid
, x_back
, y_back
, w
, h
);
1135 if (z_back_north
< z_back
) DrawPillar(psid
, x_back
, y_back
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][0]);
1136 if (z_back_south
< z_back
) DrawPillar(psid
, x_back
, y_back
, bottom_z
, w
, h
, &half_pillar_sub_sprite
[axis
][1]);
1141 * Retrieve the sprites required for catenary on a road/tram bridge.
1142 * @param rti RoadTypeInfo for the road or tram type to get catenary for
1143 * @param head_tile Bridge head tile with roadtype information
1144 * @param offset Sprite offset identifying flat to sloped bridge tiles
1145 * @param head Are we drawing bridge head?
1146 * @param[out] spr_back Back catenary sprite to use
1147 * @param[out] spr_front Front catenary sprite to use
1149 static void GetBridgeRoadCatenary(const RoadTypeInfo
*rti
, TileIndex head_tile
, int offset
, bool head
, SpriteID
&spr_back
, SpriteID
&spr_front
)
1151 static const SpriteID back_offsets
[6] = { 95, 96, 99, 102, 100, 101 };
1152 static const SpriteID front_offsets
[6] = { 97, 98, 103, 106, 104, 105 };
1154 /* Simplified from DrawRoadTypeCatenary() to remove all the special cases required for regular ground road */
1155 spr_back
= GetCustomRoadSprite(rti
, head_tile
, ROTSG_CATENARY_BACK
, head
? TCX_NORMAL
: TCX_ON_BRIDGE
);
1156 spr_front
= GetCustomRoadSprite(rti
, head_tile
, ROTSG_CATENARY_FRONT
, head
? TCX_NORMAL
: TCX_ON_BRIDGE
);
1157 if (spr_back
== 0 && spr_front
== 0) {
1158 spr_back
= SPR_TRAMWAY_BASE
+ back_offsets
[offset
];
1159 spr_front
= SPR_TRAMWAY_BASE
+ front_offsets
[offset
];
1161 if (spr_back
!= 0) spr_back
+= 23 + offset
;
1162 if (spr_front
!= 0) spr_front
+= 23 + offset
;
1167 * Draws the road and trambits over an already drawn (lower end) of a bridge.
1168 * @param head_tile bridge head tile with roadtype information
1169 * @param x the x of the bridge
1170 * @param y the y of the bridge
1171 * @param z the z of the bridge
1172 * @param offset sprite offset identifying flat to sloped bridge tiles
1173 * @param head are we drawing bridge head?
1175 static void DrawBridgeRoadBits(TileIndex head_tile
, int x
, int y
, int z
, int offset
, bool head
)
1177 RoadType road_rt
= GetRoadTypeRoad(head_tile
);
1178 RoadType tram_rt
= GetRoadTypeTram(head_tile
);
1179 const RoadTypeInfo
*road_rti
= road_rt
== INVALID_ROADTYPE
? nullptr : GetRoadTypeInfo(road_rt
);
1180 const RoadTypeInfo
*tram_rti
= tram_rt
== INVALID_ROADTYPE
? nullptr : GetRoadTypeInfo(tram_rt
);
1182 SpriteID seq_back
[4] = { 0 };
1183 bool trans_back
[4] = { false };
1184 SpriteID seq_front
[4] = { 0 };
1185 bool trans_front
[4] = { false };
1187 static const SpriteID overlay_offsets
[6] = { 0, 1, 11, 12, 13, 14 };
1188 if (head
|| !IsInvisibilitySet(TO_BRIDGES
)) {
1189 /* Road underlay takes precedence over tram */
1190 trans_back
[0] = !head
&& IsTransparencySet(TO_BRIDGES
);
1191 if (road_rti
!= nullptr) {
1192 if (road_rti
->UsesOverlay()) {
1193 seq_back
[0] = GetCustomRoadSprite(road_rti
, head_tile
, ROTSG_BRIDGE
, head
? TCX_NORMAL
: TCX_ON_BRIDGE
) + offset
;
1195 } else if (tram_rti
!= nullptr) {
1196 if (tram_rti
->UsesOverlay()) {
1197 seq_back
[0] = GetCustomRoadSprite(tram_rti
, head_tile
, ROTSG_BRIDGE
, head
? TCX_NORMAL
: TCX_ON_BRIDGE
) + offset
;
1199 seq_back
[0] = SPR_TRAMWAY_BRIDGE
+ offset
;
1203 /* Draw road overlay */
1204 trans_back
[1] = !head
&& IsTransparencySet(TO_BRIDGES
);
1205 if (road_rti
!= nullptr) {
1206 if (road_rti
->UsesOverlay()) {
1207 seq_back
[1] = GetCustomRoadSprite(road_rti
, head_tile
, ROTSG_OVERLAY
, head
? TCX_NORMAL
: TCX_ON_BRIDGE
);
1208 if (seq_back
[1] != 0) seq_back
[1] += overlay_offsets
[offset
];
1212 /* Draw tram overlay */
1213 trans_back
[2] = !head
&& IsTransparencySet(TO_BRIDGES
);
1214 if (tram_rti
!= nullptr) {
1215 if (tram_rti
->UsesOverlay()) {
1216 seq_back
[2] = GetCustomRoadSprite(tram_rti
, head_tile
, ROTSG_OVERLAY
, head
? TCX_NORMAL
: TCX_ON_BRIDGE
);
1217 if (seq_back
[2] != 0) seq_back
[2] += overlay_offsets
[offset
];
1218 } else if (road_rti
!= nullptr) {
1219 seq_back
[2] = SPR_TRAMWAY_OVERLAY
+ overlay_offsets
[offset
];
1223 /* Road catenary takes precedence over tram */
1224 trans_back
[3] = IsTransparencySet(TO_CATENARY
);
1225 trans_front
[0] = IsTransparencySet(TO_CATENARY
);
1226 if (road_rti
!= nullptr && HasRoadCatenaryDrawn(road_rt
)) {
1227 GetBridgeRoadCatenary(road_rti
, head_tile
, offset
, head
, seq_back
[3], seq_front
[0]);
1228 } else if (tram_rti
!= nullptr && HasRoadCatenaryDrawn(tram_rt
)) {
1229 GetBridgeRoadCatenary(tram_rti
, head_tile
, offset
, head
, seq_back
[3], seq_front
[0]);
1233 static const uint size_x
[6] = { 1, 16, 16, 1, 16, 1 };
1234 static const uint size_y
[6] = { 16, 1, 1, 16, 1, 16 };
1235 static const uint front_bb_offset_x
[6] = { 15, 0, 0, 15, 0, 15 };
1236 static const uint front_bb_offset_y
[6] = { 0, 15, 15, 0, 15, 0 };
1238 /* The sprites under the vehicles are drawn as SpriteCombine. StartSpriteCombine() has already been called
1239 * The bounding boxes here are the same as for bridge front/roof */
1240 for (uint i
= 0; i
< lengthof(seq_back
); ++i
) {
1241 if (seq_back
[i
] != 0) {
1242 AddSortableSpriteToDraw(seq_back
[i
], PAL_NONE
,
1243 x
, y
, size_x
[offset
], size_y
[offset
], 0x28, z
,
1248 /* Start a new SpriteCombine for the front part */
1250 StartSpriteCombine();
1252 for (uint i
= 0; i
< lengthof(seq_front
); ++i
) {
1253 if (seq_front
[i
] != 0) {
1254 AddSortableSpriteToDraw(seq_front
[i
], PAL_NONE
,
1255 x
, y
, size_x
[offset
] + front_bb_offset_x
[offset
], size_y
[offset
] + front_bb_offset_y
[offset
], 0x28, z
,
1257 front_bb_offset_x
[offset
], front_bb_offset_y
[offset
]);
1263 * Draws a tunnel of bridge tile.
1264 * For tunnels, this is rather simple, as you only need to draw the entrance.
1265 * Bridges are a bit more complex. base_offset is where the sprite selection comes into play
1266 * and it works a bit like a bitmask.<p> For bridge heads:
1267 * @param ti TileInfo of the structure to draw
1268 * <ul><li>Bit 0: direction</li>
1269 * <li>Bit 1: northern or southern heads</li>
1270 * <li>Bit 2: Set if the bridge head is sloped</li>
1271 * <li>Bit 3 and more: Railtype Specific subset</li>
1273 * Please note that in this code, "roads" are treated as railtype 1, whilst the real railtypes are 0, 2 and 3
1275 static void DrawTile_TunnelBridge(TileInfo
*ti
)
1277 TransportType transport_type
= GetTunnelBridgeTransportType(ti
->tile
);
1278 DiagDirection tunnelbridge_direction
= GetTunnelBridgeDirection(ti
->tile
);
1280 if (IsTunnel(ti
->tile
)) {
1281 /* Front view of tunnel bounding boxes:
1283 * 122223 <- BB_Z_SEPARATOR
1285 * 1 3 1,3 = empty helper BB
1286 * 1 3 2 = SpriteCombine of tunnel-roof and catenary (tram & elrail)
1290 static const int _tunnel_BB
[4][12] = {
1291 /* tunnnel-roof | Z-separator | tram-catenary
1292 * w h bb_x bb_y| x y w h |bb_x bb_y w h */
1293 { 1, 0, -15, -14, 0, 15, 16, 1, 0, 1, 16, 15 }, // NE
1294 { 0, 1, -14, -15, 15, 0, 1, 16, 1, 0, 15, 16 }, // SE
1295 { 1, 0, -15, -14, 0, 15, 16, 1, 0, 1, 16, 15 }, // SW
1296 { 0, 1, -14, -15, 15, 0, 1, 16, 1, 0, 15, 16 }, // NW
1298 const int *BB_data
= _tunnel_BB
[tunnelbridge_direction
];
1300 bool catenary
= false;
1303 SpriteID railtype_overlay
= 0;
1304 if (transport_type
== TRANSPORT_RAIL
) {
1305 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(ti
->tile
));
1306 image
= rti
->base_sprites
.tunnel
;
1307 if (rti
->UsesOverlay()) {
1308 /* Check if the railtype has custom tunnel portals. */
1309 railtype_overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_TUNNEL_PORTAL
);
1310 if (railtype_overlay
!= 0) image
= SPR_RAILTYPE_TUNNEL_BASE
; // Draw blank grass tunnel base.
1313 image
= SPR_TUNNEL_ENTRY_REAR_ROAD
;
1316 if (HasTunnelBridgeSnowOrDesert(ti
->tile
)) image
+= railtype_overlay
!= 0 ? 8 : 32;
1318 image
+= tunnelbridge_direction
* 2;
1319 DrawGroundSprite(image
, PAL_NONE
);
1321 if (transport_type
== TRANSPORT_ROAD
) {
1322 RoadType road_rt
= GetRoadTypeRoad(ti
->tile
);
1323 RoadType tram_rt
= GetRoadTypeTram(ti
->tile
);
1324 const RoadTypeInfo
*road_rti
= road_rt
== INVALID_ROADTYPE
? nullptr : GetRoadTypeInfo(road_rt
);
1325 const RoadTypeInfo
*tram_rti
= tram_rt
== INVALID_ROADTYPE
? nullptr : GetRoadTypeInfo(tram_rt
);
1326 uint sprite_offset
= DiagDirToAxis(tunnelbridge_direction
) == AXIS_X
? 1 : 0;
1328 DrawRoadOverlays(ti
, PAL_NONE
, road_rti
, tram_rti
, sprite_offset
, sprite_offset
);
1330 /* Road catenary takes precedence over tram */
1331 SpriteID catenary_sprite_base
= 0;
1332 if (road_rti
!= nullptr && HasRoadCatenaryDrawn(road_rt
)) {
1333 catenary_sprite_base
= GetCustomRoadSprite(road_rti
, ti
->tile
, ROTSG_CATENARY_FRONT
);
1334 if (catenary_sprite_base
== 0) {
1335 catenary_sprite_base
= SPR_TRAMWAY_TUNNEL_WIRES
;
1337 catenary_sprite_base
+= 19;
1339 } else if (tram_rti
!= nullptr && HasRoadCatenaryDrawn(tram_rt
)) {
1340 catenary_sprite_base
= GetCustomRoadSprite(tram_rti
, ti
->tile
, ROTSG_CATENARY_FRONT
);
1341 if (catenary_sprite_base
== 0) {
1342 catenary_sprite_base
= SPR_TRAMWAY_TUNNEL_WIRES
;
1344 catenary_sprite_base
+= 19;
1348 if (catenary_sprite_base
!= 0) {
1350 StartSpriteCombine();
1351 AddSortableSpriteToDraw(catenary_sprite_base
+ 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
);
1354 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(ti
->tile
));
1355 if (rti
->UsesOverlay()) {
1356 SpriteID surface
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_TUNNEL
);
1357 if (surface
!= 0) DrawGroundSprite(surface
+ tunnelbridge_direction
, PAL_NONE
);
1360 /* PBS debugging, draw reserved tracks darker */
1361 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasTunnelBridgeReservation(ti
->tile
)) {
1362 if (rti
->UsesOverlay()) {
1363 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
);
1364 DrawGroundSprite(overlay
+ RTO_X
+ DiagDirToAxis(tunnelbridge_direction
), PALETTE_CRASH
);
1366 DrawGroundSprite(DiagDirToAxis(tunnelbridge_direction
) == AXIS_X
? rti
->base_sprites
.single_x
: rti
->base_sprites
.single_y
, PALETTE_CRASH
);
1370 if (HasRailCatenaryDrawn(GetRailType(ti
->tile
))) {
1371 /* Maybe draw pylons on the entry side */
1372 DrawRailCatenary(ti
);
1375 StartSpriteCombine();
1376 /* Draw wire above the ramp */
1377 DrawRailCatenaryOnTunnel(ti
);
1381 if (railtype_overlay
!= 0 && !catenary
) StartSpriteCombine();
1383 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
);
1384 /* Draw railtype tunnel portal overlay if defined. */
1385 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
);
1387 if (catenary
|| railtype_overlay
!= 0) EndSpriteCombine();
1389 /* Add helper BB for sprite sorting that separates the tunnel from things beside of it. */
1390 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX
, PAL_NONE
, ti
->x
, ti
->y
, BB_data
[6], BB_data
[7], TILE_HEIGHT
, ti
->z
);
1391 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
);
1393 DrawBridgeMiddle(ti
);
1394 } else { // IsBridge(ti->tile)
1395 const PalSpriteID
*psid
;
1397 bool ice
= HasTunnelBridgeSnowOrDesert(ti
->tile
);
1399 if (transport_type
== TRANSPORT_RAIL
) {
1400 base_offset
= GetRailTypeInfo(GetRailType(ti
->tile
))->bridge_offset
;
1401 assert(base_offset
!= 8); // This one is used for roads
1406 /* as the lower 3 bits are used for other stuff, make sure they are clear */
1407 assert( (base_offset
& 0x07) == 0x00);
1409 DrawFoundation(ti
, GetBridgeFoundation(ti
->tileh
, DiagDirToAxis(tunnelbridge_direction
)));
1411 /* HACK Wizardry to convert the bridge ramp direction into a sprite offset */
1412 base_offset
+= (6 - tunnelbridge_direction
) % 4;
1414 /* Table number BRIDGE_PIECE_HEAD always refers to the bridge heads for any bridge type */
1415 if (transport_type
!= TRANSPORT_WATER
) {
1416 if (ti
->tileh
== SLOPE_FLAT
) base_offset
+= 4; // sloped bridge head
1417 psid
= &GetBridgeSpriteTable(GetBridgeType(ti
->tile
), BRIDGE_PIECE_HEAD
)[base_offset
];
1419 psid
= _aqueduct_sprites
+ base_offset
;
1423 TileIndex next
= ti
->tile
+ TileOffsByDiagDir(tunnelbridge_direction
);
1424 if (ti
->tileh
!= SLOPE_FLAT
&& ti
->z
== 0 && HasTileWaterClass(next
) && GetWaterClass(next
) == WATER_CLASS_SEA
) {
1425 DrawShoreTile(ti
->tileh
);
1427 DrawClearLandTile(ti
, 3);
1430 DrawGroundSprite(SPR_FLAT_SNOW_DESERT_TILE
+ SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
1435 /* Draw Trambits and PBS Reservation as SpriteCombine */
1436 if (transport_type
== TRANSPORT_ROAD
|| transport_type
== TRANSPORT_RAIL
) StartSpriteCombine();
1438 /* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
1439 * it doesn't disappear behind it
1441 /* Bridge heads are drawn solid no matter how invisibility/transparency is set */
1442 AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, ti
->x
, ti
->y
, 16, 16, ti
->tileh
== SLOPE_FLAT
? 0 : 8, ti
->z
);
1444 if (transport_type
== TRANSPORT_ROAD
) {
1445 uint offset
= tunnelbridge_direction
;
1447 if (ti
->tileh
!= SLOPE_FLAT
) {
1448 offset
= (offset
+ 1) & 1;
1454 /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
1455 DrawBridgeRoadBits(ti
->tile
, ti
->x
, ti
->y
, z
, offset
, true);
1458 } else if (transport_type
== TRANSPORT_RAIL
) {
1459 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(ti
->tile
));
1460 if (rti
->UsesOverlay()) {
1461 SpriteID surface
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_BRIDGE
);
1463 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(tunnelbridge_direction
))) {
1464 AddSortableSpriteToDraw(surface
+ ((DiagDirToAxis(tunnelbridge_direction
) == AXIS_X
) ? RTBO_X
: RTBO_Y
), PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 0, ti
->z
+ 8);
1466 AddSortableSpriteToDraw(surface
+ RTBO_SLOPE
+ tunnelbridge_direction
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
1469 /* Don't fallback to non-overlay sprite -- the spec states that
1470 * if an overlay is present then the bridge surface must be
1474 /* PBS debugging, draw reserved tracks darker */
1475 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasTunnelBridgeReservation(ti
->tile
)) {
1476 if (rti
->UsesOverlay()) {
1477 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
);
1478 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(tunnelbridge_direction
))) {
1479 AddSortableSpriteToDraw(overlay
+ RTO_X
+ DiagDirToAxis(tunnelbridge_direction
), PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 0, ti
->z
+ 8);
1481 AddSortableSpriteToDraw(overlay
+ RTO_SLOPE_NE
+ tunnelbridge_direction
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
1484 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(tunnelbridge_direction
))) {
1485 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);
1487 AddSortableSpriteToDraw(rti
->base_sprites
.single_sloped
+ tunnelbridge_direction
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
1493 if (HasRailCatenaryDrawn(GetRailType(ti
->tile
))) {
1494 DrawRailCatenary(ti
);
1498 DrawBridgeMiddle(ti
);
1504 * Compute bridge piece. Computes the bridge piece to display depending on the position inside the bridge.
1505 * bridges pieces sequence (middle parts).
1506 * Note that it is not covering the bridge heads, which are always referenced by the same sprite table.
1507 * bridge len 1: BRIDGE_PIECE_NORTH
1508 * bridge len 2: BRIDGE_PIECE_NORTH BRIDGE_PIECE_SOUTH
1509 * bridge len 3: BRIDGE_PIECE_NORTH BRIDGE_PIECE_MIDDLE_ODD BRIDGE_PIECE_SOUTH
1510 * bridge len 4: BRIDGE_PIECE_NORTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_SOUTH
1511 * bridge len 5: BRIDGE_PIECE_NORTH BRIDGE_PIECE_INNER_NORTH BRIDGE_PIECE_MIDDLE_EVEN BRIDGE_PIECE_INNER_SOUTH BRIDGE_PIECE_SOUTH
1512 * 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
1513 * 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
1514 * #0 - always as first, #1 - always as last (if len>1)
1515 * #2,#3 are to pair in order
1516 * for odd bridges: #5 is going in the bridge middle if on even position, #4 on odd (counting from 0)
1517 * @param north Northernmost tile of bridge
1518 * @param south Southernmost tile of bridge
1519 * @return Index of bridge piece
1521 static BridgePieces
CalcBridgePiece(uint north
, uint south
)
1524 return BRIDGE_PIECE_NORTH
;
1525 } else if (south
== 1) {
1526 return BRIDGE_PIECE_SOUTH
;
1527 } else if (north
< south
) {
1528 return north
& 1 ? BRIDGE_PIECE_INNER_SOUTH
: BRIDGE_PIECE_INNER_NORTH
;
1529 } else if (north
> south
) {
1530 return south
& 1 ? BRIDGE_PIECE_INNER_NORTH
: BRIDGE_PIECE_INNER_SOUTH
;
1532 return north
& 1 ? BRIDGE_PIECE_MIDDLE_EVEN
: BRIDGE_PIECE_MIDDLE_ODD
;
1537 * Draw the middle bits of a bridge.
1538 * @param ti Tile information of the tile to draw it on.
1540 void DrawBridgeMiddle(const TileInfo
*ti
)
1542 /* Sectional view of bridge bounding boxes:
1544 * 1 2 1,2 = SpriteCombine of Bridge front/(back&floor) and RoadCatenary
1545 * 1 2 3 = empty helper BB
1546 * 1 7 2 4,5 = pillars under higher bridges
1547 * 1 6 88888 6 2 6 = elrail-pylons
1548 * 1 6 88888 6 2 7 = elrail-wire
1549 * 1 6 88888 6 2 <- TILE_HEIGHT 8 = rail-vehicle on bridge
1550 * 3333333333333 <- BB_Z_SEPARATOR
1552 * 4 5 <- BB_HEIGHT_UNDER_BRIDGE
1558 if (!IsBridgeAbove(ti
->tile
)) return;
1560 TileIndex rampnorth
= GetNorthernBridgeEnd(ti
->tile
);
1561 TileIndex rampsouth
= GetSouthernBridgeEnd(ti
->tile
);
1562 TransportType transport_type
= GetTunnelBridgeTransportType(rampsouth
);
1564 Axis axis
= GetBridgeAxis(ti
->tile
);
1565 BridgePieces piece
= CalcBridgePiece(
1566 GetTunnelBridgeLength(ti
->tile
, rampnorth
) + 1,
1567 GetTunnelBridgeLength(ti
->tile
, rampsouth
) + 1
1570 const PalSpriteID
*psid
;
1572 if (transport_type
!= TRANSPORT_WATER
) {
1573 BridgeType type
= GetBridgeType(rampsouth
);
1574 drawfarpillar
= !HasBit(GetBridgeSpec(type
)->flags
, 0);
1577 if (transport_type
== TRANSPORT_RAIL
) {
1578 base_offset
= GetRailTypeInfo(GetRailType(rampsouth
))->bridge_offset
;
1583 psid
= base_offset
+ GetBridgeSpriteTable(type
, piece
);
1585 drawfarpillar
= true;
1586 psid
= _aqueduct_sprites
;
1589 if (axis
!= AXIS_X
) psid
+= 4;
1593 uint bridge_z
= GetBridgePixelHeight(rampsouth
);
1594 int z
= bridge_z
- BRIDGE_Z_START
;
1596 /* Add a bounding box that separates the bridge from things below it. */
1597 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX
, PAL_NONE
, x
, y
, 16, 16, 1, bridge_z
- TILE_HEIGHT
+ BB_Z_SEPARATOR
);
1599 /* Draw Trambits as SpriteCombine */
1600 if (transport_type
== TRANSPORT_ROAD
|| transport_type
== TRANSPORT_RAIL
) StartSpriteCombine();
1602 /* Draw floor and far part of bridge*/
1603 if (!IsInvisibilitySet(TO_BRIDGES
)) {
1604 if (axis
== AXIS_X
) {
1605 AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, x
, y
, 16, 1, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 0, BRIDGE_Z_START
);
1607 AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, x
, y
, 1, 16, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 0, BRIDGE_Z_START
);
1613 if (transport_type
== TRANSPORT_ROAD
) {
1614 /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
1615 DrawBridgeRoadBits(rampsouth
, x
, y
, bridge_z
, axis
^ 1, false);
1616 } else if (transport_type
== TRANSPORT_RAIL
) {
1617 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(rampsouth
));
1618 if (rti
->UsesOverlay() && !IsInvisibilitySet(TO_BRIDGES
)) {
1619 SpriteID surface
= GetCustomRailSprite(rti
, rampsouth
, RTSG_BRIDGE
, TCX_ON_BRIDGE
);
1621 AddSortableSpriteToDraw(surface
+ axis
, PAL_NONE
, x
, y
, 16, 16, 0, bridge_z
, IsTransparencySet(TO_BRIDGES
));
1625 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& !IsInvisibilitySet(TO_BRIDGES
) && HasTunnelBridgeReservation(rampnorth
)) {
1626 if (rti
->UsesOverlay()) {
1627 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
);
1628 AddSortableSpriteToDraw(overlay
+ RTO_X
+ axis
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 0, bridge_z
, IsTransparencySet(TO_BRIDGES
));
1630 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
));
1636 if (HasRailCatenaryDrawn(GetRailType(rampsouth
))) {
1637 DrawRailCatenaryOnBridge(ti
);
1641 /* draw roof, the component of the bridge which is logically between the vehicle and the camera */
1642 if (!IsInvisibilitySet(TO_BRIDGES
)) {
1643 if (axis
== AXIS_X
) {
1645 if (psid
->sprite
& SPRITE_MASK
) AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, x
, y
, 16, 4, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 0, 3, BRIDGE_Z_START
);
1648 if (psid
->sprite
& SPRITE_MASK
) AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, x
, y
, 4, 16, 0x28, z
, IsTransparencySet(TO_BRIDGES
), 3, 0, BRIDGE_Z_START
);
1652 /* Draw TramFront as SpriteCombine */
1653 if (transport_type
== TRANSPORT_ROAD
) EndSpriteCombine();
1655 /* Do not draw anything more if bridges are invisible */
1656 if (IsInvisibilitySet(TO_BRIDGES
)) return;
1659 DrawBridgePillars(psid
, ti
, axis
, drawfarpillar
, x
, y
, z
);
1663 static int GetSlopePixelZ_TunnelBridge(TileIndex tile
, uint x
, uint y
)
1666 Slope tileh
= GetTilePixelSlope(tile
, &z
);
1671 if (IsTunnel(tile
)) {
1672 uint pos
= (DiagDirToAxis(GetTunnelBridgeDirection(tile
)) == AXIS_X
? y
: x
);
1674 /* In the tunnel entrance? */
1675 if (5 <= pos
&& pos
<= 10) return z
;
1676 } else { // IsBridge(tile)
1677 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
1678 uint pos
= (DiagDirToAxis(dir
) == AXIS_X
? y
: x
);
1680 z
+= ApplyPixelFoundationToSlope(GetBridgeFoundation(tileh
, DiagDirToAxis(dir
)), &tileh
);
1682 /* On the bridge ramp? */
1683 if (5 <= pos
&& pos
<= 10) {
1686 if (tileh
!= SLOPE_FLAT
) return z
+ TILE_HEIGHT
;
1689 default: NOT_REACHED();
1690 case DIAGDIR_NE
: delta
= (TILE_SIZE
- 1 - x
) / 2; break;
1691 case DIAGDIR_SE
: delta
= y
/ 2; break;
1692 case DIAGDIR_SW
: delta
= x
/ 2; break;
1693 case DIAGDIR_NW
: delta
= (TILE_SIZE
- 1 - y
) / 2; break;
1695 return z
+ 1 + delta
;
1699 return z
+ GetPartialPixelZ(x
, y
, tileh
);
1702 static Foundation
GetFoundation_TunnelBridge(TileIndex tile
, Slope tileh
)
1704 return IsTunnel(tile
) ? FOUNDATION_NONE
: GetBridgeFoundation(tileh
, DiagDirToAxis(GetTunnelBridgeDirection(tile
)));
1707 static void GetTileDesc_TunnelBridge(TileIndex tile
, TileDesc
*td
)
1709 TransportType tt
= GetTunnelBridgeTransportType(tile
);
1711 if (IsTunnel(tile
)) {
1712 td
->str
= (tt
== TRANSPORT_RAIL
) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD
: STR_LAI_TUNNEL_DESCRIPTION_ROAD
;
1713 } else { // IsBridge(tile)
1714 td
->str
= (tt
== TRANSPORT_WATER
) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT
: GetBridgeSpec(GetBridgeType(tile
))->transport_name
[tt
];
1716 td
->owner
[0] = GetTileOwner(tile
);
1718 Owner road_owner
= INVALID_OWNER
;
1719 Owner tram_owner
= INVALID_OWNER
;
1720 RoadType road_rt
= GetRoadTypeRoad(tile
);
1721 RoadType tram_rt
= GetRoadTypeTram(tile
);
1722 if (road_rt
!= INVALID_ROADTYPE
) {
1723 const RoadTypeInfo
*rti
= GetRoadTypeInfo(road_rt
);
1724 td
->roadtype
= rti
->strings
.name
;
1725 td
->road_speed
= rti
->max_speed
/ 2;
1726 road_owner
= GetRoadOwner(tile
, RTT_ROAD
);
1728 if (tram_rt
!= INVALID_ROADTYPE
) {
1729 const RoadTypeInfo
*rti
= GetRoadTypeInfo(tram_rt
);
1730 td
->tramtype
= rti
->strings
.name
;
1731 td
->tram_speed
= rti
->max_speed
/ 2;
1732 tram_owner
= GetRoadOwner(tile
, RTT_TRAM
);
1735 /* Is there a mix of owners? */
1736 if ((tram_owner
!= INVALID_OWNER
&& tram_owner
!= td
->owner
[0]) ||
1737 (road_owner
!= INVALID_OWNER
&& road_owner
!= td
->owner
[0])) {
1739 if (road_owner
!= INVALID_OWNER
) {
1740 td
->owner_type
[i
] = STR_LAND_AREA_INFORMATION_ROAD_OWNER
;
1741 td
->owner
[i
] = road_owner
;
1744 if (tram_owner
!= INVALID_OWNER
) {
1745 td
->owner_type
[i
] = STR_LAND_AREA_INFORMATION_TRAM_OWNER
;
1746 td
->owner
[i
] = tram_owner
;
1750 if (tt
== TRANSPORT_RAIL
) {
1751 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(tile
));
1752 td
->rail_speed
= rti
->max_speed
;
1753 td
->railtype
= rti
->strings
.name
;
1755 if (!IsTunnel(tile
)) {
1756 uint16 spd
= GetBridgeSpec(GetBridgeType(tile
))->speed
;
1757 if (td
->rail_speed
== 0 || spd
< td
->rail_speed
) {
1758 td
->rail_speed
= spd
;
1761 } else if (tt
== TRANSPORT_ROAD
&& !IsTunnel(tile
)) {
1762 uint16 spd
= GetBridgeSpec(GetBridgeType(tile
))->speed
;
1763 if (road_rt
!= INVALID_ROADTYPE
&& (td
->road_speed
== 0 || spd
< td
->road_speed
)) td
->road_speed
= spd
;
1764 if (tram_rt
!= INVALID_ROADTYPE
&& (td
->tram_speed
== 0 || spd
< td
->tram_speed
)) td
->tram_speed
= spd
;
1769 static void TileLoop_TunnelBridge(TileIndex tile
)
1771 bool snow_or_desert
= HasTunnelBridgeSnowOrDesert(tile
);
1772 switch (_settings_game
.game_creation
.landscape
) {
1774 /* As long as we do not have a snow density, we want to use the density
1775 * from the entry edge. For tunnels this is the lowest point for bridges the highest point.
1776 * (Independent of foundations) */
1777 int z
= IsBridge(tile
) ? GetTileMaxZ(tile
) : GetTileZ(tile
);
1778 if (snow_or_desert
!= (z
> GetSnowLine())) {
1779 SetTunnelBridgeSnowOrDesert(tile
, !snow_or_desert
);
1780 MarkTileDirtyByTile(tile
);
1786 if (GetTropicZone(tile
) == TROPICZONE_DESERT
&& !snow_or_desert
) {
1787 SetTunnelBridgeSnowOrDesert(tile
, true);
1788 MarkTileDirtyByTile(tile
);
1797 static TrackStatus
GetTileTrackStatus_TunnelBridge(TileIndex tile
, TransportType mode
, uint sub_mode
, DiagDirection side
)
1799 TransportType transport_type
= GetTunnelBridgeTransportType(tile
);
1800 if (transport_type
!= mode
|| (transport_type
== TRANSPORT_ROAD
&& !HasTileRoadType(tile
, (RoadTramType
)sub_mode
))) return 0;
1802 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
1803 if (side
!= INVALID_DIAGDIR
&& side
!= ReverseDiagDir(dir
)) return 0;
1804 return CombineTrackStatus(TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir
)), TRACKDIR_BIT_NONE
);
1807 static void ChangeTileOwner_TunnelBridge(TileIndex tile
, Owner old_owner
, Owner new_owner
)
1809 TileIndex other_end
= GetOtherTunnelBridgeEnd(tile
);
1810 /* Set number of pieces to zero if it's the southern tile as we
1811 * don't want to update the infrastructure counts twice. */
1812 uint num_pieces
= tile
< other_end
? (GetTunnelBridgeLength(tile
, other_end
) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR
: 0;
1814 for (RoadTramType rtt
: _roadtramtypes
) {
1815 /* Update all roadtypes, no matter if they are present */
1816 if (GetRoadOwner(tile
, rtt
) == old_owner
) {
1817 RoadType rt
= GetRoadType(tile
, rtt
);
1818 if (rt
!= INVALID_ROADTYPE
) {
1819 /* Update company infrastructure counts. A full diagonal road tile has two road bits.
1820 * No need to dirty windows here, we'll redraw the whole screen anyway. */
1821 Company::Get(old_owner
)->infrastructure
.road
[rt
] -= num_pieces
* 2;
1822 if (new_owner
!= INVALID_OWNER
) Company::Get(new_owner
)->infrastructure
.road
[rt
] += num_pieces
* 2;
1825 SetRoadOwner(tile
, rtt
, new_owner
== INVALID_OWNER
? OWNER_NONE
: new_owner
);
1829 if (!IsTileOwner(tile
, old_owner
)) return;
1831 /* Update company infrastructure counts for rail and water as well.
1832 * No need to dirty windows here, we'll redraw the whole screen anyway. */
1833 TransportType tt
= GetTunnelBridgeTransportType(tile
);
1834 Company
*old
= Company::Get(old_owner
);
1835 if (tt
== TRANSPORT_RAIL
) {
1836 old
->infrastructure
.rail
[GetRailType(tile
)] -= num_pieces
;
1837 if (new_owner
!= INVALID_OWNER
) Company::Get(new_owner
)->infrastructure
.rail
[GetRailType(tile
)] += num_pieces
;
1838 } else if (tt
== TRANSPORT_WATER
) {
1839 old
->infrastructure
.water
-= num_pieces
;
1840 if (new_owner
!= INVALID_OWNER
) Company::Get(new_owner
)->infrastructure
.water
+= num_pieces
;
1843 if (new_owner
!= INVALID_OWNER
) {
1844 SetTileOwner(tile
, new_owner
);
1846 if (tt
== TRANSPORT_RAIL
) {
1847 /* Since all of our vehicles have been removed, it is safe to remove the rail
1848 * bridge / tunnel. */
1849 [[maybe_unused
]] CommandCost ret
= DoCommand(tile
, 0, 0, DC_EXEC
| DC_BANKRUPT
, CMD_LANDSCAPE_CLEAR
);
1850 assert(ret
.Succeeded());
1852 /* In any other case, we can safely reassign the ownership to OWNER_NONE. */
1853 SetTileOwner(tile
, OWNER_NONE
);
1859 * Frame when the 'enter tunnel' sound should be played. This is the second
1860 * frame on a tile, so the sound is played shortly after entering the tunnel
1861 * tile, while the vehicle is still visible.
1863 static const byte TUNNEL_SOUND_FRAME
= 1;
1866 * Frame when a vehicle should be hidden in a tunnel with a certain direction.
1867 * This differs per direction, because of visibility / bounding box issues.
1868 * Note that direction, in this case, is the direction leading into the tunnel.
1869 * When entering a tunnel, hide the vehicle when it reaches the given frame.
1870 * When leaving a tunnel, show the vehicle when it is one frame further
1871 * to the 'outside', i.e. at (TILE_SIZE-1) - (frame) + 1
1873 extern const byte _tunnel_visibility_frame
[DIAGDIR_END
] = {12, 8, 8, 12};
1875 static VehicleEnterTileStatus
VehicleEnter_TunnelBridge(Vehicle
*v
, TileIndex tile
, int x
, int y
)
1877 int z
= GetSlopePixelZ(x
, y
) - v
->z_pos
;
1879 if (abs(z
) > 2) return VETSB_CANNOT_ENTER
;
1880 /* Direction into the wormhole */
1881 const DiagDirection dir
= GetTunnelBridgeDirection(tile
);
1882 /* Direction of the vehicle */
1883 const DiagDirection vdir
= DirToDiagDir(v
->direction
);
1884 /* New position of the vehicle on the tile */
1885 byte pos
= (DiagDirToAxis(vdir
) == AXIS_X
? x
: y
) & TILE_UNIT_MASK
;
1886 /* Number of units moved by the vehicle since entering the tile */
1887 byte frame
= (vdir
== DIAGDIR_NE
|| vdir
== DIAGDIR_NW
) ? TILE_SIZE
- 1 - pos
: pos
;
1889 if (IsTunnel(tile
)) {
1890 if (v
->type
== VEH_TRAIN
) {
1891 Train
*t
= Train::From(v
);
1893 if (t
->track
!= TRACK_BIT_WORMHOLE
&& dir
== vdir
) {
1894 if (t
->IsFrontEngine() && frame
== TUNNEL_SOUND_FRAME
) {
1895 if (!PlayVehicleSound(t
, VSE_TUNNEL
) && RailVehInfo(t
->engine_type
)->engclass
== 0) {
1896 SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL
, v
);
1898 return VETSB_CONTINUE
;
1900 if (frame
== _tunnel_visibility_frame
[dir
]) {
1902 t
->track
= TRACK_BIT_WORMHOLE
;
1903 t
->vehstatus
|= VS_HIDDEN
;
1904 return VETSB_ENTERED_WORMHOLE
;
1908 if (dir
== ReverseDiagDir(vdir
) && frame
== TILE_SIZE
- _tunnel_visibility_frame
[dir
] && z
== 0) {
1909 /* We're at the tunnel exit ?? */
1911 t
->track
= DiagDirToDiagTrackBits(vdir
);
1913 t
->vehstatus
&= ~VS_HIDDEN
;
1914 return VETSB_ENTERED_WORMHOLE
;
1916 } else if (v
->type
== VEH_ROAD
) {
1917 RoadVehicle
*rv
= RoadVehicle::From(v
);
1920 if (rv
->state
!= RVSB_WORMHOLE
&& dir
== vdir
) {
1921 if (frame
== _tunnel_visibility_frame
[dir
]) {
1922 /* Frame should be equal to the next frame number in the RV's movement */
1923 assert(frame
== rv
->frame
+ 1);
1925 rv
->state
= RVSB_WORMHOLE
;
1926 rv
->vehstatus
|= VS_HIDDEN
;
1927 return VETSB_ENTERED_WORMHOLE
;
1929 return VETSB_CONTINUE
;
1933 /* We're at the tunnel exit ?? */
1934 if (dir
== ReverseDiagDir(vdir
) && frame
== TILE_SIZE
- _tunnel_visibility_frame
[dir
] && z
== 0) {
1936 rv
->state
= DiagDirToDiagTrackdir(vdir
);
1938 rv
->vehstatus
&= ~VS_HIDDEN
;
1939 return VETSB_ENTERED_WORMHOLE
;
1942 } else { // IsBridge(tile)
1943 if (v
->type
!= VEH_SHIP
) {
1944 /* modify speed of vehicle */
1945 uint16 spd
= GetBridgeSpec(GetBridgeType(tile
))->speed
;
1947 if (v
->type
== VEH_ROAD
) spd
*= 2;
1948 Vehicle
*first
= v
->First();
1949 first
->cur_speed
= std::min(first
->cur_speed
, spd
);
1953 /* Vehicle enters bridge at the last frame inside this tile. */
1954 if (frame
!= TILE_SIZE
- 1) return VETSB_CONTINUE
;
1957 Train
*t
= Train::From(v
);
1958 t
->track
= TRACK_BIT_WORMHOLE
;
1959 ClrBit(t
->gv_flags
, GVF_GOINGUP_BIT
);
1960 ClrBit(t
->gv_flags
, GVF_GOINGDOWN_BIT
);
1965 RoadVehicle
*rv
= RoadVehicle::From(v
);
1966 rv
->state
= RVSB_WORMHOLE
;
1967 /* There are no slopes inside bridges / tunnels. */
1968 ClrBit(rv
->gv_flags
, GVF_GOINGUP_BIT
);
1969 ClrBit(rv
->gv_flags
, GVF_GOINGDOWN_BIT
);
1974 Ship::From(v
)->state
= TRACK_BIT_WORMHOLE
;
1977 default: NOT_REACHED();
1979 return VETSB_ENTERED_WORMHOLE
;
1980 } else if (vdir
== ReverseDiagDir(dir
)) {
1984 Train
*t
= Train::From(v
);
1985 if (t
->track
== TRACK_BIT_WORMHOLE
) {
1986 t
->track
= DiagDirToDiagTrackBits(vdir
);
1987 return VETSB_ENTERED_WORMHOLE
;
1993 RoadVehicle
*rv
= RoadVehicle::From(v
);
1994 if (rv
->state
== RVSB_WORMHOLE
) {
1995 rv
->state
= DiagDirToDiagTrackdir(vdir
);
1997 return VETSB_ENTERED_WORMHOLE
;
2003 Ship
*ship
= Ship::From(v
);
2004 if (ship
->state
== TRACK_BIT_WORMHOLE
) {
2005 ship
->state
= DiagDirToDiagTrackBits(vdir
);
2006 return VETSB_ENTERED_WORMHOLE
;
2011 default: NOT_REACHED();
2015 return VETSB_CONTINUE
;
2018 static CommandCost
TerraformTile_TunnelBridge(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
2020 if (_settings_game
.construction
.build_on_slopes
&& AutoslopeEnabled() && IsBridge(tile
) && GetTunnelBridgeTransportType(tile
) != TRANSPORT_WATER
) {
2021 DiagDirection direction
= GetTunnelBridgeDirection(tile
);
2022 Axis axis
= DiagDirToAxis(direction
);
2025 Slope tileh_old
= GetTileSlope(tile
, &z_old
);
2027 /* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
2028 if ((direction
== DIAGDIR_NW
) || (direction
== DIAGDIR_NE
)) {
2029 CheckBridgeSlope(BRIDGE_PIECE_SOUTH
, axis
, &tileh_old
, &z_old
);
2030 res
= CheckBridgeSlope(BRIDGE_PIECE_SOUTH
, axis
, &tileh_new
, &z_new
);
2032 CheckBridgeSlope(BRIDGE_PIECE_NORTH
, axis
, &tileh_old
, &z_old
);
2033 res
= CheckBridgeSlope(BRIDGE_PIECE_NORTH
, axis
, &tileh_new
, &z_new
);
2036 /* Surface slope is valid and remains unchanged? */
2037 if (res
.Succeeded() && (z_old
== z_new
) && (tileh_old
== tileh_new
)) return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
2040 return DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
2043 extern const TileTypeProcs _tile_type_tunnelbridge_procs
= {
2044 DrawTile_TunnelBridge
, // draw_tile_proc
2045 GetSlopePixelZ_TunnelBridge
, // get_slope_z_proc
2046 ClearTile_TunnelBridge
, // clear_tile_proc
2047 nullptr, // add_accepted_cargo_proc
2048 GetTileDesc_TunnelBridge
, // get_tile_desc_proc
2049 GetTileTrackStatus_TunnelBridge
, // get_tile_track_status_proc
2050 nullptr, // click_tile_proc
2051 nullptr, // animate_tile_proc
2052 TileLoop_TunnelBridge
, // tile_loop_proc
2053 ChangeTileOwner_TunnelBridge
, // change_tile_owner_proc
2054 nullptr, // add_produced_cargo_proc
2055 VehicleEnter_TunnelBridge
, // vehicle_enter_tile_proc
2056 GetFoundation_TunnelBridge
, // get_foundation_proc
2057 TerraformTile_TunnelBridge
, // terraform_tile_proc