OpenTTD Source 15.0-RC4
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
14#include "stdafx.h"
15#include "viewport_func.h"
16#include "command_func.h"
17#include "town.h"
18#include "train.h"
19#include "ship.h"
20#include "roadveh.h"
22#include "newgrf_sound.h"
23#include "autoslope.h"
24#include "tunnelbridge_map.h"
25#include "strings_func.h"
27#include "clear_func.h"
28#include "vehicle_func.h"
29#include "sound_func.h"
30#include "tunnelbridge.h"
31#include "cheat_type.h"
32#include "elrail_func.h"
33#include "pbs.h"
34#include "company_base.h"
35#include "newgrf_railtype.h"
36#include "newgrf_roadtype.h"
37#include "object_base.h"
38#include "water.h"
39#include "company_gui.h"
40#include "tunnelbridge_cmd.h"
41#include "landscape_cmd.h"
42#include "terraform_cmd.h"
43
44#include "table/strings.h"
45#include "table/bridge_land.h"
46
47#include "safeguards.h"
48
51
53static const int BRIDGE_Z_START = 3;
54
55
64void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height)
65{
66 TileIndexDiff delta = TileOffsByDiagDir(direction);
67 for (TileIndex t = begin; t != end; t += delta) {
68 MarkTileDirtyByTile(t, bridge_height - TileHeight(t));
69 }
71}
72
81
84{
85 std::ranges::copy(_orig_bridge, std::begin(_bridge));
86}
87
95{
96 if (length < 2) return length;
97
98 length -= 2;
99 int sum = 2;
100 for (int delta = 1;; delta++) {
101 for (int count = 0; count < delta; count++) {
102 if (length == 0) return sum;
103 sum += delta;
104 length--;
105 }
106 }
107}
108
116{
117 if (tileh == SLOPE_FLAT ||
118 ((tileh == SLOPE_NE || tileh == SLOPE_SW) && axis == AXIS_X) ||
119 ((tileh == SLOPE_NW || tileh == SLOPE_SE) && axis == AXIS_Y)) return FOUNDATION_NONE;
120
121 return (HasSlopeHighestCorner(tileh) ? InclinedFoundation(axis) : FlatteningFoundation(tileh));
122}
123
131bool HasBridgeFlatRamp(Slope tileh, Axis axis)
132{
133 ApplyFoundationToSlope(GetBridgeFoundation(tileh, axis), tileh);
134 /* If the foundation slope is flat the bridge has a non-flat ramp and vice versa. */
135 return (tileh != SLOPE_FLAT);
136}
137
145{
146 assert(piece < NUM_BRIDGE_PIECES);
147 const BridgeSpec *bridge = GetBridgeSpec(bridge_type);
148 return piece < bridge->sprite_table.size() && !bridge->sprite_table[piece].empty();
149}
150
157static std::span<const PalSpriteID> GetBridgeSpriteTable(BridgeType bridge_type, BridgePieces piece)
158{
159 assert(piece < NUM_BRIDGE_PIECES);
160
161 const BridgeSpec *bridge = GetBridgeSpec(bridge_type);
162 if (piece < bridge->sprite_table.size() && !bridge->sprite_table[piece].empty()) return bridge->sprite_table[piece];
163
164 return _bridge_sprite_table[bridge_type][piece];
165}
166
173static uint8_t GetBridgeSpriteTableBaseOffset(TransportType transport_type, TileIndex ramp)
174{
175 switch (transport_type) {
177 case TRANSPORT_ROAD: return 8;
178 default: NOT_REACHED();
179 }
180}
181
188{
189 /* Bridge ramps are ordered SW, SE, NE, NW instead of NE, SE, SW, NW. */
190 static constexpr uint8_t ramp_offsets[DIAGDIR_END] = {2, 1, 0, 3};
191 return ramp_offsets[diagdir];
192}
193
200{
201 return axis == AXIS_X ? 0 : 4;
202}
203
213static CommandCost CheckBridgeSlope(BridgePieces bridge_piece, Axis axis, Slope &tileh, int &z)
214{
215 assert(bridge_piece == BRIDGE_PIECE_NORTH || bridge_piece == BRIDGE_PIECE_SOUTH);
216
217 Foundation f = GetBridgeFoundation(tileh, axis);
218 z += ApplyFoundationToSlope(f, tileh);
219
220 Slope valid_inclined;
221 if (bridge_piece == BRIDGE_PIECE_NORTH) {
222 valid_inclined = (axis == AXIS_X ? SLOPE_NE : SLOPE_NW);
223 } else {
224 valid_inclined = (axis == AXIS_X ? SLOPE_SW : SLOPE_SE);
225 }
226 if ((tileh != SLOPE_FLAT) && (tileh != valid_inclined)) return CMD_ERROR;
227
228 if (f == FOUNDATION_NONE) return CommandCost();
229
230 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
231}
232
241{
242 if (flags.Test(DoCommandFlag::QueryCost)) {
243 if (bridge_len <= _settings_game.construction.max_bridge_length) return CommandCost();
244 return CommandCost(STR_ERROR_BRIDGE_TOO_LONG);
245 }
246
247 if (bridge_type >= MAX_BRIDGES) return CMD_ERROR;
248
249 const BridgeSpec *b = GetBridgeSpec(bridge_type);
251
253
254 if (b->min_length > bridge_len) return CMD_ERROR;
255 if (bridge_len <= max) return CommandCost();
256 return CommandCost(STR_ERROR_BRIDGE_TOO_LONG);
257}
258
265{
266 Money base_cost = _price[base_price];
267
268 /* Add the cost of the transport that is on the tunnel/bridge. */
269 switch (GetTunnelBridgeTransportType(tile)) {
270 case TRANSPORT_ROAD: {
271 RoadType road_rt = GetRoadTypeRoad(tile);
272 RoadType tram_rt = GetRoadTypeTram(tile);
273
274 if (road_rt != INVALID_ROADTYPE) {
275 base_cost += 2 * RoadClearCost(road_rt);
276 }
277 if (tram_rt != INVALID_ROADTYPE) {
278 base_cost += 2 * RoadClearCost(tram_rt);
279 }
280 } break;
281
282 case TRANSPORT_RAIL: base_cost += RailClearCost(GetRailType(tile)); break;
283 /* Aqueducts have their own clear price. */
284 case TRANSPORT_WATER: base_cost = _price[PR_CLEAR_AQUEDUCT]; break;
285 default: break;
286 }
287
288 return base_cost;
289}
290
291static CommandCost CheckBuildAbove(TileIndex tile, DoCommandFlags flags, Axis axis, int height)
292{
293 if (_tile_type_procs[GetTileType(tile)]->check_build_above_proc != nullptr) {
294 return _tile_type_procs[GetTileType(tile)]->check_build_above_proc(tile, flags, axis, height);
295 }
296 /* A tile without a handler must be cleared. */
297 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
298}
299
310CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex tile_start, TransportType transport_type, BridgeType bridge_type, uint8_t road_rail_type)
311{
312 CompanyID company = _current_company;
313
314 RailType railtype = INVALID_RAILTYPE;
315 RoadType roadtype = INVALID_ROADTYPE;
316
317 if (!IsValidTile(tile_start)) return CommandCost(STR_ERROR_BRIDGE_THROUGH_MAP_BORDER);
318
319 /* type of bridge */
320 switch (transport_type) {
321 case TRANSPORT_ROAD:
322 roadtype = (RoadType)road_rail_type;
323 if (!ValParamRoadType(roadtype)) return CMD_ERROR;
324 break;
325
326 case TRANSPORT_RAIL:
327 railtype = (RailType)road_rail_type;
328 if (!ValParamRailType(railtype)) return CMD_ERROR;
329 break;
330
331 case TRANSPORT_WATER:
332 break;
333
334 default:
335 /* Airports don't have bridges. */
336 return CMD_ERROR;
337 }
338
339 if (company == OWNER_DEITY) {
340 if (transport_type != TRANSPORT_ROAD) return CMD_ERROR;
341 const Town *town = CalcClosestTownFromTile(tile_start);
342
343 company = OWNER_TOWN;
344
345 /* If we are not within a town, we are not owned by the town */
346 if (town == nullptr || DistanceSquare(tile_start, town->xy) > town->cache.squared_town_zone_radius[to_underlying(HouseZone::TownEdge)]) {
347 company = OWNER_NONE;
348 }
349 }
350
351 if (tile_start == tile_end) {
352 return CommandCost(STR_ERROR_CAN_T_START_AND_END_ON);
353 }
354
355 Axis direction;
356 if (TileX(tile_start) == TileX(tile_end)) {
357 direction = AXIS_Y;
358 } else if (TileY(tile_start) == TileY(tile_end)) {
359 direction = AXIS_X;
360 } else {
361 return CommandCost(STR_ERROR_START_AND_END_MUST_BE_IN);
362 }
363
364 if (tile_end < tile_start) std::swap(tile_start, tile_end);
365
366 uint bridge_len = GetTunnelBridgeLength(tile_start, tile_end);
367 if (transport_type != TRANSPORT_WATER) {
368 /* set and test bridge length, availability */
369 CommandCost ret = CheckBridgeAvailability(bridge_type, bridge_len, flags);
370 if (ret.Failed()) return ret;
371 } else {
372 if (bridge_len > _settings_game.construction.max_bridge_length) return CommandCost(STR_ERROR_BRIDGE_TOO_LONG);
373 }
374 bridge_len += 2; // begin and end tiles/ramps
375
376 auto [tileh_start, z_start] = GetTileSlopeZ(tile_start);
377 auto [tileh_end, z_end] = GetTileSlopeZ(tile_end);
378 bool pbs_reservation = false;
379
380 CommandCost terraform_cost_north = CheckBridgeSlope(BRIDGE_PIECE_NORTH, direction, tileh_start, z_start);
381 CommandCost terraform_cost_south = CheckBridgeSlope(BRIDGE_PIECE_SOUTH, direction, tileh_end, z_end);
382
383 /* Aqueducts can't be built of flat land. */
384 if (transport_type == TRANSPORT_WATER && (tileh_start == SLOPE_FLAT || tileh_end == SLOPE_FLAT)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
385 if (z_start != z_end) return CommandCost(STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT);
386
388 Owner owner;
389 bool is_new_owner;
390 RoadType road_rt = INVALID_ROADTYPE;
391 RoadType tram_rt = INVALID_ROADTYPE;
392 if (IsBridgeTile(tile_start) && IsBridgeTile(tile_end) &&
393 GetOtherBridgeEnd(tile_start) == tile_end &&
394 GetTunnelBridgeTransportType(tile_start) == transport_type) {
395 /* Replace a current bridge. */
396
397 switch (transport_type) {
398 case TRANSPORT_RAIL:
399 /* Keep the reservation, the path stays valid. */
400 pbs_reservation = HasTunnelBridgeReservation(tile_start);
401 break;
402
403 case TRANSPORT_ROAD:
404 /* Do not remove road types when upgrading a bridge */
405 road_rt = GetRoadTypeRoad(tile_start);
406 tram_rt = GetRoadTypeTram(tile_start);
407 break;
408
409 default: break;
410 }
411
412 /* If this is a railway bridge, make sure the railtypes match. */
413 if (transport_type == TRANSPORT_RAIL && GetRailType(tile_start) != railtype) {
414 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
415 }
416
417 /* If this is a road bridge, make sure the roadtype matches. */
418 if (transport_type == TRANSPORT_ROAD) {
419 RoadType existing_rt = RoadTypeIsRoad(roadtype) ? road_rt : tram_rt;
420 if (existing_rt != roadtype && existing_rt != INVALID_ROADTYPE) {
421 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
422 }
423 }
424
425 if (!flags.Test(DoCommandFlag::QueryCost)) {
426 /* Do not replace the bridge with the same bridge type. */
427 if ((bridge_type == GetBridgeType(tile_start)) && (transport_type != TRANSPORT_ROAD || road_rt == roadtype || tram_rt == roadtype)) {
428 return CommandCost(STR_ERROR_ALREADY_BUILT);
429 }
430
431 /* Do not replace town bridges with lower speed bridges, unless in scenario editor. */
432 if (IsTileOwner(tile_start, OWNER_TOWN) && _game_mode != GM_EDITOR) {
433 Town *t = ClosestTownFromTile(tile_start, UINT_MAX);
434 if (t == nullptr) return CMD_ERROR;
435
436 if (GetBridgeSpec(bridge_type)->speed < GetBridgeSpec(GetBridgeType(tile_start))->speed) {
437 return CommandCostWithParam(STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS, t->index);
438 } else {
439 ChangeTownRating(t, RATING_TUNNEL_BRIDGE_UP_STEP, RATING_MAXIMUM, flags);
440 }
441 }
442 }
443
444 /* Do not allow replacing another company's bridges. */
445 if (!IsTileOwner(tile_start, company) && !IsTileOwner(tile_start, OWNER_TOWN) && !IsTileOwner(tile_start, OWNER_NONE)) {
446 return CommandCost(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
447 }
448
449 /* The cost of clearing the current bridge. */
450 cost.AddCost(bridge_len * TunnelBridgeClearCost(tile_start, PR_CLEAR_BRIDGE));
451 owner = GetTileOwner(tile_start);
452
453 /* If bridge belonged to bankrupt company, it has a new owner now */
454 is_new_owner = (owner == OWNER_NONE);
455 if (is_new_owner) owner = company;
456
457 /* Check if the new bridge is compatible with tiles underneath. */
458 TileIndexDiff delta = (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
459 for (TileIndex tile = tile_start + delta; tile != tile_end; tile += delta) {
460 CommandCost ret = CheckBuildAbove(tile, flags, direction, z_start + 1);
461 if (ret.Failed()) return ret;
462 cost.AddCost(ret.GetCost());
463 }
464 } else {
465 /* Build a new bridge. */
466
467 bool allow_on_slopes = (_settings_game.construction.build_on_slopes && transport_type != TRANSPORT_WATER);
468
469 /* Try and clear the start landscape */
470 CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_start);
471 if (ret.Failed()) return ret;
472 cost = ret;
473
474 if (terraform_cost_north.Failed() || (terraform_cost_north.GetCost() != 0 && !allow_on_slopes)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
475 cost.AddCost(terraform_cost_north.GetCost());
476
477 /* Try and clear the end landscape */
478 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile_end);
479 if (ret.Failed()) return ret;
480 cost.AddCost(ret.GetCost());
481
482 /* false - end tile slope check */
483 if (terraform_cost_south.Failed() || (terraform_cost_south.GetCost() != 0 && !allow_on_slopes)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
484 cost.AddCost(terraform_cost_south.GetCost());
485
486 /* Check for bridges above the bridge ramps. */
487 for (TileIndex tile : {tile_start, tile_end}) {
488 if (!IsBridgeAbove(tile)) continue;
489
490 if (direction == GetBridgeAxis(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
491
492 if (z_start + 1 == GetBridgeHeight(GetNorthernBridgeEnd(tile))) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
493 }
494
495 TileIndexDiff delta = TileOffsByAxis(direction);
496 for (TileIndex tile = tile_start + delta; tile != tile_end; tile += delta) {
497 if (GetTileMaxZ(tile) > z_start) return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_TERRAIN);
498
499 if (z_start >= (GetTileZ(tile) + _settings_game.construction.max_bridge_height)) {
500 /*
501 * Disallow too high bridges.
502 * Properly rendering a map where very high bridges (might) exist is expensive.
503 * See http://www.tt-forums.net/viewtopic.php?f=33&t=40844&start=980#p1131762
504 * for a detailed discussion. z_start here is one heightlevel below the bridge level.
505 */
506 return CommandCost(STR_ERROR_BRIDGE_TOO_HIGH_FOR_TERRAIN);
507 }
508
509 if (IsBridgeAbove(tile)) {
510 /* Disallow crossing bridges for the time being */
511 return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
512 }
513
514 ret = CheckBuildAbove(tile, flags, direction, z_start + 1);
515 if (ret.Failed()) return ret;
516 cost.AddCost(ret.GetCost());
517
518 if (flags.Test(DoCommandFlag::Execute)) {
519 /* We do this here because when replacing a bridge with another
520 * type calling SetBridgeMiddle isn't needed. After all, the
521 * tile already has the has_bridge_above bits set. */
522 SetBridgeMiddle(tile, direction);
523 }
524 }
525
526 owner = company;
527 is_new_owner = true;
528 }
529
530 bool hasroad = road_rt != INVALID_ROADTYPE;
531 bool hastram = tram_rt != INVALID_ROADTYPE;
532 if (transport_type == TRANSPORT_ROAD) {
533 if (RoadTypeIsRoad(roadtype)) road_rt = roadtype;
534 if (RoadTypeIsTram(roadtype)) tram_rt = roadtype;
535 }
536
537 /* do the drill? */
538 if (flags.Test(DoCommandFlag::Execute)) {
539 DiagDirection dir = AxisToDiagDir(direction);
540
541 Company *c = Company::GetIfValid(company);
542 switch (transport_type) {
543 case TRANSPORT_RAIL:
544 /* Add to company infrastructure count if required. */
545 if (is_new_owner && c != nullptr) c->infrastructure.rail[railtype] += bridge_len * TUNNELBRIDGE_TRACKBIT_FACTOR;
546 MakeRailBridgeRamp(tile_start, owner, bridge_type, dir, railtype);
547 MakeRailBridgeRamp(tile_end, owner, bridge_type, ReverseDiagDir(dir), railtype);
548 SetTunnelBridgeReservation(tile_start, pbs_reservation);
549 SetTunnelBridgeReservation(tile_end, pbs_reservation);
550 break;
551
552 case TRANSPORT_ROAD: {
553 if (is_new_owner) {
554 /* Also give unowned present roadtypes to new owner */
555 if (hasroad && GetRoadOwner(tile_start, RTT_ROAD) == OWNER_NONE) hasroad = false;
556 if (hastram && GetRoadOwner(tile_start, RTT_TRAM) == OWNER_NONE) hastram = false;
557 }
558 if (c != nullptr) {
559 /* Add all new road types to the company infrastructure counter. */
560 if (!hasroad && road_rt != INVALID_ROADTYPE) {
561 /* A full diagonal road tile has two road bits. */
562 c->infrastructure.road[road_rt] += bridge_len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
563 }
564 if (!hastram && tram_rt != INVALID_ROADTYPE) {
565 /* A full diagonal road tile has two road bits. */
566 c->infrastructure.road[tram_rt] += bridge_len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
567 }
568 }
569 Owner owner_road = hasroad ? GetRoadOwner(tile_start, RTT_ROAD) : company;
570 Owner owner_tram = hastram ? GetRoadOwner(tile_start, RTT_TRAM) : company;
571 MakeRoadBridgeRamp(tile_start, owner, owner_road, owner_tram, bridge_type, dir, road_rt, tram_rt);
572 MakeRoadBridgeRamp(tile_end, owner, owner_road, owner_tram, bridge_type, ReverseDiagDir(dir), road_rt, tram_rt);
573 break;
574 }
575
576 case TRANSPORT_WATER:
577 if (is_new_owner && c != nullptr) c->infrastructure.water += bridge_len * TUNNELBRIDGE_TRACKBIT_FACTOR;
578 MakeAqueductBridgeRamp(tile_start, owner, dir);
579 MakeAqueductBridgeRamp(tile_end, owner, ReverseDiagDir(dir));
580 CheckForDockingTile(tile_start);
581 CheckForDockingTile(tile_end);
582 break;
583
584 default:
585 NOT_REACHED();
586 }
587
588 /* Mark all tiles dirty */
589 MarkBridgeDirty(tile_start, tile_end, AxisToDiagDir(direction), z_start);
591 }
592
593 if (flags.Test(DoCommandFlag::Execute) && transport_type == TRANSPORT_RAIL) {
594 Track track = AxisToTrack(direction);
595 AddSideToSignalBuffer(tile_start, INVALID_DIAGDIR, company);
596 YapfNotifyTrackLayoutChange(tile_start, track);
597 }
598
599 /* Human players that build bridges get a selection to choose from (DoCommandFlag::QueryCost)
600 * It's unnecessary to execute this command every time for every bridge.
601 * So it is done only for humans and cost is computed in bridge_gui.cpp.
602 * For (non-spectated) AI, Towns this has to be of course calculated. */
603 Company *c = Company::GetIfValid(company);
604 if (!flags.Test(DoCommandFlag::QueryCost) || (c != nullptr && c->is_ai && company != _local_company)) {
605 switch (transport_type) {
606 case TRANSPORT_ROAD:
607 if (road_rt != INVALID_ROADTYPE) {
608 cost.AddCost(bridge_len * 2 * RoadBuildCost(road_rt));
609 }
610 if (tram_rt != INVALID_ROADTYPE) {
611 cost.AddCost(bridge_len * 2 * RoadBuildCost(tram_rt));
612 }
613 break;
614
615 case TRANSPORT_RAIL: cost.AddCost(bridge_len * RailBuildCost(railtype)); break;
616 default: break;
617 }
618
619 if (c != nullptr) bridge_len = CalcBridgeLenCostFactor(bridge_len);
620
621 if (transport_type != TRANSPORT_WATER) {
622 cost.AddCost((int64_t)bridge_len * _price[PR_BUILD_BRIDGE] * GetBridgeSpec(bridge_type)->price >> 8);
623 } else {
624 /* Aqueducts use a separate base cost. */
625 cost.AddCost((int64_t)bridge_len * _price[PR_BUILD_AQUEDUCT]);
626 }
627
628 }
629
630 return cost;
631}
632
633
642CommandCost CmdBuildTunnel(DoCommandFlags flags, TileIndex start_tile, TransportType transport_type, uint8_t road_rail_type)
643{
644 CompanyID company = _current_company;
645
646 RailType railtype = INVALID_RAILTYPE;
647 RoadType roadtype = INVALID_ROADTYPE;
649 switch (transport_type) {
650 case TRANSPORT_RAIL:
651 railtype = (RailType)road_rail_type;
652 if (!ValParamRailType(railtype)) return CMD_ERROR;
653 break;
654
655 case TRANSPORT_ROAD:
656 roadtype = (RoadType)road_rail_type;
657 if (!ValParamRoadType(roadtype)) return CMD_ERROR;
658 break;
659
660 default: return CMD_ERROR;
661 }
662
663 if (company == OWNER_DEITY) {
664 if (transport_type != TRANSPORT_ROAD) return CMD_ERROR;
665 const Town *town = CalcClosestTownFromTile(start_tile);
666
667 company = OWNER_TOWN;
668
669 /* If we are not within a town, we are not owned by the town */
670 if (town == nullptr || DistanceSquare(start_tile, town->xy) > town->cache.squared_town_zone_radius[to_underlying(HouseZone::TownEdge)]) {
671 company = OWNER_NONE;
672 }
673 }
674
675 auto [start_tileh, start_z] = GetTileSlopeZ(start_tile);
676 DiagDirection direction = GetInclinedSlopeDirection(start_tileh);
677 if (direction == INVALID_DIAGDIR) return CommandCost(STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL);
678
679 if (HasTileWaterGround(start_tile)) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
680
681 CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, start_tile);
682 if (ret.Failed()) return ret;
683
684 /* XXX - do NOT change 'ret' in the loop, as it is used as the price
685 * for the clearing of the entrance of the tunnel. Assigning it to
686 * cost before the loop will yield different costs depending on start-
687 * position, because of increased-cost-by-length: 'cost += cost >> 3' */
688
689 TileIndexDiff delta = TileOffsByDiagDir(direction);
690 DiagDirection tunnel_in_way_dir;
691 if (DiagDirToAxis(direction) == AXIS_Y) {
692 tunnel_in_way_dir = (TileX(start_tile) < (Map::MaxX() / 2)) ? DIAGDIR_SW : DIAGDIR_NE;
693 } else {
694 tunnel_in_way_dir = (TileY(start_tile) < (Map::MaxX() / 2)) ? DIAGDIR_SE : DIAGDIR_NW;
695 }
696
697 TileIndex end_tile = start_tile;
698
699 /* Tile shift coefficient. Will decrease for very long tunnels to avoid exponential growth of price*/
700 int tiles_coef = 3;
701 /* Number of tiles from start of tunnel */
702 int tiles = 0;
703 /* Number of tiles at which the cost increase coefficient per tile is halved */
704 int tiles_bump = 25;
705
707 Slope end_tileh;
708 int end_z;
709 for (;;) {
710 end_tile += delta;
711 if (!IsValidTile(end_tile)) return CommandCost(STR_ERROR_TUNNEL_THROUGH_MAP_BORDER);
712 std::tie(end_tileh, end_z) = GetTileSlopeZ(end_tile);
713
714 if (start_z == end_z) break;
715
716 if (!_cheats.crossing_tunnels.value && IsTunnelInWayDir(end_tile, start_z, tunnel_in_way_dir)) {
717 return CommandCost(STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY);
718 }
719
720 tiles++;
721 if (tiles == tiles_bump) {
722 tiles_coef++;
723 tiles_bump *= 2;
724 }
725
726 cost.AddCost(_price[PR_BUILD_TUNNEL]);
727 cost.AddCost(cost.GetCost() >> tiles_coef); // add a multiplier for longer tunnels
728 }
729
730 /* Add the cost of the entrance */
731 cost.AddCost(_price[PR_BUILD_TUNNEL]);
732 cost.AddCost(ret.GetCost());
733
734 /* if the command fails from here on we want the end tile to be highlighted */
735 _build_tunnel_endtile = end_tile;
736
737 if (tiles > _settings_game.construction.max_tunnel_length) return CommandCost(STR_ERROR_TUNNEL_TOO_LONG);
738
739 if (HasTileWaterGround(end_tile)) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
740
741 /* Clear the tile in any case */
742 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, end_tile);
743 if (ret.Failed()) return CommandCost(STR_ERROR_UNABLE_TO_EXCAVATE_LAND);
744 cost.AddCost(ret.GetCost());
745
746 /* slope of end tile must be complementary to the slope of the start tile */
747 if (end_tileh != ComplementSlope(start_tileh)) {
748 /* Mark the tile as already cleared for the terraform command.
749 * Do this for all tiles (like trees), not only objects. */
750 ClearedObjectArea *coa = FindClearedObject(end_tile);
751 if (coa == nullptr) {
752 coa = &_cleared_object_areas.emplace_back(end_tile, TileArea(end_tile, 1, 1));
753 }
754
755 /* Hide the tile from the terraforming command */
756 TileIndex old_first_tile = coa->first_tile;
758
759 /* CMD_TERRAFORM_LAND may append further items to _cleared_object_areas,
760 * however it will never erase or re-order existing items.
761 * _cleared_object_areas is a value-type self-resizing vector, therefore appending items
762 * may result in a backing-store re-allocation, which would invalidate the coa pointer.
763 * The index of the coa pointer into the _cleared_object_areas vector remains valid,
764 * and can be used safely after the CMD_TERRAFORM_LAND operation.
765 * Deliberately clear the coa pointer to avoid leaving dangling pointers which could
766 * inadvertently be dereferenced.
767 */
768 ClearedObjectArea *begin = _cleared_object_areas.data();
769 assert(coa >= begin && coa < begin + _cleared_object_areas.size());
770 size_t coa_index = coa - begin;
771 assert(coa_index < UINT_MAX); // more than 2**32 cleared areas would be a bug in itself
772 coa = nullptr;
773
774 ret = std::get<0>(Command<CMD_TERRAFORM_LAND>::Do(flags, end_tile, end_tileh & start_tileh, false));
775 _cleared_object_areas[(uint)coa_index].first_tile = old_first_tile;
776 if (ret.Failed()) return CommandCost(STR_ERROR_UNABLE_TO_EXCAVATE_LAND);
777 cost.AddCost(ret.GetCost());
778 }
779 cost.AddCost(_price[PR_BUILD_TUNNEL]);
780
781 /* Pay for the rail/road in the tunnel including entrances */
782 switch (transport_type) {
783 case TRANSPORT_ROAD: cost.AddCost((tiles + 2) * RoadBuildCost(roadtype) * 2); break;
784 case TRANSPORT_RAIL: cost.AddCost((tiles + 2) * RailBuildCost(railtype)); break;
785 default: NOT_REACHED();
786 }
787
788 if (flags.Test(DoCommandFlag::Execute)) {
789 Company *c = Company::GetIfValid(company);
790 uint num_pieces = (tiles + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
791 if (transport_type == TRANSPORT_RAIL) {
792 if (c != nullptr) c->infrastructure.rail[railtype] += num_pieces;
793 MakeRailTunnel(start_tile, company, direction, railtype);
794 MakeRailTunnel(end_tile, company, ReverseDiagDir(direction), railtype);
795 AddSideToSignalBuffer(start_tile, INVALID_DIAGDIR, company);
796 YapfNotifyTrackLayoutChange(start_tile, DiagDirToDiagTrack(direction));
797 } else {
798 if (c != nullptr) c->infrastructure.road[roadtype] += num_pieces * 2; // A full diagonal road has two road bits.
799 RoadType road_rt = RoadTypeIsRoad(roadtype) ? roadtype : INVALID_ROADTYPE;
800 RoadType tram_rt = RoadTypeIsTram(roadtype) ? roadtype : INVALID_ROADTYPE;
801 MakeRoadTunnel(start_tile, company, direction, road_rt, tram_rt);
802 MakeRoadTunnel(end_tile, company, ReverseDiagDir(direction), road_rt, tram_rt);
803 }
805 }
806
807 return cost;
808}
809
810
817{
818 /* Floods can remove anything as well as the scenario editor */
819 if (_current_company == OWNER_WATER || _game_mode == GM_EDITOR) return CommandCost();
820
821 switch (GetTunnelBridgeTransportType(tile)) {
822 case TRANSPORT_ROAD: {
823 RoadType road_rt = GetRoadTypeRoad(tile);
824 RoadType tram_rt = GetRoadTypeTram(tile);
825 Owner road_owner = _current_company;
826 Owner tram_owner = _current_company;
827
828 if (road_rt != INVALID_ROADTYPE) road_owner = GetRoadOwner(tile, RTT_ROAD);
829 if (tram_rt != INVALID_ROADTYPE) tram_owner = GetRoadOwner(tile, RTT_TRAM);
830
831 /* We can remove unowned road and if the town allows it */
833 /* Town does not allow */
834 return CheckTileOwnership(tile);
835 }
836 if (road_owner == OWNER_NONE || road_owner == OWNER_TOWN) road_owner = _current_company;
837 if (tram_owner == OWNER_NONE) tram_owner = _current_company;
838
839 CommandCost ret = CheckOwnership(road_owner, tile);
840 if (ret.Succeeded()) ret = CheckOwnership(tram_owner, tile);
841 return ret;
842 }
843
844 case TRANSPORT_RAIL:
845 return CheckOwnership(GetTileOwner(tile));
846
847 case TRANSPORT_WATER: {
848 /* Always allow to remove aqueducts without owner. */
849 Owner aqueduct_owner = GetTileOwner(tile);
850 if (aqueduct_owner == OWNER_NONE) aqueduct_owner = _current_company;
851 return CheckOwnership(aqueduct_owner);
852 }
853
854 default: NOT_REACHED();
855 }
856}
857
865{
867 if (ret.Failed()) return ret;
868
869 TileIndex endtile = GetOtherTunnelEnd(tile);
870
871 ret = TunnelBridgeIsFree(tile, endtile);
872 if (ret.Failed()) return ret;
873
874 _build_tunnel_endtile = endtile;
875
876 Town *t = nullptr;
877 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
878 t = ClosestTownFromTile(tile, UINT_MAX); // town penalty rating
879
880 /* Check if you are allowed to remove the tunnel owned by a town
881 * Removal depends on difficulty settings */
883 if (ret.Failed()) return ret;
884 }
885
886 /* checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
887 * you have a "Poor" (0) town rating */
888 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
890 }
891
892 Money base_cost = TunnelBridgeClearCost(tile, PR_CLEAR_TUNNEL);
893 uint len = GetTunnelBridgeLength(tile, endtile) + 2; // Don't forget the end tiles.
894
895 if (flags.Test(DoCommandFlag::Execute)) {
897 /* We first need to request values before calling DoClearSquare */
899 Track track = DiagDirToDiagTrack(dir);
900 Owner owner = GetTileOwner(tile);
901
902 Train *v = nullptr;
903 if (HasTunnelBridgeReservation(tile)) {
904 v = GetTrainForReservation(tile, track);
905 if (v != nullptr) FreeTrainTrackReservation(v);
906 }
907
908 if (Company::IsValidID(owner)) {
909 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
911 }
912
913 DoClearSquare(tile);
914 DoClearSquare(endtile);
915
916 /* cannot use INVALID_DIAGDIR for signal update because the tunnel doesn't exist anymore */
917 AddSideToSignalBuffer(tile, ReverseDiagDir(dir), owner);
918 AddSideToSignalBuffer(endtile, dir, owner);
919
920 YapfNotifyTrackLayoutChange(tile, track);
921 YapfNotifyTrackLayoutChange(endtile, track);
922
923 if (v != nullptr) TryPathReserve(v);
924 } else {
925 /* A full diagonal road tile has two road bits. */
926 UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
927 UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
928
929 DoClearSquare(tile);
930 DoClearSquare(endtile);
931 }
932 }
933
934 return CommandCost(EXPENSES_CONSTRUCTION, len * base_cost);
935}
936
937
945{
947 if (ret.Failed()) return ret;
948
949 TileIndex endtile = GetOtherBridgeEnd(tile);
950
951 ret = TunnelBridgeIsFree(tile, endtile);
952 if (ret.Failed()) return ret;
953
954 DiagDirection direction = GetTunnelBridgeDirection(tile);
955 TileIndexDiff delta = TileOffsByDiagDir(direction);
956
957 Town *t = nullptr;
958 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
959 t = ClosestTownFromTile(tile, UINT_MAX); // town penalty rating
960
961 /* Check if you are allowed to remove the bridge owned by a town
962 * Removal depends on difficulty settings */
964 if (ret.Failed()) return ret;
965 }
966
967 /* checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
968 * you have a "Poor" (0) town rating */
969 if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
971 }
972
973 Money base_cost = TunnelBridgeClearCost(tile, PR_CLEAR_BRIDGE);
974 uint len = GetTunnelBridgeLength(tile, endtile) + 2; // Don't forget the end tiles.
975
976 if (flags.Test(DoCommandFlag::Execute)) {
977 /* read this value before actual removal of bridge */
978 bool rail = GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL;
979 Owner owner = GetTileOwner(tile);
980 int height = GetBridgeHeight(tile);
981 Train *v = nullptr;
982
983 if (rail && HasTunnelBridgeReservation(tile)) {
984 v = GetTrainForReservation(tile, DiagDirToDiagTrack(direction));
985 if (v != nullptr) FreeTrainTrackReservation(v);
986 }
987
988 /* Update company infrastructure counts. */
989 if (rail) {
990 if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
991 } else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) {
992 /* A full diagonal road tile has two road bits. */
993 UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
994 UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
995 } else { // Aqueduct
996 if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
997 }
999
1000 DoClearSquare(tile);
1001 DoClearSquare(endtile);
1002
1003 for (TileIndex c = tile + delta; c != endtile; c += delta) {
1004 /* do not let trees appear from 'nowhere' after removing bridge */
1006 int minz = GetTileMaxZ(c) + 3;
1007 if (height < minz) SetRoadside(c, Roadside::Paved);
1008 }
1010 MarkTileDirtyByTile(c, height - TileHeight(c));
1011 }
1012
1013 if (rail) {
1014 /* cannot use INVALID_DIAGDIR for signal update because the bridge doesn't exist anymore */
1015 AddSideToSignalBuffer(tile, ReverseDiagDir(direction), owner);
1016 AddSideToSignalBuffer(endtile, direction, owner);
1017
1018 Track track = DiagDirToDiagTrack(direction);
1019 YapfNotifyTrackLayoutChange(tile, track);
1020 YapfNotifyTrackLayoutChange(endtile, track);
1021
1022 if (v != nullptr) TryPathReserve(v, true);
1023 }
1024 }
1025
1026 return CommandCost(EXPENSES_CONSTRUCTION, len * base_cost);
1027}
1028
1036{
1037 if (IsTunnel(tile)) {
1038 if (flags.Test(DoCommandFlag::Auto)) return CommandCost(STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST);
1039 return DoClearTunnel(tile, flags);
1040 } else { // IsBridge(tile)
1041 if (flags.Test(DoCommandFlag::Auto)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
1042 return DoClearBridge(tile, flags);
1043 }
1044}
1045
1056static inline void DrawPillar(const PalSpriteID &psid, int x, int y, int z, uint8_t w, uint8_t h, const SubSprite *subsprite)
1057{
1058 static const int PILLAR_Z_OFFSET = TILE_HEIGHT - BRIDGE_Z_START;
1059 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);
1060}
1061
1073static int DrawPillarColumn(int z_bottom, int z_top, const PalSpriteID &psid, int x, int y, int w, int h)
1074{
1075 int cur_z;
1076 for (cur_z = z_top; cur_z >= z_bottom; cur_z -= TILE_HEIGHT) {
1077 DrawPillar(psid, x, y, cur_z, w, h, nullptr);
1078 }
1079 return cur_z;
1080}
1081
1093static void DrawBridgePillars(const PalSpriteID &psid, const TileInfo *ti, Axis axis, bool drawfarpillar, int x, int y, int z_bridge)
1094{
1095 static const int bounding_box_size[2] = {16, 2};
1096 static const int back_pillar_offset[2] = { 0, 9};
1097
1098 static const int INF = 1000;
1099 static const SubSprite half_pillar_sub_sprite[2][2] = {
1100 { { -14, -INF, INF, INF }, { -INF, -INF, -15, INF } }, // X axis, north and south
1101 { { -INF, -INF, 15, INF }, { 16, -INF, INF, INF } }, // Y axis, north and south
1102 };
1103
1104 if (psid.sprite == 0) return;
1105
1106 /* Determine ground height under pillars */
1107 DiagDirection south_dir = AxisToDiagDir(axis);
1108 int z_front_north = ti->z;
1109 int z_back_north = ti->z;
1110 int z_front_south = ti->z;
1111 int z_back_south = ti->z;
1112 GetSlopePixelZOnEdge(ti->tileh, south_dir, z_front_south, z_back_south);
1113 GetSlopePixelZOnEdge(ti->tileh, ReverseDiagDir(south_dir), z_front_north, z_back_north);
1114
1115 /* Shared height of pillars */
1116 int z_front = std::max(z_front_north, z_front_south);
1117 int z_back = std::max(z_back_north, z_back_south);
1118
1119 /* x and y size of bounding-box of pillars */
1120 int w = bounding_box_size[axis];
1121 int h = bounding_box_size[OtherAxis(axis)];
1122 /* sprite position of back facing pillar */
1123 int x_back = x - back_pillar_offset[axis];
1124 int y_back = y - back_pillar_offset[OtherAxis(axis)];
1125
1126 /* Draw front pillars */
1127 int bottom_z = DrawPillarColumn(z_front, z_bridge, psid, x, y, w, h);
1128 if (z_front_north < z_front) DrawPillar(psid, x, y, bottom_z, w, h, &half_pillar_sub_sprite[axis][0]);
1129 if (z_front_south < z_front) DrawPillar(psid, x, y, bottom_z, w, h, &half_pillar_sub_sprite[axis][1]);
1130
1131 /* Draw back pillars, skip top two parts, which are hidden by the bridge */
1132 int z_bridge_back = z_bridge - 2 * (int)TILE_HEIGHT;
1133 if (drawfarpillar && (z_back_north <= z_bridge_back || z_back_south <= z_bridge_back)) {
1134 bottom_z = DrawPillarColumn(z_back, z_bridge_back, psid, x_back, y_back, w, h);
1135 if (z_back_north < z_back) DrawPillar(psid, x_back, y_back, bottom_z, w, h, &half_pillar_sub_sprite[axis][0]);
1136 if (z_back_south < z_back) DrawPillar(psid, x_back, y_back, bottom_z, w, h, &half_pillar_sub_sprite[axis][1]);
1137 }
1138}
1139
1149static void GetBridgeRoadCatenary(const RoadTypeInfo *rti, TileIndex head_tile, int offset, bool head, SpriteID &spr_back, SpriteID &spr_front)
1150{
1151 static const SpriteID back_offsets[6] = { 95, 96, 99, 102, 100, 101 };
1152 static const SpriteID front_offsets[6] = { 97, 98, 103, 106, 104, 105 };
1153
1154 /* Simplified from DrawRoadTypeCatenary() to remove all the special cases required for regular ground road */
1155 spr_back = GetCustomRoadSprite(rti, head_tile, ROTSG_CATENARY_BACK, head ? TCX_NORMAL : TCX_ON_BRIDGE);
1156 spr_front = GetCustomRoadSprite(rti, head_tile, ROTSG_CATENARY_FRONT, head ? TCX_NORMAL : TCX_ON_BRIDGE);
1157 if (spr_back == 0 && spr_front == 0) {
1158 spr_back = SPR_TRAMWAY_BASE + back_offsets[offset];
1159 spr_front = SPR_TRAMWAY_BASE + front_offsets[offset];
1160 } else {
1161 if (spr_back != 0) spr_back += 23 + offset;
1162 if (spr_front != 0) spr_front += 23 + offset;
1163 }
1164}
1165
1176static void DrawBridgeRoadBits(TileIndex head_tile, int x, int y, int z, int offset, bool head, bool is_custom_layout)
1177{
1178 RoadType road_rt = GetRoadTypeRoad(head_tile);
1179 RoadType tram_rt = GetRoadTypeTram(head_tile);
1180 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
1181 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
1182
1183 SpriteID seq_back[4] = { 0 };
1184 bool trans_back[4] = { false };
1185 SpriteID seq_front[4] = { 0 };
1186 bool trans_front[4] = { false };
1187
1188 static const SpriteID overlay_offsets[6] = { 0, 1, 11, 12, 13, 14 };
1189 if (head || !IsInvisibilitySet(TO_BRIDGES)) {
1190 /* Road underlay takes precedence over tram */
1191 trans_back[0] = !head && IsTransparencySet(TO_BRIDGES);
1192 if (road_rti != nullptr) {
1193 if (road_rti->UsesOverlay()) {
1194 seq_back[0] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_BRIDGE, head ? TCX_NORMAL : TCX_ON_BRIDGE) + offset;
1195 } else if (is_custom_layout) {
1196 /* For custom layouts draw a custom bridge deck. */
1197 seq_back[0] = SPR_BRIDGE_DECKS_ROAD + offset;
1198 }
1199 } else if (tram_rti != nullptr) {
1200 if (tram_rti->UsesOverlay()) {
1201 seq_back[0] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_BRIDGE, head ? TCX_NORMAL : TCX_ON_BRIDGE) + offset;
1202 } else {
1203 seq_back[0] = SPR_TRAMWAY_BRIDGE + offset;
1204 }
1205 }
1206
1207 /* Draw road overlay */
1208 trans_back[1] = !head && IsTransparencySet(TO_BRIDGES);
1209 if (road_rti != nullptr) {
1210 if (road_rti->UsesOverlay()) {
1211 seq_back[1] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_OVERLAY, head ? TCX_NORMAL : TCX_ON_BRIDGE);
1212 if (seq_back[1] != 0) seq_back[1] += overlay_offsets[offset];
1213 }
1214 }
1215
1216 /* Draw tram overlay */
1217 trans_back[2] = !head && IsTransparencySet(TO_BRIDGES);
1218 if (tram_rti != nullptr) {
1219 if (tram_rti->UsesOverlay()) {
1220 seq_back[2] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_OVERLAY, head ? TCX_NORMAL : TCX_ON_BRIDGE);
1221 if (seq_back[2] != 0) seq_back[2] += overlay_offsets[offset];
1222 } else if (road_rti != nullptr) {
1223 seq_back[2] = SPR_TRAMWAY_OVERLAY + overlay_offsets[offset];
1224 }
1225 }
1226
1227 /* Road catenary takes precedence over tram */
1228 trans_back[3] = IsTransparencySet(TO_CATENARY);
1229 trans_front[0] = IsTransparencySet(TO_CATENARY);
1230 if (road_rti != nullptr && HasRoadCatenaryDrawn(road_rt)) {
1231 GetBridgeRoadCatenary(road_rti, head_tile, offset, head, seq_back[3], seq_front[0]);
1232 } else if (tram_rti != nullptr && HasRoadCatenaryDrawn(tram_rt)) {
1233 GetBridgeRoadCatenary(tram_rti, head_tile, offset, head, seq_back[3], seq_front[0]);
1234 }
1235 }
1236
1237 static constexpr SpriteBounds back_bounds[6] = {
1238 {{}, {0, TILE_SIZE, 40}, {}},
1239 {{}, {TILE_SIZE, 0, 40}, {}},
1240 {{}, {TILE_SIZE, 0, 40}, {}},
1241 {{}, {0, TILE_SIZE, 40}, {}},
1242 {{}, {TILE_SIZE, 0, 40}, {}},
1243 {{}, {0, TILE_SIZE, 40}, {}},
1244 };
1245
1246 /* The sprites under the vehicles are drawn as SpriteCombine. StartSpriteCombine() has already been called
1247 * The bounding boxes here are the same as for bridge front/roof */
1248 for (uint i = 0; i < lengthof(seq_back); ++i) {
1249 if (seq_back[i] != 0) {
1250 AddSortableSpriteToDraw(seq_back[i], PAL_NONE, x, y, z, back_bounds[offset], trans_back[i]);
1251 }
1252 }
1253
1254 /* Start a new SpriteCombine for the front part */
1257
1258 static constexpr SpriteBounds front_bounds[6] = {
1259 {{15, 0, 0}, {0, TILE_SIZE, 40}, {-15, 0, 0}},
1260 {{0, 15, 0}, {TILE_SIZE, 0, 40}, {0, -15, 0}},
1261 {{0, 15, 0}, {TILE_SIZE, 0, 40}, {0, -15, 0}},
1262 {{15, 0, 0}, {0, TILE_SIZE, 40}, {-15, 0, 0}},
1263 {{0, 15, 0}, {TILE_SIZE, 0, 40}, {0, -15, 0}},
1264 {{15, 0, 0}, {0, TILE_SIZE, 40}, {-15, 0, 0}},
1265 };
1266
1267 for (uint i = 0; i < lengthof(seq_front); ++i) {
1268 if (seq_front[i] != 0) {
1269 AddSortableSpriteToDraw(seq_front[i], PAL_NONE, x, y, z, front_bounds[offset], trans_front[i]);
1270 }
1271 }
1272}
1273
1288{
1289 TransportType transport_type = GetTunnelBridgeTransportType(ti->tile);
1290 DiagDirection tunnelbridge_direction = GetTunnelBridgeDirection(ti->tile);
1291
1292 if (IsTunnel(ti->tile)) {
1293 /* Front view of tunnel bounding boxes:
1294 *
1295 * 122223 <- BB_Z_SEPARATOR
1296 * 1 3
1297 * 1 3 1,3 = empty helper BB
1298 * 1 3 2 = SpriteCombine of tunnel-roof and catenary (tram & elrail)
1299 *
1300 */
1301
1302 /* Tunnel sprites are positioned at 15,15, but the bounding box covers most of the tile. */
1303 static constexpr SpriteBounds roof_bounds[DIAGDIR_END] = {
1304 {{0, 1, BB_Z_SEPARATOR}, {TILE_SIZE, TILE_SIZE - 1, 1}, {TILE_SIZE - 1, TILE_SIZE - 2, -BB_Z_SEPARATOR}}, // NE
1305 {{1, 0, BB_Z_SEPARATOR}, {TILE_SIZE - 1, TILE_SIZE, 1}, {TILE_SIZE - 2, TILE_SIZE - 1, -BB_Z_SEPARATOR}}, // SE
1306 {{0, 1, BB_Z_SEPARATOR}, {TILE_SIZE, TILE_SIZE - 1, 1}, {TILE_SIZE - 1, TILE_SIZE - 2, -BB_Z_SEPARATOR}}, // SW
1307 {{1, 0, BB_Z_SEPARATOR}, {TILE_SIZE - 1, TILE_SIZE, 1}, {TILE_SIZE - 2, TILE_SIZE - 1, -BB_Z_SEPARATOR}}, // NW
1308 };
1309
1310 /* Catenary sprites are positioned at 0,0, with the same bounding box as above. */
1311 static constexpr SpriteBounds catenary_bounds[DIAGDIR_END] = {
1312 {{0, 1, BB_Z_SEPARATOR}, {TILE_SIZE, TILE_SIZE - 1, 1}, {0, -1, -BB_Z_SEPARATOR}}, // NE
1313 {{1, 0, BB_Z_SEPARATOR}, {TILE_SIZE - 1, TILE_SIZE, 1}, {-1, 0, -BB_Z_SEPARATOR}}, // SE
1314 {{0, 1, BB_Z_SEPARATOR}, {TILE_SIZE, TILE_SIZE - 1, 1}, {0, -1, -BB_Z_SEPARATOR}}, // SW
1315 {{1, 0, BB_Z_SEPARATOR}, {TILE_SIZE - 1, TILE_SIZE, 1}, {-1, 0, -BB_Z_SEPARATOR}}, // NW
1316 };
1317
1318 static constexpr SpriteBounds rear_sep[DIAGDIR_END] = {
1319 {{}, {TILE_SIZE, 1, TILE_HEIGHT}, {}}, // NE
1320 {{}, {1, TILE_SIZE, TILE_HEIGHT}, {}}, // SE
1321 {{}, {TILE_SIZE, 1, TILE_HEIGHT}, {}}, // SW
1322 {{}, {1, TILE_SIZE, TILE_HEIGHT}, {}}, // NW
1323 };
1324
1325 static constexpr SpriteBounds front_sep[DIAGDIR_END] = {
1326 {{0, TILE_SIZE - 1, 0}, {TILE_SIZE, 1, TILE_HEIGHT}, {}}, // NE
1327 {{TILE_SIZE - 1, 0, 0}, {1, TILE_SIZE, TILE_HEIGHT}, {}}, // SE
1328 {{0, TILE_SIZE - 1, 0}, {TILE_SIZE, 1, TILE_HEIGHT}, {}}, // SW
1329 {{TILE_SIZE - 1, 0, 0}, {1, TILE_SIZE, TILE_HEIGHT}, {}}, // NW
1330 };
1331
1332 bool catenary = false;
1333
1334 SpriteID image;
1335 SpriteID railtype_overlay = 0;
1336 if (transport_type == TRANSPORT_RAIL) {
1337 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
1338 image = rti->base_sprites.tunnel;
1339 if (rti->UsesOverlay()) {
1340 /* Check if the railtype has custom tunnel portals. */
1341 railtype_overlay = GetCustomRailSprite(rti, ti->tile, RTSG_TUNNEL_PORTAL);
1342 if (railtype_overlay != 0) image = SPR_RAILTYPE_TUNNEL_BASE; // Draw blank grass tunnel base.
1343 }
1344 } else {
1345 image = SPR_TUNNEL_ENTRY_REAR_ROAD;
1346 }
1347
1348 if (HasTunnelBridgeSnowOrDesert(ti->tile)) image += railtype_overlay != 0 ? 8 : 32;
1349
1350 image += tunnelbridge_direction * 2;
1351 DrawGroundSprite(image, PAL_NONE);
1352
1353 if (transport_type == TRANSPORT_ROAD) {
1354 RoadType road_rt = GetRoadTypeRoad(ti->tile);
1355 RoadType tram_rt = GetRoadTypeTram(ti->tile);
1356 const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
1357 const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
1358 uint sprite_offset = DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? 1 : 0;
1359 bool draw_underlay = true;
1360
1361 /* Road underlay takes precedence over tram */
1362 if (road_rti != nullptr) {
1363 if (road_rti->UsesOverlay()) {
1364 SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_TUNNEL);
1365 if (ground != 0) {
1366 DrawGroundSprite(ground + tunnelbridge_direction, PAL_NONE);
1367 draw_underlay = false;
1368 }
1369 }
1370 } else {
1371 if (tram_rti->UsesOverlay()) {
1372 SpriteID ground = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_TUNNEL);
1373 if (ground != 0) {
1374 DrawGroundSprite(ground + tunnelbridge_direction, PAL_NONE);
1375 draw_underlay = false;
1376 }
1377 }
1378 }
1379
1380 DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset, draw_underlay);
1381
1382 /* Road catenary takes precedence over tram */
1383 SpriteID catenary_sprite_base = 0;
1384 if (road_rti != nullptr && HasRoadCatenaryDrawn(road_rt)) {
1385 catenary_sprite_base = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_CATENARY_FRONT);
1386 if (catenary_sprite_base == 0) {
1387 catenary_sprite_base = SPR_TRAMWAY_TUNNEL_WIRES;
1388 } else {
1389 catenary_sprite_base += 19;
1390 }
1391 } else if (tram_rti != nullptr && HasRoadCatenaryDrawn(tram_rt)) {
1392 catenary_sprite_base = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_CATENARY_FRONT);
1393 if (catenary_sprite_base == 0) {
1394 catenary_sprite_base = SPR_TRAMWAY_TUNNEL_WIRES;
1395 } else {
1396 catenary_sprite_base += 19;
1397 }
1398 }
1399
1400 if (catenary_sprite_base != 0) {
1401 catenary = true;
1403 AddSortableSpriteToDraw(catenary_sprite_base + tunnelbridge_direction, PAL_NONE, *ti, catenary_bounds[tunnelbridge_direction], IsTransparencySet(TO_CATENARY));
1404 }
1405 } else {
1406 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
1407 if (rti->UsesOverlay()) {
1408 SpriteID surface = GetCustomRailSprite(rti, ti->tile, RTSG_TUNNEL);
1409 if (surface != 0) DrawGroundSprite(surface + tunnelbridge_direction, PAL_NONE);
1410 }
1411
1412 /* PBS debugging, draw reserved tracks darker */
1413 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) {
1414 if (rti->UsesOverlay()) {
1415 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
1416 DrawGroundSprite(overlay + RTO_X + DiagDirToAxis(tunnelbridge_direction), PALETTE_CRASH);
1417 } else {
1419 }
1420 }
1421
1423 /* Maybe draw pylons on the entry side */
1424 DrawRailCatenary(ti);
1425
1426 catenary = true;
1428 /* Draw wire above the ramp */
1430 }
1431 }
1432
1433 if (railtype_overlay != 0 && !catenary) StartSpriteCombine();
1434
1435 AddSortableSpriteToDraw(image + 1, PAL_NONE, *ti, roof_bounds[tunnelbridge_direction], false);
1436 /* Draw railtype tunnel portal overlay if defined. */
1437 if (railtype_overlay != 0) AddSortableSpriteToDraw(railtype_overlay + tunnelbridge_direction, PAL_NONE, *ti, roof_bounds[tunnelbridge_direction], false);
1438
1439 if (catenary || railtype_overlay != 0) EndSpriteCombine();
1440
1441 /* Add helper BB for sprite sorting that separates the tunnel from things beside of it. */
1442 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, *ti, rear_sep[tunnelbridge_direction]);
1443 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, *ti, front_sep[tunnelbridge_direction]);
1444
1445 DrawBridgeMiddle(ti, BridgePillarFlag::EdgeNE + tunnelbridge_direction);
1446 } else { // IsBridge(ti->tile)
1447 DrawFoundation(ti, GetBridgeFoundation(ti->tileh, DiagDirToAxis(tunnelbridge_direction)));
1448 bool is_custom_layout = false; // Set if rail/road bridge uses a custom layout.
1449
1450 uint base_offset = GetBridgeRampDirectionBaseOffset(tunnelbridge_direction);
1451 std::span<const PalSpriteID> psid;
1452 if (transport_type != TRANSPORT_WATER) {
1453 BridgeType bridge_type = GetBridgeType(ti->tile);
1454 if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head
1455 base_offset += GetBridgeSpriteTableBaseOffset(transport_type, ti->tile);
1456 psid = GetBridgeSpriteTable(bridge_type, BRIDGE_PIECE_HEAD);
1457 is_custom_layout = BridgeHasCustomSpriteTable(bridge_type, BRIDGE_PIECE_HEAD);
1458 } else {
1459 psid = _aqueduct_sprite_table_heads;
1460 }
1461 psid = psid.subspan(base_offset, 1);
1462
1464 TileIndex next = ti->tile + TileOffsByDiagDir(tunnelbridge_direction);
1465 if (ti->tileh != SLOPE_FLAT && ti->z == 0 && HasTileWaterClass(next) && GetWaterClass(next) == WaterClass::Sea) {
1466 DrawShoreTile(ti->tileh);
1467 } else {
1468 DrawClearLandTile(ti, 3);
1469 }
1470 } else {
1471 DrawGroundSprite(SPR_FLAT_SNOW_DESERT_TILE + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
1472 }
1473
1474 /* draw ramp */
1475
1476 /* Draw Trambits and PBS Reservation as SpriteCombine */
1477 if (transport_type == TRANSPORT_ROAD || transport_type == TRANSPORT_RAIL) StartSpriteCombine();
1478
1479 /* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
1480 * it doesn't disappear behind it
1481 */
1482 /* Bridge heads are drawn solid no matter how invisibility/transparency is set */
1483 AddSortableSpriteToDraw(psid[0].sprite, psid[0].pal, *ti, {{}, {TILE_SIZE, TILE_SIZE, static_cast<uint8_t>(ti->tileh == SLOPE_FLAT ? 0 : TILE_HEIGHT)}, {}});
1484
1485 if (transport_type == TRANSPORT_ROAD) {
1486 uint offset = tunnelbridge_direction;
1487 int z = ti->z;
1488 if (ti->tileh != SLOPE_FLAT) {
1489 offset = (offset + 1) & 1;
1490 z += TILE_HEIGHT;
1491 } else {
1492 offset += 2;
1493 }
1494
1495 /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
1496 DrawBridgeRoadBits(ti->tile, ti->x, ti->y, z, offset, true, is_custom_layout);
1497
1499 } else if (transport_type == TRANSPORT_RAIL) {
1500 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
1501 if (is_custom_layout || rti->UsesOverlay()) {
1502 SpriteID surface = rti->UsesOverlay() ? GetCustomRailSprite(rti, ti->tile, RTSG_BRIDGE) : rti->base_sprites.bridge_deck;
1503 if (surface != 0) {
1504 if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
1505 AddSortableSpriteToDraw(surface + ((DiagDirToAxis(tunnelbridge_direction) == AXIS_X) ? RTBO_X : RTBO_Y), PAL_NONE, *ti, {{0, 0, TILE_HEIGHT}, {TILE_SIZE, TILE_SIZE, 0}, {}});
1506 } else {
1507 AddSortableSpriteToDraw(surface + RTBO_SLOPE + tunnelbridge_direction, PAL_NONE, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT}, {}});
1508 }
1509 }
1510 }
1511
1512 /* PBS debugging, draw reserved tracks darker */
1513 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasTunnelBridgeReservation(ti->tile)) {
1514 if (rti->UsesOverlay()) {
1515 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
1516 if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
1517 AddSortableSpriteToDraw(overlay + RTO_X + DiagDirToAxis(tunnelbridge_direction), PALETTE_CRASH, *ti, {{0, 0, TILE_HEIGHT}, {TILE_SIZE, TILE_SIZE, 0}, {}});
1518 } else {
1519 AddSortableSpriteToDraw(overlay + RTO_SLOPE_NE + tunnelbridge_direction, PALETTE_CRASH, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT}, {}});
1520 }
1521 } else {
1522 if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
1523 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}, {}});
1524 } else {
1525 AddSortableSpriteToDraw(rti->base_sprites.single_sloped + tunnelbridge_direction, PALETTE_CRASH, *ti, {{}, {TILE_SIZE, TILE_SIZE, TILE_HEIGHT}, {}});
1526 }
1527 }
1528 }
1529
1532 DrawRailCatenary(ti);
1533 }
1534 }
1535
1536 BridgePillarFlags blocked_pillars;
1537 if (DiagDirToAxis(tunnelbridge_direction) == AXIS_X) {
1538 blocked_pillars = {BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE};
1539 } else {
1540 blocked_pillars = {BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE};
1541 }
1542 DrawBridgeMiddle(ti, blocked_pillars);
1543 }
1544}
1545
1546
1565static BridgePieces CalcBridgePiece(uint north, uint south)
1566{
1567 if (north == 1) {
1568 return BRIDGE_PIECE_NORTH;
1569 } else if (south == 1) {
1570 return BRIDGE_PIECE_SOUTH;
1571 } else if (north < south) {
1572 return north & 1 ? BRIDGE_PIECE_INNER_SOUTH : BRIDGE_PIECE_INNER_NORTH;
1573 } else if (north > south) {
1574 return south & 1 ? BRIDGE_PIECE_INNER_NORTH : BRIDGE_PIECE_INNER_SOUTH;
1575 } else {
1576 return north & 1 ? BRIDGE_PIECE_MIDDLE_EVEN : BRIDGE_PIECE_MIDDLE_ODD;
1577 }
1578}
1579
1590{
1591 if (transport_type == TRANSPORT_WATER) return BRIDGEPILLARFLAGS_ALL_CORNERS;
1592
1593 const BridgeSpec *spec = GetBridgeSpec(type);
1595 BridgePieces piece = CalcBridgePiece(GetTunnelBridgeLength(tile, rampnorth) + 1, GetTunnelBridgeLength(tile, rampsouth) + 1);
1596 Axis axis = TileX(rampnorth) == TileX(rampsouth) ? AXIS_Y : AXIS_X;
1597
1598 return spec->pillar_flags[piece][axis == AXIS_Y ? 1 : 0];
1599 }
1600
1601 return BRIDGEPILLARFLAGS_ALL_CORNERS;
1602}
1603
1609void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
1610{
1611 /* Sectional view of bridge bounding boxes:
1612 *
1613 * 1 2 1,2 = SpriteCombine of Bridge front/(back&floor) and RoadCatenary
1614 * 1 2 3 = empty helper BB
1615 * 1 7 2 4,5 = pillars under higher bridges
1616 * 1 6 88888 6 2 6 = elrail-pylons
1617 * 1 6 88888 6 2 7 = elrail-wire
1618 * 1 6 88888 6 2 <- TILE_HEIGHT 8 = rail-vehicle on bridge
1619 * 3333333333333 <- BB_Z_SEPARATOR
1620 * <- unused
1621 * 4 5 <- BB_HEIGHT_UNDER_BRIDGE
1622 * 4 5
1623 * 4 5
1624 *
1625 */
1626
1627 if (!IsBridgeAbove(ti->tile)) return;
1628
1629 TileIndex rampnorth = GetNorthernBridgeEnd(ti->tile);
1630 TileIndex rampsouth = GetSouthernBridgeEnd(ti->tile);
1631 TransportType transport_type = GetTunnelBridgeTransportType(rampsouth);
1632 Axis axis = GetBridgeAxis(ti->tile);
1633 BridgePillarFlags pillars;
1634 bool is_custom_layout; // Set if rail/road bridge uses a custom layout.
1635
1636 uint base_offset = GetBridgeMiddleAxisBaseOffset(axis);
1637 std::span<const PalSpriteID> psid;
1638 bool drawfarpillar;
1639 if (transport_type != TRANSPORT_WATER) {
1640 BridgeType bridge_type = GetBridgeType(rampsouth);
1641 BridgePieces bridge_piece = CalcBridgePiece(GetTunnelBridgeLength(ti->tile, rampnorth) + 1, GetTunnelBridgeLength(ti->tile, rampsouth) + 1);
1642 drawfarpillar = !HasBit(GetBridgeSpec(bridge_type)->flags, 0);
1643 base_offset += GetBridgeSpriteTableBaseOffset(transport_type, rampsouth);
1644 psid = GetBridgeSpriteTable(bridge_type, bridge_piece);
1645 pillars = GetBridgeTilePillarFlags(ti->tile, rampnorth, rampsouth, bridge_type, transport_type);
1646 is_custom_layout = BridgeHasCustomSpriteTable(bridge_type, bridge_piece);
1647 } else {
1648 drawfarpillar = true;
1649 psid = _aqueduct_sprite_table_middle;
1650 pillars = BRIDGEPILLARFLAGS_ALL_CORNERS;
1651 }
1652 psid = psid.subspan(base_offset, 3);
1653
1654 int x = ti->x;
1655 int y = ti->y;
1656 uint bridge_z = GetBridgePixelHeight(rampsouth);
1657 int z = bridge_z - BRIDGE_Z_START;
1658
1659 /* Add a bounding box that separates the bridge from things below it. */
1660 AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, x, y, bridge_z - TILE_HEIGHT + BB_Z_SEPARATOR, {{}, {TILE_SIZE, TILE_SIZE, 1}, {}});
1661
1662 /* Draw Trambits as SpriteCombine */
1663 if (transport_type == TRANSPORT_ROAD || transport_type == TRANSPORT_RAIL) StartSpriteCombine();
1664
1665 /* Draw floor and far part of bridge*/
1667 if (axis == AXIS_X) {
1668 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));
1669 } else {
1670 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));
1671 }
1672 }
1673
1674 if (transport_type == TRANSPORT_ROAD) {
1675 /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
1676 DrawBridgeRoadBits(rampsouth, x, y, bridge_z, axis ^ 1, false, is_custom_layout);
1677 } else if (transport_type == TRANSPORT_RAIL) {
1678 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(rampsouth));
1679 if ((is_custom_layout || rti->UsesOverlay()) && !IsInvisibilitySet(TO_BRIDGES)) {
1680 SpriteID surface = rti->UsesOverlay() ? GetCustomRailSprite(rti, rampsouth, RTSG_BRIDGE, TCX_ON_BRIDGE) : rti->base_sprites.bridge_deck;
1681 if (surface != 0) {
1682 AddSortableSpriteToDraw(surface + axis, PAL_NONE, x, y, bridge_z, {{}, {TILE_SIZE, TILE_SIZE, 0}, {}}, IsTransparencySet(TO_BRIDGES));
1683 }
1684 }
1685
1687 if (rti->UsesOverlay()) {
1688 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
1689 AddSortableSpriteToDraw(overlay + RTO_X + axis, PALETTE_CRASH, ti->x, ti->y, bridge_z, {{}, {TILE_SIZE, TILE_SIZE, 0}, {}}, IsTransparencySet(TO_BRIDGES));
1690 } else {
1692 }
1693 }
1694
1696
1697 if (HasRailCatenaryDrawn(GetRailType(rampsouth))) {
1699 }
1700 }
1701
1702 /* draw roof, the component of the bridge which is logically between the vehicle and the camera */
1704 if (axis == AXIS_X) {
1705 y += 12;
1706 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));
1707 } else {
1708 x += 12;
1709 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));
1710 }
1711 }
1712
1713 /* Draw TramFront as SpriteCombine */
1714 if (transport_type == TRANSPORT_ROAD) EndSpriteCombine();
1715
1716 /* Do not draw anything more if bridges are invisible */
1717 if (IsInvisibilitySet(TO_BRIDGES)) return;
1718
1719 if (blocked_pillars.Any(pillars)) return;
1720 DrawBridgePillars(psid[2], ti, axis, drawfarpillar, x, y, z);
1721}
1722
1723
1724static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y, bool ground_vehicle)
1725{
1726 auto [tileh, z] = GetTilePixelSlope(tile);
1727
1728 x &= 0xF;
1729 y &= 0xF;
1730
1731 if (IsTunnel(tile)) {
1732 /* In the tunnel entrance? */
1733 if (ground_vehicle) return z;
1734 } else { // IsBridge(tile)
1737
1738 /* On the bridge ramp? */
1739 if (ground_vehicle) {
1740 if (tileh != SLOPE_FLAT) return z + TILE_HEIGHT;
1741
1742 switch (dir) {
1743 default: NOT_REACHED();
1744 case DIAGDIR_NE: tileh = SLOPE_NE; break;
1745 case DIAGDIR_SE: tileh = SLOPE_SE; break;
1746 case DIAGDIR_SW: tileh = SLOPE_SW; break;
1747 case DIAGDIR_NW: tileh = SLOPE_NW; break;
1748 }
1749 }
1750 }
1751
1752 return z + GetPartialPixelZ(x, y, tileh);
1753}
1754
1755static Foundation GetFoundation_TunnelBridge(TileIndex tile, Slope tileh)
1756{
1758}
1759
1760static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc &td)
1761{
1763
1764 if (IsTunnel(tile)) {
1765 td.str = (tt == TRANSPORT_RAIL) ? STR_LAI_TUNNEL_DESCRIPTION_RAILROAD : STR_LAI_TUNNEL_DESCRIPTION_ROAD;
1766 } else { // IsBridge(tile)
1767 td.str = (tt == TRANSPORT_WATER) ? STR_LAI_BRIDGE_DESCRIPTION_AQUEDUCT : GetBridgeSpec(GetBridgeType(tile))->transport_name[tt];
1768 }
1769 td.owner[0] = GetTileOwner(tile);
1770
1771 Owner road_owner = INVALID_OWNER;
1772 Owner tram_owner = INVALID_OWNER;
1773 RoadType road_rt = (tt == TRANSPORT_ROAD) ? GetRoadTypeRoad(tile) : INVALID_ROADTYPE;
1774 RoadType tram_rt = (tt == TRANSPORT_ROAD) ? GetRoadTypeTram(tile) : INVALID_ROADTYPE;
1775 if (road_rt != INVALID_ROADTYPE) {
1776 const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
1777 td.roadtype = rti->strings.name;
1778 td.road_speed = rti->max_speed / 2;
1779 road_owner = GetRoadOwner(tile, RTT_ROAD);
1780 }
1781 if (tram_rt != INVALID_ROADTYPE) {
1782 const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
1783 td.tramtype = rti->strings.name;
1784 td.tram_speed = rti->max_speed / 2;
1785 tram_owner = GetRoadOwner(tile, RTT_TRAM);
1786 }
1787
1788 /* Is there a mix of owners? */
1789 if ((tram_owner != INVALID_OWNER && tram_owner != td.owner[0]) ||
1790 (road_owner != INVALID_OWNER && road_owner != td.owner[0])) {
1791 uint i = 1;
1792 if (road_owner != INVALID_OWNER) {
1793 td.owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
1794 td.owner[i] = road_owner;
1795 i++;
1796 }
1797 if (tram_owner != INVALID_OWNER) {
1798 td.owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
1799 td.owner[i] = tram_owner;
1800 }
1801 }
1802
1803 if (tt == TRANSPORT_RAIL) {
1804 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
1805 td.rail_speed = rti->max_speed;
1806 td.railtype = rti->strings.name;
1807
1808 if (!IsTunnel(tile)) {
1809 uint16_t spd = GetBridgeSpec(GetBridgeType(tile))->speed;
1810 /* rail speed special-cases 0 as unlimited, hides display of limit etc. */
1811 if (spd == UINT16_MAX) spd = 0;
1812 if (td.rail_speed == 0 || spd < td.rail_speed) {
1813 td.rail_speed = spd;
1814 }
1815 }
1816 } else if (tt == TRANSPORT_ROAD && !IsTunnel(tile)) {
1817 uint16_t spd = GetBridgeSpec(GetBridgeType(tile))->speed;
1818 /* road speed special-cases 0 as unlimited, hides display of limit etc. */
1819 if (spd == UINT16_MAX) spd = 0;
1820 if (road_rt != INVALID_ROADTYPE && (td.road_speed == 0 || spd < td.road_speed)) td.road_speed = spd;
1821 if (tram_rt != INVALID_ROADTYPE && (td.tram_speed == 0 || spd < td.tram_speed)) td.tram_speed = spd;
1822 }
1823}
1824
1825
1826static void TileLoop_TunnelBridge(TileIndex tile)
1827{
1828 bool snow_or_desert = HasTunnelBridgeSnowOrDesert(tile);
1830 case LandscapeType::Arctic: {
1831 /* As long as we do not have a snow density, we want to use the density
1832 * from the entry edge. For tunnels this is the lowest point for bridges the highest point.
1833 * (Independent of foundations) */
1834 int z = IsBridge(tile) ? GetTileMaxZ(tile) : GetTileZ(tile);
1835 if (snow_or_desert != (z > GetSnowLine())) {
1836 SetTunnelBridgeSnowOrDesert(tile, !snow_or_desert);
1837 MarkTileDirtyByTile(tile);
1838 }
1839 break;
1840 }
1841
1842 case LandscapeType::Tropic:
1843 if (GetTropicZone(tile) == TROPICZONE_DESERT && !snow_or_desert) {
1844 SetTunnelBridgeSnowOrDesert(tile, true);
1845 MarkTileDirtyByTile(tile);
1846 }
1847 break;
1848
1849 default:
1850 break;
1851 }
1852}
1853
1854static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
1855{
1856 TransportType transport_type = GetTunnelBridgeTransportType(tile);
1857 if (transport_type != mode || (transport_type == TRANSPORT_ROAD && !HasTileRoadType(tile, (RoadTramType)sub_mode))) return 0;
1858
1860 if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0;
1862}
1863
1864static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner new_owner)
1865{
1866 TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
1867 /* Set number of pieces to zero if it's the southern tile as we
1868 * don't want to update the infrastructure counts twice. */
1869 uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
1870
1872
1873 if (tt == TRANSPORT_ROAD) {
1874 for (RoadTramType rtt : _roadtramtypes) {
1875 /* Update all roadtypes, no matter if they are present */
1876 if (GetRoadOwner(tile, rtt) == old_owner) {
1877 RoadType rt = GetRoadType(tile, rtt);
1878 if (rt != INVALID_ROADTYPE) {
1879 /* Update company infrastructure counts. A full diagonal road tile has two road bits.
1880 * No need to dirty windows here, we'll redraw the whole screen anyway. */
1881 Company::Get(old_owner)->infrastructure.road[rt] -= num_pieces * 2;
1882 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += num_pieces * 2;
1883 }
1884
1885 SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
1886 }
1887 }
1888 }
1889
1890 if (!IsTileOwner(tile, old_owner)) return;
1891
1892 /* Update company infrastructure counts for rail and water as well.
1893 * No need to dirty windows here, we'll redraw the whole screen anyway. */
1894 Company *old = Company::Get(old_owner);
1895 if (tt == TRANSPORT_RAIL) {
1896 old->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1897 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.rail[GetRailType(tile)] += num_pieces;
1898 } else if (tt == TRANSPORT_WATER) {
1899 old->infrastructure.water -= num_pieces;
1900 if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.water += num_pieces;
1901 }
1902
1903 if (new_owner != INVALID_OWNER) {
1904 SetTileOwner(tile, new_owner);
1905 } else {
1906 if (tt == TRANSPORT_RAIL) {
1907 /* Since all of our vehicles have been removed, it is safe to remove the rail
1908 * bridge / tunnel. */
1910 assert(ret.Succeeded());
1911 } else {
1912 /* In any other case, we can safely reassign the ownership to OWNER_NONE. */
1913 SetTileOwner(tile, OWNER_NONE);
1914 }
1915 }
1916}
1917
1930template <typename T>
1931static void PrepareToEnterBridge(T *gv)
1932{
1933 if (HasBit(gv->gv_flags, GVF_GOINGUP_BIT)) {
1934 gv->z_pos++;
1935 ClrBit(gv->gv_flags, GVF_GOINGUP_BIT);
1936 } else {
1937 ClrBit(gv->gv_flags, GVF_GOINGDOWN_BIT);
1938 }
1939}
1940
1946static const uint8_t TUNNEL_SOUND_FRAME = 1;
1947
1956extern const uint8_t _tunnel_visibility_frame[DIAGDIR_END] = {12, 8, 8, 12};
1957
1958static VehicleEnterTileStates VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y)
1959{
1960 int z = GetSlopePixelZ(x, y, true) - v->z_pos;
1961
1962 if (abs(z) > 2) return VehicleEnterTileState::CannotEnter;
1963 /* Direction into the wormhole */
1964 const DiagDirection dir = GetTunnelBridgeDirection(tile);
1965 /* Direction of the vehicle */
1966 const DiagDirection vdir = DirToDiagDir(v->direction);
1967 /* New position of the vehicle on the tile */
1968 uint8_t pos = (DiagDirToAxis(vdir) == AXIS_X ? x : y) & TILE_UNIT_MASK;
1969 /* Number of units moved by the vehicle since entering the tile */
1970 uint8_t frame = (vdir == DIAGDIR_NE || vdir == DIAGDIR_NW) ? TILE_SIZE - 1 - pos : pos;
1971
1972 if (IsTunnel(tile)) {
1973 if (v->type == VEH_TRAIN) {
1974 Train *t = Train::From(v);
1975
1976 if (t->track != TRACK_BIT_WORMHOLE && dir == vdir) {
1977 if (t->IsFrontEngine() && frame == TUNNEL_SOUND_FRAME) {
1978 if (!PlayVehicleSound(t, VSE_TUNNEL) && RailVehInfo(t->engine_type)->engclass == 0) {
1979 SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
1980 }
1981 return {};
1982 }
1983 if (frame == _tunnel_visibility_frame[dir]) {
1984 t->tile = tile;
1985 t->track = TRACK_BIT_WORMHOLE;
1988 }
1989 }
1990
1991 if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) {
1992 /* We're at the tunnel exit ?? */
1993 t->tile = tile;
1994 t->track = DiagDirToDiagTrackBits(vdir);
1995 assert(t->track);
1998 }
1999 } else if (v->type == VEH_ROAD) {
2001
2002 /* Enter tunnel? */
2003 if (rv->state != RVSB_WORMHOLE && dir == vdir) {
2004 if (frame == _tunnel_visibility_frame[dir]) {
2005 /* Frame should be equal to the next frame number in the RV's movement */
2006 assert(frame == rv->frame + 1);
2007 rv->tile = tile;
2008 rv->state = RVSB_WORMHOLE;
2011 } else {
2012 return {};
2013 }
2014 }
2015
2016 /* We're at the tunnel exit ?? */
2017 if (dir == ReverseDiagDir(vdir) && frame == TILE_SIZE - _tunnel_visibility_frame[dir] && z == 0) {
2018 rv->tile = tile;
2019 rv->state = DiagDirToDiagTrackdir(vdir);
2020 rv->frame = frame;
2023 }
2024 }
2025 } else { // IsBridge(tile)
2026 if (v->type != VEH_SHIP) {
2027 /* modify speed of vehicle */
2028 uint16_t spd = GetBridgeSpec(GetBridgeType(tile))->speed;
2029
2030 if (v->type == VEH_ROAD) spd *= 2;
2031 Vehicle *first = v->First();
2032 first->cur_speed = std::min(first->cur_speed, spd);
2033 }
2034
2035 if (vdir == dir) {
2036 /* Vehicle enters bridge at the last frame inside this tile. */
2037 if (frame != TILE_SIZE - 1) return {};
2038 switch (v->type) {
2039 case VEH_TRAIN: {
2040 Train *t = Train::From(v);
2041 t->track = TRACK_BIT_WORMHOLE;
2043 break;
2044 }
2045
2046 case VEH_ROAD: {
2048 rv->state = RVSB_WORMHOLE;
2050 break;
2051 }
2052
2053 case VEH_SHIP:
2055 break;
2056
2057 default: NOT_REACHED();
2058 }
2060 } else if (vdir == ReverseDiagDir(dir)) {
2061 v->tile = tile;
2062 switch (v->type) {
2063 case VEH_TRAIN: {
2064 Train *t = Train::From(v);
2065 if (t->track == TRACK_BIT_WORMHOLE) {
2066 t->track = DiagDirToDiagTrackBits(vdir);
2068 }
2069 break;
2070 }
2071
2072 case VEH_ROAD: {
2074 if (rv->state == RVSB_WORMHOLE) {
2075 rv->state = DiagDirToDiagTrackdir(vdir);
2076 rv->frame = 0;
2078 }
2079 break;
2080 }
2081
2082 case VEH_SHIP: {
2083 Ship *ship = Ship::From(v);
2084 if (ship->state == TRACK_BIT_WORMHOLE) {
2085 ship->state = DiagDirToDiagTrackBits(vdir);
2087 }
2088 break;
2089 }
2090
2091 default: NOT_REACHED();
2092 }
2093 }
2094 }
2095 return {};
2096}
2097
2098static CommandCost TerraformTile_TunnelBridge(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
2099{
2101 DiagDirection direction = GetTunnelBridgeDirection(tile);
2102 Axis axis = DiagDirToAxis(direction);
2103 CommandCost res;
2104 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
2105
2106 /* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
2107 if ((direction == DIAGDIR_NW) || (direction == DIAGDIR_NE)) {
2108 CheckBridgeSlope(BRIDGE_PIECE_SOUTH, axis, tileh_old, z_old);
2109 res = CheckBridgeSlope(BRIDGE_PIECE_SOUTH, axis, tileh_new, z_new);
2110 } else {
2111 CheckBridgeSlope(BRIDGE_PIECE_NORTH, axis, tileh_old, z_old);
2112 res = CheckBridgeSlope(BRIDGE_PIECE_NORTH, axis, tileh_new, z_new);
2113 }
2114
2115 /* Surface slope is valid and remains unchanged? */
2116 if (res.Succeeded() && (z_old == z_new) && (tileh_old == tileh_new)) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
2117 }
2118
2119 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
2120}
2121
2122static CommandCost CheckBuildAbove_TunnelBridge(TileIndex tile, DoCommandFlags flags, Axis axis, int height)
2123{
2124 if (IsTunnel(tile)) return CommandCost();
2125
2126 if (axis != DiagDirToAxis(GetTunnelBridgeDirection(tile)) && height > GetBridgeHeight(tile)) {
2127 return CommandCost();
2128 }
2129
2130 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
2131}
2132
2133extern const TileTypeProcs _tile_type_tunnelbridge_procs = {
2134 DrawTile_TunnelBridge, // draw_tile_proc
2135 GetSlopePixelZ_TunnelBridge, // get_slope_z_proc
2136 ClearTile_TunnelBridge, // clear_tile_proc
2137 nullptr, // add_accepted_cargo_proc
2138 GetTileDesc_TunnelBridge, // get_tile_desc_proc
2139 GetTileTrackStatus_TunnelBridge, // get_tile_track_status_proc
2140 nullptr, // click_tile_proc
2141 nullptr, // animate_tile_proc
2142 TileLoop_TunnelBridge, // tile_loop_proc
2143 ChangeTileOwner_TunnelBridge, // change_tile_owner_proc
2144 nullptr, // add_produced_cargo_proc
2145 VehicleEnter_TunnelBridge, // vehicle_enter_tile_proc
2146 GetFoundation_TunnelBridge, // get_foundation_proc
2147 TerraformTile_TunnelBridge, // terraform_tile_proc
2148 CheckBuildAbove_TunnelBridge, // check_build_above_proc
2149};
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 It consists of a number of arrays.
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
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
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::@22 strings
Strings associated with the rail type.
uint8_t bridge_offset
Bridge offset.
Definition rail.h:185
struct RailTypeInfo::@19 base_sprites
Struct containing the main sprites.
SpriteID tunnel
tunnel sprites base
Definition rail.h:133
SpriteID single_sloped
single piece of rail for slopes
Definition rail.h:131
struct RoadTypeInfo::@25 strings
Strings associated with the rail type.
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
static Year year
Current year, starting at 0.
Functions related to clear (MP_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.
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.
uint GetPartialPixelZ(int x, int y, Slope corners)
Determines height at given coordinate of a slope.
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.
const TileTypeProcs *const _tile_type_procs[16]
Tile callback functions for each type of tile.
Definition landscape.cpp:69
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.).
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:48
@ SLOPE_SW
south and west corner are raised
Definition slope_type.h:56
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:49
@ SLOPE_NE
north and east corner are raised
Definition slope_type.h:58
@ SLOPE_SE
south and east corner are raised
Definition slope_type.h:57
@ SLOPE_NW
north and west corner are raised
Definition slope_type.h:55
Foundation
Enumeration for Foundations.
Definition slope_type.h:93
@ FOUNDATION_NONE
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:94
Functions related to sound.
@ SND_05_TRAIN_THROUGH_TUNNEL
3 == 0x03 Train enters tunnel: steam engine
Definition sound_type.h:50
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:86
TileIndex first_tile
The first tile being cleared, which then causes the whole object to be cleared.
Definition object_base.h:87
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 MP_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.
Tindex index
Index of this pool item.
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
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:36
uint16_t rail_speed
Speed limit of rail (bridges and track)
Definition tile_cmd.h:49
StringID str
Description of the tile.
Definition tile_cmd.h:37
std::array< Owner, 4 > owner
Name of the owner(s)
Definition tile_cmd.h:39
uint16_t tram_speed
Speed limit of tram (bridges and track)
Definition tile_cmd.h:53
StringID roadtype
Type of road on the tile.
Definition tile_cmd.h:50
StringID tramtype
Type of tram on the tile.
Definition tile_cmd.h:52
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:48
uint16_t road_speed
Speed limit of road (bridges and track)
Definition tile_cmd.h:51
std::array< StringID, 4 > owner_type
Type of each owner.
Definition tile_cmd.h:40
Tile information, used while rendering the tile.
Definition tile_cmd.h:30
Slope tileh
Slope of the tile.
Definition tile_cmd.h:31
TileIndex tile
Tile index.
Definition tile_cmd.h:32
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:152
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:78
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
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:551
@ 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.