OpenTTD Source 20260721-master-g25ec12c62d
rail_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
9
10#include "stdafx.h"
11#include "viewport_func.h"
12#include "command_func.h"
13#include "depot_base.h"
15#include "newgrf_debug.h"
16#include "newgrf_railtype.h"
17#include "train.h"
18#include "autoslope.h"
19#include "water.h"
20#include "tunnelbridge_map.h"
21#include "vehicle_func.h"
22#include "sound_func.h"
23#include "tunnelbridge.h"
24#include "elrail_func.h"
25#include "town.h"
26#include "pbs.h"
27#include "company_base.h"
28#include "core/backup_type.hpp"
31#include "strings_func.h"
32#include "company_gui.h"
33#include "object_map.h"
34#include "rail_cmd.h"
35#include "landscape_cmd.h"
36
37#include "table/strings.h"
38#include "table/railtypes.h"
39#include "table/track_land.h"
40
41#include "safeguards.h"
42
44typedef std::vector<Train *> TrainList;
45
46RailTypeInfo _railtypes[RAILTYPE_END];
47std::vector<RailType> _sorted_railtypes;
48RailTypes _railtypes_hidden_mask;
49
52 SIGNAL_TO_SOUTHWEST,
53 SIGNAL_TO_NORTHEAST,
54 SIGNAL_TO_SOUTHEAST,
55 SIGNAL_TO_NORTHWEST,
56 SIGNAL_TO_EAST,
57 SIGNAL_TO_WEST,
58 SIGNAL_TO_SOUTH,
59 SIGNAL_TO_NORTH,
60};
61
66{
67 static_assert(std::size(_original_railtypes) <= std::size(_railtypes));
68
69 auto insert = std::copy(std::begin(_original_railtypes), std::end(_original_railtypes), std::begin(_railtypes));
70 std::fill(insert, std::end(_railtypes), RailTypeInfo{});
71
72 _railtypes_hidden_mask = {};
73}
74
75void ResolveRailTypeGUISprites(RailTypeInfo *rti)
76{
78 if (cursors_base != 0) {
79 rti->gui_sprites.build_ns_rail = cursors_base + 0;
80 rti->gui_sprites.build_x_rail = cursors_base + 1;
81 rti->gui_sprites.build_ew_rail = cursors_base + 2;
82 rti->gui_sprites.build_y_rail = cursors_base + 3;
83 rti->gui_sprites.auto_rail = cursors_base + 4;
84 rti->gui_sprites.build_depot = cursors_base + 5;
85 rti->gui_sprites.build_tunnel = cursors_base + 6;
86 rti->gui_sprites.convert_rail = cursors_base + 7;
87 rti->cursor.rail_ns = cursors_base + 8;
88 rti->cursor.rail_swne = cursors_base + 9;
89 rti->cursor.rail_ew = cursors_base + 10;
90 rti->cursor.rail_nwse = cursors_base + 11;
91 rti->cursor.autorail = cursors_base + 12;
92 rti->cursor.depot = cursors_base + 13;
93 rti->cursor.tunnel = cursors_base + 14;
94 rti->cursor.convert = cursors_base + 15;
95 }
96
97 /* Array of default GUI signal sprite numbers. */
101
104 }}};
105
106 for (SignalType type : EnumRange(SignalType::End)) {
108 SpriteID red = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SignalState::Red, true);
109 SpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SignalState::Green, true);
110 rti->gui_sprites.signals[type][var][SignalState::Red] = (red != 0) ? red + SIGNAL_TO_SOUTH : _signal_lookup[var][type];
111 rti->gui_sprites.signals[type][var][SignalState::Green] = (green != 0) ? green + SIGNAL_TO_SOUTH : _signal_lookup[var][type] + 1;
112 }
113 }
114}
115
122static bool CompareRailTypes(const RailType &first, const RailType &second)
123{
125}
126
131{
132 _sorted_railtypes.clear();
133 for (RailTypeInfo &rti : _railtypes) {
134 RailType rt = rti.Index();
135
136 ResolveRailTypeGUISprites(&rti);
137 _railtypes_hidden_mask.Set(rt, rti.flags.Test(RailTypeFlag::Hidden));
138
139 if (rti.label.Empty()) continue;
140 _sorted_railtypes.push_back(rt);
141 }
142 std::sort(_sorted_railtypes.begin(), _sorted_railtypes.end(), CompareRailTypes);
143}
144
150RailType AllocateRailType(RailTypeLabel label)
151{
152 auto it = std::ranges::find(_railtypes, RailTypeLabel{}, &RailTypeInfo::label);
153 if (it == std::end(_railtypes)) return INVALID_RAILTYPE;
154
155 RailTypeInfo &rti = *it;
156 RailType rt = rti.Index();
157
158 /* Set up new rail type based on default rail. */
160 rti.label = label;
161 rti.alternate_labels.clear();
162
163 /* Make us compatible with ourself. */
164 rti.powered_railtypes = rt;
165 rti.compatible_railtypes = rt;
166
167 /* We also introduce ourself. */
168 rti.introduces_railtypes = rt;
169
170 /* Default sort order; order of allocation, but with some
171 * offsets so it's easier for NewGRF to pick a spot without
172 * changing the order of other (original) rail types.
173 * The << is so you can place other railtypes in between the
174 * other railtypes, the 7 is to be able to place something
175 * before the first (default) rail type. */
176 rti.sorting_order = rt << 4 | 7;
177
178 return rt;
179}
180
183 0xFF, // Dummy value to prevent `index - 1` while accesing.
184 14, 15, 22, 13,
185 0, 21, 17, 12,
186 23, 0, 18, 20,
187 19, 16
188};
189
190
191/* 4
192 * ---------
193 * |\ /|
194 * | \ 1/ |
195 * | \ / |
196 * | \ / |
197 * 16| \ |32
198 * | / \2 |
199 * | / \ |
200 * | / \ |
201 * |/ \|
202 * ---------
203 * 8
204 */
205
206
207
208/* MAP2 byte: abcd???? => Signal On? Same coding as map3lo
209 * MAP3LO byte: abcd???? => Signal Exists?
210 * a and b are for diagonals, upper and left,
211 * one for each direction. (ie a == NE->SW, b ==
212 * SW->NE, or v.v., I don't know. b and c are
213 * similar for lower and right.
214 * MAP2 byte: ????abcd => Type of ground.
215 * MAP3LO byte: ????abcd => Type of rail.
216 * MAP5: 00abcdef => rail
217 * 01abcdef => rail w/ signals
218 * 10uuuuuu => unused
219 * 11uuuudd => rail depot
220 */
221
231{
232 return EnsureNoTrainOnTrackBits(tile, track);
233}
234
242{
243 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
244
245 /* So, we have a tile with tracks on it (and possibly signals). Let's see
246 * what tracks first */
247 TrackBits current = GetTrackBits(tile); // The current track layout.
248 TrackBits future = current | to_build; // The track layout we want to build.
249
250 /* Are we really building something new? */
251 if (current == future) {
252 /* Nothing new is being built */
253 return CommandCost(STR_ERROR_ALREADY_BUILT);
254 }
255
256 /* Normally, we may overlap and any combination is valid */
257 return CommandCost();
258}
259
260
282
304
313{
314 if (bits.None()) return Foundation::None;
315
316 if (IsSteepSlope(tileh)) {
317 /* Test for inclined foundations */
318 if (bits == Track::X) return Foundation::InclinedX;
319 if (bits == Track::Y) return Foundation::InclinedY;
320
321 /* Get higher track */
322 Corner highest_corner = GetHighestSlopeCorner(tileh);
323 TrackBits higher_track = CornerToTrackBits(highest_corner);
324
325 /* Only higher track? */
326 if (bits == higher_track) return HalftileFoundation(highest_corner);
327
328 /* Overlap with higher track? */
329 if (TracksOverlap(bits | higher_track)) return Foundation::Invalid;
330
331 /* either lower track or both higher and lower track */
332 return bits.Any(higher_track) ? Foundation::SteepBoth : Foundation::SteepLower;
333 } else {
334 if (TrackBits{bits}.Reset(_valid_tracks_without_foundation[tileh]).None()) return Foundation::None;
335
336 bool valid_on_leveled = TrackBits{bits}.Reset(_valid_tracks_on_leveled_foundation[tileh]).None();
337
338 Corner track_corner;
339 switch (bits.base()) {
340 case TrackBits{Track::Left}.base(): track_corner = CORNER_W; break;
341 case TrackBits{Track::Lower}.base(): track_corner = CORNER_S; break;
342 case TrackBits{Track::Right}.base(): track_corner = CORNER_E; break;
343 case TrackBits{Track::Upper}.base(): track_corner = CORNER_N; break;
344
345 case TRACK_BIT_HORZ.base():
346 if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
347 if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
348 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
349
350 case TRACK_BIT_VERT.base():
351 if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
352 if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
353 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
354
355 case TrackBits{Track::X}.base():
357 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
358
359 case TrackBits{Track::Y}.base():
361 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
362
363 default:
364 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
365 }
366 /* Single diagonal track */
367
368 /* Track must be at least valid on leveled foundation */
369 if (!valid_on_leveled) return Foundation::Invalid;
370
371 /* If slope has three raised corners, build leveled foundation */
373
374 /* If neighboured corners of track_corner are lowered, build halftile foundation */
375 if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
376
377 /* else special anti-zig-zag foundation */
378 return SpecialRailFoundation(track_corner);
379 }
380}
381
382
392static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
393{
394 /* don't allow building on the lower side of a coast */
396 if (!IsSteepSlope(tileh) && TrackBits{rail_bits}.Set(existing).Reset(_valid_tracks_on_leveled_foundation[tileh]).Any()) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
397 }
398
399 Foundation f_new = GetRailFoundation(tileh, TrackBits{rail_bits}.Set(existing));
400
401 /* check track/slope combination */
402 if ((f_new == Foundation::Invalid) ||
403 ((f_new != Foundation::None) && (!_settings_game.construction.build_on_slopes))) {
404 return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
405 }
406
407 Foundation f_old = GetRailFoundation(tileh, existing);
408 return CommandCost(ExpensesType::Construction, f_new != f_old ? _price[Price::BuildFoundation] : (Money)0);
409}
410
411/* Validate functions for rail building */
412static inline bool ValParamTrackOrientation(Track track)
413{
414 return IsValidTrack(track);
415}
416
426CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
427{
429
430 if (!ValParamRailType(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
431
432 Slope tileh = GetTileSlope(tile);
433 TrackBits trackbit = track;
434
435 switch (GetTileType(tile)) {
436 case TileType::Railway: {
438 if (ret.Failed()) return ret;
439
440 if (!IsPlainRail(tile)) return Command<Commands::LandscapeClear>::Do(flags, tile); // just get appropriate error message
441
442 if (!IsCompatibleRail(GetRailType(tile), railtype)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
443
444 ret = CheckTrackCombination(tile, trackbit);
445 if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
446 if (ret.Failed()) return ret;
447
448 ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
449 if (ret.Failed()) return ret;
450 cost.AddCost(ret.GetCost());
451
452 if (HasSignals(tile) && TracksOverlap(GetTrackBits(tile) | track)) {
453 /* If adding the new track causes any overlap, all signals must be removed first */
454 if (!auto_remove_signals) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
455
456 for (Track existing_track : GetTrackBits(tile)) {
457 if (HasSignalOnTrack(tile, existing_track)) {
458 CommandCost ret_remove_signals = Command<Commands::RemoveSignal>::Do(flags, tile, existing_track);
459 if (ret_remove_signals.Failed()) return ret_remove_signals;
460 cost.AddCost(ret_remove_signals.GetCost());
461 }
462 }
463 }
464
465 /* If the rail types don't match, try to convert only if engines of
466 * the new rail type are not powered on the present rail type and engines of
467 * the present rail type are powered on the new rail type. */
468 if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
469 if (HasPowerOnRail(GetRailType(tile), railtype)) {
470 ret = Command<Commands::ConvertRail>::Do(flags, tile, tile, railtype, false);
471 if (ret.Failed()) return ret;
472 cost.AddCost(ret.GetCost());
473 } else {
474 return CMD_ERROR;
475 }
476 }
477
478 if (flags.Test(DoCommandFlag::Execute)) {
480 TrackBits bits = GetTrackBits(tile);
481 SetTrackBits(tile, bits | trackbit);
482 /* Subtract old infrastructure count. */
483 uint pieces = bits.Count();
484 if (TracksOverlap(bits)) pieces *= pieces;
485 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
486 /* Add new infrastructure count. */
487 pieces = (bits | trackbit).Count();
488 if (TracksOverlap(bits | trackbit)) pieces *= pieces;
489 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
491 }
492 break;
493 }
494
495 case TileType::Road: {
496 /* Level crossings may only be built on these slopes */
497 if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
498
499 if (!_settings_game.construction.crossing_with_competitor && _current_company != OWNER_DEITY) {
501 if (ret.Failed()) return ret;
502 }
503
505 if (ret.Failed()) return ret;
506
507 if (IsNormalRoad(tile)) {
508 if (HasRoadWorks(tile)) return CommandCost(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
509
510 if (GetDisallowedRoadDirections(tile).Any()) return CommandCost(STR_ERROR_CROSSING_ON_ONEWAY_ROAD);
511
512 if (RailNoLevelCrossings(railtype)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_RAIL);
513
514 RoadType roadtype_road = GetRoadTypeRoad(tile);
515 RoadType roadtype_tram = GetRoadTypeTram(tile);
516
517 if (roadtype_road != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_road)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
518 if (roadtype_tram != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_tram)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
519
522 if ((track == Track::X && !(road | tram).Any(ROAD_X)) ||
523 (track == Track::Y && !(road | tram).Any(ROAD_Y))) {
524 Owner road_owner = GetRoadOwner(tile, RoadTramType::Road);
525 Owner tram_owner = GetRoadOwner(tile, RoadTramType::Tram);
526 /* Disallow breaking end-of-line of someone else
527 * so trams can still reverse on this tile. */
528 if (Company::IsValidID(tram_owner) && tram.Count() == 1) {
529 ret = CheckOwnership(tram_owner);
530 if (ret.Failed()) return ret;
531 }
532
533 uint num_new_road_pieces = road.Any() ? 2 - road.Count() : 0;
534 if (num_new_road_pieces > 0) {
535 cost.AddCost(num_new_road_pieces * RoadBuildCost(roadtype_road));
536 }
537
538 uint num_new_tram_pieces = tram.Any() ? 2 - tram.Count() : 0;
539 if (num_new_tram_pieces > 0) {
540 cost.AddCost(num_new_tram_pieces * RoadBuildCost(roadtype_tram));
541 }
542
543 if (flags.Test(DoCommandFlag::Execute)) {
544 MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == Track::X ? Axis::Y : Axis::X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile));
545 UpdateLevelCrossing(tile, false);
547 Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR;
549 if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) {
550 Company::Get(road_owner)->infrastructure.road[roadtype_road] += num_new_road_pieces;
552 }
553 if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) {
554 Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces;
556 }
557 }
558 break;
559 }
560 }
561
562 if (IsLevelCrossing(tile) && GetCrossingRailTrack(tile) == trackbit) {
563 return CommandCost(STR_ERROR_ALREADY_BUILT);
564 }
565 [[fallthrough]];
566 }
567
568 default: {
569 /* Will there be flat water on the lower halftile? */
570 bool water_ground = IsTileType(tile, TileType::Water) && IsSlopeWithOneCornerRaised(tileh);
571
572 CommandCost ret = CheckRailSlope(tileh, trackbit, {}, tile);
573 if (ret.Failed()) return ret;
574 cost.AddCost(ret.GetCost());
575
576 ret = Command<Commands::LandscapeClear>::Do(flags, tile);
577 if (ret.Failed()) return ret;
578 cost.AddCost(ret.GetCost());
579
580 if (water_ground) {
583 }
584
585 if (flags.Test(DoCommandFlag::Execute)) {
586 MakeRailNormal(tile, _current_company, trackbit, railtype);
587 if (water_ground) {
590 }
591 Company::Get(_current_company)->infrastructure.rail[railtype]++;
593 }
594 break;
595 }
596 }
597
598 if (flags.Test(DoCommandFlag::Execute)) {
601 YapfNotifyTrackLayoutChange(tile, track);
602 }
603
604 cost.AddCost(RailBuildCost(railtype));
605 return cost;
606}
607
616{
618 bool crossing = false;
619
620 if (!ValParamTrackOrientation(track)) return CMD_ERROR;
621
622 /* Need to read tile owner now because it may change when the rail is removed
623 * Also, in case of floods, _current_company != owner
624 * There may be invalid tiletype even in exec run (when removing long track),
625 * so do not call GetTileOwner(tile) in any case here */
626 Owner owner = INVALID_OWNER;
627
628 Train *v = nullptr;
629
630 switch (GetTileType(tile)) {
631 case TileType::Road: {
632 if (!IsLevelCrossing(tile) || GetCrossingRailTrack(tile) != track) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
633
636 if (ret.Failed()) return ret;
637 }
638
639 if (!flags.Test(DoCommandFlag::Bankrupt)) {
641 if (ret.Failed()) return ret;
642 }
643
644 cost.AddCost(RailClearCost(GetRailType(tile)));
645
646 if (flags.Test(DoCommandFlag::Execute)) {
648
649 if (HasReservedTracks(tile, track)) {
650 v = GetTrainForReservation(tile, track);
651 if (v != nullptr) FreeTrainTrackReservation(v);
652 }
653
654 owner = GetTileOwner(tile);
655 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR;
659 }
660 break;
661 }
662
663 case TileType::Railway: {
664 TrackBits present;
665 /* There are no rails present at depots. */
666 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
667
670 if (ret.Failed()) return ret;
671 }
672
673 CommandCost ret = EnsureNoTrainOnTrack(tile, track);
674 if (ret.Failed()) return ret;
675
676 present = GetTrackBits(tile);
677 if (!present.Test(track)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
678 if (present == TRACK_BIT_CROSS) crossing = true;
679
680 cost.AddCost(RailClearCost(GetRailType(tile)));
681
682 /* Charge extra to remove signals on the track, if they are there */
683 if (HasSignalOnTrack(tile, track)) {
684 cost.AddCost(Command<Commands::RemoveSignal>::Do(flags, tile, track));
685 }
686
687 if (flags.Test(DoCommandFlag::Execute)) {
688 if (HasReservedTracks(tile, track)) {
689 v = GetTrainForReservation(tile, track);
690 if (v != nullptr) FreeTrainTrackReservation(v);
691 }
692
693 owner = GetTileOwner(tile);
694
695 /* Subtract old infrastructure count. */
696 uint pieces = present.Count();
697 if (TracksOverlap(present)) pieces *= pieces;
698 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= pieces;
699 /* Add new infrastructure count. */
700 present.Flip(track);
701 pieces = present.Count();
702 if (TracksOverlap(present)) pieces *= pieces;
703 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] += pieces;
705
706 if (present.None()) {
707 Slope tileh = GetTileSlope(tile);
708 /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */
710 bool docking = IsDockingTile(tile);
711 MakeShore(tile);
712 SetDockingTile(tile, docking);
713 } else {
714 DoClearSquare(tile);
715 }
717 } else {
718 SetTrackBits(tile, present);
720 }
721 }
722 break;
723 }
724
725 default: return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
726 }
727
728 if (flags.Test(DoCommandFlag::Execute)) {
729 /* if we got that far, 'owner' variable is set correctly */
730 assert(Company::IsValidID(owner));
731
733 if (crossing) {
734 /* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
735 * are removing one of these pieces, we'll need to update signals for
736 * both directions explicitly, as after the track is removed it won't
737 * 'connect' with the other piece. */
738 AddTrackToSignalBuffer(tile, Track::X, owner);
739 AddTrackToSignalBuffer(tile, Track::Y, owner);
742 } else {
743 AddTrackToSignalBuffer(tile, track, owner);
744 YapfNotifyTrackLayoutChange(tile, track);
745 }
746
747 if (v != nullptr) TryPathReserve(v, true);
748 }
749
750 return cost;
751}
752
753
762{
763 assert(IsPlainRailTile(t));
764
765 bool flooded = false;
766 if (GetRailGroundType(t) == RailGroundType::HalfTileWater) return flooded;
767
768 Slope tileh = GetTileSlope(t);
769 TrackBits rail_bits = GetTrackBits(t);
770
771 if (IsSlopeWithOneCornerRaised(tileh)) {
773
774 TrackBits to_remove = lower_track & rail_bits;
775 if (to_remove.Any()) {
777 flooded = Command<Commands::RemoveRail>::Do(DoCommandFlag::Execute, t, FindFirstTrack(to_remove)).Succeeded();
778 if (!flooded) return flooded; // not yet floodable
779
780 rail_bits.Reset(to_remove);
781 if (rail_bits.None()) {
782 MakeShore(t);
784 return flooded;
785 }
786 }
787
788 if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
789 flooded = true;
792 }
793 } else {
794 /* Make shore on steep slopes and 'three-corners-raised'-slopes. */
795 if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), tileh) == 0) {
796 if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) {
797 flooded = true;
800 }
801 }
802 }
803 return flooded;
804}
805
808 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
809 { 0, 0 },
810 { 0, 0 },
811 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
812 { 0, 0 },
813 { 0, 0 }
814}}};
815
823{
824 return static_cast<Trackdir>(to_underlying(trackdir) ^ 1);
825}
826
827static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
828{
829 int x = TileX(start);
830 int y = TileY(start);
831 int ex = TileX(end);
832 int ey = TileY(end);
833
834 if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR;
835
836 /* calculate delta x,y from start to end tile */
837 int dx = ex - x;
838 int dy = ey - y;
839
840 /* calculate delta x,y for the first direction */
841 int trdx = _trackdelta[*trackdir].x;
842 int trdy = _trackdelta[*trackdir].y;
843
844 if (!IsDiagonalTrackdir(*trackdir)) {
845 trdx += _trackdelta[GetOtherTrackdir(*trackdir)].x;
846 trdy += _trackdelta[GetOtherTrackdir(*trackdir)].y;
847 }
848
849 /* validate the direction */
850 while ((trdx <= 0 && dx > 0) ||
851 (trdx >= 0 && dx < 0) ||
852 (trdy <= 0 && dy > 0) ||
853 (trdy >= 0 && dy < 0)) {
854 if (!HasBit(to_underlying(*trackdir), 3)) { // first direction is invalid, try the other
855 *trackdir = ReverseTrackdir(*trackdir); // reverse the direction
856 trdx = -trdx;
857 trdy = -trdy;
858 } else { // other direction is invalid too, invalid drag
859 return CMD_ERROR;
860 }
861 }
862
863 /* (for diagonal tracks, this is already made sure of by above test), but:
864 * for non-diagonal tracks, check if the start and end tile are on 1 line */
865 if (!IsDiagonalTrackdir(*trackdir)) {
866 trdx = _trackdelta[*trackdir].x;
867 trdy = _trackdelta[*trackdir].y;
868 if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) return CMD_ERROR;
869 }
870
871 return CommandCost();
872}
873
886static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
887{
889
890 if ((!remove && !ValParamRailType(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
891 if (end_tile >= Map::Size() || tile >= Map::Size()) return CMD_ERROR;
892
893 Trackdir trackdir = TrackToTrackdir(track);
894
895 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
896 if (ret.Failed()) return ret;
897
898 bool had_success = false;
899 CommandCost last_error = CMD_ERROR;
900 for (;;) {
901 ret = remove ? Command<Commands::RemoveRail>::Do(flags, tile, TrackdirToTrack(trackdir)) : Command<Commands::BuildRail>::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals);
902 if (!remove && !fail_on_obstacle && last_error.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) had_success = true;
903
904 if (ret.Failed()) {
905 last_error = std::move(ret);
906 if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
907 if (fail_on_obstacle) return last_error;
908 if (had_success) break; // Keep going if we haven't constructed any rail yet, skipping the start of the drag
909 }
910
911 /* Ownership errors are more important. */
912 if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
913 } else {
914 had_success = true;
915 total_cost.AddCost(ret.GetCost());
916 }
917
918 if (tile == end_tile) break;
919
920 tile += ToTileIndexDiff(_trackdelta[trackdir]);
921
922 /* toggle railbit for the non-diagonal tracks */
923 if (!IsDiagonalTrackdir(trackdir)) trackdir = GetOtherTrackdir(trackdir);
924 }
925
926 if (had_success) return total_cost;
927 return last_error;
928}
929
943CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
944{
945 return CmdRailTrackHelper(flags, start_tile, end_tile, railtype, track, false, auto_remove_signals, fail_on_obstacle);
946}
947
959{
960 return CmdRailTrackHelper(flags, start_tile, end_tile, INVALID_RAILTYPE, track, true, false, false);
961}
962
972{
973 /* check railtype and valid direction for depot (0 through 3), 4 in total */
974 if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
975
976 Slope tileh = GetTileSlope(tile);
977
979
980 /* Prohibit construction if
981 * The tile is non-flat AND
982 * 1) build-on-slopes is disabled
983 * 2) the tile is steep i.e. spans two height levels
984 * 3) the exit points in the wrong direction
985 */
986
987 if (tileh != SLOPE_FLAT) {
988 if (!_settings_game.construction.build_on_slopes) return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
989 if (!CanBuildDepotByTileh(dir, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
991 }
992
993 /* Allow the user to rotate the depot instead of having to destroy it and build it again */
994 bool rotate_existing_depot = false;
995 Train *v = nullptr;
996 if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
998 if (ret.Failed()) return ret;
999
1000 DiagDirection old_dir = GetRailDepotDirection(tile);
1001 if (dir == old_dir) return CommandCost();
1002
1003 ret = EnsureNoVehicleOnGround(tile);
1004 if (ret.Failed()) return ret;
1005
1006 if (HasDepotReservation(tile)) {
1007 v = GetTrainForReservation(tile, DiagDirToDiagTrack(old_dir));
1008 }
1009
1010 rotate_existing_depot = true;
1011 }
1012
1013 if (!rotate_existing_depot) {
1014 cost.AddCost(Command<Commands::LandscapeClear>::Do(flags, tile));
1015 if (cost.Failed()) return cost;
1016
1017 if (IsBridgeAbove(tile)) {
1019 if (height_diff > 0) return CommandCostWithParam(STR_ERROR_BRIDGE_TOO_LOW_FOR_TRAIN_DEPOT, height_diff * TILE_HEIGHT_STEP);
1020 }
1021
1022 if (!Depot::CanAllocateItem()) return CMD_ERROR;
1023 }
1024
1025 if (flags.Test(DoCommandFlag::Execute)) {
1026 if (v != nullptr) FreeTrainTrackReservation(v);
1027
1028 if (rotate_existing_depot) {
1029 SetDepotReservation(tile, false);
1030 SetRailDepotExitDirection(tile, dir);
1031 } else {
1032 Depot *d = Depot::Create(tile);
1033
1034 MakeRailDepot(tile, _current_company, d->index, dir, railtype);
1035 MakeDefaultName(d);
1036
1037 Company::Get(_current_company)->infrastructure.rail[railtype]++;
1039 }
1040
1041 MarkTileDirtyByTile(tile);
1044
1045 if (v != nullptr) TryPathReserve(v, true);
1046 }
1047
1049 cost.AddCost(RailBuildCost(railtype));
1050 return cost;
1051}
1052
1071CommandCost CmdBuildSingleSignal(DoCommandFlags flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy)
1072{
1073 if (sigtype >= SignalType::End || sigvar >= SignalVariant::End) return CMD_ERROR;
1074 if (cycle_start > cycle_stop || cycle_stop >= SignalType::End) return CMD_ERROR;
1075
1076 if (ctrl_pressed) sigvar = (sigvar == SignalVariant::Electric ? SignalVariant::Semaphore : SignalVariant::Electric);
1077
1078 /* You can only build signals on plain rail tiles, and the selected track must exist */
1079 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
1080 !HasTrack(tile, track)) {
1081 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1082 }
1083 /* Protect against invalid signal copying */
1084 if (signals_copy != 0 && (signals_copy & SignalOnTrack(track)) == 0) return CMD_ERROR;
1085
1086 CommandCost ret = CheckTileOwnership(tile);
1087 if (ret.Failed()) return ret;
1088
1089 /* See if this is a valid track combination for signals (no overlap) */
1090 if (TracksOverlap(GetTrackBits(tile))) return CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
1091
1092 /* In case we don't want to change an existing signal, return without error. */
1093 if (skip_existing_signals && HasSignalOnTrack(tile, track)) return CommandCost();
1094
1095 /* you can not convert a signal if no signal is on track */
1096 if (convert_signal && !HasSignalOnTrack(tile, track)) return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1097
1098 CommandCost cost;
1099 if (!HasSignalOnTrack(tile, track)) {
1100 /* build new signals */
1102 } else {
1103 if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
1104 /* convert signals <-> semaphores */
1106
1107 } else if (convert_signal) {
1108 /* convert button pressed */
1109 if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
1110 /* it costs money to change signal variant (light or semaphore) */
1112 } else {
1113 /* it is free to change signal type (block, exit, entry, combo, path, etc) */
1114 cost = CommandCost();
1115 }
1116
1117 } else {
1118 /* it is free to change orientation or number of signals on the tile (for block/presignals which allow signals in both directions) */
1119 cost = CommandCost();
1120 }
1121 }
1122
1123 if (flags.Test(DoCommandFlag::Execute)) {
1124 Train *v = nullptr;
1125 /* The new/changed signal could block our path. As this can lead to
1126 * stale reservations, we clear the path reservation here and try
1127 * to redo it later on. */
1128 if (HasReservedTracks(tile, track)) {
1129 v = GetTrainForReservation(tile, track);
1130 if (v != nullptr) FreeTrainTrackReservation(v);
1131 }
1132
1133 if (!HasSignals(tile)) {
1134 /* there are no signals at all on this tile yet */
1135 SetHasSignals(tile, true);
1136 SetSignalStates(tile, 0xF); // all signals are on
1137 SetPresentSignals(tile, 0); // no signals built by default
1138 SetSignalType(tile, track, sigtype);
1139 SetSignalVariant(tile, track, sigvar);
1140 }
1141
1142 /* Subtract old signal infrastructure count. */
1143 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1144
1145 if (signals_copy == 0) {
1146 if (!HasSignalOnTrack(tile, track)) {
1147 /* build new signals */
1148 SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
1149 SetSignalType(tile, track, sigtype);
1150 SetSignalVariant(tile, track, sigvar);
1151 while (num_dir_cycle-- > 0) CycleSignalSide(tile, track);
1152 } else {
1153 if (convert_signal) {
1154 /* convert signal button pressed */
1155 if (ctrl_pressed) {
1156 /* toggle the present signal variant: SignalVariant::Electric <-> SignalVariant::Semaphore */
1158 /* Query current signal type so the check for PBS signals below works. */
1159 sigtype = GetSignalType(tile, track);
1160 } else {
1161 /* convert the present signal to the chosen type and variant */
1162 SetSignalType(tile, track, sigtype);
1163 SetSignalVariant(tile, track, sigvar);
1164 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1166 }
1167 }
1168
1169 } else if (ctrl_pressed) {
1170 /* cycle between cycle_start and cycle_end */
1171 sigtype = static_cast<SignalType>(to_underlying(GetSignalType(tile, track)) + 1);
1172
1173 if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
1174
1175 SetSignalType(tile, track, sigtype);
1176 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1178 }
1179 } else {
1180 /* cycle the signal side: both -> left -> right -> both -> ... */
1181 CycleSignalSide(tile, track);
1182 /* Query current signal type so the check for PBS signals below works. */
1183 sigtype = GetSignalType(tile, track);
1184 }
1185 }
1186 } else {
1187 /* If CmdBuildManySignals is called with copying signals, just copy the
1188 * direction of the first signal given as parameter by CmdBuildManySignals */
1189 SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (signals_copy & SignalOnTrack(track)));
1190 SetSignalVariant(tile, track, sigvar);
1191 SetSignalType(tile, track, sigtype);
1192 }
1193
1194 /* Add new signal infrastructure count. */
1195 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1197
1198 if (IsPbsSignal(sigtype)) {
1199 /* PBS signals should show red unless they are on reserved tiles without a train. */
1200 uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
1201 SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((GetRailReservationTrackBits(tile).Test(track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask));
1202 }
1203 MarkTileDirtyByTile(tile);
1205 YapfNotifyTrackLayoutChange(tile, track);
1206 if (v != nullptr && v->track != Track::Depot) {
1207 Train *moving_front = v->GetMovingFront();
1208 /* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1209 if (!((v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) ||
1210 !IsSafeWaitingPosition(v, moving_front->tile, moving_front->GetVehicleTrackdir(), true, _settings_game.pf.forbid_90_deg)) {
1211 TryPathReserve(v, true);
1212 }
1213 }
1214 }
1215
1216 return cost;
1217}
1218
1219static bool AdvanceSignalAutoFill(TileIndex &tile, Trackdir &trackdir, bool remove)
1220{
1221 /* We only process starting tiles of tunnels or bridges so jump to the other end before moving further. */
1223
1224 tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
1225 if (tile == INVALID_TILE) return false;
1226
1227 /* Check for track bits on the new tile */
1229
1230 if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
1231 trackdirbits &= TrackdirReachesTrackdirs(trackdir);
1232
1233 /* No track bits, must stop */
1234 if (trackdirbits.None()) return false;
1235
1236 /* Get the first track dir */
1237 trackdir = RemoveFirstTrackdir(trackdirbits);
1238
1239 /* Any left? It's a junction so we stop */
1240 if (trackdirbits.Any()) return false;
1241
1242 switch (GetTileType(tile)) {
1243 case TileType::Railway:
1244 if (IsRailDepot(tile)) return false;
1245 if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1246 break;
1247
1248 case TileType::Road:
1249 if (!IsLevelCrossing(tile)) return false;
1250 break;
1251
1253 if (GetTunnelBridgeTransportType(tile) != TransportType::Rail) return false;
1254 if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
1255 break;
1256 }
1257
1258 default: return false;
1259 }
1260 return true;
1261}
1262
1278static CommandCost CmdSignalTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool remove, bool autofill, bool minimise_gaps, int signal_density)
1279{
1281
1282 if (end_tile >= Map::Size() || !ValParamTrackOrientation(track)) return CMD_ERROR;
1283 if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
1284 if (!remove && (sigtype >= SignalType::End || sigvar >= SignalVariant::End)) return CMD_ERROR;
1285
1286 if (!IsPlainRailTile(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1287 TileIndex start_tile = tile;
1288
1289 /* Interpret signal_density as the logical length of said amount of tiles in X/Y direction. */
1290 signal_density *= TILE_AXIAL_DISTANCE;
1291
1292 Trackdir trackdir = TrackToTrackdir(track);
1293 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
1294 if (ret.Failed()) return ret;
1295
1296 track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
1297 Trackdir start_trackdir = trackdir;
1298
1299 /* Must start on a valid track to be able to avoid loops */
1300 if (!HasTrack(tile, track)) return CMD_ERROR;
1301
1302 uint8_t signals;
1303 /* copy the signal-style of the first rail-piece if existing */
1304 if (HasSignalOnTrack(tile, track)) {
1305 signals = GetPresentSignals(tile) & SignalOnTrack(track);
1306 assert(signals != 0);
1307
1308 /* copy signal/semaphores style (independent of CTRL) */
1309 sigvar = GetSignalVariant(tile, track);
1310
1311 sigtype = GetSignalType(tile, track);
1312 /* Don't but copy entry or exit-signal type */
1313 if (sigtype == SignalType::Entry || sigtype == SignalType::Exit) sigtype = SignalType::Block;
1314 } else { // no signals exist, drag a two-way signal stretch
1315 signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
1316 }
1317
1318 uint8_t signal_dir = 0;
1319 if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0);
1320 if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
1321
1322 /* signal_ctr - amount of tiles already processed
1323 * last_used_ctr - amount of tiles before previously placed signal
1324 * signals_density - setting to put signal on every Nth tile (double space on |, -- tracks)
1325 * last_suitable_ctr - amount of tiles before last possible signal place
1326 * last_suitable_tile - last tile where it is possible to place a signal
1327 * last_suitable_trackdir - trackdir of the last tile
1328 **********
1329 * trackdir - trackdir to build with autorail
1330 * semaphores - semaphores or signals
1331 * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1332 * and convert all others to semaphore/signal
1333 * remove - 1 remove signals, 0 build signals */
1334 int signal_ctr = 0;
1335 int last_used_ctr = -signal_density; // to force signal at first tile
1336 int last_suitable_ctr = 0;
1337 TileIndex last_suitable_tile = INVALID_TILE;
1338 Trackdir last_suitable_trackdir = Trackdir::Invalid;
1339 CommandCost last_error = CMD_ERROR;
1340 bool had_success = false;
1341 auto build_signal = [&](TileIndex tile, Trackdir trackdir, bool test_only) {
1342 /* Pick the correct orientation for the track direction */
1343 uint8_t signals = 0;
1344 if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
1345 if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
1346
1347 DoCommandFlags do_flags = test_only ? DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) : flags;
1348 CommandCost ret = remove ? Command<Commands::RemoveSignal>::Do(do_flags, tile, TrackdirToTrack(trackdir)) : Command<Commands::BuildSignal>::Do(do_flags, tile, TrackdirToTrack(trackdir), sigtype, sigvar, false, signal_ctr == 0, mode, SignalType::Block, SignalType::Block, 0, signals);
1349
1350 if (test_only) return ret.Succeeded();
1351
1352 if (ret.Succeeded()) {
1353 had_success = true;
1354 total_cost.AddCost(ret.GetCost());
1355 } else {
1356 /* The "No railway" error is the least important one. */
1357 if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
1358 last_error.GetErrorMessage() == INVALID_STRING_ID) {
1359 last_error = ret;
1360 }
1361 }
1362 return ret.Succeeded();
1363 };
1364
1365 for (;;) {
1366 if (remove) {
1367 /* In remove mode last_* stuff doesn't matter, we simply try to clear every tile. */
1368 build_signal(tile, trackdir, false);
1369 } else if (minimise_gaps) {
1370 /* We're trying to minimize gaps wherever possible, so keep track of last suitable
1371 * position and use it if current gap exceeds required signal density. */
1372
1373 if (signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1374 /* We overshot so build a signal in last good location. */
1375 if (build_signal(last_suitable_tile, last_suitable_trackdir, false)) {
1376 last_suitable_tile = INVALID_TILE;
1377 last_used_ctr = last_suitable_ctr;
1378 }
1379 }
1380
1381 if (signal_ctr == last_used_ctr + signal_density) {
1382 /* Current gap matches the required density, build a signal. */
1383 if (build_signal(tile, trackdir, false)) {
1384 last_used_ctr = signal_ctr;
1385 last_suitable_tile = INVALID_TILE;
1386 }
1387 } else {
1388 /* Test tile for a potential signal spot. */
1389 if (build_signal(tile, trackdir, true)) {
1390 last_suitable_tile = tile;
1391 last_suitable_ctr = signal_ctr;
1392 last_suitable_trackdir = trackdir;
1393 }
1394 }
1395 } else if (signal_ctr >= last_used_ctr + signal_density) {
1396 /* We're always keeping regular interval between signals so doesn't matter whether we succeed or not. */
1397 build_signal(tile, trackdir, false);
1398 last_used_ctr = signal_ctr;
1399 }
1400
1401 if (autofill) {
1402 switch (GetTileType(tile)) {
1403 case TileType::Railway:
1404 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1405 break;
1406
1407 case TileType::Road:
1408 signal_ctr += TILE_AXIAL_DISTANCE;
1409 break;
1410
1413 if (remove || minimise_gaps) {
1414 signal_ctr += len;
1415 } else {
1416 /* To keep regular interval we need to emulate placing signals on a bridge.
1417 * We start with TILE_AXIAL_DISTANCE as one bridge tile gets processed in the main loop. */
1418 signal_ctr += TILE_AXIAL_DISTANCE;
1419 for (uint i = TILE_AXIAL_DISTANCE; i < len; i += TILE_AXIAL_DISTANCE) {
1420 if (signal_ctr >= last_used_ctr + signal_density) last_used_ctr = signal_ctr;
1421 signal_ctr += TILE_AXIAL_DISTANCE;
1422 }
1423 }
1424 break;
1425 }
1426
1427 default: break;
1428 }
1429
1430 if (!AdvanceSignalAutoFill(tile, trackdir, remove)) break;
1431
1432 /* Prevent possible loops */
1433 if (tile == start_tile && trackdir == start_trackdir) break;
1434 } else {
1435 if (tile == end_tile) break;
1436
1437 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1438 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1439
1440 tile += ToTileIndexDiff(_trackdelta[trackdir]);
1441 if (!IsDiagonalTrackdir(trackdir)) trackdir = GetOtherTrackdir(trackdir);
1442 }
1443 }
1444
1445 /* We may end up with the current gap exceeding the signal density so fix that if needed. */
1446 if (!remove && minimise_gaps && signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1447 build_signal(last_suitable_tile, last_suitable_trackdir, false);
1448 }
1449
1450 return had_success ? total_cost : last_error;
1451}
1452
1469CommandCost CmdBuildSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool autofill, bool minimise_gaps, uint8_t signal_density)
1470{
1471 return CmdSignalTrackHelper(flags, tile, end_tile, track, sigtype, sigvar, mode, false, autofill, minimise_gaps, signal_density);
1472}
1473
1482{
1483 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
1484 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1485 }
1486 if (!HasSignalOnTrack(tile, track)) {
1487 return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1488 }
1489
1490 /* Only water can remove signals from anyone */
1492 CommandCost ret = CheckTileOwnership(tile);
1493 if (ret.Failed()) return ret;
1494 }
1495
1496 /* Do it? */
1497 if (flags.Test(DoCommandFlag::Execute)) {
1498 Train *v = nullptr;
1499 if (HasReservedTracks(tile, track)) {
1500 v = GetTrainForReservation(tile, track);
1501 } else if (IsPbsSignal(GetSignalType(tile, track))) {
1502 /* PBS signal, might be the end of a path reservation. */
1503 Trackdir td = TrackToTrackdir(track);
1504 for (int i = 0; v == nullptr && i < 2; i++, td = ReverseTrackdir(td)) {
1505 /* Only test the active signal side. */
1506 if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
1509 if (HasReservedTracks(next, tracks)) {
1511 }
1512 }
1513 }
1514 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1515 SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
1516 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1518
1519 /* removed last signal from tile? */
1520 if (GetPresentSignals(tile) == 0) {
1521 SetSignalStates(tile, 0);
1522 SetHasSignals(tile, false);
1523 SetSignalVariant(tile, Track::Invalid, SignalVariant::Electric); // remove any possible semaphores
1524 }
1525
1526 AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
1527 YapfNotifyTrackLayoutChange(tile, track);
1528 if (v != nullptr) TryPathReserve(v, false);
1529
1530 MarkTileDirtyByTile(tile);
1531 }
1532
1534}
1535
1547CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
1548{
1549 return CmdSignalTrackHelper(flags, tile, end_tile, track, SignalType::Block, SignalVariant::Electric, false, true, autofill, false, 1); // bit 5 is remove bit
1550}
1551
1562CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
1563{
1564 TileIndex area_end = tile;
1565
1566 if (!ValParamRailType(totype)) return CMD_ERROR;
1567 if (area_start >= Map::Size()) return CMD_ERROR;
1568
1569 TrainList affected_trains;
1570
1572 CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
1573 bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
1574
1575 std::unique_ptr<TileIterator> iter = TileIterator::Create(area_start, area_end, diagonal);
1576 for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
1577 TileType tt = GetTileType(tile);
1578
1579 /* Check if there is any track on tile */
1580 switch (tt) {
1581 case TileType::Railway:
1582 break;
1583 case TileType::Station:
1584 if (!HasStationRail(tile)) continue;
1585 break;
1586 case TileType::Road:
1587 if (!IsLevelCrossing(tile)) continue;
1588 if (RailNoLevelCrossings(totype)) {
1589 error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
1590 continue;
1591 }
1592 break;
1595 break;
1596 default: continue;
1597 }
1598
1599 /* Original railtype we are converting from */
1600 RailType type = GetRailType(tile);
1601
1602 /* Converting to the same type or converting 'hidden' elrail -> rail */
1603 if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
1604
1605 /* Trying to convert other's rail */
1606 CommandCost ret = CheckTileOwnership(tile);
1607 if (ret.Failed()) {
1608 error = std::move(ret);
1609 continue;
1610 }
1611
1612 std::vector<Train *> vehicles_affected;
1613
1614 /* Vehicle on the tile when not converting Rail <-> ElRail
1615 * Tunnels and bridges have special check later */
1616 if (tt != TileType::TunnelBridge) {
1617 if (!IsCompatibleRail(type, totype)) {
1619 if (ret.Failed()) {
1620 error = std::move(ret);
1621 continue;
1622 }
1623 }
1624 if (flags.Test(DoCommandFlag::Execute)) { // we can safely convert, too
1625 for (Track track : GetReservedTrackbits(tile)) {
1626 Train *v = GetTrainForReservation(tile, track);
1627 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1628 /* No power on new rail type, reroute. */
1630 vehicles_affected.push_back(v);
1631 }
1632 }
1633
1634 /* Update the company infrastructure counters. */
1635 if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
1636 Company *c = Company::Get(GetTileOwner(tile));
1637 uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
1638 if (IsPlainRailTile(tile)) {
1639 TrackBits bits = GetTrackBits(tile);
1640 num_pieces = bits.Count();
1641 if (TracksOverlap(bits)) num_pieces *= num_pieces;
1642 }
1643 c->infrastructure.rail[type] -= num_pieces;
1644 c->infrastructure.rail[totype] += num_pieces;
1646 }
1647
1648 SetRailType(tile, totype);
1649 MarkTileDirtyByTile(tile);
1650 /* update power of train on this tile */
1651 for (Vehicle *v : VehiclesOnTile(tile)) {
1652 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1653 }
1654 }
1655 }
1656
1657 switch (tt) {
1658 default: NOT_REACHED();
1659 case TileType::Railway:
1660 switch (GetRailTileType(tile)) {
1662 if (flags.Test(DoCommandFlag::Execute)) {
1663 /* notify YAPF about the track layout change */
1665
1666 /* Update build vehicle window related to this depot */
1667 InvalidateWindowData(WindowClass::VehicleDepot, tile);
1668 InvalidateWindowData(WindowClass::BuildVehicle, tile);
1669 }
1670 found_convertible_track = true;
1671 cost.AddCost(RailConvertCost(type, totype));
1672 break;
1673
1674 default: // RailTileType::Normal, RailTileType::Signals
1675 if (flags.Test(DoCommandFlag::Execute)) {
1676 /* notify YAPF about the track layout change */
1677 for (Track track : GetTrackBits(tile)) {
1678 YapfNotifyTrackLayoutChange(tile, track);
1679 }
1680 }
1681 found_convertible_track = true;
1682 cost.AddCost(RailConvertCost(type, totype) * GetTrackBits(tile).Count());
1683 break;
1684 }
1685 break;
1686
1688 TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
1689
1690 /* If both ends of tunnel/bridge are in the range, do not try to convert twice -
1691 * it would cause assert because of different test and exec runs */
1692 if (endtile < tile) {
1693 if (diagonal) {
1694 if (DiagonalTileArea(area_start, area_end).Contains(endtile)) continue;
1695 } else {
1696 if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
1697 }
1698 }
1699
1700 /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
1701 if (!IsCompatibleRail(GetRailType(tile), totype)) {
1702 ret = TunnelBridgeIsFree(tile, endtile);
1703 if (ret.Failed()) {
1704 error = std::move(ret);
1705 continue;
1706 }
1707 }
1708
1709 if (flags.Test(DoCommandFlag::Execute)) {
1711 if (HasTunnelBridgeReservation(tile)) {
1712 Train *v = GetTrainForReservation(tile, track);
1713 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1714 /* No power on new rail type, reroute. */
1716 vehicles_affected.push_back(v);
1717 }
1718 }
1719
1720 /* Update the company infrastructure counters. */
1721 uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
1722 Company *c = Company::Get(GetTileOwner(tile));
1723 c->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1724 c->infrastructure.rail[totype] += num_pieces;
1726
1727 SetRailType(tile, totype);
1728 SetRailType(endtile, totype);
1729
1730 for (Vehicle *v : VehiclesOnTile(tile)) {
1731 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1732 }
1733 for (Vehicle *v : VehiclesOnTile(endtile)) {
1734 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1735 }
1736
1737 YapfNotifyTrackLayoutChange(tile, track);
1738 YapfNotifyTrackLayoutChange(endtile, track);
1739
1740 if (IsBridge(tile)) {
1741 MarkBridgeDirty(tile);
1742 } else {
1743 MarkTileDirtyByTile(tile);
1744 MarkTileDirtyByTile(endtile);
1745 }
1746 }
1747
1748 found_convertible_track = true;
1749 cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
1750 break;
1751 }
1752
1753 case TileType::Station:
1754 case TileType::Road:
1755 if (flags.Test(DoCommandFlag::Execute)) {
1756 Track track = ((tt == TileType::Station) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile));
1757 YapfNotifyTrackLayoutChange(tile, track);
1758 }
1759
1760 found_convertible_track = true;
1761 cost.AddCost(RailConvertCost(type, totype));
1762 break;
1763 }
1764
1765 for (uint i = 0; i < vehicles_affected.size(); ++i) {
1766 TryPathReserve(vehicles_affected[i], true);
1767 }
1768 }
1769
1770 if (flags.Test(DoCommandFlag::Execute)) {
1771 /* Railtype changed, update trains as when entering different track */
1772 for (Train *v : affected_trains) {
1773 v->ConsistChanged(CCF_TRACK);
1774 }
1775 }
1776
1777 return found_convertible_track ? cost : error;
1778}
1779
1780static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlags flags)
1781{
1783 CommandCost ret = CheckTileOwnership(tile);
1784 if (ret.Failed()) return ret;
1785 }
1786
1788 if (ret.Failed()) return ret;
1789
1790 if (flags.Test(DoCommandFlag::Execute)) {
1791 /* read variables before the depot is removed */
1793 Owner owner = GetTileOwner(tile);
1794 Train *v = nullptr;
1795
1796 if (HasDepotReservation(tile)) {
1798 if (v != nullptr) FreeTrainTrackReservation(v);
1799 }
1800
1801 Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
1803
1804 delete Depot::GetByTile(tile);
1805 DoClearSquare(tile);
1806 AddSideToSignalBuffer(tile, dir, owner);
1808 if (v != nullptr) TryPathReserve(v, true);
1809 }
1810
1812}
1813
1816{
1818
1819 if (flags.Test(DoCommandFlag::Auto)) {
1820 if (!IsTileOwner(tile, _current_company)) {
1821 return CommandCost(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
1822 }
1823
1824 if (IsPlainRail(tile)) {
1825 return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
1826 } else {
1827 return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
1828 }
1829 }
1830
1831 switch (GetRailTileType(tile)) {
1833 case RailTileType::Normal: {
1834 Slope tileh = GetTileSlope(tile);
1835 /* Is there flat water on the lower halftile that gets cleared expensively? */
1836 bool water_ground = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh));
1837
1838 for (Track track : GetTrackBits(tile)) {
1839 CommandCost ret = Command<Commands::RemoveRail>::Do(flags, tile, track);
1840 if (ret.Failed()) return ret;
1841 cost.AddCost(ret.GetCost());
1842 }
1843
1844 /* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
1845 * Same holds for non-companies clearing the tile, e.g. disasters. */
1846 if (water_ground && !flags.Test(DoCommandFlag::Bankrupt) && Company::IsValidID(_current_company)) {
1848 if (ret.Failed()) return ret;
1849
1850 /* The track was removed, and left a coast tile. Now also clear the water. */
1851 if (flags.Test(DoCommandFlag::Execute)) {
1852 DoClearSquare(tile);
1853 }
1855 }
1856
1857 return cost;
1858 }
1859
1861 return RemoveTrainDepot(tile, flags);
1862
1863 default:
1864 return CMD_ERROR;
1865 }
1866}
1867
1876static uint GetSafeSlopeZ(uint x, uint y, Track track)
1877{
1878 switch (track) {
1879 case Track::Upper: x &= ~0xF; y &= ~0xF; break;
1880 case Track::Lower: x |= 0xF; y |= 0xF; break;
1881 case Track::Left: x |= 0xF; y &= ~0xF; break;
1882 case Track::Right: x &= ~0xF; y |= 0xF; break;
1883 default: break;
1884 }
1885 return GetSlopePixelZ(x, y);
1886}
1887
1893{
1894 switch (_settings_game.construction.train_signal_side) {
1896 return false;
1898 return true;
1900 return _settings_game.vehicle.road_side == RoadVehicleDrivingSide::Right;
1901 default:
1902 NOT_REACHED();
1903 }
1904}
1905
1906static void DrawSingleSignal(TileIndex tile, const RailTypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos)
1907{
1908 static const Point SignalPositions[2][12] = {
1909 { // Signals on the left side
1910 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1911 { 8, 5}, {14, 1}, { 1, 14}, { 9, 11}, { 1, 0}, { 3, 10},
1912 /* LOWER LOWER X X Y Y */
1913 {11, 4}, {14, 14}, {11, 3}, { 4, 13}, { 3, 4}, {11, 13}
1914 }, { // Signals on the right side
1915 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1916 {14, 1}, {12, 10}, { 4, 6}, { 1, 14}, {10, 4}, { 0, 1},
1917 /* LOWER LOWER X X Y Y */
1918 {14, 14}, { 5, 12}, {11, 13}, { 4, 3}, {13, 4}, { 3, 11}
1919 }
1920 };
1921
1922 bool signal_on_right = IsTrainSignalSideRight();
1923 uint x = TileX(tile) * TILE_SIZE + SignalPositions[signal_on_right][pos].x;
1924 uint y = TileY(tile) * TILE_SIZE + SignalPositions[signal_on_right][pos].y;
1925
1926 SignalType type = GetSignalType(tile, track);
1927 SignalVariant variant = GetSignalVariant(tile, track);
1928
1929 SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition);
1930 if (sprite != 0) {
1931 sprite += image;
1932 } else {
1933 /* Normal electric signals are stored in a different sprite block than all other signals. */
1934 sprite = (type == SignalType::Block && variant == SignalVariant::Electric) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
1935 sprite += to_underlying(type) * 16 + to_underlying(variant) * 64 + image * 2 + to_underlying(condition) + (type >= SignalType::Path ? 64 : 0);
1936 }
1937
1938 AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, GetSafeSlopeZ(x, y, track), {{}, {1, 1, BB_HEIGHT_UNDER_BRIDGE}, {}});
1939}
1940
1942struct FenceOffset : SpriteBounds {
1944
1945 constexpr FenceOffset(Corner height_ref, int8_t origin_x, int8_t origin_y, uint8_t extent_x, uint8_t extent_y) :
1946 SpriteBounds({origin_x, origin_y, 0}, {extent_x, extent_y, 4}, {}), height_ref(height_ref) {}
1947};
1948
1950static const FenceOffset _fence_offsets[] = {
1951 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_FLAT_X_NW
1952 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_FLAT_Y_NE
1953 { CORNER_W, 8, 8, 1, 1 }, // RFO_FLAT_LEFT
1954 { CORNER_N, 8, 8, 1, 1 }, // RFO_FLAT_UPPER
1955 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_SW_NW
1956 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_SE_NE
1957 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_NE_NW
1958 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_NW_NE
1959 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_FLAT_X_SE
1960 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_FLAT_Y_SW
1961 { CORNER_E, 8, 8, 1, 1 }, // RFO_FLAT_RIGHT
1962 { CORNER_S, 8, 8, 1, 1 }, // RFO_FLAT_LOWER
1963 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_SW_SE
1964 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_SE_SW
1965 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_NE_SE
1966 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_NW_SW
1967};
1968
1976static void DrawTrackFence(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites, RailFenceOffset rfo)
1977{
1978 int z = ti->z;
1979 if (IsValidCorner(_fence_offsets[rfo].height_ref)) {
1981 }
1982 AddSortableSpriteToDraw(psid.sprite + (rfo % num_sprites), psid.pal, ti->x, ti->y, z, _fence_offsets[rfo]);
1983}
1984
1991static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1992{
1994 if (ti->tileh & SLOPE_NW) rfo = (ti->tileh & SLOPE_W) ? RFO_SLOPE_SW_NW : RFO_SLOPE_NE_NW;
1995 DrawTrackFence(ti, psid, num_sprites, rfo);
1996}
1997
2004static void DrawTrackFence_SE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
2005{
2007 if (ti->tileh & SLOPE_SE) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SW_SE : RFO_SLOPE_NE_SE;
2008 DrawTrackFence(ti, psid, num_sprites, rfo);
2009}
2010
2017static void DrawTrackFence_NE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
2018{
2020 if (ti->tileh & SLOPE_NE) rfo = (ti->tileh & SLOPE_E) ? RFO_SLOPE_SE_NE : RFO_SLOPE_NW_NE;
2021 DrawTrackFence(ti, psid, num_sprites, rfo);
2022}
2023
2030static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
2031{
2033 if (ti->tileh & SLOPE_SW) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SE_SW : RFO_SLOPE_NW_SW;
2034 DrawTrackFence(ti, psid, num_sprites, rfo);
2035}
2036
2043static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
2044{
2045 /* Base sprite for track fences.
2046 * Note: Halftile slopes only have fences on the upper part. */
2047 uint num_sprites = 0;
2048 PalSpriteID psid{
2050 .pal = pal,
2051 };
2052 if (psid.sprite == 0) {
2054 num_sprites = 8;
2055 }
2056
2057 assert(num_sprites > 0);
2058
2059 switch (GetRailGroundType(ti->tile)) {
2060 case RailGroundType::FenceNW: DrawTrackFence_NW(ti, psid, num_sprites); break;
2061 case RailGroundType::FenceSE: DrawTrackFence_SE(ti, psid, num_sprites); break;
2062 case RailGroundType::FenceSENW: DrawTrackFence_NW(ti, psid, num_sprites);
2063 DrawTrackFence_SE(ti, psid, num_sprites); break;
2064 case RailGroundType::FenceNE: DrawTrackFence_NE(ti, psid, num_sprites); break;
2065 case RailGroundType::FenceSW: DrawTrackFence_SW(ti, psid, num_sprites); break;
2066 case RailGroundType::FenceNESW: DrawTrackFence_NE(ti, psid, num_sprites);
2067 DrawTrackFence_SW(ti, psid, num_sprites); break;
2068 case RailGroundType::FenceVert1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2069 case RailGroundType::FenceVert2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2070 case RailGroundType::FenceHoriz1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2071 case RailGroundType::FenceHoriz2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2073 Corner track_corner;
2074 if (IsHalftileSlope(ti->tileh)) {
2075 /* Steep slope or one-corner-raised slope with halftile foundation */
2076 track_corner = GetHalftileSlopeCorner(ti->tileh);
2077 } else {
2078 /* Three-corner-raised slope */
2080 }
2081 switch (track_corner) {
2082 case CORNER_W: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2083 case CORNER_S: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2084 case CORNER_E: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2085 case CORNER_N: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2086 default: NOT_REACHED();
2087 }
2088 break;
2089 }
2090 default: break;
2091 }
2092}
2093
2094static const int INF = 1000;
2097 { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right
2098 { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top
2099 { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left
2100 { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2101}}};
2102
2103static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
2104{
2105 DrawGroundSprite(sprite, pal, nullptr, 0, (ti->tileh & s) ? -8 : 0);
2106}
2107
2108static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti)
2109{
2111 Foundation f = GetRailFoundation(ti->tileh, track);
2112 Corner halftile_corner = CORNER_INVALID;
2113
2115 /* Save halftile corner */
2117 /* Draw lower part first */
2118 track.Reset(CornerToTrackBits(halftile_corner));
2120 }
2121
2122 DrawFoundation(ti, f);
2123 /* DrawFoundation modifies ti */
2124
2125 /* Draw ground */
2126 if (rgt == RailGroundType::HalfTileWater) {
2127 if (track.Any() || IsSteepSlope(ti->tileh)) {
2128 /* three-corner-raised slope or steep slope with track on upper part */
2129 DrawShoreTile(ti->tileh);
2130 } else {
2131 /* single-corner-raised slope with track on upper part */
2133 }
2134 } else {
2135 SpriteID image;
2136
2137 switch (rgt) {
2138 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2140 default: image = SPR_FLAT_GRASS_TILE; break;
2141 }
2142
2143 image += SlopeToSpriteOffset(ti->tileh);
2144
2145 DrawGroundSprite(image, PAL_NONE);
2146 }
2147
2148 bool no_combine = ti->tileh == SLOPE_FLAT && rti->flags.Test(RailTypeFlag::NoSpriteCombine);
2151 TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TrackBits{};
2152
2153 if (track.None()) {
2154 /* Half-tile foundation, no track here? */
2155 } else if (no_combine) {
2156 /* Use trackbits as direct index from ground sprite, subtract 1
2157 * because there is no sprite for no bits. */
2158 DrawGroundSprite(ground + track.base() - 1, PAL_NONE);
2159
2160 /* Draw reserved track bits */
2161 if (pbs.Test(Track::X)) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2162 if (pbs.Test(Track::Y)) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2163 if (pbs.Test(Track::Upper)) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2164 if (pbs.Test(Track::Lower)) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2165 if (pbs.Test(Track::Right)) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2166 if (pbs.Test(Track::Left)) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2167 } else if (ti->tileh == SLOPE_NW && track == Track::Y) {
2168 DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
2169 if (pbs.Any()) DrawGroundSprite(overlay + RTO_SLOPE_NW, PALETTE_CRASH);
2170 } else if (ti->tileh == SLOPE_NE && track == Track::X) {
2171 DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
2172 if (pbs.Any()) DrawGroundSprite(overlay + RTO_SLOPE_NE, PALETTE_CRASH);
2173 } else if (ti->tileh == SLOPE_SE && track == Track::Y) {
2174 DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
2175 if (pbs.Any()) DrawGroundSprite(overlay + RTO_SLOPE_SE, PALETTE_CRASH);
2176 } else if (ti->tileh == SLOPE_SW && track == Track::X) {
2177 DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
2178 if (pbs.Any()) DrawGroundSprite(overlay + RTO_SLOPE_SW, PALETTE_CRASH);
2179 } else {
2180 switch (track.base()) {
2181 /* Draw single ground sprite when not overlapping. No track overlay
2182 * is necessary for these sprites. */
2183 case TrackBits{Track::X}.base(): DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
2184 case TrackBits{Track::Y}.base(): DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
2185 case TrackBits{Track::Upper}.base(): DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
2186 case TrackBits{Track::Lower}.base(): DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2187 case TrackBits{Track::Right}.base(): DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
2188 case TrackBits{Track::Left}.base(): DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2189 case TRACK_BIT_CROSS.base(): DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
2190 case TRACK_BIT_HORZ.base():
2191 DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
2192 DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S);
2193 break;
2194 case TRACK_BIT_VERT.base():
2195 DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
2196 DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W);
2197 break;
2198
2199 default:
2200 /* We're drawing a junction tile */
2201 if (!track.Any(TRACK_BIT_3WAY_NE)) {
2202 DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
2203 } else if (!track.Any(TRACK_BIT_3WAY_SW)) {
2204 DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
2205 } else if (!track.Any(TRACK_BIT_3WAY_NW)) {
2206 DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
2207 } else if (!track.Any(TRACK_BIT_3WAY_SE)) {
2208 DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
2209 } else {
2210 DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2211 }
2212
2213 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2214 track.Reset(pbs);
2215
2216 /* Draw regular track bits */
2217 if (track.Test(Track::X)) DrawGroundSprite(overlay + RTO_X, PAL_NONE);
2218 if (track.Test(Track::Y)) DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
2219 if (track.Test(Track::Upper)) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
2220 if (track.Test(Track::Lower)) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
2221 if (track.Test(Track::Right)) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
2222 if (track.Test(Track::Left)) DrawGroundSprite(overlay + RTO_W, PAL_NONE);
2223 }
2224
2225 /* Draw reserved track bits */
2226 if (pbs.Test(Track::X)) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2227 if (pbs.Test(Track::Y)) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2228 if (pbs.Test(Track::Upper)) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2229 if (pbs.Test(Track::Lower)) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2230 if (pbs.Test(Track::Right)) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2231 if (pbs.Test(Track::Left)) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2232 }
2233
2234 if (IsValidCorner(halftile_corner)) {
2235 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2238
2239 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2240 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2241
2242 SpriteID image;
2243 switch (rgt) {
2244 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2247 default: image = SPR_FLAT_GRASS_TILE; break;
2248 }
2249
2250 image += SlopeToSpriteOffset(fake_slope);
2251
2252 DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
2253
2254 track = CornerToTrackBits(halftile_corner);
2255
2256 int offset;
2257 switch (FindFirstTrack(track)) {
2258 default: NOT_REACHED();
2259 case Track::Upper: offset = RTO_N; break;
2260 case Track::Lower: offset = RTO_S; break;
2261 case Track::Right: offset = RTO_E; break;
2262 case Track::Left: offset = RTO_W; break;
2263 }
2264
2265 DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
2266 if (_settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, track)) {
2267 DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
2268 }
2269 }
2270}
2271
2280{
2281 /* If none of the tracks end up in the NE corner, return the ground sprite
2282 * where the NE of the tile is not covered. Repeat for the other directions.
2283 * What remains are junctions where all directions are covered. */
2284 if (!track.Any(TRACK_BIT_3WAY_NE)) return 0;
2285 if (!track.Any(TRACK_BIT_3WAY_SW)) return 1;
2286 if (!track.Any(TRACK_BIT_3WAY_NW)) return 2;
2287 if (!track.Any(TRACK_BIT_3WAY_SE)) return 3;
2288 return 4;
2289}
2290
2296static void DrawTrackBits(TileInfo *ti, TrackBits track)
2297{
2298 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2299
2300 if (rti->UsesOverlay()) {
2301 DrawTrackBitsOverlay(ti, track, rti);
2302 return;
2303 }
2304
2306 Foundation f = GetRailFoundation(ti->tileh, track);
2307 Corner halftile_corner = CORNER_INVALID;
2308
2310 /* Save halftile corner */
2312 /* Draw lower part first */
2313 track.Reset(CornerToTrackBits(halftile_corner));
2315 }
2316
2317 DrawFoundation(ti, f);
2318 /* DrawFoundation modifies ti */
2319
2320 SpriteID image;
2321 PaletteID pal = PAL_NONE;
2322 const SubSprite *sub = nullptr;
2323 bool junction = false;
2324
2325 /* Select the sprite to use. */
2326 if (track.None()) {
2327 /* Clear ground (only track on halftile foundation) */
2328 if (rgt == RailGroundType::HalfTileWater) {
2329 if (IsSteepSlope(ti->tileh)) {
2330 DrawShoreTile(ti->tileh);
2331 image = 0;
2332 } else {
2333 image = SPR_FLAT_WATER_TILE;
2334 }
2335 } else {
2336 switch (rgt) {
2337 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2339 default: image = SPR_FLAT_GRASS_TILE; break;
2340 }
2341 image += SlopeToSpriteOffset(ti->tileh);
2342 }
2343 } else {
2344 if (ti->tileh != SLOPE_FLAT) {
2345 /* track on non-flat ground */
2346 image = _track_sloped_sprites[ti->tileh] + rti->base_sprites.track_y;
2347 } else {
2348 /* track on flat ground */
2349 switch (track.base()) {
2350 /* single track, select combined track + ground sprite*/
2351 case TrackBits{Track::Y}.base(): image = rti->base_sprites.track_y; break;
2352 case TrackBits{Track::X}.base(): image = rti->base_sprites.track_y + 1; break;
2353 case TrackBits{Track::Upper}.base(): image = rti->base_sprites.track_y + 2; break;
2354 case TrackBits{Track::Lower}.base(): image = rti->base_sprites.track_y + 3; break;
2355 case TrackBits{Track::Right}.base(): image = rti->base_sprites.track_y + 4; break;
2356 case TrackBits{Track::Left}.base(): image = rti->base_sprites.track_y + 5; break;
2357 case TRACK_BIT_CROSS.base(): image = rti->base_sprites.track_y + 6; break;
2358
2359 /* double diagonal track, select combined track + ground sprite*/
2360 case TRACK_BIT_HORZ.base(): image = rti->base_sprites.track_ns; break;
2361 case TRACK_BIT_VERT.base(): image = rti->base_sprites.track_ns + 1; break;
2362
2363 /* junction, select only ground sprite, handle track sprite later */
2364 default:
2365 junction = true;
2367 break;
2368 }
2369 }
2370
2371 switch (rgt) {
2373 case RailGroundType::SnowOrDesert: image += rti->snow_offset; break;
2375 /* three-corner-raised slope */
2376 DrawShoreTile(ti->tileh);
2378 sub = &(_halftile_sub_sprite[track_corner]);
2379 break;
2380 }
2381 default: break;
2382 }
2383 }
2384
2385 if (image != 0) DrawGroundSprite(image, pal, sub);
2386
2387 /* Draw track pieces individually for junction tiles */
2388 if (junction) {
2389 if (track.Test(Track::X)) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE);
2390 if (track.Test(Track::Y)) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE);
2391 if (track.Test(Track::Upper)) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE);
2392 if (track.Test(Track::Lower)) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE);
2393 if (track.Test(Track::Left)) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
2394 if (track.Test(Track::Right)) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
2395 }
2396
2397 /* PBS debugging, draw reserved tracks darker */
2398 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation) {
2399 /* Get reservation, but mask track on halftile slope */
2400 TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
2401 if (pbs.Test(Track::X)) {
2402 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2404 } else {
2406 }
2407 }
2408 if (pbs.Test(Track::Y)) {
2409 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2411 } else {
2413 }
2414 }
2415 if (pbs.Test(Track::Upper)) DrawGroundSprite(rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_N ? -(int)TILE_HEIGHT : 0);
2416 if (pbs.Test(Track::Lower)) DrawGroundSprite(rti->base_sprites.single_s, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_S ? -(int)TILE_HEIGHT : 0);
2417 if (pbs.Test(Track::Left)) DrawGroundSprite(rti->base_sprites.single_w, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_W ? -(int)TILE_HEIGHT : 0);
2418 if (pbs.Test(Track::Right)) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
2419 }
2420
2421 if (IsValidCorner(halftile_corner)) {
2422 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2423
2424 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2425 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2426 image = _track_sloped_sprites[fake_slope] + rti->base_sprites.track_y;
2427 pal = PAL_NONE;
2428 switch (rgt) {
2431 case RailGroundType::HalfTileSnow: image += rti->snow_offset; break; // higher part has snow in this case too
2432 default: break;
2433 }
2434 DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
2435
2436 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) {
2437 static constexpr CornerIndexArray<uint8_t> corner_to_track_sprite = {3, 1, 2, 0};
2438 DrawGroundSprite(corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, -static_cast<int>(TILE_HEIGHT));
2439 }
2440 }
2441}
2442
2443static void DrawSignals(TileIndex tile, TrackBits rails, const RailTypeInfo *rti)
2444{
2445 auto MAYBE_DRAW_SIGNAL = [&](uint8_t signalbit, SignalOffsets image, uint pos, Track track) {
2446 if (IsSignalPresent(tile, signalbit)) DrawSingleSignal(tile, rti, track, GetSingleSignalState(tile, signalbit), image, pos);
2447 };
2448
2449 if (!rails.Test(Track::Y)) {
2450 if (!rails.Test(Track::X)) {
2451 if (rails.Test(Track::Left)) {
2452 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, Track::Left);
2453 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, Track::Left);
2454 }
2455 if (rails.Test(Track::Right)) {
2456 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, Track::Right);
2457 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, Track::Right);
2458 }
2459 if (rails.Test(Track::Upper)) {
2460 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, Track::Upper);
2461 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, Track::Upper);
2462 }
2463 if (rails.Test(Track::Lower)) {
2464 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, Track::Lower);
2465 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, Track::Lower);
2466 }
2467 } else {
2468 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, Track::X);
2469 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, Track::X);
2470 }
2471 } else {
2472 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, Track::Y);
2473 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 11, Track::Y);
2474 }
2475}
2476
2478static void DrawTile_Rail(TileInfo *ti)
2479{
2480 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2481 BridgePillarFlags blocked_pillars{};
2483
2484 if (IsPlainRail(ti->tile)) {
2485 TrackBits rails = GetTrackBits(ti->tile);
2486
2487 DrawTrackBits(ti, rails);
2488
2490
2492
2493 if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
2494
2495 if (IsBridgeAbove(ti->tile)) {
2496 if (rails.Any(TRACK_BIT_3WAY_NE)) blocked_pillars.Set(BridgePillarFlag::EdgeNE);
2497 if (rails.Any(TRACK_BIT_3WAY_SE)) blocked_pillars.Set(BridgePillarFlag::EdgeSE);
2498 if (rails.Any(TRACK_BIT_3WAY_SW)) blocked_pillars.Set(BridgePillarFlag::EdgeSW);
2499 if (rails.Any(TRACK_BIT_3WAY_NW)) blocked_pillars.Set(BridgePillarFlag::EdgeNW);
2500 }
2501 } else {
2502 /* draw depot */
2503 const DrawTileSprites *dts;
2505
2507
2509 /* Draw rail instead of depot */
2510 dts = &_depot_invisible_gfx_table[dir];
2511 } else {
2512 dts = &_depot_gfx_table[dir];
2513 }
2514
2515 SpriteID image;
2516 if (rti->UsesOverlay()) {
2517 image = SPR_FLAT_GRASS_TILE;
2518 } else {
2519 image = dts->ground.sprite;
2520 if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
2521 }
2522
2523 /* Adjust ground tile for desert and snow. */
2524 if (IsSnowOrDesertRailGround(ti->tile)) {
2525 if (image != SPR_FLAT_GRASS_TILE) {
2526 image += rti->snow_offset; // tile with tracks
2527 } else {
2528 image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
2529 }
2530 }
2531
2532 DrawGroundSprite(image, GroundSpritePaletteTransform(image, PAL_NONE, pal));
2533
2534 if (rti->UsesOverlay()) {
2536
2537 switch (GetRailDepotDirection(ti->tile)) {
2538 case DiagDirection::NE:
2540 [[fallthrough]];
2541 case DiagDirection::SW:
2542 DrawGroundSprite(ground + RTO_X, PAL_NONE);
2543 break;
2544 case DiagDirection::NW:
2546 [[fallthrough]];
2547 case DiagDirection::SE:
2548 DrawGroundSprite(ground + RTO_Y, PAL_NONE);
2549 break;
2550 default:
2551 break;
2552 }
2553
2554 if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2556
2557 switch (GetRailDepotDirection(ti->tile)) {
2558 case DiagDirection::NE:
2560 [[fallthrough]];
2561 case DiagDirection::SW:
2563 break;
2564 case DiagDirection::NW:
2566 [[fallthrough]];
2567 case DiagDirection::SE:
2569 break;
2570 default:
2571 break;
2572 }
2573 }
2574 } else {
2575 /* PBS debugging, draw reserved tracks darker */
2576 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2577 switch (GetRailDepotDirection(ti->tile)) {
2578 case DiagDirection::NE:
2580 [[fallthrough]];
2581 case DiagDirection::SW:
2583 break;
2584 case DiagDirection::NW:
2586 [[fallthrough]];
2587 case DiagDirection::SE:
2589 break;
2590 default:
2591 break;
2592 }
2593 }
2594 }
2595 int depot_sprite = GetCustomRailSprite(rti, ti->tile, RailSpriteType::Depot);
2596 int relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
2597
2599
2600 DrawRailTileSeq(ti, dts, TransparencyOption::Buildings, relocation, 0, pal);
2601 blocked_pillars = BRIDGEPILLARFLAGS_ALL;
2602 }
2603 DrawBridgeMiddle(ti, blocked_pillars);
2604}
2605
2613void DrawTrainDepotSprite(int x, int y, DiagDirection dir, RailType railtype)
2614{
2615 const DrawTileSprites *dts = &_depot_gfx_table[dir];
2616 const RailTypeInfo *rti = GetRailTypeInfo(railtype);
2617 SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
2618 uint32_t offset = rti->GetRailtypeSpriteOffset();
2619
2620 if (image != SPR_FLAT_GRASS_TILE) image += offset;
2622
2623 DrawSprite(image, PAL_NONE, x, y);
2624
2625 if (rti->UsesOverlay()) {
2627
2628 switch (dir) {
2629 case DiagDirection::SW: DrawSprite(ground + RTO_X, PAL_NONE, x, y); break;
2630 case DiagDirection::SE: DrawSprite(ground + RTO_Y, PAL_NONE, x, y); break;
2631 default: break;
2632 }
2633 }
2634 int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RailSpriteType::Depot);
2635 if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
2636
2637 DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette);
2638}
2639
2641static int GetSlopePixelZ_Rail(TileIndex tile, uint x, uint y, [[maybe_unused]] bool ground_vehicle)
2642{
2643 if (IsPlainRail(tile)) {
2644 auto [tileh, z] = GetTilePixelSlope(tile);
2645 if (tileh == SLOPE_FLAT) return z;
2646
2648 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
2649 } else {
2650 return GetTileMaxPixelZ(tile);
2651 }
2652}
2653
2656{
2657 return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
2658}
2659
2661static void TileLoop_Rail(TileIndex tile)
2662{
2663 RailGroundType old_ground = GetRailGroundType(tile);
2664 RailGroundType new_ground;
2665
2666 if (old_ground == RailGroundType::HalfTileWater) {
2667 TileLoop_Water(tile);
2668 return;
2669 }
2670
2671 switch (_settings_game.game_creation.landscape) {
2672 case LandscapeType::Arctic: {
2673 auto [slope, z] = GetTileSlopeZ(tile);
2674 bool half = false;
2675
2676 /* for non-flat track, use lower part of track
2677 * in other cases, use the highest part with track */
2678 if (IsPlainRail(tile)) {
2679 TrackBits track = GetTrackBits(tile);
2680 Foundation f = GetRailFoundation(slope, track);
2681
2682 switch (f) {
2683 case Foundation::None:
2684 /* no foundation - is the track on the upper side of three corners raised tile? */
2685 if (IsSlopeWithThreeCornersRaised(slope)) z++;
2686 break;
2687
2690 /* sloped track - is it on a steep slope? */
2691 if (IsSteepSlope(slope)) z++;
2692 break;
2693
2695 /* only lower part of steep slope */
2696 z++;
2697 break;
2698
2699 default:
2700 /* if it is a steep slope, then there is a track on higher part */
2701 if (IsSteepSlope(slope)) z++;
2702 z++;
2703 break;
2704 }
2705
2706 half = IsNonContinuousFoundation(f);
2707 } else {
2708 /* is the depot on a non-flat tile? */
2709 if (slope != SLOPE_FLAT) z++;
2710 }
2711
2712 /* 'z' is now the lowest part of the highest track bit -
2713 * for sloped track, it is 'z' of lower part
2714 * for two track bits, it is 'z' of higher track bit
2715 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
2716 if (z > GetSnowLine()) {
2717 if (half && z - GetSnowLine() == 1) {
2718 /* track on non-continuous foundation, lower part is not under snow */
2719 new_ground = RailGroundType::HalfTileSnow;
2720 } else {
2721 new_ground = RailGroundType::SnowOrDesert;
2722 }
2723 goto set_ground;
2724 }
2725 break;
2726 }
2727
2729 if (GetTropicZone(tile) == TropicZone::Desert) {
2730 new_ground = RailGroundType::SnowOrDesert;
2731 goto set_ground;
2732 }
2733 break;
2734
2735 default:
2736 break;
2737 }
2738
2739 new_ground = RailGroundType::Grass;
2740
2741 if (IsPlainRail(tile) && old_ground != RailGroundType::Barren) { // wait until bottom is green
2742 /* determine direction of fence */
2743 TrackBits rail = GetTrackBits(tile);
2744
2745 Owner owner = GetTileOwner(tile);
2746 DiagDirections fences{};
2747
2750
2751 /* Track bit on this edge => no fence. */
2752 if (rail.Any(dir_to_trackbits[d])) continue;
2753
2754 TileIndex tile2 = tile + TileOffsByDiagDir(d);
2755
2756 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
2757 if (!IsValidTile(tile2) || IsTileType(tile2, TileType::House) || IsTileType(tile2, TileType::Industry) ||
2759 fences.Set(d);
2760 }
2761 }
2762
2763 switch (fences.base()) {
2764 case DiagDirections{}.base(): break;
2765 case DiagDirections{DiagDirection::NE}.base(): new_ground = RailGroundType::FenceNE; break;
2766 case DiagDirections{DiagDirection::SE}.base(): new_ground = RailGroundType::FenceSE; break;
2767 case DiagDirections{DiagDirection::SW}.base(): new_ground = RailGroundType::FenceSW; break;
2768 case DiagDirections{DiagDirection::NW}.base(): new_ground = RailGroundType::FenceNW; break;
2775 default: NOT_REACHED();
2776 }
2777 }
2778
2779set_ground:
2780 if (old_ground != new_ground) {
2781 SetRailGroundType(tile, new_ground);
2782 MarkTileDirtyByTile(tile);
2783 }
2784}
2785
2786
2789{
2790 /* Case of half tile slope with water. */
2792 TrackBits tb = GetTrackBits(tile);
2793 switch (tb.base()) {
2794 default: NOT_REACHED();
2795 case TrackBits{Track::Upper}.base(): tb = Track::Lower; break;
2796 case TrackBits{Track::Lower}.base(): tb = Track::Upper; break;
2797 case TrackBits{Track::Left}.base(): tb = Track::Right; break;
2798 case TrackBits{Track::Right}.base(): tb = Track::Left; break;
2799 }
2800 return {TrackBitsToTrackdirBits(tb), {}};
2801 }
2802
2803 if (mode != TransportType::Rail) return {};
2804
2805 TrackBits trackbits{};
2806 TrackdirBits red_signals{};
2807
2808 switch (GetRailTileType(tile)) {
2809 default: NOT_REACHED();
2811 trackbits = GetTrackBits(tile);
2812 break;
2813
2814 case RailTileType::Signals: {
2815 trackbits = GetTrackBits(tile);
2816 uint8_t a = GetPresentSignals(tile);
2817 uint b = GetSignalStates(tile);
2818
2819 b &= a;
2820
2821 /* When signals are not present (in neither direction),
2822 * we pretend them to be green. Otherwise, it depends on
2823 * the signal type. For signals that are only active from
2824 * one side, we set the missing signals explicitly to
2825 * `green'. Otherwise, they implicitly become `red'. */
2826 if (!IsOnewaySignal(tile, Track::Upper) || (a & SignalOnTrack(Track::Upper)) == 0) b |= ~a & SignalOnTrack(Track::Upper);
2827 if (!IsOnewaySignal(tile, Track::Lower) || (a & SignalOnTrack(Track::Lower)) == 0) b |= ~a & SignalOnTrack(Track::Lower);
2828
2829 if ((b & 0x8) == 0) red_signals.Set({Trackdir::Left_N, Trackdir::X_NE, Trackdir::Y_SE, Trackdir::Upper_E});
2830 if ((b & 0x4) == 0) red_signals.Set({Trackdir::Left_S, Trackdir::X_SW, Trackdir::Y_NW, Trackdir::Upper_W});
2831 if ((b & 0x2) == 0) red_signals.Set({Trackdir::Right_N, Trackdir::Lower_E});
2832 if ((b & 0x1) == 0) red_signals.Set({Trackdir::Right_S, Trackdir::Lower_W});
2833
2834 break;
2835 }
2836
2837 case RailTileType::Depot: {
2839
2840 if (side != DiagDirection::Invalid && side != dir) break;
2841
2842 trackbits = DiagDirToDiagTrack(dir);
2843 break;
2844 }
2845 }
2846
2847 return {TrackBitsToTrackdirBits(trackbits), red_signals};
2848}
2849
2851static bool ClickTile_Rail(TileIndex tile)
2852{
2853 if (!IsRailDepot(tile)) return false;
2854
2856 return true;
2857}
2858
2861{
2862 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
2863 td.rail_speed = rti->max_speed;
2864 td.railtype = rti->strings.name;
2865 td.owner[0] = GetTileOwner(tile);
2866 switch (GetRailTileType(tile)) {
2868 td.str = STR_LAI_RAIL_DESCRIPTION_TRACK;
2869 break;
2870
2871 case RailTileType::Signals: {
2873 {
2874 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
2875 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2876 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2877 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2878 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2879 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
2880 },
2881 {
2882 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2883 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS,
2884 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2885 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2886 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2887 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
2888 },
2889 {
2890 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2891 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2892 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
2893 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2894 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2895 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
2896 },
2897 {
2898 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2899 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2900 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2901 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
2902 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2903 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
2904 },
2905 {
2906 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2907 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2908 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2909 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2910 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
2911 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
2912 },
2913 {
2914 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
2915 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
2916 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
2917 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
2918 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
2919 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
2920 },
2921 }}};
2922
2923 SignalType primary_signal;
2924 SignalType secondary_signal;
2925 if (HasSignalOnTrack(tile, Track::Upper)) {
2926 primary_signal = GetSignalType(tile, Track::Upper);
2927 secondary_signal = HasSignalOnTrack(tile, Track::Lower) ? GetSignalType(tile, Track::Lower) : primary_signal;
2928 } else {
2929 secondary_signal = primary_signal = GetSignalType(tile, Track::Lower);
2930 }
2931
2932 td.str = signal_type[secondary_signal][primary_signal];
2933 break;
2934 }
2935
2937 td.str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
2938 if (_settings_game.vehicle.train_acceleration_model != AccelerationModel::Original) {
2939 if (td.rail_speed > 0) {
2940 td.rail_speed = std::min<uint16_t>(td.rail_speed, 61);
2941 } else {
2942 td.rail_speed = 61;
2943 }
2944 }
2945 td.build_date = Depot::GetByTile(tile)->build_date;
2946 break;
2947
2948 default:
2949 NOT_REACHED();
2950 }
2951}
2952
2954static void ChangeTileOwner_Rail(TileIndex tile, Owner old_owner, Owner new_owner)
2955{
2956 if (!IsTileOwner(tile, old_owner)) return;
2957
2958 if (new_owner != INVALID_OWNER) {
2959 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
2960 uint num_pieces = 1;
2961 if (IsPlainRail(tile)) {
2962 TrackBits bits = GetTrackBits(tile);
2963 num_pieces = bits.Count();
2964 if (TracksOverlap(bits)) num_pieces *= num_pieces;
2965 }
2966 RailType rt = GetRailType(tile);
2967 Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;
2968 Company::Get(new_owner)->infrastructure.rail[rt] += num_pieces;
2969
2970 if (HasSignals(tile)) {
2971 uint num_sigs = CountBits(GetPresentSignals(tile));
2972 Company::Get(old_owner)->infrastructure.signal -= num_sigs;
2973 Company::Get(new_owner)->infrastructure.signal += num_sigs;
2974 }
2975
2976 SetTileOwner(tile, new_owner);
2977 } else {
2978 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
2979 }
2980}
2981
2984 {15, 8}, // NE
2985 {8, 0}, // SE
2986 {0, 8}, // SW
2987 {8, 15}, // NW
2988}}};
2989
2992 {10, 8}, // NE
2993 {8, 4}, // SE
2994 {4, 8}, // SW
2995 {8, 10}, // NW
2996}}};
2997
3003 {-1, 0}, // NE
3004 {0, 1}, // SE
3005 {1, 0}, // SW
3006 {0, -1}, // NW
3007}}};
3008
3016{
3018 int length = v->CalcNextVehicleOffset() + 1;
3019
3020 switch (dir) {
3021 case DiagDirection::NE: return (static_cast<int>(v->x_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].x - length));
3022 case DiagDirection::SE: return -(static_cast<int>(v->y_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].y + length));
3023 case DiagDirection::SW: return -(static_cast<int>(v->x_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].x + length));
3024 case DiagDirection::NW: return (static_cast<int>(v->y_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].y - length));
3025 default: NOT_REACHED();
3026 }
3027}
3028
3031{
3032 /* This routine applies only to trains in depot tiles. */
3033 if (v->type != VehicleType::Train || !IsRailDepotTile(tile)) return {};
3034
3035 /* Depot direction. */
3037
3038 Coord2D<uint8_t> fract_coord{
3039 static_cast<uint8_t>(x & TILE_UNIT_MASK),
3040 static_cast<uint8_t>(y & TILE_UNIT_MASK)};
3041
3042 /* Make sure a train is not entering the tile from behind. */
3043 if (_fractcoords_behind[dir] == fract_coord) return VehicleEnterTileState::CannotEnter;
3044
3045 /* Leaving depot? */
3046 if (v->GetMovingDirection() == DiagDirToDir(dir)) {
3047 /* Calculate the point where the following wagon should be activated. */
3048 int length = Train::From(v)->CalcNextVehicleOffset() + 1;
3049
3050 Coord2D<uint8_t> fract_coord_leave{
3051 static_cast<uint8_t>(_fractcoords_enter[dir].x + length * _deltacoord_leaveoffset[dir].x),
3052 static_cast<uint8_t>(_fractcoords_enter[dir].y + length * _deltacoord_leaveoffset[dir].y)};
3053
3054 if (fract_coord_leave == fract_coord) {
3055 /* Leave the depot. */
3056 if ((v = v->GetMovingNext()) != nullptr) {
3059 }
3060 }
3061 } else if (_fractcoords_enter[dir] == fract_coord) {
3062 /* Entering depot. */
3063 assert(DiagDirToDir(ReverseDiagDir(dir)) == v->GetMovingDirection());
3066 if (v->GetMovingNext() == nullptr) {
3067 Train *consist = Train::From(v)->First();
3069 /* Trains always drive forwards out of a depot.
3070 * This allows a player to easily reset a confused train,
3071 * and matches the behaviour of the \c VehicleRailFlag::Reversed variable. */
3073 } else {
3074 for (Train *u = consist; u != nullptr; u = u->Next()) {
3075 u->direction = ReverseDir(u->direction);
3076 }
3077 }
3078 VehicleEnterDepot(consist);
3079 }
3080 v->tile = tile;
3081
3082 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
3084 }
3085
3086 return {};
3087}
3088
3101static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
3102{
3103 if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3104
3105 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3106 if (CheckRailSlope(tileh_new, rail_bits, {}, tile).Failed()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3107
3108 /* Get the slopes on top of the foundations */
3109 z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), tileh_old);
3110 z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), tileh_new);
3111
3112 Corner track_corner;
3113 switch (rail_bits.base()) {
3114 case TrackBits{Track::Left}.base(): track_corner = CORNER_W; break;
3115 case TrackBits{Track::Lower}.base(): track_corner = CORNER_S; break;
3116 case TrackBits{Track::Right}.base(): track_corner = CORNER_E; break;
3117 case TrackBits{Track::Upper}.base(): track_corner = CORNER_N; break;
3118
3119 /* Surface slope must not be changed */
3120 default:
3121 if (z_old != z_new || tileh_old != tileh_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3123 }
3124
3125 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3126 z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
3127 z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
3128 if (z_old != z_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3129
3131 /* Make the ground dirty, if surface slope has changed */
3132 if (tileh_old != tileh_new) {
3133 /* If there is flat water on the lower halftile add the cost for clearing it */
3136 }
3137 return cost;
3138}
3139
3141static CommandCost TerraformTile_Rail(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
3142{
3143 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
3144 if (IsPlainRail(tile)) {
3145 TrackBits rail_bits = GetTrackBits(tile);
3146 /* Is there flat water on the lower halftile that must be cleared expensively? */
3147 bool was_water = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh_old));
3148
3149 /* Allow clearing the water only if there is no ship */
3150 if (was_water && HasVehicleOnTile(tile, [](const Vehicle *v) {
3151 return v->type == VehicleType::Ship;
3152 })) return CommandCost(STR_ERROR_SHIP_IN_THE_WAY);
3153
3154 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3155 CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
3156
3157 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3158 Corner allowed_corner;
3159 switch (rail_bits.base()) {
3160 case TrackBits{Track::Right}.base(): allowed_corner = CORNER_W; break;
3161 case TrackBits{Track::Upper}.base(): allowed_corner = CORNER_S; break;
3162 case TrackBits{Track::Left}.base(): allowed_corner = CORNER_E; break;
3163 case TrackBits{Track::Lower}.base(): allowed_corner = CORNER_N; break;
3164 default: return autoslope_result;
3165 }
3166
3167 Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
3168
3169 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3170 if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
3171
3172 /* Everything is valid, which only changes allowed_corner */
3173 for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
3174 if (allowed_corner == corner) continue;
3175 if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
3176 }
3177
3178 /* Make the ground dirty */
3180
3181 /* allow terraforming */
3182 return CommandCost(ExpensesType::Construction, was_water ? _price[Price::ClearWater] : (Money)0);
3183 } else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
3184 AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
3186 }
3187 return Command<Commands::LandscapeClear>::Do(flags, tile);
3188}
3189
3191static CommandCost CheckBuildAbove_Rail(TileIndex tile, [[maybe_unused]] DoCommandFlags flags, [[maybe_unused]] Axis axis, int height)
3192{
3193 if (IsPlainRail(tile)) return CommandCost();
3194
3195 int height_diff = GetTileMaxZ(tile) + MINIMAL_DEPOT_BRIDGE_HEIGHT - height;
3196 if (height_diff <= 0) return CommandCost();
3197 return CommandCostWithParam(STR_ERROR_BRIDGE_TOO_LOW_FOR_TRAIN_DEPOT, height_diff * TILE_HEIGHT_STEP);
3198}
3199
3201extern const TileTypeProcs _tile_type_rail_procs = {
3202 .draw_tile_proc = DrawTile_Rail,
3203 .get_slope_pixel_z_proc = GetSlopePixelZ_Rail,
3204 .clear_tile_proc = ClearTile_Rail,
3205 .get_tile_desc_proc = GetTileDesc_Rail,
3206 .get_tile_track_status_proc = GetTileTrackStatus_Rail,
3207 .click_tile_proc = ClickTile_Rail,
3208 .tile_loop_proc = TileLoop_Rail,
3209 .change_tile_owner_proc = ChangeTileOwner_Rail,
3210 .vehicle_enter_tile_proc = VehicleEnterTile_Rail,
3211 .get_foundation_proc = GetFoundation_Rail,
3212 .terraform_tile_proc = TerraformTile_Rail,
3213 .check_build_above_proc = CheckBuildAbove_Rail,
3214};
@ None
Tile is not animated.
Functions related to autoslope.
bool AutoslopeCheckForEntranceEdge(TileIndex tile, int z_new, Slope tileh_new, DiagDirection entrance)
Autoslope check for tiles with an entrance on an edge.
Definition autoslope.h:31
bool AutoslopeEnabled()
Tests if autoslope is enabled for _current_company.
Definition autoslope.h:65
Class for backupping variables and making sure they are restored later.
@ DrivingBackwards
Vehicle is driving backwards.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
Draw the middle bits of a bridge.
TileIndex GetSouthernBridgeEnd(TileIndex t)
Finds the southern end of a bridge starting at a middle tile.
int GetBridgeHeight(TileIndex t)
Get the height ('z') of a bridge.
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
bool IsBridge(Tile t)
Checks if this is a bridge, instead of a tunnel.
Definition bridge_map.h:24
EnumBitSet< BridgePillarFlag, uint8_t > BridgePillarFlags
Bitset of BridgePillarFlag elements.
Definition bridge_type.h:53
@ EdgeNE
Northeast edge is obstructed.
Definition bridge_type.h:46
@ EdgeSW
Southwest edge is obstructed.
Definition bridge_type.h:48
@ EdgeNW
Northwest edge is obstructed.
Definition bridge_type.h:49
@ EdgeSE
Southeast edge is obstructed.
Definition bridge_type.h:47
uint Count() const
Count the number of set bits.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr bool None() const
Test if none of the values are set.
constexpr Timpl & Flip()
Flip all bits.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
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.
void MakeError(StringID message)
Makes this CommandCost behave like an error command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
StringID GetErrorMessage() const
Returns the error message of a command.
Iterate a range of enum values.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:117
RailType Index() const
Get the RailType for this RailTypeInfo.
Definition rail.cpp:26
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:127
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:222
SpriteID build_tunnel
button for building a tunnel
Definition rail.h:150
CursorID rail_swne
Cursor for building rail in X direction.
Definition rail.h:157
SpriteID convert_rail
button for converting rail
Definition rail.h:151
struct RailTypeInfo::@157247141350136173143103254227157213063052244122 strings
Strings associated with the rail type.
CursorID convert
Cursor for converting track.
Definition rail.h:163
CursorID depot
Cursor for building a depot.
Definition rail.h:161
RailTypes powered_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype generates power
Definition rail.h:179
RailTypes introduces_railtypes
Bitmask of which other railtypes are introduced when this railtype is introduced.
Definition rail.h:257
SpriteID ground
ground sprite for a 3-way switch
Definition rail.h:126
CursorID rail_nwse
Cursor for building rail in Y direction.
Definition rail.h:159
SpriteID build_x_rail
button for building single rail in X direction
Definition rail.h:145
uint8_t sorting_order
The sorting order of this railtype for the toolbar dropdown.
Definition rail.h:262
RailTypeLabel label
Unique 32 bit rail type identifier.
Definition rail.h:227
SpriteID single_n
single piece of rail in the northern corner
Definition rail.h:129
CursorID rail_ew
Cursor for building rail in E-W direction.
Definition rail.h:158
SpriteID auto_rail
button for the autorail construction
Definition rail.h:148
CursorID autorail
Cursor for autorail tool.
Definition rail.h:160
SpriteID single_y
single piece of rail in Y direction, without ground
Definition rail.h:128
StringID name
Name of this rail type.
Definition rail.h:167
RailTypes compatible_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype can physically travel
Definition rail.h:182
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:289
struct RailTypeInfo::@047376261311064105134233254254216006075341230376 gui_sprites
struct containing the sprites for the rail GUI.
SpriteID single_s
single piece of rail in the southern corner
Definition rail.h:130
RailTypeFlags flags
Bit mask of rail type flags.
Definition rail.h:202
SpriteID build_ew_rail
button for building single rail in E-W direction
Definition rail.h:146
SpriteID build_y_rail
button for building single rail in Y direction
Definition rail.h:147
FlatSet< RailTypeLabel > alternate_labels
Rail type labels this type provides in addition to the main label.
Definition rail.h:232
EnumIndexArray< EnumIndexArray< EnumIndexArray< SpriteID, SignalState, SignalState::End >, SignalVariant, SignalVariant::End >, SignalType, SignalType::End > signals
signal GUI sprites (type, variant, state)
Definition rail.h:152
SpriteID track_ns
two pieces of rail in North and South corner (East-West direction)
Definition rail.h:125
struct RailTypeInfo::@332027037331076264023214171276243307073252216167 base_sprites
Struct containing the main sprites.
SpriteID snow_offset
sprite number difference between a piece of track on a snowy ground and the corresponding one on norm...
Definition rail.h:176
SpriteID track_y
single piece of rail in Y direction, with ground
Definition rail.h:124
SpriteID build_depot
button for building depots
Definition rail.h:149
SpriteID single_w
single piece of rail in the western corner
Definition rail.h:132
SpriteID single_e
single piece of rail in the eastern corner
Definition rail.h:131
SpriteID build_ns_rail
button for building single rail in N-S direction
Definition rail.h:144
CursorID rail_ns
Cursor for building rail in N-S direction.
Definition rail.h:156
SpriteID tunnel
tunnel sprites base
Definition rail.h:135
SpriteID single_sloped
single piece of rail for slopes
Definition rail.h:133
struct RailTypeInfo::@057010233226120022371265166156055202241326366156 cursor
Cursors associated with the rail type.
static std::unique_ptr< TileIterator > Create(TileIndex corner1, TileIndex corner2, bool diagonal)
Create either an OrthogonalTileIterator or DiagonalTileIterator given the diagonal parameter.
Definition tilearea.cpp:292
Iterate over all vehicles on a tile.
CommandCost CommandCostWithParam(StringID str, uint64_t value)
Return an error status, with string and parameter.
Definition command.cpp:416
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
@ Execute
execute the given command
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
EnumBitSet< DoCommandFlag, uint16_t > DoCommandFlags
Bitset of DoCommandFlag elements.
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.
PaletteID GetCompanyPalette(CompanyID company)
Get the palette for recolouring with a company colour.
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 INVALID_OWNER
An invalid owner.
static constexpr Owner OWNER_WATER
The tile/execution is done by "water".
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
Base for all depots (except hangars).
bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh)
Find out if the slope of the tile is suitable to build a depot of given direction.
Definition depot_func.h:27
void ShowDepotWindow(TileIndex tile, VehicleType type)
Opens a depot window.
static constexpr uint8_t MINIMAL_DEPOT_BRIDGE_HEIGHT
Minimal height for a bridge above any depot tile.
Definition depot_type.h:21
Direction DiagDirToDir(DiagDirection dir)
Convert a DiagDirection to a Direction.
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
Direction ReverseDir(Direction d)
Return the reverse of a direction.
bool IsValidDiagDirection(DiagDirection d)
Checks if an integer value is a valid DiagDirection.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
EnumIndexArray< T, DiagDirection, DiagDirection::End > DiagDirectionIndexArray
Array with DiagDirection as index.
Axis
Enumeration for the two axis X and Y.
@ X
The X axis.
@ Y
The y axis.
DiagDirection
Enumeration for diagonal directions.
@ Invalid
Flag for an invalid DiagDirection.
@ SW
Southwest.
@ NW
Northwest.
@ End
Used for iterations.
@ NE
Northeast, upper right on your monitor.
@ SE
Southeast.
EnumBitSet< DiagDirection, uint8_t > DiagDirections
Bitset of DiagDirection elements.
Prices _price
Prices and also the fractional part.
Definition economy.cpp:106
static const uint LEVELCROSSING_TRACKBIT_FACTOR
Multiplier for how many regular track bits a level crossing counts.
@ Construction
Construction costs.
static const uint TUNNELBRIDGE_TRACKBIT_FACTOR
Multiplier for how many regular track bits a tunnel/bridge counts.
@ ClearRough
Price for destroying rough land.
@ BuildSignals
Price for building rail signals.
@ BuildFoundation
Price for building foundation under other constructions e.g. roads, rails, depots,...
@ ClearWater
Price for destroying water e.g. see, rives.
@ ClearSignals
Price for destroying rail signals.
@ ClearDepotTrain
Price for destroying train depots.
@ BuildDepotTrain
Price for building train depots.
void DrawRailCatenary(const TileInfo *ti)
Draws overhead wires and pylons for electric railways.
Definition elrail.cpp:553
Header file for electrified rail specific functions.
bool HasRailCatenaryDrawn(RailType rt)
Test if we should draw rail catenary.
Definition elrail_func.h:32
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
EnumClassIndexContainer< std::array< T, to_underlying(N)>, Index > EnumIndexArray
A typedef for EnumClassIndexContainer using std::array as the backing container type.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1037
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
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.
static const SpriteID SPR_RAIL_DEPOT_SE_1
Definition sprites.h:456
static const SpriteID SPR_ORIGINAL_SIGNALS_BASE
Sprite number of the first block sginall.
Definition sprites.h:439
static const SpriteID SPR_TRACK_FENCE_FLAT_X
Definition sprites.h:518
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.
int GetSlopeZInCorner(Slope tileh, Corner corner)
Determine the Z height of a corner relative to TileZ.
TrackStatus GetTileTrackStatus(TileIndex tile, TransportType mode, RoadTramType sub_mode, DiagDirection side)
Returns information about trackdirs and signal states.
const TileTypeProcs _tile_type_rail_procs
TileTypeProcs definitions for TileType::Rail tiles.
Definition landscape.cpp:53
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
int GetSlopePixelZInCorner(Slope tileh, Corner corner)
Determine the Z height of a corner relative to TileZ.
Definition landscape.h:55
Command definitions related to landscape (slopes etc.).
@ Arctic
Landscape with snow levels.
@ Tropic
Landscape with distinct rainforests and deserts,.
#define Point
Macro that prevents name conflicts between included headers.
TileIndex AddTileIndexDiffCWrap(TileIndex tile, TileIndexDiffC diff)
Add a TileIndexDiffC to a TileIndex and returns the new one.
Definition map_func.h:519
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:444
TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir)
Adds a DiagDir to a tile.
Definition map_func.h:615
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:429
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:419
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:574
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
@ First
The first client ID.
@ RailTypes
Rail types feature.
Definition newgrf.h:95
@ UpperHalftile
Querying information about the upper part of a tile with halftile foundation.
@ Normal
Nothing special.
@ Any
Use first found.
Functions/types related to NewGRF debugging.
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
Delete inspect window for a given feature and index.
SpriteID GetCustomSignalSprite(const RailTypeInfo *rti, TileIndex tile, SignalType type, SignalVariant var, SignalState state, bool gui)
Get the sprite to draw for a given signal.
SpriteID GetCustomRailSprite(const RailTypeInfo *rti, TileIndex tile, RailSpriteType rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of rail types.
Map accessors for object tiles.
bool IsObjectType(Tile t, ObjectType type)
Check whether the object on a tile is of a specific type.
Definition object_map.h:25
static const ObjectType OBJECT_OWNED_LAND
Owned land 'flag'.
Definition object_type.h:21
@ FullDetail
Also draw details of track and roads.
Definition openttd.h:50
@ Menu
In the main menu.
Definition openttd.h:19
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:345
TrackBits GetReservedTrackbits(TileIndex t)
Get the reserved trackbits for any tile, regardless of type.
Definition pbs.cpp:24
bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
Determine whether a certain track on a tile is a safe position to end a path.
Definition pbs.cpp:395
PBS support routines.
bool HasReservedTracks(TileIndex tile, TrackBits tracks)
Check whether some of tracks is reserved on a tile.
Definition pbs.h:58
bool ValParamRailType(const RailType rail)
Validate functions for rail building.
Definition rail.cpp:92
Money RailConvertCost(RailType from, RailType to)
Calculates the cost of rail conversion.
Definition rail.h:459
@ Ground
Main group of ground images.
Definition rail.h:45
@ GroundComplete
Complete ground images.
Definition rail.h:55
@ Overlay
Images for overlaying track.
Definition rail.h:44
@ UI
Cursor and toolbar icon images.
Definition rail.h:43
@ Depot
Depot images.
Definition rail.h:51
@ Fences
Fence images.
Definition rail.h:52
Money RailClearCost(RailType railtype)
Returns the 'cost' of clearing the specified railtype.
Definition rail.h:442
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:431
std::vector< RailType > _sorted_railtypes
Sorted list of rail types.
Definition rail_cmd.cpp:47
RailFenceOffset
Offsets from base sprite for fence sprites.
Definition rail.h:95
@ RFO_FLAT_RIGHT
Slope FLAT, Track RIGHT, Fence W.
Definition rail.h:106
@ RFO_FLAT_Y_NE
Slope FLAT, Track Y, Fence NE.
Definition rail.h:97
@ RFO_SLOPE_NW_SW
Slope NW, Track Y, Fence SW.
Definition rail.h:111
@ RFO_FLAT_X_SE
Slope FLAT, Track X, Fence SE.
Definition rail.h:104
@ RFO_SLOPE_NW_NE
Slope NW, Track Y, Fence NE.
Definition rail.h:103
@ RFO_SLOPE_SE_SW
Slope SE, Track Y, Fence SW.
Definition rail.h:109
@ RFO_SLOPE_NE_SE
Slope NE, Track X, Fence SE.
Definition rail.h:110
@ RFO_FLAT_LOWER
Slope FLAT, Track LOWER, Fence N.
Definition rail.h:107
@ RFO_SLOPE_SW_SE
Slope SW, Track X, Fence SE.
Definition rail.h:108
@ RFO_FLAT_UPPER
Slope FLAT, Track UPPER, Fence S.
Definition rail.h:99
@ RFO_SLOPE_SE_NE
Slope SE, Track Y, Fence NE.
Definition rail.h:101
@ RFO_FLAT_Y_SW
Slope FLAT, Track Y, Fence SW.
Definition rail.h:105
@ RFO_FLAT_LEFT
Slope FLAT, Track LEFT, Fence E.
Definition rail.h:98
@ RFO_SLOPE_NE_NW
Slope NE, Track X, Fence NW.
Definition rail.h:102
@ RFO_FLAT_X_NW
Slope FLAT, Track X, Fence NW.
Definition rail.h:96
@ RFO_SLOPE_SW_NW
Slope SW, Track X, Fence NW.
Definition rail.h:100
bool IsCompatibleRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType can drive on a tile with a given RailType.
Definition rail.h:354
bool HasPowerOnRail(RailType enginetype, RailType tiletype)
Checks if an engine of the given RailType got power on a tile with a given RailType.
Definition rail.h:379
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:303
@ Hidden
Bit number for hiding from selection.
Definition rail.h:30
@ NoSpriteCombine
Bit number for using non-combined junctions.
Definition rail.h:31
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:65
@ RTO_S
Piece of rail in southern corner.
Definition rail.h:67
@ RTO_JUNCTION_NE
Ballast for junction 'pointing' NE.
Definition rail.h:76
@ RTO_JUNCTION_NSEW
Ballast for full junction.
Definition rail.h:79
@ RTO_JUNCTION_SW
Ballast for junction 'pointing' SW.
Definition rail.h:75
@ RTO_SLOPE_SE
Piece of rail on slope with south-east raised.
Definition rail.h:71
@ RTO_E
Piece of rail in eastern corner.
Definition rail.h:68
@ RTO_W
Piece of rail in western corner.
Definition rail.h:69
@ RTO_CROSSING_XY
Crossing of X and Y rail, with ballast.
Definition rail.h:74
@ RTO_SLOPE_SW
Piece of rail on slope with south-west raised.
Definition rail.h:72
@ RTO_SLOPE_NW
Piece of rail on slope with north-west raised.
Definition rail.h:73
@ RTO_JUNCTION_NW
Ballast for junction 'pointing' NW.
Definition rail.h:78
@ RTO_JUNCTION_SE
Ballast for junction 'pointing' SE.
Definition rail.h:77
@ RTO_SLOPE_NE
Piece of rail on slope with north-east raised.
Definition rail.h:70
@ RTO_X
Piece of rail in X direction.
Definition rail.h:64
@ RTO_N
Piece of rail in northern corner.
Definition rail.h:66
bool RailNoLevelCrossings(RailType rt)
Test if a RailType disallows build of level crossings.
Definition rail.h:401
CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
Remove signals on a stretch of track.
int TicksToLeaveDepot(const Train *v)
Compute number of ticks when next wagon will leave a depot.
static bool IsTrainSignalSideRight()
Helper to determine whether the train signals are to be placed on the right side.
static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build)
Check that the new track bits may be built.
Definition rail_cmd.cpp:241
void DrawTrainDepotSprite(int x, int y, DiagDirection dir, RailType railtype)
Draw train depot sprite in the UI.
static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
Build or remove a stretch of railroad tracks.
Definition rail_cmd.cpp:886
static Trackdir GetOtherTrackdir(Trackdir trackdir)
Get the other Trackdir for a non-diagonal Trackdir.
Definition rail_cmd.cpp:822
static const int INF
Big number compared to tilesprite size.
CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
Build a train depot.
Definition rail_cmd.cpp:971
static void DrawTile_Rail(TileInfo *ti)
Tile callback function signature for drawing a tile and its contents to the screen.
CommandCost CmdBuildSingleSignal(DoCommandFlags flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy)
Build signals, alternate between double/single, signal/semaphore, pre/exit/combo-signals,...
CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
Build rail on a stretch of track.
Definition rail_cmd.cpp:943
CommandCost CmdBuildSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool autofill, bool minimise_gaps, uint8_t signal_density)
Build signals on a stretch of track.
static CommandCost ClearTile_Rail(TileIndex tile, DoCommandFlags flags)
Tile callback function signature for clearing a tile.
bool FloodHalftile(TileIndex t)
Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
Definition rail_cmd.cpp:761
RailType AllocateRailType(RailTypeLabel label)
Allocate a new rail type label.
Definition rail_cmd.cpp:150
static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
Tests if a track can be build on a tile.
Definition rail_cmd.cpp:392
CommandCost CmdRemoveSingleRail(DoCommandFlags flags, TileIndex tile, Track track)
Remove a single piece of track.
Definition rail_cmd.cpp:615
CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
Remove signals.
static constexpr NonSteepSlopeIndexArray< uint8_t > _track_sloped_sprites
Lookup table to convert tile's slope into corresponding track sprite offset.
Definition rail_cmd.cpp:182
static uint GetSafeSlopeZ(uint x, uint y, Track track)
Get surface height in point (x,y) On tiles with halftile foundations move (x,y) to a safe point wrt.
static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
Tests if a vehicle interacts with the specified track.
Definition rail_cmd.cpp:230
static constexpr DiagDirectionIndexArray< Coord2D< uint8_t > > _fractcoords_enter
Coordinates where a train should enter a depot for each depot direction.
static CommandCost CmdSignalTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool remove, bool autofill, bool minimise_gaps, int signal_density)
Build many signals by dragging; AutoSignals.
static int GetSlopePixelZ_Rail(TileIndex tile, uint x, uint y, bool ground_vehicle)
Tile callback function signature for obtaining the world Z coordinate of a given point of a tile.
static void DrawTrackBits(TileInfo *ti, TrackBits track)
Draw ground sprite and track bits.
static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
Draw track fences.
SignalOffsets
Enum holding the signal offset in the sprite sheet according to the side it is representing.
Definition rail_cmd.cpp:51
CommandCost CmdRemoveRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, Track track)
Build rail on a stretch of track.
Definition rail_cmd.cpp:958
static VehicleEnterTileStates VehicleEnterTile_Rail(Vehicle *v, TileIndex tile, int x, int y)
Tile callback function for a vehicle entering a tile.
CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
Build a single piece of rail.
Definition rail_cmd.cpp:426
static constexpr DiagDirectionIndexArray< Coord2D< uint8_t > > _fractcoords_behind
Coordinates to detect when a train is approaching a depot from behind for each depot direction.
CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
Convert one rail type to the other.
void InitRailTypes()
Resolve sprites of custom rail types.
Definition rail_cmd.cpp:130
static TrackStatus GetTileTrackStatus_Rail(TileIndex tile, TransportType mode, RoadTramType sub_mode, DiagDirection side)
Tile callback function signature for getting the possible tracks that can be taken on a given tile by...
static void ChangeTileOwner_Rail(TileIndex tile, Owner old_owner, Owner new_owner)
Tile callback function signature for changing the owner of a tile.
static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at SW border matching the tile slope.
static bool ClickTile_Rail(TileIndex tile)
Tile callback function signature for clicking a tile.
static void GetTileDesc_Rail(TileIndex tile, TileDesc &td)
Tile callback function signature for obtaining a tile description.
void ResetRailTypes()
Reset all rail type information to its default values.
Definition rail_cmd.cpp:65
static const TrackdirIndexArray< TileIndexDiffC > _trackdelta
Tile deltas for each trackdir.
Definition rail_cmd.cpp:807
static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
Tests if autoslope is allowed.
static const FenceOffset _fence_offsets[]
Offsets for drawing fences.
static constexpr CornerIndexArray< SubSprite > _halftile_sub_sprite
SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites.
static constexpr NonSteepSlopeIndexArray< TrackBits > _valid_tracks_without_foundation
Valid TrackBits on a specific (non-steep)-slope without foundation.
Definition rail_cmd.cpp:262
static int GetJunctionGroundSpriteOffset(TrackBits track)
Returns which of the 5 junction-'Rail underlays' to use for the given track bits.
static constexpr NonSteepSlopeIndexArray< TrackBits > _valid_tracks_on_leveled_foundation
Valid TrackBits on a specific (non-steep)-slope with leveled foundation.
Definition rail_cmd.cpp:284
static bool CompareRailTypes(const RailType &first, const RailType &second)
Compare railtypes based on their sorting order.
Definition rail_cmd.cpp:122
static Foundation GetFoundation_Rail(TileIndex tile, Slope tileh)
Tile callback function signature for getting the foundation of a tile.
static constexpr DiagDirectionIndexArray< Coord2D< int8_t > > _deltacoord_leaveoffset
Offsets (to be multiplied by length) from the depot enter coordinates where a train should appear whe...
static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at NW border matching the tile slope.
Foundation GetRailFoundation(Slope tileh, TrackBits bits)
Checks if a track combination is valid on a specific slope and returns the needed foundation.
Definition rail_cmd.cpp:312
static void DrawTrackFence_NE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at NE border matching the tile slope.
static void DrawTrackFence_SE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at SE border matching the tile slope.
static CommandCost CheckBuildAbove_Rail(TileIndex tile, DoCommandFlags flags, Axis axis, int height)
Tile callback function signature to test if a bridge can be built above a tile.
std::vector< Train * > TrainList
Helper type for lists/vectors of trains.
Definition rail_cmd.cpp:44
static void TileLoop_Rail(TileIndex tile)
Tile callback function signature for running periodic tile updates.
static CommandCost TerraformTile_Rail(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
Tile callback function signature of the terraforming callback.
static void DrawTrackFence(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites, RailFenceOffset rfo)
Draw a track fence.
Command definitions for rail.
static RailTileType GetRailTileType(Tile t)
Returns the RailTileType (normal with or without signals, waypoint or depot).
Definition rail_map.h:36
static bool IsPlainRail(Tile t)
Returns whether this is plain rails, with or without signals.
Definition rail_map.h:49
uint GetSignalStates(Tile tile)
Set the states of the signals (Along/AgainstTrackDir).
Definition rail_map.h:420
void MakeRailDepot(Tile tile, Owner owner, DepotID depot_id, DiagDirection dir, RailType rail_type)
Make a rail depot.
Definition rail_map.h:657
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
bool HasSignalOnTrackdir(Tile tile, Trackdir trackdir)
Checks for the presence of signals along the given trackdir on the given rail tile.
Definition rail_map.h:490
static bool IsRailDepot(Tile t)
Is this rail tile a rail depot?
Definition rail_map.h:95
SignalVariant GetSignalVariant(Tile t, Track track)
Get the signal variant for a track on a tile.
Definition rail_map.h:385
RailGroundType GetRailGroundType(Tile t)
Get the ground type for rail tiles.
Definition rail_map.h:601
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
static bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
bool IsPbsSignal(SignalType s)
Checks whether the given signal is a path based signal.
Definition rail_map.h:291
void CycleSignalSide(Tile t, Track track)
Cycle to the next signal side at the given track on a tile.
Definition rail_map.h:368
void MakeRailNormal(Tile t, Owner o, TrackBits b, RailType r)
Make the given tile a normal rail.
Definition rail_map.h:624
@ Normal
Normal rail tile without signals.
Definition rail_map.h:24
@ Depot
Depot (one entrance).
Definition rail_map.h:26
@ Signals
Normal rail tile with signals.
Definition rail_map.h:25
Track GetRailDepotTrack(Tile t)
Returns the track of a depot, ignoring direction.
Definition rail_map.h:182
void SetTrackReservation(Tile t, TrackBits b)
Sets the reserved track bits of the tile.
Definition rail_map.h:209
bool IsSignalPresent(Tile t, uint8_t signalbit)
Checks whether the given signals is present.
Definition rail_map.h:462
DiagDirection GetRailDepotDirection(Tile t)
Returns the direction the depot is facing to.
Definition rail_map.h:171
void SetTrackBits(Tile t, TrackBits b)
Sets the track bits of the given tile.
Definition rail_map.h:147
RailGroundType
The ground 'under' the rail.
Definition rail_map.h:568
@ FenceSENW
Grass with a fence at the NW and SE edges.
Definition rail_map.h:573
@ FenceVert2
Grass with a fence at the western side.
Definition rail_map.h:578
@ HalfTileSnow
Snow only on higher part of slope (steep or one corner raised).
Definition rail_map.h:583
@ Barren
Nothing (dirt).
Definition rail_map.h:569
@ FenceNESW
Grass with a fence at the NE and SW edges.
Definition rail_map.h:576
@ FenceHoriz2
Grass with a fence at the northern side.
Definition rail_map.h:580
@ SnowOrDesert
Icy or sandy.
Definition rail_map.h:581
@ HalfTileWater
Grass with a fence and shore or water on the free halftile.
Definition rail_map.h:582
@ FenceVert1
Grass with a fence at the eastern side.
Definition rail_map.h:577
@ FenceNW
Grass with a fence at the NW edge.
Definition rail_map.h:571
@ FenceSE
Grass with a fence at the SE edge.
Definition rail_map.h:572
@ FenceNE
Grass with a fence at the NE edge.
Definition rail_map.h:574
@ Grass
Grassy.
Definition rail_map.h:570
@ FenceSW
Grass with a fence at the SW edge.
Definition rail_map.h:575
@ FenceHoriz1
Grass with a fence at the southern side.
Definition rail_map.h:579
uint GetPresentSignals(Tile tile)
Get whether the given signals are present (Along/AgainstTrackDir).
Definition rail_map.h:451
bool HasSignalOnTrack(Tile tile, Track track)
Checks for the presence of signals (either way) on the given track on the given rail tile.
Definition rail_map.h:474
bool IsOnewaySignal(Tile t, Track track)
Is the signal at the given track on a tile a one way signal?
Definition rail_map.h:357
void SetPresentSignals(Tile tile, uint signals)
Set whether the given signals are present (Along/AgainstTrackDir).
Definition rail_map.h:441
void SetRailGroundType(Tile t, RailGroundType rgt)
Set the ground type for rail tiles.
Definition rail_map.h:591
void SetRailDepotExitDirection(Tile tile, DiagDirection dir)
Sets the exit direction of a rail depot.
Definition rail_map.h:643
SignalType GetSignalType(Tile t, Track track)
Get the signal type for a track on a tile.
Definition rail_map.h:303
void SetDepotReservation(Tile t, bool b)
Set the reservation state of the depot.
Definition rail_map.h:268
TrackBits GetRailReservationTrackBits(Tile t)
Returns the reserved track bits of the tile.
Definition rail_map.h:194
void SetSignalType(Tile t, Track track, SignalType s)
Set the signal type for a track on a tile.
Definition rail_map.h:317
bool HasDepotReservation(Tile t)
Get the reservation state of the depot.
Definition rail_map.h:256
void SetHasSignals(Tile tile, bool signals)
Add/remove the 'has signal' bit from the RailTileType.
Definition rail_map.h:83
bool IsSnowOrDesertRailGround(Tile t)
Is the given rail tile snowy or deserty.
Definition rail_map.h:611
bool HasSignals(Tile t)
Checks if a rail tile has signals.
Definition rail_map.h:72
void SetSignalVariant(Tile t, Track track, SignalVariant v)
Set the signal variant for a track on a tile.
Definition rail_map.h:398
static bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
bool HasTrack(Tile tile, Track track)
Returns whether the given track is present on the given tile.
Definition rail_map.h:160
SignalState GetSingleSignalState(Tile t, uint8_t signalbit)
Get the state of a single signal.
Definition rail_map.h:431
void SetSignalStates(Tile tile, uint state)
Set the states of the signals (Along/AgainstTrackDir).
Definition rail_map.h:410
void SetRailType(Tile t, RailType r)
Sets the rail type of the given tile.
Definition rail_map.h:125
EnumBitSet< RailType, uint64_t > RailTypes
Bitset of RailType elements.
Definition rail_type.h:37
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:26
@ RAILTYPE_END
Used for iterations.
Definition rail_type.h:32
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:33
@ RAILTYPE_ELECTRIC
Electric rails.
Definition rail_type.h:29
@ RAILTYPE_RAIL
Standard non-electric rails.
Definition rail_type.h:28
All the railtype-specific information is stored here.
static const RailTypeInfo _original_railtypes[]
Global Railtype definition.
Definition railtypes.h:18
bool RoadNoLevelCrossing(RoadType roadtype)
Test if road disallows level crossings.
Definition road.h:285
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:242
void UpdateAdjacentLevelCrossingTilesOnLevelCrossingRemoval(TileIndex tile, Axis road_axis)
Update adjacent level crossing tiles in this multi-track crossing, due to removal of a level crossing...
void MarkDirtyAdjacentLevelCrossingTiles(TileIndex tile, Axis road_axis)
Find adjacent level crossing tiles in this multi-track crossing and mark them dirty.
void UpdateLevelCrossing(TileIndex tile, bool sound=true, bool force_bar=false)
Update a level crossing to barred or open (crossing may include multiple adjacent tiles).
RoadType GetRoadTypeRoad(Tile t)
Get the road type for RoadTramType being RoadTramType::Road.
Definition road_map.h:152
bool IsLevelCrossingTile(Tile t)
Return whether a tile is a level crossing tile.
Definition road_map.h:79
RoadBits GetRoadBits(Tile t, RoadTramType rtt)
Get the present road bits for a specific road type.
Definition road_map.h:112
Axis GetCrossingRoadAxis(Tile t)
Get the road axis of a level crossing.
Definition road_map.h:335
Track GetCrossingRailTrack(Tile tile)
Get the rail track of a level crossing.
Definition road_map.h:368
DisallowedRoadDirections GetDisallowedRoadDirections(Tile t)
Gets the disallowed directions.
Definition road_map.h:311
RoadType GetRoadTypeTram(Tile t)
Get the road type for RoadTramType being RoadTramType::Tram.
Definition road_map.h:163
void MakeRoadNormal(Tile t, RoadBits bits, RoadType road_rt, RoadType tram_rt, TownID town, Owner road, Owner tram)
Make a normal road tile.
Definition road_map.h:635
void MakeRoadCrossing(Tile t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadType road_rt, RoadType tram_rt, TownID town)
Make a level crossing.
Definition road_map.h:661
RoadBits GetCrossingRoadBits(Tile tile)
Get the road bits of a level crossing.
Definition road_map.h:358
Owner GetRoadOwner(Tile t, RoadTramType rtt)
Get the owner of a specific road type.
Definition road_map.h:244
bool IsLevelCrossing(Tile t)
Return whether a tile is a level crossing.
Definition road_map.h:69
static bool IsNormalRoad(Tile t)
Return whether a tile is a normal road.
Definition road_map.h:48
bool HasRoadWorks(Tile t)
Check if a tile has road works.
Definition road_map.h:508
static constexpr RoadBits ROAD_X
Full road along the x-axis (south-west + north-east).
Definition road_type.h:67
EnumBitSet< RoadBit, uint8_t > RoadBits
Bitset of RoadBit elements.
Definition road_type.h:65
static constexpr RoadBits ROAD_Y
Full road along the y-axis (north-west + south-east).
Definition road_type.h:68
RoadType
The different roadtypes we support.
Definition road_type.h:24
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:29
RoadTramType
The different types of road type.
Definition road_type.h:38
@ Invalid
Invalid marker.
Definition road_type.h:42
@ Tram
Tram type.
Definition road_type.h:40
@ Road
Road type.
Definition road_type.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
@ RoadVehicleDrivingSide
Signals at the driving side of road vehicles.
@ Right
Signals at the right side.
@ Left
Signals at the left side.
@ Right
Drive on the right side.
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:598
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:630
uint8_t SignalAgainstTrackdir(Trackdir trackdir)
Maps a trackdir to the bit that stores its status in the map arrays, in the direction against the tra...
Definition signal_func.h:36
uint8_t SignalAlongTrackdir(Trackdir trackdir)
Maps a trackdir to the bit that stores its status in the map arrays, in the direction along with the ...
Definition signal_func.h:24
uint8_t SignalOnTrack(Track track)
Maps a Track to the bits that store the status of the two signals that can be present on the given tr...
Definition signal_func.h:48
SignalType
Type of signal, i.e.
Definition signal_type.h:24
@ End
End marker.
Definition signal_type.h:31
@ Path
normal path signal.
Definition signal_type.h:29
@ Entry
presignal block entry.
Definition signal_type.h:26
@ Block
block signal.
Definition signal_type.h:25
@ Exit
presignal block exit.
Definition signal_type.h:27
SignalState
These are states in which a signal can be.
Definition signal_type.h:40
@ Green
The signal is green.
Definition signal_type.h:42
@ Red
The signal is red.
Definition signal_type.h:41
SignalVariant
Variant of the signal, i.e.
Definition signal_type.h:16
@ End
End marker.
Definition signal_type.h:19
@ Electric
Light signal.
Definition signal_type.h:17
@ Semaphore
Old-fashioned semaphore signal.
Definition signal_type.h:18
bool IsSpecialRailFoundation(Foundation f)
Tests if a foundation is a special rail foundation for single horizontal/vertical track.
Definition slope_func.h:355
Slope SlopeWithThreeCornersRaised(Corner corner)
Returns the slope with all except one corner raised.
Definition slope_func.h:216
Corner OppositeCorner(Corner corner)
Returns the opposite corner.
Definition slope_func.h:194
static constexpr Corner GetHalftileSlopeCorner(Slope s)
Returns the leveled halftile of a halftile slope.
Definition slope_func.h:158
static constexpr Slope RemoveHalftileSlope(Slope s)
Removes a halftile slope from a slope.
Definition slope_func.h:70
bool IsSlopeWithOneCornerRaised(Slope s)
Tests if a specific slope has exactly one corner raised.
Definition slope_func.h:98
uint SlopeToSpriteOffset(Slope s)
Returns the Sprite offset for a given Slope.
Definition slope_func.h:423
bool IsNonContinuousFoundation(Foundation f)
Tests if a foundation is a non-continuous foundation, i.e.
Definition slope_func.h:330
Corner GetHighestSlopeCorner(Slope s)
Returns the highest corner of a slope (one corner raised or a steep slope).
Definition slope_func.h:136
Corner GetHalftileFoundationCorner(Foundation f)
Returns the halftile corner of a halftile-foundation.
Definition slope_func.h:343
static constexpr bool IsValidCorner(Corner corner)
Rangecheck for Corner enumeration.
Definition slope_func.h:24
static constexpr bool IsSteepSlope(Slope s)
Checks if a slope is steep.
Definition slope_func.h:36
bool IsSlopeWithThreeCornersRaised(Slope s)
Tests if a specific slope has exactly three corners raised.
Definition slope_func.h:205
Foundation FlatteningFoundation(Slope s)
Returns the foundation needed to flatten a slope.
Definition slope_func.h:377
static constexpr bool IsHalftileSlope(Slope s)
Checks for non-continuous slope on halftile foundations.
Definition slope_func.h:57
Foundation HalftileFoundation(Corner corner)
Returns the halftile foundation for single horizontal/vertical track.
Definition slope_func.h:399
Foundation SpecialRailFoundation(Corner corner)
Returns the special rail foundation for single horizontal/vertical track.
Definition slope_func.h:411
Slope ComplementSlope(Slope s)
Return the complement of a slope.
Definition slope_func.h:86
Slope SlopeWithOneCornerRaised(Corner corner)
Returns the slope with a specific corner raised.
Definition slope_func.h:109
Corner
Enumeration of tile corners.
Definition slope_type.h:21
Slope
Enumeration for the slope-type.
Definition slope_type.h:53
@ SLOPE_W
the west corner of the tile is raised
Definition slope_type.h:55
@ SLOPE_ELEVATED
bit mask containing all 'simple' slopes
Definition slope_type.h:66
@ SLOPE_NS
north and south corner are raised
Definition slope_type.h:65
@ SLOPE_E
the east corner of the tile is raised
Definition slope_type.h:57
@ SLOPE_S
the south corner of the tile is raised
Definition slope_type.h:56
@ SLOPE_N
the north corner of the tile is raised
Definition slope_type.h:58
@ SLOPE_SW
south and west corner are raised
Definition slope_type.h:61
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:54
@ SLOPE_NE
north and east corner are raised
Definition slope_type.h:63
@ SLOPE_SE
south and east corner are raised
Definition slope_type.h:62
@ SLOPE_NW
north and west corner are raised
Definition slope_type.h:60
@ SLOPE_EW
east and west corner are raised
Definition slope_type.h:64
Foundation
Enumeration for Foundations.
Definition slope_type.h:119
@ Leveled
The tile is leveled up to a flat slope.
Definition slope_type.h:121
@ SteepLower
The tile has a steep slope. The lowest corner is raised by a foundation to allow building railroad on...
Definition slope_type.h:124
@ Invalid
Used inside "rail_cmd.cpp" to indicate invalid slope/track combination.
Definition slope_type.h:141
@ None
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:120
@ InclinedY
The tile has an along Y-axis inclined foundation.
Definition slope_type.h:123
@ SteepBoth
The tile has a steep slope. The lowest corner is raised by a foundation and the upper halftile is lev...
Definition slope_type.h:127
@ InclinedX
The tile has an along X-axis inclined foundation.
Definition slope_type.h:122
static const uint32_t VALID_LEVEL_CROSSING_SLOPES
Constant bitset with safe slopes for building a level crossing.
Definition slope_type.h:112
EnumClassIndexContainer< std::array< T, SLOPE_ELEVATED >, Slope > NonSteepSlopeIndexArray
Array with non steep Slope as index.
Definition slope_type.h:104
EnumIndexArray< T, Corner, CORNER_END > CornerIndexArray
Array with Corner as index.
Definition slope_type.h:35
Functions related to sound.
void DrawRailTileSeqInGUI(int x, int y, const DrawTileSprites *dts, int32_t total_offset, uint32_t newgrf_offset, PaletteID default_palette)
Draw tile sprite sequence in GUI with railroad specifics.
Definition sprite.h:122
void DrawRailTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, int32_t total_offset, uint32_t newgrf_offset, PaletteID default_palette)
Draw tile sprite sequence on tile with railroad specifics.
Definition sprite.h:108
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:207
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_PBS
Definition sprites.h:1528
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY
Definition sprites.h:1529
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_NORM
Definition sprites.h:1524
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PBS
Definition sprites.h:1522
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_COMBO
Definition sprites.h:1521
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_ENTRY
Definition sprites.h:1525
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_ENTRY
Definition sprites.h:1519
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1793
static const SpriteID SPR_FLAT_BARE_LAND
Definition sprites.h:673
static const SpriteID SPR_FLAT_WATER_TILE
Definition sprites.h:684
static const SpriteID SPR_FLAT_SNOW_DESERT_TILE
Definition sprites.h:688
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_NORM
Definition sprites.h:1518
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_COMBO
Definition sprites.h:1527
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_EXIT
Definition sprites.h:1520
static const SpriteID SPR_FLAT_GRASS_TILE
Definition sprites.h:676
static const PaletteID PALETTE_TO_BARE_LAND
sets colour to bare land stuff for rail, road and crossings
Definition sprites.h:1781
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_EXIT
Definition sprites.h:1526
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY
Definition sprites.h:1523
@ Count
by amount of cargo
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
Track GetRailStationTrack(Tile t)
Get the rail track of a rail station tile.
bool IsStationTileBlocked(Tile t)
Is tile t a blocked tile?
bool HasStationRail(Tile t)
Has this station tile a rail?
Definition of base types and functions in a cross-platform compatible way.
Functions related to OTTD's strings.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames).
Class to backup a specific variable and restore it upon destruction of this object to prevent stack v...
VehicleFlags vehicle_flags
Used for gradual loading and other miscellaneous things (.
constexpr bool Empty() const
Check whether the label is empty.
VehicleType type
Type of vehicle.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
A coordinate with two dimensions.
T y
Y coordinate.
T x
X coordinate.
T x
X coordinate.
T y
Y coordinate.
T z
Z coordinate.
Represents a diagonal tile area.
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:55
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:56
Offsets for drawing fences.
Corner height_ref
Corner to use height offset from.
static uint Size()
Get the size of the map.
Definition map_func.h:280
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:67
Represents the covered area of e.g.
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 T * Create(Targs &&... args)
static Company * Get(auto index)
static bool CanAllocateItem(size_t n=1)
T * GetMovingFront() const
Get the moving front of the vehicle chain.
T * Next() const
Get next vehicle in the chain.
static Train * From(Vehicle *v)
T * First() const
Get the first vehicle in the chain.
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:40
uint16_t rail_speed
Speed limit of rail (bridges and track).
Definition tile_cmd.h:53
StringID str
Description of the tile.
Definition tile_cmd.h:41
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:45
std::array< Owner, 4 > owner
Name of the owner(s).
Definition tile_cmd.h:43
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:52
Tile information, used while rendering the tile.
Definition tile_cmd.h:34
Slope tileh
Slope of the tile.
Definition tile_cmd.h:35
TileIndex tile
Tile index.
Definition tile_cmd.h:36
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:214
Track status of a tile.
Definition track_type.h:105
TrackdirBits trackdirs
Trackdirs present on the tile.
Definition track_type.h:106
'Train' is either a loco or a wagon.
Definition train.h:97
Trackdir GetVehicleTrackdir() const override
Get the tracks of the train vehicle.
RailTypes railtypes
On which rail types the train can run.
Definition train.h:108
TrackBits track
On which track the train currently is.
Definition train.h:110
int CalcNextVehicleOffset() const
Calculate the offset from this vehicle's center to the following center taking the vehicle lengths in...
Definition train.h:180
Vehicle data structure.
Direction GetMovingDirection() const
Get the moving direction of this vehicle chain.
VehStates vehstatus
Status.
Order current_order
The current order (+ status, like: loading).
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
uint16_t cur_speed
current speed
Vehicle * GetMovingNext() const
Get the next vehicle in the vehicle chain, relative to its current movement.
TileIndex tile
Current tile index.
@ CannotEnter
The vehicle cannot enter the tile.
Definition tile_cmd.h:27
@ EnteredWormhole
The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/...
Definition tile_cmd.h:26
EnumBitSet< VehicleEnterTileState, uint8_t > VehicleEnterTileStates
Bitset of VehicleEnterTileState elements.
Definition tile_cmd.h:31
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:135
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
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
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
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
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
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
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
@ Desert
Tile is desert.
Definition tile_type.h:83
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static constexpr uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
TileType
The different types of tiles.
Definition tile_type.h:48
@ TunnelBridge
Tunnel entry/exit and bridge heads.
Definition tile_type.h:58
@ Water
Water tile.
Definition tile_type.h:55
@ Station
A tile of a station or airport.
Definition tile_type.h:54
@ Object
Contains objects such as transmitters and owned land.
Definition tile_type.h:59
@ Industry
Part of an industry.
Definition tile_type.h:57
@ Railway
A tile with railway.
Definition tile_type.h:50
@ House
A house by a town.
Definition tile_type.h:52
@ Road
A tile with road and/or tram tracks.
Definition tile_type.h:51
Definition of the game-calendar-timer.
Base of the town class.
void MakeDefaultName(T *obj)
Set the default name for a depot/waypoint.
Definition town.h:334
TownID GetTownIndex(Tile t)
Get the index of which town this house/street is attached to.
Definition town_map.h:23
Trackdir TrackToTrackdir(Track track)
Returns a Trackdir for the given Track.
Definition track_func.h:252
Track TrackdirToTrack(Trackdir trackdir)
Returns the Track that a given Trackdir represents.
Definition track_func.h:235
TrackBits CornerToTrackBits(Corner corner)
Returns a single horizontal/vertical trackbit that is in a specific tile corner.
Definition track_func.h:74
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:292
Track TrackBitsToTrack(TrackBits tracks)
Converts TrackBits to Track.
Definition track_func.h:166
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:220
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:540
TrackdirBits TrackdirReachesTrackdirs(Trackdir trackdir)
Maps a trackdir to the trackdirs that can be reached from it (ie, when entering the next tile.
Definition track_func.h:479
bool IsValidTrack(Track track)
Checks if a Track is valid.
Definition track_func.h:24
Track FindFirstTrack(TrackBits tracks)
Returns first Track from TrackBits or Track::Invalid.
Definition track_func.h:150
Trackdir RemoveFirstTrackdir(TrackdirBits &trackdirs)
Removes first Trackdir from TrackdirBits and returns it.
Definition track_func.h:130
bool IsDiagonalTrackdir(Trackdir trackdir)
Checks if a given Trackdir is diagonal.
Definition track_func.h:526
Track AxisToTrack(Axis a)
Convert an Axis to the corresponding Track Axis::X -> Track::X Axis::Y -> Track::Y Uses the fact that...
Definition track_func.h:62
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:343
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a DiagDirection to the associated diagonal Track.
Definition track_func.h:419
TrackBits TrackdirBitsToTrackBits(TrackdirBits bits)
Discards all directional information from a TrackdirBits value.
Definition track_func.h:281
Sprites to use and how to display them for train depot tiles.
static const DiagDirectionIndexArray< DrawTileSpriteSpan > _depot_gfx_table
Sprite layout of a depot for each direction.
Definition track_land.h:32
static const DiagDirectionIndexArray< DrawTileSpriteSpan > _depot_invisible_gfx_table
Sprite layout of an invisible depot with for each direction.
Definition track_land.h:40
static constexpr TrackBits TRACK_BIT_3WAY_NE
"Arrow" to the north-east
Definition track_type.h:48
static constexpr TrackBits TRACK_BIT_HORZ
Upper and lower track.
Definition track_type.h:46
EnumBitSet< Trackdir, uint16_t > TrackdirBits
Bitset of Trackdir elements.
Definition track_type.h:93
EnumBitSet< Track, uint8_t > TrackBits
Bitset of Track elements.
Definition track_type.h:43
static constexpr TrackBits TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:52
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:63
@ Right_N
Right track and direction to north.
Definition track_type.h:77
@ X_NE
X-axis and direction to north-east.
Definition track_type.h:64
@ Right_S
Right track and direction to south.
Definition track_type.h:69
@ Left_S
Left track and direction to south.
Definition track_type.h:68
@ Lower_E
Lower track and direction to east.
Definition track_type.h:67
@ X_SW
X-axis and direction to south-west.
Definition track_type.h:72
@ Invalid
Flag for an invalid trackdir.
Definition track_type.h:82
@ Y_SE
Y-axis and direction to south-east.
Definition track_type.h:65
@ Y_NW
Y-axis and direction to north-west.
Definition track_type.h:73
@ Upper_W
Upper track and direction to west.
Definition track_type.h:74
@ Upper_E
Upper track and direction to east.
Definition track_type.h:66
@ Left_N
Left track and direction to north.
Definition track_type.h:76
@ Lower_W
Lower track and direction to west.
Definition track_type.h:75
static constexpr TrackBits TRACK_BIT_VERT
Left and right track.
Definition track_type.h:47
static constexpr TrackBits TRACK_BIT_CROSS
X-Y-axis cross.
Definition track_type.h:45
static constexpr TrackBits TRACK_BIT_3WAY_SW
"Arrow" to the south-west
Definition track_type.h:50
static constexpr TrackBits TRACK_BIT_3WAY_SE
"Arrow" to the south-east
Definition track_type.h:49
EnumIndexArray< T, Trackdir, Trackdir::End > TrackdirIndexArray
Array with Trackdir as index.
Definition track_type.h:90
static constexpr TrackBits TRACK_BIT_3WAY_NW
"Arrow" to the north-west
Definition track_type.h:51
Track
These are used to specify a single track.
Definition track_type.h:19
@ X
Track along the x-axis (north-east to south-west).
Definition track_type.h:21
@ Upper
Track in the upper corner of the tile (north).
Definition track_type.h:23
@ Invalid
Flag for an invalid track.
Definition track_type.h:32
@ Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ Right
Track in the right corner of the tile (east).
Definition track_type.h:26
@ Left
Track in the left corner of the tile (west).
Definition track_type.h:25
@ Depot
Special flag indicating a vehicle is inside a depot.
Definition track_type.h:30
@ Lower
Track in the lower corner of the tile (south).
Definition track_type.h:24
Base for the train class.
static constexpr ConsistChangeFlags CCF_TRACK
Valid changes while vehicle is driving, and possibly changing tracks.
Definition train.h:53
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 IsInvisibilitySet(TransparencyOption to)
Check if the invisibility option bit is set and if we aren't in the game menu (there's never transpar...
DisplayOptions _display_opt
What do we want to draw/do?
@ Buildings
company buildings - depots, stations, HQ, ...
TransportType
Available types of transport.
@ Water
Transport over water.
@ Rail
Transport by train.
Header file for things common for tunnels and bridges.
void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height)
Mark bridge tiles dirty.
uint GetTunnelBridgeLength(TileIndex begin, TileIndex end)
Calculates the length of a tunnel or a bridge (without end tiles).
Functions that have tunnels and bridges in common.
DiagDirection GetTunnelBridgeDirection(Tile t)
Get the direction pointing to the other end.
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.
CommandCost EnsureNoVehicleOnGround(TileIndex tile)
Ensure there is no vehicle at the ground at the given position.
Definition vehicle.cpp:557
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1562
CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
Tests if a vehicle interacts with the specified track bits.
Definition vehicle.cpp:605
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:581
const uint TILE_AXIAL_DISTANCE
Logical length of the tile in any DiagDirection used in vehicle movement.
const uint TILE_CORNER_DISTANCE
Logical length of the tile corner crossing in any non-diagonal direction used in vehicle movement.
@ Hidden
Vehicle is not visible.
@ Stopped
Vehicle is stopped by the player.
Functions related to vehicles.
bool HasVehicleOnTile(TileIndex tile, UnaryPred &&predicate)
Loop over vehicles on a tile, and check whether a predicate is true for any of them.
@ Ship
Ship vehicle type.
@ Train
Train vehicle type.
@ Original
Original acceleration model.
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:664
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 const int TILE_HEIGHT_STEP
One Z unit tile height difference is displayed as 50m.
static constexpr int BB_HEIGHT_UNDER_BRIDGE
Some values for constructing bounding boxes (BB).
Functions related to water management.
void TileLoop_Water(TileIndex tile)
Tile callback function signature for running periodic tile updates.
@ None
The tile does not flood neighboured tiles.
Definition water.h:20
FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
Returns the behaviour of a tile during flooding.
void CheckForDockingTile(TileIndex t)
Mark the supplied tile as a docking tile if it is suitable for docking.
bool IsPossibleDockingTile(Tile t)
Check whether it is feasible that the given tile could be a docking tile.
void MakeShore(Tile t)
Helper function to make a coast tile.
Definition water_map.h:385
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:375
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:364
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3315
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.