OpenTTD Source 20260129-master-g2bb01bd0e4
tunnelbridge_cmd.cpp
Go to the documentation of this file.
1/*
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
13#include "stdafx.h"
14#include "viewport_func.h"
15#include "command_func.h"
16#include "town.h"
17#include "train.h"
18#include "ship.h"
19#include "roadveh.h"
21#include "newgrf_sound.h"
22#include "autoslope.h"
23#include "tunnelbridge_map.h"
24#include "strings_func.h"
26#include "clear_func.h"
27#include "vehicle_func.h"
28#include "sound_func.h"
29#include "tunnelbridge.h"
30#include "cheat_type.h"
31#include "elrail_func.h"
32#include "pbs.h"
33#include "company_base.h"
34#include "newgrf_railtype.h"
35#include "newgrf_roadtype.h"
36#include "object_base.h"
37#include "water.h"
38#include "company_gui.h"
39#include "tunnelbridge_cmd.h"
40#include "landscape_cmd.h"
41#include "terraform_cmd.h"
42
43#include "table/strings.h"
44#include "table/bridge_land.h"
45
46#include "safeguards.h"
47
50
52static const int BRIDGE_Z_START = 3;
53
54
63void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height)
64{
65 TileIndexDiff delta = TileOffsByDiagDir(direction);
66 for (TileIndex t = begin; t != end; t += delta) {
67 MarkTileDirtyByTile(t, bridge_height - TileHeight(t));
68 }
70}
71
80
83{
84 std::ranges::copy(_orig_bridge, std::begin(_bridge));
85}
86
94{
95 if (length < 2) return length;
96
97 length -= 2;
98 int sum = 2;
99 for (int delta = 1;; delta++) {
100 for (int count = 0; count < delta; count++) {
101 if (length == 0) return sum;
102 sum += delta;
103 length--;
104 }
105 }
106}
107
115{
116 if (tileh == SLOPE_FLAT ||
117 ((tileh == SLOPE_NE || tileh == SLOPE_SW) && axis == AXIS_X) ||
118 ((tileh == SLOPE_NW || tileh == SLOPE_SE) && axis == AXIS_Y)) return FOUNDATION_NONE;
119
120 return (HasSlopeHighestCorner(tileh) ? InclinedFoundation(axis) : FlatteningFoundation(tileh));
121}
122
130bool HasBridgeFlatRamp(Slope tileh, Axis axis)
131{
132 ApplyFoundationToSlope(GetBridgeFoundation(tileh, axis), tileh);
133 /* If the foundation slope is flat the bridge has a non-flat ramp and vice versa. */
134 return (tileh != SLOPE_FLAT);
135}
136
144{
145 assert(piece < NUM_BRIDGE_PIECES);
146 const BridgeSpec *bridge = GetBridgeSpec(bridge_type);
147 return piece < bridge->sprite_table.size() && !bridge->sprite_table[piece].empty();
148}
149
156static std::span<const PalSpriteID> GetBridgeSpriteTable(BridgeType bridge_type, BridgePieces piece)
157{
158 assert(piece < NUM_BRIDGE_PIECES);
159
160 const BridgeSpec *bridge = GetBridgeSpec(bridge_type);
161 if (piece < bridge->sprite_table.size() && !bridge->sprite_table[piece].empty()) return bridge->sprite_table[piece];
162
163 return _bridge_sprite_table[bridge_type][piece];
164}
165
172static uint8_t GetBridgeSpriteTableBaseOffset(TransportType transport_type, TileIndex ramp)
173{
174 switch (transport_type) {
176 case TRANSPORT_ROAD: return 8;
177 default: NOT_REACHED();
178 }
179}
180
187{
188 /* Bridge ramps are ordered SW, SE, NE, NW instead of NE, SE, SW, NW. */
189 static constexpr uint8_t ramp_offsets[DIAGDIR_END] = {2, 1, 0, 3};
190 return ramp_offsets[diagdir];
191}
192
199{
200 return axis == AXIS_X ? 0 : 4;
201}
202
212static CommandCost CheckBridgeSlope(BridgePieces bridge_piece, Axis axis, Slope &tileh, int &z)
213{
214 assert(bridge_piece == BRIDGE_PIECE_NORTH || bridge_piece == BRIDGE_PIECE_SOUTH);
215
216 Foundation f = GetBridgeFoundation(tileh, axis);
217 z += ApplyFoundationToSlope(f, tileh);
218
219 Slope valid_inclined;
220 if (bridge_piece == BRIDGE_PIECE_NORTH) {
221 valid_inclined = (axis == AXIS_X ? SLOPE_NE : SLOPE_NW);
222 } else {
223 valid_inclined = (axis == AXIS_X ? SLOPE_SW : SLOPE_SE);
224 }
225 if ((tileh != SLOPE_FLAT) && (tileh != valid_inclined)) return CMD_ERROR;
226
227 if (f == FOUNDATION_NONE) return CommandCost();
228
230}
231
240{
241 if (flags.Test(DoCommandFlag::QueryCost)) {
242 if (bridge_len <= _settings_game.construction.max_bridge_length) return CommandCost();
243 return CommandCost(STR_ERROR_BRIDGE_TOO_LONG);
244 }
245
246 if (bridge_type >= MAX_BRIDGES) return CMD_ERROR;
247
248 const BridgeSpec *b = GetBridgeSpec(bridge_type);
250
252
253 if (b->min_length > bridge_len) return CMD_ERROR;
254 if (bridge_len <= max) return CommandCost();
255 return CommandCost(STR_ERROR_BRIDGE_TOO_LONG);
256}
257
264{
265 Money base_cost = _price[base_price];
266
267 /* Add the cost of the transport that is on the tunnel/bridge. */
268 switch (GetTunnelBridgeTransportType(tile)) {
269 case TRANSPORT_ROAD: {
270 RoadType road_rt = GetRoadTypeRoad(tile);
271 RoadType tram_rt = GetRoadTypeTram(tile);
272
273 if (road_rt != INVALID_ROADTYPE) {
274 base_cost += 2 * RoadClearCost(road_rt);
275 }
276 if (tram_rt != INVALID_ROADTYPE) {
277 base_cost += 2 * RoadClearCost(tram_rt);
278 }
279 } break;
280
281 case TRANSPORT_RAIL: base_cost += RailClearCost(GetRailType(tile)); break;
282 /* Aqueducts have their own clear price. */
283 case TRANSPORT_WATER: base_cost = _price[Price::ClearAqueduct]; break;
284 default: break;
285 }
286
287 return base_cost;
288}
289
290static CommandCost CheckBuildAbove(TileIndex tile, DoCommandFlags flags, Axis axis, int height)
291{
292 if (_tile_type_procs[GetTileType(tile)]->check_build_above_proc != nullptr) {
293 return _tile_type_procs[GetTileType(tile)]->check_build_above_proc(tile, flags, axis, height);
294 }
295 /* A tile without a handler must be cleared. */
296 return Command<Commands::LandscapeClear>::Do(flags, tile);
297}
298
309CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex tile_start, TransportType transport_type, BridgeType bridge_type, uint8_t road_rail_type)
310{
311 CompanyID company = _current_company;
312
313 RailType railtype = INVALID_RAILTYPE;
314 RoadType roadtype = INVALID_ROADTYPE;
315
316 for (TileIndex t : {tile_start, tile_end}) {
317 if (!IsValidTile(t)) return CommandCost(STR_ERROR_BRIDGE_THROUGH_MAP_BORDER);
318 /* User cannot modify height of tiles with one coordinate equal to zero, they are always at the sea level, and you can't build bridge in the sea.
319 * Furthermore, they are void tiles unless map is infinite water. If we don't return for them here, we will still fail as their slope is invalid. */
320 if (TileX(t) == 0 || TileY(t) == 0) return CommandCost(transport_type == TRANSPORT_WATER ? STR_ERROR_BRIDGE_THROUGH_MAP_BORDER : STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
321 }
322
323 /* type of bridge */
324 switch (transport_type) {
325 case TRANSPORT_ROAD:
326 roadtype = (RoadType)road_rail_type;
327 if (!ValParamRoadType(roadtype)) return CMD_ERROR;
328 break;
329
330 case TRANSPORT_RAIL:
331 railtype = (RailType)road_rail_type;
332 if (!ValParamRailType(railtype)) return CMD_ERROR;
333 break;
334
335 case TRANSPORT_WATER:
336 break;
337
338 default:
339 /* Airports don't have bridges. */
340 return CMD_ERROR;
341 }
342
343 if (company == OWNER_DEITY) {
344 if (transport_type != TRANSPORT_ROAD) return CMD_ERROR;
345 const Town *town = CalcClosestTownFromTile(tile_start);
346
347 company = OWNER_TOWN;
348
349 /* If we are not within a town, we are not owned by the town */
350 if (town == nullptr || DistanceSquare(tile_start, town->xy) > town->cache.squared_town_zone_radius[to_underlying(HouseZone::TownEdge)]) {
351 company = OWNER_NONE;
352 }
353 }
354
355 if (tile_start == tile_end) {
356 return CommandCost(STR_ERROR_CAN_T_START_AND_END_ON);
357 }
358
359 Axis direction;
360 if (TileX(tile_start) == TileX(tile_end)) {
361 direction = AXIS_Y;
362 } else if (TileY(tile_start) == TileY(tile_end)) {
363 direction = AXIS_X;
364 } else {
365 return CommandCost(STR_ERROR_START_AND_END_MUST_BE_IN);
366 }
367
368 if (tile_end < tile_start) std::swap(tile_start, tile_end);
369
370 uint bridge_len = GetTunnelBridgeLength(tile_start, tile_end);
371 if (transport_type != TRANSPORT_WATER) {
372 /* set and test bridge length, availability */
373 CommandCost ret = CheckBridgeAvailability(bridge_type, bridge_len, flags);
374 if (ret.Failed()) return ret;
375 } else {
376 if (bridge_len > _settings_game.construction.max_bridge_length) return CommandCost(STR_ERROR_BRIDGE_TOO_LONG);
377 }
378 bridge_len += 2; // begin and end tiles/ramps
379
380 auto [tileh_start, z_start] = GetTileSlopeZ(tile_start);
381 auto [tileh_end, z_end] = GetTileSlopeZ(tile_end);
382 bool pbs_reservation = false;
383
384 CommandCost terraform_cost_north = CheckBridgeSlope(BRIDGE_PIECE_NORTH, direction, tileh_start, z_start);
385 CommandCost terraform_cost_south = CheckBridgeSlope(BRIDGE_PIECE_SOUTH, direction, tileh_end, z_end);
386
387 /* Aqueducts can't be built of flat land. */
388 if (transport_type == TRANSPORT_WATER && (tileh_start == SLOPE_FLAT || tileh_end == SLOPE_FLAT)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
389 if (z_start != z_end) return CommandCost(STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT);
390
392 Owner owner;
393 bool is_new_owner;
394 RoadType road_rt = INVALID_ROADTYPE;
395 RoadType tram_rt = INVALID_ROADTYPE;
396 if (IsBridgeTile(tile_start) && IsBridgeTile(tile_end) &&
397 GetOtherBridgeEnd(tile_start) == tile_end &&
398 GetTunnelBridgeTransportType(tile_start) == transport_type) {
399 /* Replace a current bridge. */
400
401 switch (transport_type) {
402 case TRANSPORT_RAIL:
403 /* Keep the reservation, the path stays valid. */
404 pbs_reservation = HasTunnelBridgeReservation(tile_start);
405 break;
406
407 case TRANSPORT_ROAD:
408 /* Do not remove road types when upgrading a bridge */
409 road_rt = GetRoadTypeRoad(tile_start);
410 tram_rt = GetRoadTypeTram(tile_start);
411 break;
412
413 default: break;
414 }
415
416 /* If this is a railway bridge, make sure the railtypes match. */
417 if (transport_type == TRANSPORT_RAIL && GetRailType(tile_start) != railtype) {
418 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
419 }
420
421 /* If this is a road bridge, make sure the roadtype matches. */
422 if (transport_type == TRANSPORT_ROAD) {
423 RoadType existing_rt = RoadTypeIsRoad(roadtype) ? road_rt : tram_rt;
424 if (existing_rt != roadtype && existing_rt != INVALID_ROADTYPE) {
425 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
426 }
427 }
428
429 if (!flags.Test(DoCommandFlag::QueryCost)) {
430 /* Do not replace the bridge with the same bridge type. */
431 if ((bridge_type == GetBridgeType(tile_start)) && (transport_type != TRANSPORT_ROAD || road_rt == roadtype || tram_rt == roadtype)) {
432 return CommandCost(STR_ERROR_ALREADY_BUILT);
433 }
434
435 /* Do not replace town bridges with lower speed bridges, unless in scenario editor. */
436 if (IsTileOwner(tile_start, OWNER_TOWN) && _game_mode != GM_EDITOR) {
437 Town *t = ClosestTownFromTile(tile_start, UINT_MAX);
438 if (t == nullptr) return CMD_ERROR;
439
440 if (GetBridgeSpec(bridge_type)->speed < GetBridgeSpec(GetBridgeType(tile_start))->speed) {
441 return CommandCostWithParam(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS, t->index);
442 } else {
443 ChangeTownRating(t, RATING_TUNNEL_BRIDGE_UP_STEP, RATING_MAXIMUM, flags);
444 }
445 }
446 }
447
448 /* Do not allow replacing another company's bridges. */
449 if (!IsTileOwner(tile_start, company) && !IsTileOwner(tile_start, OWNER_TOWN) && !IsTileOwner(tile_start, OWNER_NONE)) {
450 return CommandCost(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
451 }
452
453 /* The cost of clearing the current bridge. */
454 cost.AddCost(bridge_len * TunnelBridgeClearCost(tile_start, Price::ClearBridge));
455 owner = GetTileOwner(tile_start);
456
457 /* If bridge belonged to bankrupt company, it has a new owner now */
458 is_new_owner = (owner == OWNER_NONE);
459 if (is_new_owner) owner = company;
460
461 /* Check if the new bridge is compatible with tiles underneath. */
462 TileIndexDiff delta = (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
463 for (TileIndex tile = tile_start + delta; tile != tile_end; tile += delta) {
464 CommandCost ret = CheckBuildAbove(tile, flags, direction, z_start + 1);
465 if (ret.Failed()) return ret;
466 cost.AddCost(ret.GetCost());
467 }
468 } else {
469 /* Build a new bridge. */
470
471 bool allow_on_slopes = (_settings_game.construction.build_on_slopes && transport_type != TRANSPORT_WATER);
472
473 /* Try and clear the start landscape */
474 CommandCost ret = Command<Commands::LandscapeClear>::Do(flags, tile_start);
475 if (ret.Failed()) return ret;
476 cost = ret;
477
478 if (terraform_cost_north.Failed() || (terraform_cost_north.GetCost() != 0 && !allow_on_slopes)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
479 cost.AddCost(terraform_cost_north.GetCost());
480
481 /* Try and clear the end landscape */
482 ret = Command<Commands::LandscapeClear>::Do(flags, tile_end);
483 if (ret.Failed()) return ret;
484 cost.AddCost(ret.GetCost());
485
486 /* false - end tile slope check */
487 if (terraform_cost_south.Failed() || (terraform_cost_south.GetCost() != 0 && !allow_on_slopes)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
488 cost.AddCost(terraform_cost_south.GetCost());
489
490 /* Check for bridges above the bridge ramps. */
491 for (TileIndex tile : {tile_start, tile_end}) {
492 if (!IsBridgeAbove(tile)) continue;
493
494 if (direction == GetBridgeAxis(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
495
496 if (z_start + 1 == GetBridgeHeight(GetNorthernBridgeEnd(tile))) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
497 }
498
499 TileIndexDiff delta = TileOffsByAxis(direction);
500 for (TileIndex tile = tile_start + delta; tile != tile_end; tile += delta) {
501 if (GetTileMaxZ(tile) > z_start) return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_TERRAIN);
502
503 if (z_start >= (GetTileZ(tile) + _settings_game.construction.max_bridge_height)) {
504 /*
505 * Disallow too high bridges.
506 * Properly rendering a map where very high bridges (might) exist is expensive.
507 * See http://www.tt-forums.net/viewtopic.php?f=33&t=40844&start=980#p1131762
508 * for a detailed discussion. z_start here is one heightlevel below the bridge level.
509 */
510 return CommandCost(STR_ERROR_BRIDGE_TOO_HIGH_FOR_TERRAIN);
511 }
512
513 if (IsBridgeAbove(tile)) {
514 /* Disallow crossing bridges for the time being */
515 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
516 }
517
518 ret = CheckBuildAbove(tile, flags, direction, z_start + 1);
519 if (ret.Failed()) return ret;
520 cost.AddCost(ret.GetCost());
521
522 if (flags.Test(DoCommandFlag::Execute)) {
523 /* We do this here because when replacing a bridge with another
524 * type calling SetBridgeMiddle isn't needed. After all, the
525 * tile already has the has_bridge_above bits set. */
526 SetBridgeMiddle(tile, direction);
527 }
528 }
529
530 owner = company;
531 is_new_owner = true;
532 }
533
534 bool hasroad = road_rt != INVALID_ROADTYPE;
535 bool hastram = tram_rt != INVALID_ROADTYPE;
536 if (transport_type == TRANSPORT_ROAD) {
537 if (RoadTypeIsRoad(roadtype)) road_rt = roadtype;
538 if (RoadTypeIsTram(roadtype)) tram_rt = roadtype;
539 }
540
541 /* do the drill? */
542 if (flags.Test(DoCommandFlag::Execute)) {
543 DiagDirection dir = AxisToDiagDir(direction);
544
545 Company *c = Company::GetIfValid(company);
546 switch (transport_type) {
547 case TRANSPORT_RAIL:
548 /* Add to company infrastructure count if required. */
549 if (is_new_owner && c != nullptr) c->infrastructure.rail[railtype] += bridge_len * TUNNELBRIDGE_TRACKBIT_FACTOR;
550 MakeRailBridgeRamp(tile_start, owner, bridge_type, dir, railtype);
551 MakeRailBridgeRamp(tile_end, owner, bridge_type, ReverseDiagDir(dir), railtype);
552 SetTunnelBridgeReservation(tile_start, pbs_reservation);
553 SetTunnelBridgeReservation(tile_end, pbs_reservation);
554 break;
555
556 case TRANSPORT_ROAD: {
557 if (is_new_owner) {
558 /* Also give unowned present roadtypes to new owner */
559 if (hasroad && GetRoadOwner(tile_start, RTT_ROAD) == OWNER_NONE) hasroad = false;
560 if (hastram && GetRoadOwner(tile_start, RTT_TRAM) == OWNER_NONE) hastram = false;
561 }
562 if (c != nullptr) {
563 /* Add all new road types to the company infrastructure counter. */
564 if (!hasroad && road_rt != INVALID_ROADTYPE) {
565 /* A full diagonal road tile has two road bits. */
566 c->infrastructure.road[road_rt] += bridge_len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
567 }
568 if (!hastram && tram_rt != INVALID_ROADTYPE) {
569 /* A full diagonal road tile has two road bits. */
570 c->infrastructure.road[tram_rt] += bridge_len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
571 }
572 }
573 Owner owner_road = hasroad ? GetRoadOwner(tile_start, RTT_ROAD) : company;
574 Owner owner_tram = hastram ? GetRoadOwner(tile_start, RTT_TRAM) : company;
575 MakeRoadBridgeRamp(tile_start, owner, owner_road, owner_tram, bridge_type, dir, road_rt, tram_rt);
576 MakeRoadBridgeRamp(tile_end, owner, owner_road, owner_tram, bridge_type, ReverseDiagDir(dir), road_rt, tram_rt);
577 break;
578 }
579
580 case TRANSPORT_WATER:
581 if (is_new_owner && c != nullptr) c->infrastructure.water += bridge_len * TUNNELBRIDGE_TRACKBIT_FACTOR;
582 MakeAqueductBridgeRamp(tile_start, owner, dir);
583 MakeAqueductBridgeRamp(tile_end, owner, ReverseDiagDir(dir));
584 CheckForDockingTile(tile_start);
585 CheckForDockingTile(tile_end);
586 break;
587
588 default:
589 NOT_REACHED();
590 }
591
592 /* Mark all tiles dirty */
593 MarkBridgeDirty(tile_start, tile_end, AxisToDiagDir(direction), z_start);
595 }
596
597 if (flags.Test(DoCommandFlag::Execute) && transport_type == TRANSPORT_RAIL) {
598 Track track = AxisToTrack(direction);
599 AddSideToSignalBuffer(tile_start, INVALID_DIAGDIR, company);
600 YapfNotifyTrackLayoutChange(tile_start, track);
601 }
602
603 /* Human players that build bridges get a selection to choose from (DoCommandFlag::QueryCost)
604 * It's unnecessary to execute this command every time for every bridge.
605 * So it is done only for humans and cost is computed in bridge_gui.cpp.
606 * For (non-spectated) AI, Towns this has to be of course calculated. */
607 Company *c = Company::GetIfValid(company);
608 if (!flags.Test(DoCommandFlag::QueryCost) || (c != nullptr && c->is_ai && company != _local_company)) {
609 switch (transport_type) {
610 case TRANSPORT_ROAD:
611 if (road_rt != INVALID_ROADTYPE) {
612 cost.AddCost(bridge_len * 2 * RoadBuildCost(road_rt));
613 }
614 if (tram_rt != INVALID_ROADTYPE) {
615 cost.AddCost(bridge_len * 2 * RoadBuildCost(tram_rt));
616 }
617 break;
618
619 case TRANSPORT_RAIL: cost.AddCost(bridge_len * RailBuildCost(railtype)); break;
620 default: break;
621 }
622
623 if (c != nullptr) bridge_len = CalcBridgeLenCostFactor(bridge_len);
624
625 if (transport_type != TRANSPORT_WATER) {
626 cost.AddCost((int64_t)bridge_len * _price[Price::BuildBridge] * GetBridgeSpec(bridge_type)->price >> 8);
627 } else {
628 /* Aqueducts use a separate base cost. */
629 cost.AddCost((int64_t)bridge_len * _price[Price::BuildAqueduct]);
630 }
631
632 }
633
634 return cost;
635}
636
637
646CommandCost CmdBuildTunnel(DoCommandFlags flags, TileIndex start_tile, TransportType transport_type, uint8_t road_rail_type)
647{
648 CompanyID company = _current_company;
649
650 RailType railtype = INVALID_RAILTYPE;
651 RoadType roadtype = INVALID_ROADTYPE;
653 switch (transport_type) {
654 case TRANSPORT_RAIL:
655 railtype = (RailType)road_rail_type;
656 if (!ValParamRailType(railtype)) return CMD_ERROR;
657 break;
658
659 case TRANSPORT_ROAD:
660 roadtype = (RoadType)road_rail_type;
661 if (!ValParamRoadType(roadtype)) return CMD_ERROR;
662 break;
663
664 default: return CMD_ERROR;
665 }
666
667 if (company == OWNER_DEITY) {
668 if (transport_type != TRANSPORT_ROAD) return CMD_ERROR;
669 const Town *town = CalcClosestTownFromTile(start_tile);
670
671 company = OWNER_TOWN;
672
673 /* If we are not within a town, we are not owned by the town */
674 if (town == nullptr || DistanceSquare(start_tile, town->xy) > town->cache.squared_town_zone_radius[to_underlying(HouseZone::TownEdge)]) {
675 company = OWNER_NONE;
676 }
677 }
678
679 auto [start_tileh, start_z] = GetTileSlopeZ(start_tile);
680 DiagDirection direction = GetInclinedSlopeDirection(start_tileh);
681 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL);
682
683 if (HasTileWaterGround(start_tile)) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
684
685 CommandCost ret = Command<Commands::LandscapeClear>::Do(flags, start_tile);
686 if (ret.Failed()) return ret;
687
688 /* XXX - do NOT change 'ret' in the loop, as it is used as the price
689 * for the clearing of the entrance of the tunnel. Assigning it to
690 * cost before the loop will yield different costs depending on start-
691 * position, because of increased-cost-by-length: 'cost += cost >> 3' */
692
693 TileIndexDiff delta = TileOffsByDiagDir(direction);
694 DiagDirection tunnel_in_way_dir;
695 if (DiagDirToAxis(direction) == AXIS_Y) {
696 tunnel_in_way_dir = (TileX(start_tile) < (Map::MaxX() / 2)) ? DIAGDIR_SW : DIAGDIR_NE;
697 } else {
698 tunnel_in_way_dir = (TileY(start_tile) < (Map::MaxX() / 2)) ? DIAGDIR_SE : DIAGDIR_NW;
699 }
700
701 TileIndex end_tile = start_tile;
702
703 /* Tile shift coefficient. Will decrease for very long tunnels to avoid exponential growth of price*/
704 int tiles_coef = 3;
705 /* Number of tiles from start of tunnel */
706 int tiles = 0;
707 /* Number of tiles at which the cost increase coefficient per tile is halved */
708 int tiles_bump = 25;
709
711 Slope end_tileh;
712 int end_z;
713 for (;;) {
714 end_tile += delta;
715 if (!IsValidTile(end_tile)) return CommandCost(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER);
716 std::tie(end_tileh, end_z) = GetTileSlopeZ(end_tile);
717
718 if (start_z == end_z) break;
719
720 if (!_cheats.crossing_tunnels.value && IsTunnelInWayDir(end_tile, start_z, tunnel_in_way_dir)) {
721 return CommandCost(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY);
722 }
723
724 tiles++;
725 if (tiles == tiles_bump) {
726 tiles_coef++;
727 tiles_bump *= 2;
728 }
729
730 cost.AddCost(_price[Price::BuildTunnel]);
731 cost.AddCost(cost.GetCost() >> tiles_coef); // add a multiplier for longer tunnels
732 }
733
734 /* Add the cost of the entrance */
735 cost.AddCost(_price[Price::BuildTunnel]);
736 cost.AddCost(ret.GetCost());
737
738 /* if the command fails from here on we want the end tile to be highlighted */
739 _build_tunnel_endtile = end_tile;
740
741 if (tiles > _settings_game.construction.max_tunnel_length) return CommandCost(STR_ERROR_TUNNEL_TOO_LONG);
742
743 if (HasTileWaterGround(end_tile)) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
744
745 /* Clear the tile in any case */
746 ret = Command<Commands::LandscapeClear>::Do(flags, end_tile);
747 if (ret.Failed()) return CommandCost(STR_ERROR_UNABLE_TO_EXCAVATE_LAND);
748 cost.AddCost(ret.GetCost());
749
750 /* slope of end tile must be complementary to the slope of the start tile */
751 if (end_tileh != ComplementSlope(start_tileh)) {
752 /* Mark the tile as already cleared for the terraform command.
753 * Do this for all tiles (like trees), not only objects. */
754 ClearedObjectArea *coa = FindClearedObject(end_tile);
755 if (coa == nullptr) {
756 coa = &_cleared_object_areas.emplace_back(end_tile, TileArea(end_tile, 1, 1));
757 }
758
759 /* Hide the tile from the terraforming command */
760 TileIndex old_first_tile = coa->first_tile;
762
763 /* Commands::TerraformLand may append further items to _cleared_object_areas,
764 * however it will never erase or re-order existing items.
765 * _cleared_object_areas is a value-type self-resizing vector, therefore appending items
766 * may result in a backing-store re-allocation, which would invalidate the coa pointer.
767 * The index of the coa pointer into the _cleared_object_areas vector remains valid,
768 * and can be used safely after the Commands::TerraformLand operation.
769 * Deliberately clear the coa pointer to avoid leaving dangling pointers which could
770 * inadvertently be dereferenced.
771 */
772 ClearedObjectArea *begin = _cleared_object_areas.data();
773 assert(coa >= begin && coa < begin + _cleared_object_areas.size());
774 size_t coa_index = coa - begin;
775 assert(coa_index < UINT_MAX); // more than 2**32 cleared areas would be a bug in itself
776 coa = nullptr;
777
778 ret = std::get<0>(Command<Commands::TerraformLand>::Do(flags, end_tile, end_tileh & start_tileh, false));
779 _cleared_object_areas[(uint)coa_index].first_tile = old_first_tile;
780 if (ret.Failed()) return CommandCost(STR_ERROR_UNABLE_TO_EXCAVATE_LAND);
781 cost.AddCost(ret.GetCost());
782 }
783 cost.AddCost(_price[Price::BuildTunnel]);
784
785 /* Pay for the rail/road in the tunnel including entrances */
786 switch (transport_type) {
787 case TRANSPORT_ROAD: cost.AddCost((tiles + 2) * RoadBuildCost(roadtype) * 2); break;
788 case TRANSPORT_RAIL: cost.AddCost((tiles + 2) * RailBuildCost(railtype)); break;
789 default: NOT_REACHED();
790 }
791
792 if (flags.Test(DoCommandFlag::Execute)) {
793 Company *c = Company::GetIfValid(company);
794 uint num_pieces = (tiles + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
795 if (transport_type == TRANSPORT_RAIL) {
796 if (c != nullptr) c->infrastructure.rail[railtype] += num_pieces;
797 MakeRailTunnel(start_tile, company, direction, railtype);
798 MakeRailTunnel(end_tile, company, ReverseDiagDir(direction), railtype);
799 AddSideToSignalBuffer(start_tile, INVALID_DIAGDIR, company);
800 YapfNotifyTrackLayoutChange(start_tile, DiagDirToDiagTrack(direction));
801 } else {
802 if (c != nullptr) c->infrastructure.road[roadtype] += num_pieces * 2; // A full diagonal road has two road bits.
803 RoadType road_rt = RoadTypeIsRoad(roadtype) ? roadtype : INVALID_ROADTYPE;
804 RoadType tram_rt = RoadTypeIsTram(roadtype) ? roadtype : INVALID_ROADTYPE;
805 MakeRoadTunnel(start_tile, company, direction, road_rt, tram_rt);
806 MakeRoadTunnel(end_tile, company, ReverseDiagDir(direction), road_rt, tram_rt);
807 }
809 }
810
811 return cost;
812}
813
814
821{
822 /* Floods can remove anything as well as the scenario editor */
823 if (_current_company == OWNER_WATER || _game_mode == GM_EDITOR) return CommandCost();
824
825 switch (GetTunnelBridgeTransportType(tile)) {
826 case TRANSPORT_ROAD: {
827 RoadType road_rt = GetRoadTypeRoad(tile);
828 RoadType tram_rt = GetRoadTypeTram(tile);
829 Owner road_owner = _current_company;
830 Owner tram_owner = _current_company;
831
832 if (road_rt != INVALID_ROADTYPE) road_owner = GetRoadOwner(tile, RTT_ROAD);
833 if (tram_rt != INVALID_ROADTYPE) tram_owner = GetRoadOwner(tile, RTT_TRAM);
834
835 /* We can remove unowned road and if the town allows it */
837 /* Town does not allow */
838 return CheckTileOwnership(tile);
839 }
840 if (road_owner == OWNER_NONE || road_owner == OWNER_TOWN) road_owner = _current_company;
841 if (tram_owner == OWNER_NONE) tram_owner = _current_company;
842
843 CommandCost ret = CheckOwnership(road_owner, tile);
844 if (ret.Succeeded()) ret = CheckOwnership(tram_owner, tile);
845 return ret;
846 }
847
848 case TRANSPORT_RAIL:
849 return CheckOwnership(GetTileOwner(tile));
850
851 case TRANSPORT_WATER: {
852 /* Always allow to remove aqueducts without owner. */
853 Owner aqueduct_owner = GetTileOwner(tile);
854 if (aqueduct_owner == OWNER_NONE) aqueduct_owner = _current_company;
855 return CheckOwnership(aqueduct_owner);
856 }
857
858 default: NOT_REACHED();
859 }
860}
861
869{
871 if (ret.Failed()) return ret;
872
873 TileIndex endtile = GetOtherTunnelEnd(tile);
874
875 ret = TunnelBridgeIsFree(tile, endtile);
876 if (ret.Failed()) return ret;
877
878 _build_tunnel_endtile = endtile;
879
880 Town *t = nullptr;
881 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
882 t = ClosestTownFromTile(tile, UINT_MAX); // town penalty rating
883
884 /* Check if you are allowed to remove the tunnel owned by a town
885 * Removal depends on difficulty settings */
887 if (ret.Failed()) return ret;
888 }
889
890 /* checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
891 * you have a "Poor" (0) town rating */
892 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
894 }
895
897 uint len = GetTunnelBridgeLength(tile, endtile) + 2; // Don't forget the end tiles.
898
899 if (flags.Test(DoCommandFlag::Execute)) {
901 /* We first need to request values before calling DoClearSquare */
903 Track track = DiagDirToDiagTrack(dir);
904 Owner owner = GetTileOwner(tile);
905
906 Train *v = nullptr;
907 if (HasTunnelBridgeReservation(tile)) {
908 v = GetTrainForReservation(tile, track);
909 if (v != nullptr) FreeTrainTrackReservation(v);
910 }
911
912 if (Company::IsValidID(owner)) {
913 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
915 }
916
917 DoClearSquare(tile);
918 DoClearSquare(endtile);
919
920 /* cannot use INVALID_DIAGDIR for signal update because the tunnel doesn't exist anymore */
921 AddSideToSignalBuffer(tile, ReverseDiagDir(dir), owner);
922 AddSideToSignalBuffer(endtile, dir, owner);
923
924 YapfNotifyTrackLayoutChange(tile, track);
925 YapfNotifyTrackLayoutChange(endtile, track);
926
927 if (v != nullptr) TryPathReserve(v);
928 } else {
929 /* A full diagonal road tile has two road bits. */
930 UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
931 UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
932
933 DoClearSquare(tile);
934 DoClearSquare(endtile);
935 }
936 }
937
938 return CommandCost(EXPENSES_CONSTRUCTION, len * base_cost);
939}
940
941
949{
951 if (ret.Failed()) return ret;
952
953 TileIndex endtile = GetOtherBridgeEnd(tile);
954
955 ret = TunnelBridgeIsFree(tile, endtile);
956 if (ret.Failed()) return ret;
957
958 DiagDirection direction = GetTunnelBridgeDirection(tile);
959 TileIndexDiff delta = TileOffsByDiagDir(direction);
960
961 Town *t = nullptr;
962 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
963 t = ClosestTownFromTile(tile, UINT_MAX); // town penalty rating
964
965 /* Check if you are allowed to remove the bridge owned by a town
966 * Removal depends on difficulty settings */
968 if (ret.Failed()) return ret;
969 }
970
971 /* checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
972 * you have a "Poor" (0) town rating */
973 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
975 }
976
978 uint len = GetTunnelBridgeLength(tile, endtile) + 2; // Don't forget the end tiles.
979
980 if (flags.Test(DoCommandFlag::Execute)) {
981 /* read this value before actual removal of bridge */
982 bool rail = GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL;
983 Owner owner = GetTileOwner(tile);
984 int height = GetBridgeHeight(tile);
985 Train *v = nullptr;
986
987 if (rail && HasTunnelBridgeReservation(tile)) {
988 v = GetTrainForReservation(tile, DiagDirToDiagTrack(direction));
989 if (v != nullptr) FreeTrainTrackReservation(v);
990 }
991
992 /* Update company infrastructure counts. */
993 if (rail) {
994 if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
995 } else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) {
996 /* A full diagonal road tile has two road bits. */
997 UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
998 UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
999 } else { // Aqueduct
1000 if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
1001 }
1003
1004 DoClearSquare(tile);
1005 DoClearSquare(endtile);
1006
1007 for (TileIndex c = tile + delta; c != endtile; c += delta) {
1008 /* do not let trees appear from 'nowhere' after removing bridge */
1010 int minz = GetTileMaxZ(c) + 3;
1011 if (height < minz) SetRoadside(c, Roadside::Paved);
1012 }
1014 MarkTileDirtyByTile(c, height - TileHeight(c));
1015 }
1016
1017 if (rail) {
1018 /* cannot use INVALID_DIAGDIR for signal update because the bridge doesn't exist anymore */
1019 AddSideToSignalBuffer(tile, ReverseDiagDir(direction), owner);
1020 AddSideToSignalBuffer(endtile, direction, owner);
1021
1022 Track track = DiagDirToDiagTrack(direction);
1023 YapfNotifyTrackLayoutChange(tile, track);
1024 YapfNotifyTrackLayoutChange(endtile, track);
1025
1026 if (v != nullptr) TryPathReserve(v, true);
1027 }
1028 }
1029
1030 return CommandCost(EXPENSES_CONSTRUCTION, len * base_cost);
1031}
1032
1040{
1041 if (IsTunnel(tile)) {
1042 if (flags.Test(DoCommandFlag::Auto)) return CommandCost(STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST);
1043 return DoClearTunnel(tile, flags);
1044 } else { // IsBridge(tile)
1045 if (flags.Test(DoCommandFlag::Auto)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
1046 return DoClearBridge(tile, flags);
1047 }
1048}
1049
1060static inline void DrawPillar(const PalSpriteID &psid, int x, int y, int z, uint8_t w, uint8_t h, const SubSprite *subsprite)
1061{
1062 static const int PILLAR_Z_OFFSET = TILE_HEIGHT - BRIDGE_Z_START;
1063 AddSortableSpriteToDraw(psid.sprite, psid.pal, x, y, z, {{0, 0, -PILLAR_Z_OFFSET}, {w, h, BB_HEIGHT_UNDER_BRIDGE}, {0, 0, PILLAR_Z_OFFSET}}, IsTransparencySet(TO_BRIDGES), subsprite);
1064}
1065
1077static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID &psid, int x, int y, int w, int h)
1078{
1079 int cur_z;
1080 for (cur_z = z_top; cur_z >= z_bottom; cur_z -= TILE_HEIGHT) {
1081 DrawPillar(psid, x, y, cur_z, w, h, nullptr);
1082 }
1083 return cur_z;
1084}
1085
1097static void DrawBridgePillars(const PalSpriteID &psid, const TileInfo *ti, Axis axis, bool drawfarpillar, int x, int y, int z_bridge)
1098{
1099 static const int bounding_box_size[2] = {16, 2};
1100 static const int back_pillar_offset[2] = { 0, 9};
1101
1102 static const int INF = 1000;
1103 static const SubSprite half_pillar_sub_sprite[2][2] = {
1104 { { -14, -INF, INF, INF }, { -INF, -INF, -15, INF } }, // X axis, north and south
1105 { { -INF, -INF, 15, INF }, { 16, -INF, INF, INF } }, // Y axis, north and south
1106 };
1107
1108 if (psid.sprite == 0) return;
1109
1110 /* Determine ground height under pillars */
1111 DiagDirection south_dir = AxisToDiagDir(axis);
1112 int z_front_north = ti->z;
1113 int z_back_north = ti->z;
1114 int z_front_south = ti->z;
1115 int z_back_south = ti->z;
1116 GetSlopePixelZOnEdge(ti->tileh, south_dir, z_front_south, z_back_south);
1117 GetSlopePixelZOnEdge(ti->tileh, ReverseDiagDir(south_dir), z_front_north, z_back_north);
1118
1119 /* Shared height of pillars */
1120 int z_front = std::max(z_front_north, z_front_south);
1121 int z_back = std::max(z_back_north, z_back_south);
1122
1123 /* x and y size of bounding-box of pillars */
1124 int w = bounding_box_size[axis];
1125 int h = bounding_box_size[OtherAxis(axis)];
1126 /* sprite position of back facing pillar */
1127 int x_back = x - back_pillar_offset[axis];
1128 int y_back = y - back_pillar_offset[OtherAxis(axis)];
1129
1130 /* Draw front pillars */
1131 int bottom_z = DrawPillarColumn(z_front, z_bridge, psid, x, y, w, h);
1132 if (z_front_north < z_front) DrawPillar(psid, x, y, bottom_z, w, h, &half_pillar_sub_sprite[axis][0]);
1133 if (z_front_south < z_front) DrawPillar(psid, x, y, bottom_z, w, h, &half_pillar_sub_sprite[axis][1]);
1134
1135 /* Draw back pillars, skip top two parts, which are hidden by the bridge */
1136 int z_bridge_back = z_bridge - 2 * (int)TILE_HEIGHT;
1137 if (drawfarpillar && (z_back_north <= z_bridge_back || z_back_south <= z_bridge_back)) {
1138 bottom_z = DrawPillarColumn(z_back, z_bridge_back, psid, x_back, y_back, w, h);
1139 if (z_back_north < z_back) DrawPillar(psid, x_back, y_back, bottom_z, w, h, &half_pillar_sub_sprite[axis][0]);
1140 if (z_back_south < z_back) DrawPillar(psid, x_back, y_back, bottom_z, w, h, &half_pillar_sub_sprite[axis][1]);
1141 }
1142}
1143
1153static void GetBridgeRoadCatenary(const RoadTypeInfo *rti, TileIndex head_tile, int offset, bool head, SpriteID &spr_back, SpriteID &spr_front)
1154{
1155 static const SpriteID back_offsets[6] = { 95, 96, 99, 102, 100, 101 };
1156 static const SpriteID front_offsets[6] = { 97, 98, 103, 106, 104, 105 };
1157
1158 /* Simplified from DrawRoadTypeCatenary() to remove all the special cases required for regular ground road */
1159 spr_back = GetCustomRoadSprite(rti, head_tile, ROTSG_CATENARY_BACK, head ? TCX_NORMAL : TCX_ON_BRIDGE);
1160 spr_front = GetCustomRoadSprite(rti, head_tile, ROTSG_CATENARY_FRONT, head ? TCX_NORMAL : TCX_ON_BRIDGE);
1161 if (spr_back == 0 && spr_front == 0) {
1162 spr_back = SPR_TRAMWAY_BASE + back_offsets[offset];
1163 spr_front = SPR_TRAMWAY_BASE + front_offsets[offset];
1164 } else {
1165 if (spr_back != 0) spr_back += 23 + offset;
1166 if (spr_front != 0) spr_front += 23 + offset;
1167 }
1168}
1169
1180static void DrawBridgeRoadBits(TileIndex head_tile, int x, int y, int z, int offset, bool head, bool is_custom_layout)
1181{
1182 RoadType road_rt = GetRoadTypeRoad(head_tile);
1183 RoadType tram_rt = GetRoadTypeTram(head_tile);
1184 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
1185 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
1186
1187 SpriteID seq_back[4] = { 0 };
1188 bool trans_back[4] = { false };
1189 SpriteID seq_front[4] = { 0 };
1190 bool trans_front[4] = { false };
1191
1192 static const SpriteID overlay_offsets[6] = { 0, 1, 11, 12, 13, 14 };
1193 if (head || !IsInvisibilitySet(TO_BRIDGES)) {
1194 /* Road underlay takes precedence over tram */
1195 trans_back[0] = !head && IsTransparencySet(TO_BRIDGES);
1196 if (road_rti != nullptr) {
1197 if (road_rti->UsesOverlay()) {
1198 seq_back[0] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_BRIDGE, head ? TCX_NORMAL : TCX_ON_BRIDGE) + offset;
1199 } else if (is_custom_layout) {
1200 /* For custom layouts draw a custom bridge deck. */
1201 seq_back[0] = SPR_BRIDGE_DECKS_ROAD + offset;
1202 }
1203 } else if (tram_rti != nullptr) {
1204 if (tram_rti->UsesOverlay()) {
1205 seq_back[0] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_BRIDGE, head ? TCX_NORMAL : TCX_ON_BRIDGE) + offset;
1206 } else {
1207 seq_back[0] = SPR_TRAMWAY_BRIDGE + offset;
1208 }
1209 }
1210
1211 /* Draw road overlay */
1212 trans_back[1] = !head && IsTransparencySet(TO_BRIDGES);
1213 if (road_rti != nullptr) {
1214 if (road_rti->UsesOverlay()) {
1215 seq_back[1] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_OVERLAY, head ? TCX_NORMAL : TCX_ON_BRIDGE);
1216 if (seq_back[1] != 0) seq_back[1] += overlay_offsets[offset];
1217 }
1218 }
1219
1220 /* Draw tram overlay */
1221 trans_back[2] = !head && IsTransparencySet(TO_BRIDGES);
1222 if (tram_rti != nullptr) {
1223 if (tram_rti->UsesOverlay()) {
1224 seq_back[2] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_OVERLAY, head ? TCX_NORMAL : TCX_ON_BRIDGE);
1225 if (seq_back[2] != 0) seq_back[2] += overlay_offsets[offset];
1226 } else if (road_rti != nullptr) {
1227 seq_back[2] = SPR_TRAMWAY_OVERLAY + overlay_offsets[offset];
1228 }
1229 }
1230
1231 /* Road catenary takes precedence over tram */
1232 trans_back[3] = IsTransparencySet(TO_CATENARY);
1233 trans_front[0] = IsTransparencySet(TO_CATENARY);
1234 if (road_rti != nullptr && HasRoadCatenaryDrawn(road_rt)) {
1235 GetBridgeRoadCatenary(road_rti, head_tile, offset, head, seq_back[3], seq_front[0]);
1236 } else if (tram_rti != nullptr && HasRoadCatenaryDrawn(tram_rt)) {
1237 GetBridgeRoadCatenary(tram_rti, head_tile, offset, head, seq_back[3], seq_front[0]);
1238 }
1239 }
1240
1241 static constexpr SpriteBounds back_bounds[6] = {
1242 {{}, {0, TILE_SIZE, 40}, {}},
1243 {{}, {TILE_SIZE, 0, 40}, {}},
1244 {{}, {TILE_SIZE, 0, 40}, {}},
1245 {{}, {0, TILE_SIZE, 40}, {}},
1246 {{}, {TILE_SIZE, 0, 40}, {}},
1247 {{}, {0, TILE_SIZE, 40}, {}},
1248 };
1249
1250 /* The sprites under the vehicles are drawn as SpriteCombine. StartSpriteCombine() has already been called
1251 * The bounding boxes here are the same as for bridge front/roof */
1252 for (uint i = 0; i < lengthof(seq_back); ++i) {
1253 if (seq_back[i] != 0) {
1254 AddSortableSpriteToDraw(seq_back[i], PAL_NONE, x, y, z, back_bounds[offset], trans_back[i]);
1255 }
1256 }
1257
1258 /* Start a new SpriteCombine for the front part */
1261
1262 static constexpr SpriteBounds front_bounds[6] = {
1263 {{15, 0, 0}, {0, TILE_SIZE, 40}, {-15, 0, 0}},
1264 {{0, 15, 0}, {TILE_SIZE, 0, 40}, {0, -15, 0}},
1265 {{0, 15, 0}, {TILE_SIZE, 0, 40}, {0, -15, 0}},
1266 {{15, 0, 0}, {0, TILE_SIZE, 40}, {-15, 0, 0}},
1267 {{0, 15, 0}, {TILE_SIZE, 0, 40}, {0, -15, 0}},
1268 {{15, 0, 0}, {0, TILE_SIZE, 40}, {-15, 0, 0}},
1269 };
1270
1271 for (uint i = 0; i < lengthof(seq_front); ++i) {
1272 if (seq_front[i] != 0) {
1273 AddSortableSpriteToDraw(seq_front[i], PAL_NONE, x, y, z, front_bounds[offset], trans_front[i]);
1274 }
1275 }
1276}
1277
1292{
1293 TransportType transport_type = GetTunnelBridgeTransportType(ti->tile);
1294 DiagDirection tunnelbridge_direction = GetTunnelBridgeDirection(ti->tile);
1295
1296 if (IsTunnel(ti->tile)) {
1297 /* Front view of tunnel bounding boxes:
1298 *
1299 * 122223 <- BB_Z_SEPARATOR
1300 * 1 3
1301 * 1 3 1,3 = empty helper BB
1302 * 1 3 2 = SpriteCombine of tunnel-roof and catenary (tram & elrail)
1303 *
1304 */
1305
1306 /* Tunnel sprites are positioned at 15,15, but the bounding box covers most of the tile. */
1307 static constexpr SpriteBounds roof_bounds[DIAGDIR_END] = {
1308 {{0, 1, BB_Z_SEPARATOR}, {TILE_SIZE, TILE_SIZE - 1, 1}, {TILE_SIZE - 1, TILE_SIZE - 2, -BB_Z_SEPARATOR}}, // NE
1309 {{1, 0, BB_Z_SEPARATOR}, {TILE_SIZE - 1, TILE_SIZE, 1}, {TILE_SIZE - 2, TILE_SIZE - 1, -BB_Z_SEPARATOR}}, // SE
1310 {{0, 1, BB_Z_SEPARATOR}, {TILE_SIZE, TILE_SIZE - 1, 1}, {TILE_SIZE - 1, TILE_SIZE - 2, -BB_Z_SEPARATOR}}, // SW
1311 {{1, 0, BB_Z_SEPARATOR}, {TILE_SIZE - 1, TILE_SIZE, 1}, {TILE_SIZE - 2, TILE_SIZE - 1, -BB_Z_SEPARATOR}}, // NW
1312 };
1313
1314 /* Catenary sprites are positioned at 0,0, with the same bounding box as above. */
1315 static constexpr SpriteBounds catenary_bounds[DIAGDIR_END] = {
1316 {{0, 1, BB_Z_SEPARATOR}, {TILE_SIZE, TILE_SIZE - 1, 1}, {0, -1, -BB_Z_SEPARATOR}}, // NE
1317 {{1, 0, BB_Z_SEPARATOR}, {TILE_SIZE - 1, TILE_SIZE, 1}, {-1, 0, -BB_Z_SEPARATOR}}, // SE
1318 {{0, 1, BB_Z_SEPARATOR}, {TILE_SIZE, TILE_SIZE - 1, 1}, {0, -1, -BB_Z_SEPARATOR}}, // SW
1319 {{1, 0, BB_Z_SEPARATOR}, {TILE_SIZE - 1, TILE_SIZE, 1}, {-1, 0, -BB_Z_SEPARATOR}}, // NW
1320 };
1321
1322 static constexpr SpriteBounds rear_sep[DIAGDIR_END] = {
1323 {{}, {TILE_SIZE, 1, TILE_HEIGHT}, {}}, // NE
1324 {{}, {1, TILE_SIZE, TILE_HEIGHT}, {}}, // SE
1325 {{}, {TILE_SIZE, 1, TILE_HEIGHT}, {}}, // SW
1326 {{}, {1, TILE_SIZE, TILE_HEIGHT}, {}}, // NW
1327 };
1328
1329 static constexpr SpriteBounds front_sep[DIAGDIR_END] = {
1330 {{0, TILE_SIZE - 1, 0}, {TILE_SIZE, 1, TILE_HEIGHT}, {}}, // NE
1331 {{TILE_SIZE - 1, 0, 0}, {1, TILE_SIZE, TILE_HEIGHT}, {}}, // SE
1332 {{0, TILE_SIZE - 1, 0}, {TILE_SIZE, 1, TILE_HEIGHT}, {}}, // SW
1333 {{TILE_SIZE - 1, 0, 0}, {1, TILE_SIZE, TILE_HEIGHT}, {}}, // NW
1334 };
1335
1336 bool catenary = false;
1337
1338 SpriteID image;
1339 SpriteID railtype_overlay = 0;
1340 if (transport_type == TRANSPORT_RAIL) {
1341 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
1342 image = rti->base_sprites.tunnel;
1343 if (rti->UsesOverlay()) {
1344 /* Check if the railtype has custom tunnel portals. */
1345 railtype_overlay = GetCustomRailSprite(rti, ti->tile, RTSG_TUNNEL_PORTAL);
1346 if (railtype_overlay != 0) image = SPR_RAILTYPE_TUNNEL_BASE; // Draw blank grass tunnel base.
1347 }
1348 } else {
1349 image = SPR_TUNNEL_ENTRY_REAR_ROAD;
1350 }
1351
1352 if (HasTunnelBridgeSnowOrDesert(ti->tile)) image += railtype_overlay != 0 ? 8 : 32;
1353
1354 image += tunnelbridge_direction * 2;
1355 DrawGroundSprite(image, PAL_NONE);
1356
1357 if (transport_type == TRANSPORT_ROAD) {
1358 RoadType road_rt = GetRoadTypeRoad(ti->tile);
1359 RoadType tram_rt = GetRoadTypeTram(ti->tile);
1360 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
1361 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
1362 uint sprite_offset = DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? 1 : 0;
1363 bool draw_underlay = true;
1364
1365 /* Road underlay takes precedence over tram */
1366 if (road_rti != nullptr) {
1367 if (road_rti->UsesOverlay()) {
1368 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_TUNNEL);
1369 if (ground != 0) {
1370 DrawGroundSprite(ground + tunnelbridge_direction, PAL_NONE);
1371 draw_underlay = false;
1372 }
1373 }
1374 } else {
1375 if (tram_rti->UsesOverlay()) {
1376 SpriteID ground = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_TUNNEL);
1377 if (ground != 0) {
1378 DrawGroundSprite(ground + tunnelbridge_direction, PAL_NONE);
1379 draw_underlay = false;
1380 }
1381 }
1382 }
1383
1384 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset, draw_underlay);
1385
1386 /* Road catenary takes precedence over tram */
1387 SpriteID catenary_sprite_base = 0;
1388 if (road_rti != nullptr && HasRoadCatenaryDrawn(road_rt)) {
1389 catenary_sprite_base = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_CATENARY_FRONT);
1390 if (catenary_sprite_base == 0) {
1391 catenary_sprite_base = SPR_TRAMWAY_TUNNEL_WIRES;
1392 } else {
1393 catenary_sprite_base += 19;
1394 }
1395 } else if (tram_rti != nullptr && HasRoadCatenaryDrawn(tram_rt)) {
1396 catenary_sprite_base = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_CATENARY_FRONT);
1397 if (catenary_sprite_base == 0) {
1398 catenary_sprite_base = SPR_TRAMWAY_TUNNEL_WIRES;
1399 } else {
1400 catenary_sprite_base += 19;
1401 }
1402 }
1403
1404 if (catenary_sprite_base != 0) {
1405 catenary = true;
1407 AddSortableSpriteToDraw(catenary_sprite_base + tunnelbridge_direction, PAL_NONE, *ti, catenary_bounds[tunnelbridge_direction], IsTransparencySet(TO_CATENARY));
1408 }
1409 } else {
1410 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
1411 if (rti->UsesOverlay()) {
1412 SpriteID surface = GetCustomRailSprite(rti, ti->tile, RTSG_TUNNEL);
1413 if (surface != 0) DrawGroundSprite(surface + tunnelbridge_direction, PAL_NONE);
1414 }
1415
1416 /* PBS debugging, draw reserved tracks darker */
1417 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) {
1418 if (rti->UsesOverlay()) {
1419 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
1420 DrawGroundSprite(overlay + RTO_X + DiagDirToAxis(tunnelbridge_direction), PALETTE_CRASH);
1421 } else {
1423 }
1424 }
1425
1427 /* Maybe draw pylons on the entry side */
1428 DrawRailCatenary(ti);
1429
1430 catenary = true;
1432 /* Draw wire above the ramp */
1434 }
1435 }
1436
1437 if (railtype_overlay != 0 && !catenary) StartSpriteCombine();
1438
1439 AddSortableSpriteToDraw(image + 1, PAL_NONE, *ti, roof_bounds[tunnelbridge_direction], false);
1440 /* Draw railtype tunnel portal overlay if defined. */
1441 if (railtype_overlay != 0) AddSortableSpriteToDraw(railtype_overlay + tunnelbridge_direction, PAL_NONE, *ti, roof_bounds[tunnelbridge_direction], false);
1442
1443 if (catenary || railtype_overlay != 0) EndSpriteCombine();
1444
1445 /* Add helper BB for sprite sorting that separates the tunnel from things beside of it. */
1446 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, *ti, rear_sep[tunnelbridge_direction]);
1447 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, *ti, front_sep[tunnelbridge_direction]);
1448
1449 DrawBridgeMiddle(ti, BridgePillarFlag::EdgeNE + tunnelbridge_direction);
1450 } else { // IsBridge(ti->tile)
1451 DrawFoundation(ti, GetBridgeFoundation(ti->tileh, DiagDirToAxis(tunnelbridge_direction)));
1452 bool is_custom_layout = false; // Set if rail/road bridge uses a custom layout.
1453
1454 uint base_offset = GetBridgeRampDirectionBaseOffset(tunnelbridge_direction);
1455 std::span<const PalSpriteID> psid;
1456 if (transport_type != TRANSPORT_WATER) {
1457 BridgeType bridge_type = GetBridgeType(ti->tile);
1458 if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head
1459 base_offset += GetBridgeSpriteTableBaseOffset(transport_type, ti->tile);
1460 psid = GetBridgeSpriteTable(bridge_type, BRIDGE_PIECE_HEAD);
1461 is_custom_layout = BridgeHasCustomSpriteTable(bridge_type, BRIDGE_PIECE_HEAD);
1462 } else {
1463 psid = _aqueduct_sprite_table_heads;
1464 }
1465 psid = psid.subspan(base_offset, 1);
1466
1468 TileIndex next = ti->tile + TileOffsByDiagDir(tunnelbridge_direction);
1469 if (ti->tileh != SLOPE_FLAT && ti->z == 0 && HasTileWaterClass(next) && GetWaterClass(next) == WaterClass::Sea) {
1470 DrawShoreTile(ti->tileh);
1471 } else {
1472 DrawClearLandTile(ti, 3);
1473 }
1474 } else {
1475 DrawGroundSprite(SPR_FLAT_SNOW_DESERT_TILE + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
1476 }
1477
1478 /* draw ramp */
1479
1480 /* Draw Trambits and PBS Reservation as SpriteCombine */
1481 if (transport_type == TRANSPORT_ROAD || transport_type == TRANSPORT_RAIL) StartSpriteCombine();
1482
1483 /* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
1484 * it doesn't disappear behind it
1485 */
1486 /* Bridge heads are drawn solid no matter how invisibility/transparency is set */
1487 AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, *ti, {{}, {TILE_SIZE, TILE_SIZE, static_cast<uint8_t>(ti->tileh == SLOPE_FLAT ? 0 : TILE_HEIGHT)}, {}});
1488
1489 if (transport_type == TRANSPORT_ROAD) {
1490 uint offset = tunnelbridge_direction;
1491 int z = ti->z;
1492 if (ti->tileh != SLOPE_FLAT) {
1493 offset = (offset + 1) & 1;
1494 z += TILE_HEIGHT;
1495 } else {
1496 offset += 2;
1497 }
1498
1499 /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
1500 DrawBridgeRoadBits(ti->tile, ti->x, ti->y, z, offset, true, is_custom_layout);
1501
1503 } else if (transport_type == TRANSPORT_RAIL) {
1504 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
1505 if (is_custom_layout || rti->UsesOverlay()) {
1506 SpriteID surface = rti->UsesOverlay() ? GetCustomRailSprite(rti, ti->tile, RTSG_BRIDGE) : rti->base_sprites.bridge_deck;
1507 if (surface != 0) {
1508 if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
1509 AddSortableSpriteToDraw(surface + ((DiagDirToAxis(tunnelbridge_direction) == AXIS_X) ? RTBO_X : RTBO_Y), PAL_NONE, *ti, {{0, 0, TILE_HEIGHT}, {TILE_SIZE, TILE_SIZE, 0}, {}});
1510 } else {
1511 AddSortableSpriteToDraw(surface + RTBO_SLOPE + tunnelbridge_direction, PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT}, {}});
1512 }
1513 }
1514 }
1515
1516 /* PBS debugging, draw reserved tracks darker */
1517 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) {
1518 if (rti->UsesOverlay()) {
1519 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
1520 if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
1521 AddSortableSpriteToDraw(overlay + RTO_X + DiagDirToAxis(tunnelbridge_direction), PALETTE_CRASH, *ti, {{0, 0, TILE_HEIGHT}, {TILE_SIZE, TILE_SIZE, 0}, {}});
1522 } else {
1523 AddSortableSpriteToDraw(overlay + RTO_SLOPE_NE + tunnelbridge_direction, PALETTE_CRASH, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT}, {}});
1524 }
1525 } else {
1526 if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
1527 AddSortableSpriteToDraw(DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH, *ti, {{0, 0, TILE_HEIGHT}, {TILE_SIZE, TILE_SIZE, 0}, {}});
1528 } else {
1529 AddSortableSpriteToDraw(rti->base_sprites.single_sloped + tunnelbridge_direction, PALETTE_CRASH, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT}, {}});
1530 }
1531 }
1532 }
1533
1536 DrawRailCatenary(ti);
1537 }
1538 }
1539
1540 BridgePillarFlags blocked_pillars;
1541 if (DiagDirToAxis(tunnelbridge_direction) == AXIS_X) {
1543 } else {
1545 }
1546 DrawBridgeMiddle(ti, blocked_pillars);
1547 }
1548}
1549
1550
1569static BridgePieces CalcBridgePiece(uint north, uint south)
1570{
1571 if (north == 1) {
1572 return BRIDGE_PIECE_NORTH;
1573 } else if (south == 1) {
1574 return BRIDGE_PIECE_SOUTH;
1575 } else if (north < south) {
1576 return north & 1 ? BRIDGE_PIECE_INNER_SOUTH : BRIDGE_PIECE_INNER_NORTH;
1577 } else if (north > south) {
1578 return south & 1 ? BRIDGE_PIECE_INNER_NORTH : BRIDGE_PIECE_INNER_SOUTH;
1579 } else {
1580 return north & 1 ? BRIDGE_PIECE_MIDDLE_EVEN : BRIDGE_PIECE_MIDDLE_ODD;
1581 }
1582}
1583
1594{
1595 if (transport_type == TRANSPORT_WATER) return BRIDGEPILLARFLAGS_ALL_CORNERS;
1596
1597 const BridgeSpec *spec = GetBridgeSpec(type);
1599 BridgePieces piece = CalcBridgePiece(GetTunnelBridgeLength(tile, rampnorth) + 1, GetTunnelBridgeLength(tile, rampsouth) + 1);
1600 Axis axis = TileX(rampnorth) == TileX(rampsouth) ? AXIS_Y : AXIS_X;
1601
1602 return spec->pillar_flags[piece][axis == AXIS_Y ? 1 : 0];
1603 }
1604
1605 return BRIDGEPILLARFLAGS_ALL_CORNERS;
1606}
1607
1613void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
1614{
1615 /* Sectional view of bridge bounding boxes:
1616 *
1617 * 1 2 1,2 = SpriteCombine of Bridge front/(back&floor) and RoadCatenary
1618 * 1 2 3 = empty helper BB
1619 * 1 7 2 4,5 = pillars under higher bridges
1620 * 1 6 88888 6 2 6 = elrail-pylons
1621 * 1 6 88888 6 2 7 = elrail-wire
1622 * 1 6 88888 6 2 <- TILE_HEIGHT 8 = rail-vehicle on bridge
1623 * 3333333333333 <- BB_Z_SEPARATOR
1624 * <- unused
1625 * 4 5 <- BB_HEIGHT_UNDER_BRIDGE
1626 * 4 5
1627 * 4 5
1628 *
1629 */
1630
1631 if (!IsBridgeAbove(ti->tile)) return;
1632
1633 TileIndex rampnorth = GetNorthernBridgeEnd(ti->tile);
1634 TileIndex rampsouth = GetSouthernBridgeEnd(ti->tile);
1635 TransportType transport_type = GetTunnelBridgeTransportType(rampsouth);
1636 Axis axis = GetBridgeAxis(ti->tile);
1637 BridgePillarFlags pillars;
1638 bool is_custom_layout; // Set if rail/road bridge uses a custom layout.
1639
1640 uint base_offset = GetBridgeMiddleAxisBaseOffset(axis);
1641 std::span<const PalSpriteID> psid;
1642 bool drawfarpillar;
1643 if (transport_type != TRANSPORT_WATER) {
1644 BridgeType bridge_type = GetBridgeType(rampsouth);
1645 BridgePieces bridge_piece = CalcBridgePiece(GetTunnelBridgeLength(ti->tile, rampnorth) + 1, GetTunnelBridgeLength(ti->tile, rampsouth) + 1);
1646 drawfarpillar = !HasBit(GetBridgeSpec(bridge_type)->flags, 0);
1647 base_offset += GetBridgeSpriteTableBaseOffset(transport_type, rampsouth);
1648 psid = GetBridgeSpriteTable(bridge_type, bridge_piece);
1649 pillars = GetBridgeTilePillarFlags(ti->tile, rampnorth, rampsouth, bridge_type, transport_type);
1650 is_custom_layout = BridgeHasCustomSpriteTable(bridge_type, bridge_piece);
1651 } else {
1652 drawfarpillar = true;
1653 psid = _aqueduct_sprite_table_middle;
1654 pillars = BRIDGEPILLARFLAGS_ALL_CORNERS;
1655 }
1656 psid = psid.subspan(base_offset, 3);
1657
1658 int x = ti->x;
1659 int y = ti->y;
1660 uint bridge_z = GetBridgePixelHeight(rampsouth);
1661 int z = bridge_z - BRIDGE_Z_START;
1662
1663 /* Add a bounding box that separates the bridge from things below it. */
1664 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, x, y, bridge_z - TILE_HEIGHT + BB_Z_SEPARATOR, {{}, {TILE_SIZE, TILE_SIZE, 1}, {}});
1665
1666 /* Draw Trambits as SpriteCombine */
1667 if (transport_type == TRANSPORT_ROAD || transport_type == TRANSPORT_RAIL) StartSpriteCombine();
1668
1669 /* Draw floor and far part of bridge*/
1671 if (axis == AXIS_X) {
1672 AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES));
1673 } else {
1674 AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, x, y, z, {{0, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {0, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES));
1675 }
1676 }
1677
1678 if (transport_type == TRANSPORT_ROAD) {
1679 /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
1680 DrawBridgeRoadBits(rampsouth, x, y, bridge_z, axis ^ 1, false, is_custom_layout);
1681 } else if (transport_type == TRANSPORT_RAIL) {
1682 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(rampsouth));
1683 if ((is_custom_layout || rti->UsesOverlay()) && !IsInvisibilitySet(TO_BRIDGES)) {
1684 SpriteID surface = rti->UsesOverlay() ? GetCustomRailSprite(rti, rampsouth, RTSG_BRIDGE, TCX_ON_BRIDGE) : rti->base_sprites.bridge_deck;
1685 if (surface != 0) {
1686 AddSortableSpriteToDraw(surface + axis, PAL_NONE, x, y, bridge_z, {{}, {TILE_SIZE, TILE_SIZE, 0}, {}}, IsTransparencySet(TO_BRIDGES));
1687 }
1688 }
1689
1691 if (rti->UsesOverlay()) {
1692 SpriteID overlay = GetCustomRailSprite(rti, rampnorth, RTSG_OVERLAY, TCX_ON_BRIDGE);
1693 AddSortableSpriteToDraw(overlay + RTO_X + axis, PALETTE_CRASH, ti->x, ti->y, bridge_z, {{}, {TILE_SIZE, TILE_SIZE, 0}, {}}, IsTransparencySet(TO_BRIDGES));
1694 } else {
1696 }
1697 }
1698
1700
1701 if (HasRailCatenaryDrawn(GetRailType(rampsouth))) {
1703 }
1704 }
1705
1706 /* draw roof, the component of the bridge which is logically between the vehicle and the camera */
1708 if (axis == AXIS_X) {
1709 y += 12;
1710 if (psid[1].sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid[1].sprite, psid[1].pal, x, y, z, {{0, 3, BRIDGE_Z_START}, {TILE_SIZE, 1, 40}, {0, -3, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES));
1711 } else {
1712 x += 12;
1713 if (psid[1].sprite & SPRITE_MASK) AddSortableSpriteToDraw(psid[1].sprite, psid[1].pal, x, y, z, {{3, 0, BRIDGE_Z_START}, {1, TILE_SIZE, 40}, {-3, 0, -BRIDGE_Z_START}}, IsTransparencySet(TO_BRIDGES));
1714 }
1715 }
1716
1717 /* Draw TramFront as SpriteCombine */
1718 if (transport_type == TRANSPORT_ROAD) EndSpriteCombine();
1719
1720 /* Do not draw anything more if bridges are invisible */
1721 if (IsInvisibilitySet(TO_BRIDGES)) return;
1722
1723 if (blocked_pillars.Any(pillars)) return;
1724 DrawBridgePillars(psid[2], ti, axis, drawfarpillar, x, y, z);
1725}
1726
1727
1728static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y, bool ground_vehicle)
1729{
1730 auto [tileh, z] = GetTilePixelSlope(tile);
1731
1732 x &= 0xF;
1733 y &= 0xF;
1734
1735 if (IsTunnel(tile)) {
1736 /* In the tunnel entrance? */
1737 if (ground_vehicle) return z;
1738 } else { // IsBridge(tile)
1741
1742 /* On the bridge ramp? */
1743 if (ground_vehicle) {
1744 if (tileh != SLOPE_FLAT) return z + TILE_HEIGHT;
1745
1746 switch (dir) {
1747 default: NOT_REACHED();
1748 case DIAGDIR_NE: tileh = SLOPE_NE; break;
1749 case DIAGDIR_SE: tileh = SLOPE_SE; break;
1750 case DIAGDIR_SW: tileh = SLOPE_SW; break;
1751 case DIAGDIR_NW: tileh = SLOPE_NW; break;
1752 }
1753 }
1754 }
1755
1756 return z + GetPartialPixelZ(x, y, tileh);
1757}
1758
1759static Foundation GetFoundation_TunnelBridge(TileIndex tile, Slope tileh)
1760{
1762}
1763
1764static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc &td)
1765{
1767
1768 if (IsTunnel(tile)) {
1769 td.str = (tt == TRANSPORT_RAIL) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD;
1770 } else { // IsBridge(tile)
1771 td.str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt];
1772 }
1773 td.owner[0] = GetTileOwner(tile);
1774
1775 Owner road_owner = INVALID_OWNER;
1776 Owner tram_owner = INVALID_OWNER;
1777 RoadType road_rt = (tt == TRANSPORT_ROAD) ? GetRoadTypeRoad(tile) : INVALID_ROADTYPE;
1778 RoadType tram_rt = (tt == TRANSPORT_ROAD) ? GetRoadTypeTram(tile) : INVALID_ROADTYPE;
1779 if (road_rt != INVALID_ROADTYPE) {
1780 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
1781 td.roadtype = rti->strings.name;
1782 td.road_speed = rti->max_speed / 2;
1783 road_owner = GetRoadOwner(tile, RTT_ROAD);
1784 }
1785 if (tram_rt != INVALID_ROADTYPE) {
1786 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
1787 td.tramtype = rti->strings.name;
1788 td.tram_speed = rti->max_speed / 2;
1789 tram_owner = GetRoadOwner(tile, RTT_TRAM);
1790 }
1791
1792 /* Is there a mix of owners? */
1793 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
1794 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
1795 uint i = 1;
1796 if (road_owner != INVALID_OWNER) {
1797 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
1798 td.owner[i] = road_owner;
1799 i++;
1800 }
1801 if (tram_owner != INVALID_OWNER) {
1802 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
1803 td.owner[i] = tram_owner;
1804 }
1805 }
1806
1807 if (tt == TRANSPORT_RAIL) {
1808 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
1809 td.rail_speed = rti->max_speed;
1810 td.railtype = rti->strings.name;
1811
1812 if (!IsTunnel(tile)) {
1813 uint16_t spd = GetBridgeSpec(GetBridgeType(tile))->speed;
1814 /* rail speed special-cases 0 as unlimited, hides display of limit etc. */
1815 if (spd == UINT16_MAX) spd = 0;
1816 if (td.rail_speed == 0 || spd < td.rail_speed) {
1817 td.rail_speed = spd;
1818 }
1819 }
1820 } else if (tt == TRANSPORT_ROAD && !IsTunnel(tile)) {
1821 uint16_t spd = GetBridgeSpec(GetBridgeType(tile))->speed;
1822 /* road speed special-cases 0 as unlimited, hides display of limit etc. */
1823 if (spd == UINT16_MAX) spd = 0;
1824 if (road_rt != INVALID_ROADTYPE && (td.road_speed == 0 || spd < td.road_speed)) td.road_speed = spd;
1825 if (tram_rt != INVALID_ROADTYPE && (td.tram_speed == 0 || spd < td.tram_speed)) td.tram_speed = spd;
1826 }
1827}
1828
1829
1830static void TileLoop_TunnelBridge(TileIndex tile)
1831{
1832 bool snow_or_desert = HasTunnelBridgeSnowOrDesert(tile);
1834 case LandscapeType::Arctic: {
1835 /* As long as we do not have a snow density, we want to use the density
1836 * from the entry edge. For tunnels this is the lowest point for bridges the highest point.
1837 * (Independent of foundations) */
1838 int z = IsBridge(tile) ? GetTileMaxZ(tile) : GetTileZ(tile);
1839 if (snow_or_desert != (z > GetSnowLine())) {
1840 SetTunnelBridgeSnowOrDesert(tile, !snow_or_desert);
1841 MarkTileDirtyByTile(tile);
1842 }
1843 break;
1844 }
1845
1847 if (GetTropicZone(tile) == TROPICZONE_DESERT && !snow_or_desert) {
1848 SetTunnelBridgeSnowOrDesert(tile, true);
1849 MarkTileDirtyByTile(tile);
1850 }
1851 break;
1852
1853 default:
1854 break;
1855 }
1856}
1857
1858static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
1859{
1860 TransportType transport_type = GetTunnelBridgeTransportType(tile);
1861 if (transport_type != mode || (transport_type == TRANSPORT_ROAD && !HasTileRoadType(tile, (RoadTramType)sub_mode))) return 0;
1862
1864 if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0;
1866}
1867
1868static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner new_owner)
1869{
1870 TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
1871 /* Set number of pieces to zero if it's the southern tile as we
1872 * don't want to update the infrastructure counts twice. */
1873 uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
1874
1876
1877 if (tt == TRANSPORT_ROAD) {
1878 for (RoadTramType rtt : _roadtramtypes) {
1879 /* Update all roadtypes, no matter if they are present */
1880 if (GetRoadOwner(tile, rtt) == old_owner) {
1881 RoadType rt = GetRoadType(tile, rtt);
1882 if (rt != INVALID_ROADTYPE) {
1883 /* Update company infrastructure counts. A full diagonal road tile has two road bits.
1884 * No need to dirty windows here, we'll redraw the whole screen anyway. */
1885 Company::Get(old_owner)->infrastructure.road[rt] -= num_pieces * 2;
1886 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += num_pieces * 2;
1887 }
1888
1889 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
1890 }
1891 }
1892 }
1893
1894 if (!IsTileOwner(tile, old_owner)) return;
1895
1896 /* Update company infrastructure counts for rail and water as well.
1897 * No need to dirty windows here, we'll redraw the whole screen anyway. */
1898 Company *old = Company::Get(old_owner);
1899 if (tt == TRANSPORT_RAIL) {
1900 old->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1901 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.rail[GetRailType(tile)] += num_pieces;
1902 } else if (tt == TRANSPORT_WATER) {
1903 old->infrastructure.water -= num_pieces;
1904 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.water += num_pieces;
1905 }
1906
1907 if (new_owner != INVALID_OWNER) {
1908 SetTileOwner(tile, new_owner);
1909 } else {
1910 if (tt == TRANSPORT_RAIL) {
1911 /* Since all of our vehicles have been removed, it is safe to remove the rail
1912 * bridge / tunnel. */
1914 assert(ret.Succeeded());
1915 } else {
1916 /* In any other case, we can safely reassign the ownership to OWNER_NONE. */
1917 SetTileOwner(tile, OWNER_NONE);
1918 }
1919 }
1920}
1921
1934template <typename T>
1935static void PrepareToEnterBridge(T *gv)
1936{
1937 if (HasBit(gv->gv_flags, GVF_GOINGUP_BIT)) {
1938 gv->z_pos++;
1939 ClrBit(gv->gv_flags, GVF_GOINGUP_BIT);
1940 } else {
1941 ClrBit(gv->gv_flags, GVF_GOINGDOWN_BIT);
1942 }
1943}
1944
1950static const uint8_t TUNNEL_SOUND_FRAME = 1;
1951
1960extern const uint8_t _tunnel_visibility_frame[DIAGDIR_END] = {12, 8, 8, 12};
1961
1962static VehicleEnterTileStates VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y)
1963{
1964 int z = GetSlopePixelZ(x, y, true) - v->z_pos;
1965
1966 if (abs(z) > 2) return VehicleEnterTileState::CannotEnter;
1967 /* Direction into the wormhole */
1968 const DiagDirection dir = GetTunnelBridgeDirection(tile);
1969 /* Direction of the vehicle */
1970 const DiagDirection vdir = DirToDiagDir(v->direction);
1971 /* New position of the vehicle on the tile */
1972 uint8_t pos = (DiagDirToAxis(vdir) == AXIS_X ? x : y) & TILE_UNIT_MASK;
1973 /* Number of units moved by the vehicle since entering the tile */
1974 uint8_t frame = (vdir == DIAGDIR_NE || vdir == DIAGDIR_NW) ? TILE_SIZE - 1 - pos : pos;
1975
1976 if (IsTunnel(tile)) {
1977 if (v->type == VEH_TRAIN) {
1978 Train *t = Train::From(v);
1979
1980 if (t->track != TRACK_BIT_WORMHOLE && dir == vdir) {
1981 if (t->IsFrontEngine() && frame == TUNNEL_SOUND_FRAME) {
1982 if (!PlayVehicleSound(t, VSE_TUNNEL) && RailVehInfo(t->engine_type)->engclass == 0) {
1983 SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
1984 }
1985 return {};
1986 }
1987 if (frame == _tunnel_visibility_frame[dir]) {
1988 t->tile = tile;
1989 t->track = TRACK_BIT_WORMHOLE;
1992 }
1993 }
1994
1995 if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) {
1996 /* We're at the tunnel exit ?? */
1997 t->tile = tile;
1998 t->track = DiagDirToDiagTrackBits(vdir);
1999 assert(t->track);
2002 }
2003 } else if (v->type == VEH_ROAD) {
2005
2006 /* Enter tunnel? */
2007 if (rv->state != RVSB_WORMHOLE && dir == vdir) {
2008 if (frame == _tunnel_visibility_frame[dir]) {
2009 /* Frame should be equal to the next frame number in the RV's movement */
2010 assert(frame == rv->frame + 1);
2011 rv->tile = tile;
2012 rv->state = RVSB_WORMHOLE;
2015 } else {
2016 return {};
2017 }
2018 }
2019
2020 /* We're at the tunnel exit ?? */
2021 if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) {
2022 rv->tile = tile;
2023 rv->state = DiagDirToDiagTrackdir(vdir);
2024 rv->frame = frame;
2027 }
2028 }
2029 } else { // IsBridge(tile)
2030 if (v->type != VEH_SHIP) {
2031 /* modify speed of vehicle */
2032 uint16_t spd = GetBridgeSpec(GetBridgeType(tile))->speed;
2033
2034 if (v->type == VEH_ROAD) spd *= 2;
2035 Vehicle *first = v->First();
2036 first->cur_speed = std::min(first->cur_speed, spd);
2037 }
2038
2039 if (vdir == dir) {
2040 /* Vehicle enters bridge at the last frame inside this tile. */
2041 if (frame != TILE_SIZE - 1) return {};
2042 switch (v->type) {
2043 case VEH_TRAIN: {
2044 Train *t = Train::From(v);
2045 t->track = TRACK_BIT_WORMHOLE;
2047 break;
2048 }
2049
2050 case VEH_ROAD: {
2052 rv->state = RVSB_WORMHOLE;
2054 break;
2055 }
2056
2057 case VEH_SHIP:
2059 break;
2060
2061 default: NOT_REACHED();
2062 }
2064 } else if (vdir == ReverseDiagDir(dir)) {
2065 v->tile = tile;
2066 switch (v->type) {
2067 case VEH_TRAIN: {
2068 Train *t = Train::From(v);
2069 if (t->track == TRACK_BIT_WORMHOLE) {
2070 t->track = DiagDirToDiagTrackBits(vdir);
2072 }
2073 break;
2074 }
2075
2076 case VEH_ROAD: {
2078 if (rv->state == RVSB_WORMHOLE) {
2079 rv->state = DiagDirToDiagTrackdir(vdir);
2080 rv->frame = 0;
2082 }
2083 break;
2084 }
2085
2086 case VEH_SHIP: {
2087 Ship *ship = Ship::From(v);
2088 if (ship->state == TRACK_BIT_WORMHOLE) {
2089 ship->state = DiagDirToDiagTrackBits(vdir);
2091 }
2092 break;
2093 }
2094
2095 default: NOT_REACHED();
2096 }
2097 }
2098 }
2099 return {};
2100}
2101
2102static CommandCost TerraformTile_TunnelBridge(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
2103{
2105 DiagDirection direction = GetTunnelBridgeDirection(tile);
2106 Axis axis = DiagDirToAxis(direction);
2107 CommandCost res;
2108 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
2109
2110 /* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
2111 if ((direction == DIAGDIR_NW) || (direction == DIAGDIR_NE)) {
2112 CheckBridgeSlope(BRIDGE_PIECE_SOUTH, axis, tileh_old, z_old);
2113 res = CheckBridgeSlope(BRIDGE_PIECE_SOUTH, axis, tileh_new, z_new);
2114 } else {
2115 CheckBridgeSlope(BRIDGE_PIECE_NORTH, axis, tileh_old, z_old);
2116 res = CheckBridgeSlope(BRIDGE_PIECE_NORTH, axis, tileh_new, z_new);
2117 }
2118
2119 /* Surface slope is valid and remains unchanged? */
2120 if (res.Succeeded() && (z_old == z_new) && (tileh_old == tileh_new)) return CommandCost(EXPENSES_CONSTRUCTION, _price[Price::BuildFoundation]);
2121 }
2122
2123 return Command<Commands::LandscapeClear>::Do(flags, tile);
2124}
2125
2126static CommandCost CheckBuildAbove_TunnelBridge(TileIndex tile, DoCommandFlags flags, Axis axis, int height)
2127{
2128 if (IsTunnel(tile)) return CommandCost();
2129
2130 if (axis != DiagDirToAxis(GetTunnelBridgeDirection(tile)) && height > GetBridgeHeight(tile)) {
2131 return CommandCost();
2132 }
2133
2134 return Command<Commands::LandscapeClear>::Do(flags, tile);
2135}
2136
2137extern const TileTypeProcs _tile_type_tunnelbridge_procs = {
2138 DrawTile_TunnelBridge, // draw_tile_proc
2139 GetSlopePixelZ_TunnelBridge, // get_slope_z_proc
2140 ClearTile_TunnelBridge, // clear_tile_proc
2141 nullptr, // add_accepted_cargo_proc
2142 GetTileDesc_TunnelBridge, // get_tile_desc_proc
2143 GetTileTrackStatus_TunnelBridge, // get_tile_track_status_proc
2144 nullptr, // click_tile_proc
2145 nullptr, // animate_tile_proc
2146 TileLoop_TunnelBridge, // tile_loop_proc
2147 ChangeTileOwner_TunnelBridge, // change_tile_owner_proc
2148 nullptr, // add_produced_cargo_proc
2149 VehicleEnter_TunnelBridge, // vehicle_enter_tile_proc
2150 GetFoundation_TunnelBridge, // get_foundation_proc
2151 TerraformTile_TunnelBridge, // terraform_tile_proc
2152 CheckBuildAbove_TunnelBridge, // check_build_above_proc
2153};
Functions related to autoslope.
bool AutoslopeEnabled()
Tests if autoslope is enabled for _current_company.
Definition autoslope.h:65
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
const BridgeSpec * GetBridgeSpec(BridgeType i)
Get the specification of a bridge type.
Definition bridge.h:60
static const uint MAX_BRIDGES
Maximal number of available bridge specs.
Definition bridge.h:18
This file contains all the sprites for bridges.
TileIndex GetSouthernBridgeEnd(TileIndex t)
Finds the southern end of a bridge starting at a middle tile.
TileIndex GetOtherBridgeEnd(TileIndex tile)
Starting at one bridge end finds the other bridge end.
TileIndex GetNorthernBridgeEnd(TileIndex t)
Finds the northern end of a bridge starting at a middle tile.
int GetBridgeHeight(TileIndex t)
Get the height ('z') of a bridge.
void SetBridgeMiddle(Tile t, Axis a)
Set that there is a bridge over the given axis.
Definition bridge_map.h:114
void MakeAqueductBridgeRamp(Tile t, Owner o, DiagDirection d)
Make a bridge ramp for aqueducts.
Definition bridge_map.h:182
bool IsBridgeTile(Tile t)
checks if there is a bridge on this tile
Definition bridge_map.h:35
void MakeRailBridgeRamp(Tile t, Owner o, BridgeType bridgetype, DiagDirection d, RailType rt)
Make a bridge ramp for rails.
Definition bridge_map.h:170
int GetBridgePixelHeight(TileIndex tile)
Get the height ('z') of a bridge in pixels.
Definition bridge_map.h:84
void ClearBridgeMiddle(Tile t)
Removes bridges from the given, that is bridges along the X and Y axis.
Definition bridge_map.h:103
void MakeRoadBridgeRamp(Tile t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadType road_rt, RoadType tram_rt)
Make a bridge ramp for roads.
Definition bridge_map.h:154
BridgeType GetBridgeType(Tile t)
Determines the type of bridge on a tile.
Definition bridge_map.h:56
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
Axis GetBridgeAxis(Tile t)
Get the axis of the bridge that goes over the tile.
Definition bridge_map.h:68
bool IsBridge(Tile t)
Checks if this is a bridge, instead of a tunnel.
Definition bridge_map.h:24
BridgePieces
This enum is related to the definition of bridge pieces, which is used to determine the proper sprite...
Definition bridge_type.h:22
@ EdgeNE
Northeast edge is obstructed.
@ EdgeSW
Southwest edge is obstructed.
@ EdgeNW
Northwest edge is obstructed.
@ EdgeSE
Southeast edge is obstructed.
uint BridgeType
Bridge spec number.
Definition bridge_type.h:15
Cheats _cheats
All the cheats.
Definition cheat.cpp:16
Types related to cheating.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
Enum-as-bit-set wrapper.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:115
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:125
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:220
struct RailTypeInfo::@18 base_sprites
Struct containing the main sprites.
SpriteID bridge_deck
bridge deck sprites base
Definition rail.h:134
SpriteID single_y
single piece of rail in Y direction, without ground
Definition rail.h:126
StringID name
Name of this rail type.
Definition rail.h:165
struct RailTypeInfo::@21 strings
Strings associated with the rail type.
uint8_t bridge_offset
Bridge offset.
Definition rail.h:185
SpriteID tunnel
tunnel sprites base
Definition rail.h:133
SpriteID single_sloped
single piece of rail for slopes
Definition rail.h:131
uint16_t max_speed
Maximum speed for vehicles travelling on this road type.
Definition road.h:116
StringID name
Name of this rail type.
Definition road.h:77
struct RoadTypeInfo::@24 strings
Strings associated with the rail type.
static Year year
Current year, starting at 0.
Functions related to clear (TileType::Clear) land.
CommandCost CommandCostWithParam(StringID str, uint64_t value)
Return an error status, with string and parameter.
Definition command.cpp:417
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Auto
don't allow building on structures
@ QueryCost
query cost only, don't build.
@ Execute
execute the given command
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
Definition of stuff that is very close to a company, like the company struct itself.
CommandCost CheckTileOwnership(TileIndex tile)
Check whether the current owner owns the stuff on the given tile.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
void DirtyCompanyInfrastructureWindows(CompanyID company)
Redraw all windows with company infrastructure counts.
GUI Functions related to companies.
static constexpr Owner OWNER_DEITY
The object is owned by a superuser / goal script.
static constexpr Owner OWNER_TOWN
A town owns the tile, or a town is expanding.
static constexpr Owner OWNER_NONE
The tile has no ownership.
static constexpr Owner INVALID_OWNER
An invalid owner.
static constexpr Owner OWNER_WATER
The tile/execution is done by "water".
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
DiagDirection AxisToDiagDir(Axis a)
Converts an Axis to a DiagDirection.
Axis OtherAxis(Axis a)
Select the other axis as provided.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
Axis
Allow incrementing of DiagDirDiff variables.
@ AXIS_X
The X axis.
@ AXIS_Y
The y axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_END
Used for iterations.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
@ DIAGDIR_SW
Southwest.
@ EXPENSES_CONSTRUCTION
Construction costs.
static const uint TUNNELBRIDGE_TRACKBIT_FACTOR
Multiplier for how many regular track bits a tunnel/bridge counts.
Price
Enumeration of all base prices for use with Prices.
@ ClearTunnel
Price for destroying tunnels.
@ BuildFoundation
Price for building foundation under other constructions e.g. roads, rails, depots,...
@ ClearAqueduct
Price for destroying aqueducts.
@ BuildAqueduct
Price for building new aqueducts.
@ BuildTunnel
Price for building tunnels.
@ BuildBridge
Price for building bridges.
@ ClearBridge
Price for destroying bridges.
void DrawRailCatenaryOnBridge(const TileInfo *ti)
Draws wires on a tunnel tile.
Definition elrail.cpp:490
void DrawRailCatenaryOnTunnel(const TileInfo *ti)
Draws wires on a tunnel tile.
Definition elrail.cpp:249
void DrawRailCatenary(const TileInfo *ti)
Draws overhead wires and pylons for electric railways.
Definition elrail.cpp:550
Header file for electrified rail specific functions.
bool HasRailCatenaryDrawn(RailType rt)
Test if we should draw rail catenary.
Definition elrail_func.h:30
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23)
Definition enum_type.hpp:17
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
@ GVF_GOINGDOWN_BIT
Vehicle is currently going downhill. (Cached track information for acceleration)
@ GVF_GOINGUP_BIT
Vehicle is currently going uphill. (Cached track information for acceleration)
uint8_t GetSnowLine()
Get the current snow line, either variable or static.
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
@ TownEdge
Edge of the town; roads without pavement.
uint GetPartialPixelZ(int x, int y, Slope corners)
Determines height at given coordinate of a slope.
const EnumClassIndexContainer< std::array< const TileTypeProcs *, to_underlying(TileType::MaxSize)>, TileType > _tile_type_procs
Tile callback functions for each type of tile.
Definition landscape.cpp:69
void DrawFoundation(TileInfo *ti, Foundation f)
Draw foundation f at tile ti.
void GetSlopePixelZOnEdge(Slope tileh, DiagDirection edge, int &z1, int &z2)
Determine the Z height of the corners of a specific tile edge.
uint ApplyFoundationToSlope(Foundation f, Slope &s)
Applies a foundation to a slope.
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
uint ApplyPixelFoundationToSlope(Foundation f, Slope &s)
Applies a foundation to a slope.
Definition landscape.h:128
Command definitions related to landscape (slopes etc.).
@ Arctic
Landscape with snow levels.
@ Tropic
Landscape with distinct rainforests and deserts,.
uint DistanceSquare(TileIndex t0, TileIndex t1)
Gets the 'Square' distance between the two given tiles.
Definition map.cpp:175
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:401
TileIndexDiff TileOffsByAxis(Axis axis)
Convert an Axis to a TileIndexDiff.
Definition map_func.h:567
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:437
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:427
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:582
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
@ TCX_ON_BRIDGE
Querying information about stuff on the bridge (via some bridgehead).
@ TCX_NORMAL
Nothing special.
SpriteID GetCustomRailSprite(const RailTypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of rail types.
SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of road types.
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event, bool force)
Checks whether a NewGRF wants to play a different vehicle sound effect.
Functions related to NewGRF provided sounds.
@ VSE_TUNNEL
Train entering a tunnel.
Base for all objects.
ClearedObjectArea * FindClearedObject(TileIndex tile)
Find the entry in _cleared_object_areas which occupies a certain tile.
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:331
PBS support routines.
bool ValParamRailType(const RailType rail)
Validate functions for rail building.
Definition rail.cpp:90
@ RTBO_Y
Piece of rail in Y direction.
Definition rail.h:85
@ RTBO_X
Piece of rail in X direction.
Definition rail.h:84
@ RTBO_SLOPE
Sloped rail pieces, in order NE, SE, SW, NW.
Definition rail.h:86
Money RailClearCost(RailType railtype)
Returns the 'cost' of clearing the specified railtype.
Definition rail.h:439
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:428
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:300
@ RTSG_TUNNEL
Main group of ground images for snow or desert.
Definition rail.h:44
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:42
@ RTSG_BRIDGE
Bridge surface images.
Definition rail.h:47
@ RTSG_TUNNEL_PORTAL
Tunnel portal overlay.
Definition rail.h:51
@ RTO_SLOPE_NE
Piece of rail on slope with north-east raised.
Definition rail.h:68
@ RTO_X
Piece of rail in X direction.
Definition rail.h:62
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
bool ValParamRoadType(RoadType roadtype)
Validate functions for rail building.
Definition road.cpp:164
Money RoadClearCost(RoadType roadtype)
Returns the cost of clearing the specified roadtype.
Definition road.h:251
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:215
@ ROTSG_OVERLAY
Optional: Images for overlaying track.
Definition road.h:38
@ ROTSG_CATENARY_BACK
Optional: Catenary back.
Definition road.h:42
@ ROTSG_BRIDGE
Required: Bridge surface images.
Definition road.h:43
@ ROTSG_CATENARY_FRONT
Optional: Catenary front.
Definition road.h:41
@ ROTSG_TUNNEL
Optional: Ground images for tunnels.
Definition road.h:40
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:240
void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count)
Update road infrastructure counts for a company.
Definition road_cmd.cpp:180
void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, uint road_offset, uint tram_offset, bool draw_underlay)
Draw road underlay and overlay sprites.
bool HasRoadCatenaryDrawn(RoadType roadtype)
Test if we should draw road catenary.
Definition road_func.h:145
void SetRoadOwner(Tile t, RoadTramType rtt, Owner o)
Set the owner of a specific road type.
Definition road_map.h:235
bool HasTileRoadType(Tile t, RoadTramType rtt)
Check if a tile has a road or a tram road type.
Definition road_map.h:195
Roadside GetRoadside(Tile tile)
Get the decorations of a road.
Definition road_map.h:473
static bool IsNormalRoadTile(Tile t)
Return whether a tile is a normal road tile.
Definition road_map.h:58
Owner GetRoadOwner(Tile t, RoadTramType rtt)
Get the owner of a specific road type.
Definition road_map.h:218
void SetRoadside(Tile tile, Roadside s)
Set the decorations of a road.
Definition road_map.h:483
@ Paved
Road with paved sidewalks.
@ Trees
Road with trees on paved sidewalks.
RoadTramType
The different types of road type.
Definition road_type.h:37
@ RTT_ROAD
Road road type.
Definition road_type.h:38
@ RTT_TRAM
Tram road type.
Definition road_type.h:39
RoadType
The different roadtypes we support.
Definition road_type.h:23
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:28
Road vehicle states.
@ RVSB_WORMHOLE
The vehicle is in a tunnel and/or bridge.
Definition roadveh.h:39
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Base for ships.
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:621
uint SlopeToSpriteOffset(Slope s)
Returns the Sprite offset for a given Slope.
Definition slope_func.h:415
bool HasSlopeHighestCorner(Slope s)
Tests if a slope has a highest corner (i.e.
Definition slope_func.h:113
Foundation FlatteningFoundation(Slope s)
Returns the foundation needed to flatten a slope.
Definition slope_func.h:369
DiagDirection GetInclinedSlopeDirection(Slope s)
Returns the direction of an inclined slope.
Definition slope_func.h:239
Foundation InclinedFoundation(Axis axis)
Returns the along a specific axis inclined foundation.
Definition slope_func.h:380
Slope ComplementSlope(Slope s)
Return the complement of a slope.
Definition slope_func.h:76
Slope
Enumeration for the slope-type.
Definition slope_type.h:47
@ SLOPE_SW
south and west corner are raised
Definition slope_type.h:55
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:48
@ SLOPE_NE
north and east corner are raised
Definition slope_type.h:57
@ SLOPE_SE
south and east corner are raised
Definition slope_type.h:56
@ SLOPE_NW
north and west corner are raised
Definition slope_type.h:54
Foundation
Enumeration for Foundations.
Definition slope_type.h:92
@ FOUNDATION_NONE
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:93
Functions related to sound.
@ SND_05_TRAIN_THROUGH_TUNNEL
3 == 0x03 Train enters tunnel: steam engine
Definition sound_type.h:51
static constexpr uint32_t SPRITE_MASK
The mask to for the main sprite.
Definition sprites.h:1570
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
static const SpriteID SPR_RAILTYPE_TUNNEL_BASE
Tunnel sprites with grass only for custom railtype tunnel.
Definition sprites.h:301
static const SpriteID SPR_TRAMWAY_BASE
Tramway sprites.
Definition sprites.h:274
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
Functions related to OTTD's strings.
VehicleType type
Type of vehicle.
Struct containing information about a single bridge type.
Definition bridge.h:27
ControlFlags ctrl_flags
control flags
Definition bridge.h:46
@ InvalidPillarFlags
Bridge pillar flags are not valid, i.e. only the tile layout has been modified.
uint8_t min_length
the minimum length (not counting start and end tile)
Definition bridge.h:36
BridgeMiddlePillarFlags pillar_flags
bridge pillar flags.
Definition bridge.h:47
std::vector< std::vector< PalSpriteID > > sprite_table
table of sprites for drawing the bridge
Definition bridge.h:44
TimerGameCalendar::Year avail_year
the year where it becomes available
Definition bridge.h:35
uint16_t speed
maximum travel speed (1 unit = 1/1.6 mph = 1 km-ish/h)
Definition bridge.h:39
uint16_t max_length
the maximum length (not counting start and end tile)
Definition bridge.h:37
bool value
tells if the bool cheat is active or not
Definition cheat_type.h:18
Cheat crossing_tunnels
allow tunnels that cross each other
Definition cheat_type.h:30
Cheat magic_bulldozer
dynamite industries, objects
Definition cheat_type.h:27
Keeps track of removed objects during execution/testruns of commands.
Definition object_base.h:85
TileIndex first_tile
The first tile being cleared, which then causes the whole object to be cleared.
Definition object_base.h:86
GUISettings gui
settings related to the GUI
std::array< uint32_t, ROADTYPE_END > road
Count of company owned track bits for each road type.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
uint32_t water
Count of company owned track bits for canals.
bool is_ai
If true, the company is (also) controlled by the computer (a NoAI program).
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
uint8_t max_bridge_height
maximum height of bridges
bool build_on_slopes
allow building on slopes
bool extra_dynamite
extra dynamite
uint16_t max_bridge_length
maximum length of bridges
uint16_t max_tunnel_length
maximum length of tunnels
T x
X coordinate.
T y
Y coordinate.
T z
Z coordinate.
bool show_track_reservation
highlight reserved tracks.
LandscapeType landscape
the landscape we're currently in
ConstructionSettings construction
construction of things in-game
GameCreationSettings game_creation
settings used during the creation of a game (map)
static uint MaxX()
Gets the maximum X coordinate within the map, including TileType::Void.
Definition map_func.h:299
Combination of a palette sprite and a 'real' sprite.
Definition gfx_type.h:22
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed)
Definition gfx_type.h:24
static Titem * Get(auto index)
Returns Titem with given index.
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
const Tindex index
Index of this pool item.
static Titem * GetIfValid(auto index)
Returns Titem with given index.
EngineClass engclass
Class of engine for this vehicle.
Definition engine_type.h:86
Buses, trucks and trams belong to this class.
Definition roadveh.h:98
uint8_t state
Definition roadveh.h:100
All ships have this type.
Definition ship.h:32
TrackBits state
The "track" the ship is following.
Definition ship.h:34
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
Used to only draw a part of the sprite.
Definition gfx_type.h:278
Tile description for the 'land area information' tool.
Definition tile_cmd.h:38
uint16_t rail_speed
Speed limit of rail (bridges and track)
Definition tile_cmd.h:51
StringID str
Description of the tile.
Definition tile_cmd.h:39
std::array< Owner, 4 > owner
Name of the owner(s)
Definition tile_cmd.h:41
uint16_t tram_speed
Speed limit of tram (bridges and track)
Definition tile_cmd.h:55
StringID roadtype
Type of road on the tile.
Definition tile_cmd.h:52
StringID tramtype
Type of tram on the tile.
Definition tile_cmd.h:54
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:50
uint16_t road_speed
Speed limit of road (bridges and track)
Definition tile_cmd.h:53
std::array< StringID, 4 > owner_type
Type of each owner.
Definition tile_cmd.h:42
Tile information, used while rendering the tile.
Definition tile_cmd.h:32
Slope tileh
Slope of the tile.
Definition tile_cmd.h:33
TileIndex tile
Tile index.
Definition tile_cmd.h:34
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:154
std::array< uint32_t, NUM_HOUSE_ZONES > squared_town_zone_radius
UpdateTownRadius updates this given the house count.
Definition town.h:56
Town data structure.
Definition town.h:63
TileIndex xy
town center tile
Definition town.h:64
TownCache cache
Container for all cacheable data.
Definition town.h:66
'Train' is either a loco or a wagon.
Definition train.h:91
Vehicle data structure.
EngineID engine_type
The type of engine used for this vehicle.
int32_t z_pos
z coordinate.
Direction direction
facing
VehStates vehstatus
Status.
Vehicle * First() const
Get the first vehicle of this vehicle chain.
uint16_t cur_speed
current speed
bool IsFrontEngine() const
Check if the vehicle is a front engine.
TileIndex tile
Current tile index.
Command definitions related to terraforming.
@ CannotEnter
The vehicle cannot enter the tile.
@ EnteredWormhole
The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/...
std::tuple< Slope, int > GetTileSlopeZ(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.cpp:55
int GetTileMaxZ(TileIndex t)
Get top height of the tile inside the map.
Definition tile_map.cpp:136
int GetTileZ(TileIndex tile)
Get bottom height of the tile.
Definition tile_map.cpp:116
static uint TileHeight(Tile tile)
Returns the height of a tile.
Definition tile_map.h:29
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
void SetTileOwner(Tile tile, Owner owner)
Sets the owner of a tile.
Definition tile_map.h:198
std::tuple< Slope, int > GetTilePixelSlope(TileIndex tile)
Return the slope of a given tile.
Definition tile_map.h:289
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
TropicZone GetTropicZone(Tile tile)
Get the tropic zone.
Definition tile_map.h:238
static TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
static constexpr uint TILE_UNIT_MASK
For masking in/out the inner-tile world coordinate units.
Definition tile_type.h:16
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:83
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static constexpr uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
Definition of the game-calendar-timer.
Base of the town class.
void ChangeTownRating(Town *t, int add, int max, DoCommandFlags flags)
Changes town rating of the current company.
@ TunnelBridgeRemove
Removal of a tunnel or bridge owned by the town.
Town * ClosestTownFromTile(TileIndex tile, uint threshold)
Return the town closest (in distance or ownership) to a given tile, within a given threshold.
Town * CalcClosestTownFromTile(TileIndex tile, uint threshold=UINT_MAX)
Return the town closest to the given tile within threshold.
CommandCost CheckforTownRating(DoCommandFlags flags, Town *t, TownRatingCheckType type)
Does the town authority allow the (destructive) action of the current company?
static constexpr int RATING_TUNNEL_BRIDGE_UP_STEP
rating increase for improving a town-owned bridge
Definition town_type.h:58
static constexpr int RATING_TUNNEL_BRIDGE_MINIMUM
minimum rating after removing tunnel or bridge
Definition town_type.h:60
static constexpr int RATING_TUNNEL_BRIDGE_DOWN_STEP
penalty for removing town owned tunnel or bridge
Definition town_type.h:59
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
TrackStatus CombineTrackStatus(TrackdirBits trackdirbits, TrackdirBits red_signals)
Builds a TrackStatus.
Definition track_func.h:388
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:524
Trackdir DiagDirToDiagTrackdir(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal trackdir that runs in that direction.
Definition track_func.h:537
Track AxisToTrack(Axis a)
Convert an Axis to the corresponding Track AXIS_X -> TRACK_X AXIS_Y -> TRACK_Y Uses the fact that the...
Definition track_func.h:66
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track incidating with that diagdir.
Definition track_func.h:512
@ TRACK_BIT_WORMHOLE
Bitflag for a wormhole (used for tunnels)
Definition track_type.h:52
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
Track
These are used to specify a single track.
Definition track_type.h:19
Base for the train class.
bool TryPathReserve(Train *v, bool mark_as_stuck=false, bool first_tile_okay=false)
Try to reserve a path to a safe position.
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
bool IsTransparencySet(TransparencyOption to)
Check if the transparency option bit is set and if we aren't in the game menu (there's never transpar...
bool IsInvisibilitySet(TransparencyOption to)
Check if the invisibility option bit is set and if we aren't in the game menu (there's never transpar...
@ TO_BRIDGES
bridges
@ TO_CATENARY
catenary
TransportType
Available types of transport.
@ TRANSPORT_RAIL
Transport by train.
@ TRANSPORT_ROAD
Transport by road vehicle.
@ TRANSPORT_WATER
Transport over water.
bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir)
Is there a tunnel in the way in the given direction?
TileIndex GetOtherTunnelEnd(TileIndex tile)
Gets the other end of the tunnel.
void MakeRailTunnel(Tile t, Owner o, DiagDirection d, RailType r)
Makes a rail tunnel entrance.
Definition tunnel_map.h:73
bool IsTunnel(Tile t)
Is this a tunnel (entrance)?
Definition tunnel_map.h:23
void MakeRoadTunnel(Tile t, Owner o, DiagDirection d, RoadType road_rt, RoadType tram_rt)
Makes a road tunnel entrance.
Definition tunnel_map.h:50
Header file for things common for tunnels and bridges.
uint GetTunnelBridgeLength(TileIndex begin, TileIndex end)
Calculates the length of a tunnel or a bridge (without end tiles)
CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex tile_start, TransportType transport_type, BridgeType bridge_type, uint8_t road_rail_type)
Build a Bridge.
static CommandCost ClearTile_TunnelBridge(TileIndex tile, DoCommandFlags flags)
Remove a tunnel or a bridge from the game.
CommandCost CmdBuildTunnel(DoCommandFlags flags, TileIndex start_tile, TransportType transport_type, uint8_t road_rail_type)
Build Tunnel.
static void DrawBridgePillars(const PalSpriteID &psid, const TileInfo *ti, Axis axis, bool drawfarpillar, int x, int y, int z_bridge)
Draws the pillars under high bridges.
static BridgePieces CalcBridgePiece(uint north, uint south)
Compute bridge piece.
static bool BridgeHasCustomSpriteTable(BridgeType bridge_type, BridgePieces piece)
Test if bridge piece uses a custom sprite table.
static CommandCost CheckAllowRemoveTunnelBridge(TileIndex tile)
Are we allowed to remove the tunnel or bridge at tile?
static Money TunnelBridgeClearCost(TileIndex tile, Price base_price)
Calculate the base cost of clearing a tunnel/bridge per tile.
Foundation GetBridgeFoundation(Slope tileh, Axis axis)
Get the foundation for a bridge.
const uint8_t _tunnel_visibility_frame[DIAGDIR_END]
Frame when a vehicle should be hidden in a tunnel with a certain direction.
static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlags flags)
Remove a tunnel from the game, update town rating, etc.
static CommandCost DoClearBridge(TileIndex tile, DoCommandFlags flags)
Remove a bridge from the game, update town rating, etc.
static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID &psid, int x, int y, int w, int h)
Draw two bridge pillars (north and south).
static BridgePillarFlags GetBridgeTilePillarFlags(TileIndex tile, TileIndex rampnorth, TileIndex rampsouth, BridgeType type, TransportType transport_type)
Get pillar information for a bridge middle tile.
static void DrawPillar(const PalSpriteID &psid, int x, int y, int z, uint8_t w, uint8_t h, const SubSprite *subsprite)
Draw a single pillar sprite.
static uint8_t GetBridgeRampDirectionBaseOffset(DiagDirection diagdir)
Get bridge sprite table base offset for the ramp part of bridge.
CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoCommandFlags flags)
Is a bridge of the specified type and length available?
void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height)
Mark bridge tiles dirty.
bool HasBridgeFlatRamp(Slope tileh, Axis axis)
Determines if the track on a bridge ramp is flat or goes up/down.
static void DrawTile_TunnelBridge(TileInfo *ti)
Draws a tunnel of bridge tile.
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
Draw the middle bits of a bridge.
static std::span< const PalSpriteID > GetBridgeSpriteTable(BridgeType bridge_type, BridgePieces piece)
Get the sprite table for a rail/road bridge piece.
static const int BRIDGE_Z_START
Z position of the bridge sprites relative to bridge height (downwards)
static uint8_t GetBridgeMiddleAxisBaseOffset(Axis axis)
Get bridge sprite table base offset for the middle part of bridge.
TileIndex _build_tunnel_endtile
The end of a tunnel; as hidden return from the tunnel build command for GUI purposes.
BridgeSpec _bridge[MAX_BRIDGES]
The specification of all bridges.
static void GetBridgeRoadCatenary(const RoadTypeInfo *rti, TileIndex head_tile, int offset, bool head, SpriteID &spr_back, SpriteID &spr_front)
Retrieve the sprites required for catenary on a road/tram bridge.
int CalcBridgeLenCostFactor(int length)
Calculate the price factor for building a long bridge.
static void PrepareToEnterBridge(T *gv)
Helper to prepare the ground vehicle when entering a bridge.
static uint8_t GetBridgeSpriteTableBaseOffset(TransportType transport_type, TileIndex ramp)
Get the sprite table transport type base offset for a rail/road bridge.
void ResetBridges()
Reset the data been eventually changed by the grf loaded.
static CommandCost CheckBridgeSlope(BridgePieces bridge_piece, Axis axis, Slope &tileh, int &z)
Determines the foundation for the bridge head, and tests if the resulting slope is valid.
static void DrawBridgeRoadBits(TileIndex head_tile, int x, int y, int z, int offset, bool head, bool is_custom_layout)
Draws the road and trambits over an already drawn (lower end) of a bridge.
static const uint8_t TUNNEL_SOUND_FRAME
Frame when the 'enter tunnel' sound should be played.
Command definitions related to tunnels and bridges.
Functions that have tunnels and bridges in common.
void SetTunnelBridgeSnowOrDesert(Tile t, bool snow_or_desert)
Tunnel: Places this tunnel entrance in a snowy or desert area, or takes it out of there.
DiagDirection GetTunnelBridgeDirection(Tile t)
Get the direction pointing to the other end.
bool HasTunnelBridgeSnowOrDesert(Tile t)
Tunnel: Is this tunnel entrance in a snowy or desert area? Bridge: Does the bridge ramp lie in a snow...
bool HasTunnelBridgeReservation(Tile t)
Get the reservation state of the rail tunnel/bridge.
TransportType GetTunnelBridgeTransportType(Tile t)
Tunnel: Get the transport type of the tunnel (road or rail) Bridge: Get the transport type of the bri...
TileIndex GetOtherTunnelBridgeEnd(Tile t)
Determines type of the wormhole and returns its other end.
void SetTunnelBridgeReservation(Tile t, bool b)
Set the reservation state of the rail tunnel/bridge.
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:552
@ Hidden
Vehicle is not visible.
Functions related to vehicles.
@ VEH_ROAD
Road vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:764
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int z, const SpriteBounds &bounds, bool transparent, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:663
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:774
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:579
Functions related to (drawing on) viewports.
static constexpr int BB_Z_SEPARATOR
Separates the bridge/tunnel from the things under/above it.
Functions related to water management.
void CheckForDockingTile(TileIndex t)
Mark the supplied tile as a docking tile if it is suitable for docking.
bool HasTileWaterGround(Tile t)
Checks whether the tile has water at the ground.
Definition water_map.h:352
bool HasTileWaterClass(Tile t)
Checks whether the tile has an waterclass associated.
Definition water_map.h:103
WaterClass GetWaterClass(Tile t)
Get the water class at a tile.
Definition water_map.h:114
Entry point for OpenTTD to YAPF's cache.
void YapfNotifyTrackLayoutChange(TileIndex tile, Track track)
Use this function to notify YAPF that track layout (or signal configuration) has change.