OpenTTD Source 20260711-master-g3fb3006dff
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 == 0) 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, 0, &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
181static const uint8_t _track_sloped_sprites[14] = {
182 14, 15, 22, 13,
183 0, 21, 17, 12,
184 23, 0, 18, 20,
185 19, 16
186};
187
188
189/* 4
190 * ---------
191 * |\ /|
192 * | \ 1/ |
193 * | \ / |
194 * | \ / |
195 * 16| \ |32
196 * | / \2 |
197 * | / \ |
198 * | / \ |
199 * |/ \|
200 * ---------
201 * 8
202 */
203
204
205
206/* MAP2 byte: abcd???? => Signal On? Same coding as map3lo
207 * MAP3LO byte: abcd???? => Signal Exists?
208 * a and b are for diagonals, upper and left,
209 * one for each direction. (ie a == NE->SW, b ==
210 * SW->NE, or v.v., I don't know. b and c are
211 * similar for lower and right.
212 * MAP2 byte: ????abcd => Type of ground.
213 * MAP3LO byte: ????abcd => Type of rail.
214 * MAP5: 00abcdef => rail
215 * 01abcdef => rail w/ signals
216 * 10uuuuuu => unused
217 * 11uuuudd => rail depot
218 */
219
229{
230 return EnsureNoTrainOnTrackBits(tile, track);
231}
232
240{
241 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
242
243 /* So, we have a tile with tracks on it (and possibly signals). Let's see
244 * what tracks first */
245 TrackBits current = GetTrackBits(tile); // The current track layout.
246 TrackBits future = current | to_build; // The track layout we want to build.
247
248 /* Are we really building something new? */
249 if (current == future) {
250 /* Nothing new is being built */
251 return CommandCost(STR_ERROR_ALREADY_BUILT);
252 }
253
254 /* Normally, we may overlap and any combination is valid */
255 return CommandCost();
256}
257
258
264 Track::X,
265
267 {},
268 Track::Y,
270
272 Track::Y,
273 {},
275
276 Track::X,
279};
280
302
311{
312 if (bits.None()) return Foundation::None;
313
314 if (IsSteepSlope(tileh)) {
315 /* Test for inclined foundations */
316 if (bits == Track::X) return Foundation::InclinedX;
317 if (bits == Track::Y) return Foundation::InclinedY;
318
319 /* Get higher track */
320 Corner highest_corner = GetHighestSlopeCorner(tileh);
321 TrackBits higher_track = CornerToTrackBits(highest_corner);
322
323 /* Only higher track? */
324 if (bits == higher_track) return HalftileFoundation(highest_corner);
325
326 /* Overlap with higher track? */
327 if (TracksOverlap(bits | higher_track)) return Foundation::Invalid;
328
329 /* either lower track or both higher and lower track */
330 return bits.Any(higher_track) ? Foundation::SteepBoth : Foundation::SteepLower;
331 } else {
332 if (TrackBits{bits}.Reset(_valid_tracks_without_foundation[tileh]).None()) return Foundation::None;
333
334 bool valid_on_leveled = TrackBits{bits}.Reset(_valid_tracks_on_leveled_foundation[tileh]).None();
335
336 Corner track_corner;
337 switch (bits.base()) {
338 case TrackBits{Track::Left}.base(): track_corner = CORNER_W; break;
339 case TrackBits{Track::Lower}.base(): track_corner = CORNER_S; break;
340 case TrackBits{Track::Right}.base(): track_corner = CORNER_E; break;
341 case TrackBits{Track::Upper}.base(): track_corner = CORNER_N; break;
342
343 case TRACK_BIT_HORZ.base():
344 if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
345 if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
346 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
347
348 case TRACK_BIT_VERT.base():
349 if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
350 if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
351 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
352
353 case TrackBits{Track::X}.base():
355 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
356
357 case TrackBits{Track::Y}.base():
359 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
360
361 default:
362 return (valid_on_leveled ? Foundation::Leveled : Foundation::Invalid);
363 }
364 /* Single diagonal track */
365
366 /* Track must be at least valid on leveled foundation */
367 if (!valid_on_leveled) return Foundation::Invalid;
368
369 /* If slope has three raised corners, build leveled foundation */
371
372 /* If neighboured corners of track_corner are lowered, build halftile foundation */
373 if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
374
375 /* else special anti-zig-zag foundation */
376 return SpecialRailFoundation(track_corner);
377 }
378}
379
380
390static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
391{
392 /* don't allow building on the lower side of a coast */
394 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);
395 }
396
397 Foundation f_new = GetRailFoundation(tileh, TrackBits{rail_bits}.Set(existing));
398
399 /* check track/slope combination */
400 if ((f_new == Foundation::Invalid) ||
401 ((f_new != Foundation::None) && (!_settings_game.construction.build_on_slopes))) {
402 return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
403 }
404
405 Foundation f_old = GetRailFoundation(tileh, existing);
406 return CommandCost(ExpensesType::Construction, f_new != f_old ? _price[Price::BuildFoundation] : (Money)0);
407}
408
409/* Validate functions for rail building */
410static inline bool ValParamTrackOrientation(Track track)
411{
412 return IsValidTrack(track);
413}
414
424CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
425{
427
428 if (!ValParamRailType(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
429
430 Slope tileh = GetTileSlope(tile);
431 TrackBits trackbit = track;
432
433 switch (GetTileType(tile)) {
434 case TileType::Railway: {
436 if (ret.Failed()) return ret;
437
438 if (!IsPlainRail(tile)) return Command<Commands::LandscapeClear>::Do(flags, tile); // just get appropriate error message
439
440 if (!IsCompatibleRail(GetRailType(tile), railtype)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
441
442 ret = CheckTrackCombination(tile, trackbit);
443 if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
444 if (ret.Failed()) return ret;
445
446 ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
447 if (ret.Failed()) return ret;
448 cost.AddCost(ret.GetCost());
449
450 if (HasSignals(tile) && TracksOverlap(GetTrackBits(tile) | track)) {
451 /* If adding the new track causes any overlap, all signals must be removed first */
452 if (!auto_remove_signals) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
453
454 for (Track existing_track : GetTrackBits(tile)) {
455 if (HasSignalOnTrack(tile, existing_track)) {
456 CommandCost ret_remove_signals = Command<Commands::RemoveSignal>::Do(flags, tile, existing_track);
457 if (ret_remove_signals.Failed()) return ret_remove_signals;
458 cost.AddCost(ret_remove_signals.GetCost());
459 }
460 }
461 }
462
463 /* If the rail types don't match, try to convert only if engines of
464 * the new rail type are not powered on the present rail type and engines of
465 * the present rail type are powered on the new rail type. */
466 if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
467 if (HasPowerOnRail(GetRailType(tile), railtype)) {
468 ret = Command<Commands::ConvertRail>::Do(flags, tile, tile, railtype, false);
469 if (ret.Failed()) return ret;
470 cost.AddCost(ret.GetCost());
471 } else {
472 return CMD_ERROR;
473 }
474 }
475
476 if (flags.Test(DoCommandFlag::Execute)) {
478 TrackBits bits = GetTrackBits(tile);
479 SetTrackBits(tile, bits | trackbit);
480 /* Subtract old infrastructure count. */
481 uint pieces = bits.Count();
482 if (TracksOverlap(bits)) pieces *= pieces;
483 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
484 /* Add new infrastructure count. */
485 pieces = (bits | trackbit).Count();
486 if (TracksOverlap(bits | trackbit)) pieces *= pieces;
487 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
489 }
490 break;
491 }
492
493 case TileType::Road: {
494 /* Level crossings may only be built on these slopes */
495 if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
496
497 if (!_settings_game.construction.crossing_with_competitor && _current_company != OWNER_DEITY) {
499 if (ret.Failed()) return ret;
500 }
501
503 if (ret.Failed()) return ret;
504
505 if (IsNormalRoad(tile)) {
506 if (HasRoadWorks(tile)) return CommandCost(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
507
508 if (GetDisallowedRoadDirections(tile).Any()) return CommandCost(STR_ERROR_CROSSING_ON_ONEWAY_ROAD);
509
510 if (RailNoLevelCrossings(railtype)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_RAIL);
511
512 RoadType roadtype_road = GetRoadTypeRoad(tile);
513 RoadType roadtype_tram = GetRoadTypeTram(tile);
514
515 if (roadtype_road != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_road)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
516 if (roadtype_tram != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_tram)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
517
520 if ((track == Track::X && !(road | tram).Any(ROAD_X)) ||
521 (track == Track::Y && !(road | tram).Any(ROAD_Y))) {
522 Owner road_owner = GetRoadOwner(tile, RoadTramType::Road);
523 Owner tram_owner = GetRoadOwner(tile, RoadTramType::Tram);
524 /* Disallow breaking end-of-line of someone else
525 * so trams can still reverse on this tile. */
526 if (Company::IsValidID(tram_owner) && tram.Count() == 1) {
527 ret = CheckOwnership(tram_owner);
528 if (ret.Failed()) return ret;
529 }
530
531 uint num_new_road_pieces = road.Any() ? 2 - road.Count() : 0;
532 if (num_new_road_pieces > 0) {
533 cost.AddCost(num_new_road_pieces * RoadBuildCost(roadtype_road));
534 }
535
536 uint num_new_tram_pieces = tram.Any() ? 2 - tram.Count() : 0;
537 if (num_new_tram_pieces > 0) {
538 cost.AddCost(num_new_tram_pieces * RoadBuildCost(roadtype_tram));
539 }
540
541 if (flags.Test(DoCommandFlag::Execute)) {
542 MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == Track::X ? Axis::Y : Axis::X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile));
543 UpdateLevelCrossing(tile, false);
545 Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR;
547 if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) {
548 Company::Get(road_owner)->infrastructure.road[roadtype_road] += num_new_road_pieces;
550 }
551 if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) {
552 Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces;
554 }
555 }
556 break;
557 }
558 }
559
560 if (IsLevelCrossing(tile) && GetCrossingRailTrack(tile) == trackbit) {
561 return CommandCost(STR_ERROR_ALREADY_BUILT);
562 }
563 [[fallthrough]];
564 }
565
566 default: {
567 /* Will there be flat water on the lower halftile? */
568 bool water_ground = IsTileType(tile, TileType::Water) && IsSlopeWithOneCornerRaised(tileh);
569
570 CommandCost ret = CheckRailSlope(tileh, trackbit, {}, tile);
571 if (ret.Failed()) return ret;
572 cost.AddCost(ret.GetCost());
573
574 ret = Command<Commands::LandscapeClear>::Do(flags, tile);
575 if (ret.Failed()) return ret;
576 cost.AddCost(ret.GetCost());
577
578 if (water_ground) {
581 }
582
583 if (flags.Test(DoCommandFlag::Execute)) {
584 MakeRailNormal(tile, _current_company, trackbit, railtype);
585 if (water_ground) {
588 }
589 Company::Get(_current_company)->infrastructure.rail[railtype]++;
591 }
592 break;
593 }
594 }
595
596 if (flags.Test(DoCommandFlag::Execute)) {
599 YapfNotifyTrackLayoutChange(tile, track);
600 }
601
602 cost.AddCost(RailBuildCost(railtype));
603 return cost;
604}
605
614{
616 bool crossing = false;
617
618 if (!ValParamTrackOrientation(track)) return CMD_ERROR;
619
620 /* Need to read tile owner now because it may change when the rail is removed
621 * Also, in case of floods, _current_company != owner
622 * There may be invalid tiletype even in exec run (when removing long track),
623 * so do not call GetTileOwner(tile) in any case here */
624 Owner owner = INVALID_OWNER;
625
626 Train *v = nullptr;
627
628 switch (GetTileType(tile)) {
629 case TileType::Road: {
630 if (!IsLevelCrossing(tile) || GetCrossingRailTrack(tile) != track) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
631
634 if (ret.Failed()) return ret;
635 }
636
637 if (!flags.Test(DoCommandFlag::Bankrupt)) {
639 if (ret.Failed()) return ret;
640 }
641
642 cost.AddCost(RailClearCost(GetRailType(tile)));
643
644 if (flags.Test(DoCommandFlag::Execute)) {
646
647 if (HasReservedTracks(tile, track)) {
648 v = GetTrainForReservation(tile, track);
649 if (v != nullptr) FreeTrainTrackReservation(v);
650 }
651
652 owner = GetTileOwner(tile);
653 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR;
657 }
658 break;
659 }
660
661 case TileType::Railway: {
662 TrackBits present;
663 /* There are no rails present at depots. */
664 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
665
668 if (ret.Failed()) return ret;
669 }
670
671 CommandCost ret = EnsureNoTrainOnTrack(tile, track);
672 if (ret.Failed()) return ret;
673
674 present = GetTrackBits(tile);
675 if (!present.Test(track)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
676 if (present == TRACK_BIT_CROSS) crossing = true;
677
678 cost.AddCost(RailClearCost(GetRailType(tile)));
679
680 /* Charge extra to remove signals on the track, if they are there */
681 if (HasSignalOnTrack(tile, track)) {
682 cost.AddCost(Command<Commands::RemoveSignal>::Do(flags, tile, track));
683 }
684
685 if (flags.Test(DoCommandFlag::Execute)) {
686 if (HasReservedTracks(tile, track)) {
687 v = GetTrainForReservation(tile, track);
688 if (v != nullptr) FreeTrainTrackReservation(v);
689 }
690
691 owner = GetTileOwner(tile);
692
693 /* Subtract old infrastructure count. */
694 uint pieces = present.Count();
695 if (TracksOverlap(present)) pieces *= pieces;
696 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= pieces;
697 /* Add new infrastructure count. */
698 present.Flip(track);
699 pieces = present.Count();
700 if (TracksOverlap(present)) pieces *= pieces;
701 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] += pieces;
703
704 if (present.None()) {
705 Slope tileh = GetTileSlope(tile);
706 /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */
708 bool docking = IsDockingTile(tile);
709 MakeShore(tile);
710 SetDockingTile(tile, docking);
711 } else {
712 DoClearSquare(tile);
713 }
715 } else {
716 SetTrackBits(tile, present);
718 }
719 }
720 break;
721 }
722
723 default: return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
724 }
725
726 if (flags.Test(DoCommandFlag::Execute)) {
727 /* if we got that far, 'owner' variable is set correctly */
728 assert(Company::IsValidID(owner));
729
731 if (crossing) {
732 /* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
733 * are removing one of these pieces, we'll need to update signals for
734 * both directions explicitly, as after the track is removed it won't
735 * 'connect' with the other piece. */
736 AddTrackToSignalBuffer(tile, Track::X, owner);
737 AddTrackToSignalBuffer(tile, Track::Y, owner);
740 } else {
741 AddTrackToSignalBuffer(tile, track, owner);
742 YapfNotifyTrackLayoutChange(tile, track);
743 }
744
745 if (v != nullptr) TryPathReserve(v, true);
746 }
747
748 return cost;
749}
750
751
760{
761 assert(IsPlainRailTile(t));
762
763 bool flooded = false;
764 if (GetRailGroundType(t) == RailGroundType::HalfTileWater) return flooded;
765
766 Slope tileh = GetTileSlope(t);
767 TrackBits rail_bits = GetTrackBits(t);
768
769 if (IsSlopeWithOneCornerRaised(tileh)) {
771
772 TrackBits to_remove = lower_track & rail_bits;
773 if (to_remove.Any()) {
775 flooded = Command<Commands::RemoveRail>::Do(DoCommandFlag::Execute, t, FindFirstTrack(to_remove)).Succeeded();
776 if (!flooded) return flooded; // not yet floodable
777
778 rail_bits.Reset(to_remove);
779 if (rail_bits.None()) {
780 MakeShore(t);
782 return flooded;
783 }
784 }
785
786 if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
787 flooded = true;
790 }
791 } else {
792 /* Make shore on steep slopes and 'three-corners-raised'-slopes. */
793 if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), tileh) == 0) {
794 if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) {
795 flooded = true;
798 }
799 }
800 }
801 return flooded;
802}
803
806 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
807 { 0, 0 },
808 { 0, 0 },
809 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
810 { 0, 0 },
811 { 0, 0 }
812}}};
813
821{
822 return static_cast<Trackdir>(to_underlying(trackdir) ^ 1);
823}
824
825static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
826{
827 int x = TileX(start);
828 int y = TileY(start);
829 int ex = TileX(end);
830 int ey = TileY(end);
831
832 if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR;
833
834 /* calculate delta x,y from start to end tile */
835 int dx = ex - x;
836 int dy = ey - y;
837
838 /* calculate delta x,y for the first direction */
839 int trdx = _trackdelta[*trackdir].x;
840 int trdy = _trackdelta[*trackdir].y;
841
842 if (!IsDiagonalTrackdir(*trackdir)) {
843 trdx += _trackdelta[GetOtherTrackdir(*trackdir)].x;
844 trdy += _trackdelta[GetOtherTrackdir(*trackdir)].y;
845 }
846
847 /* validate the direction */
848 while ((trdx <= 0 && dx > 0) ||
849 (trdx >= 0 && dx < 0) ||
850 (trdy <= 0 && dy > 0) ||
851 (trdy >= 0 && dy < 0)) {
852 if (!HasBit(to_underlying(*trackdir), 3)) { // first direction is invalid, try the other
853 *trackdir = ReverseTrackdir(*trackdir); // reverse the direction
854 trdx = -trdx;
855 trdy = -trdy;
856 } else { // other direction is invalid too, invalid drag
857 return CMD_ERROR;
858 }
859 }
860
861 /* (for diagonal tracks, this is already made sure of by above test), but:
862 * for non-diagonal tracks, check if the start and end tile are on 1 line */
863 if (!IsDiagonalTrackdir(*trackdir)) {
864 trdx = _trackdelta[*trackdir].x;
865 trdy = _trackdelta[*trackdir].y;
866 if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) return CMD_ERROR;
867 }
868
869 return CommandCost();
870}
871
884static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
885{
887
888 if ((!remove && !ValParamRailType(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
889 if (end_tile >= Map::Size() || tile >= Map::Size()) return CMD_ERROR;
890
891 Trackdir trackdir = TrackToTrackdir(track);
892
893 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
894 if (ret.Failed()) return ret;
895
896 bool had_success = false;
897 CommandCost last_error = CMD_ERROR;
898 for (;;) {
899 ret = remove ? Command<Commands::RemoveRail>::Do(flags, tile, TrackdirToTrack(trackdir)) : Command<Commands::BuildRail>::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals);
900 if (!remove && !fail_on_obstacle && last_error.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) had_success = true;
901
902 if (ret.Failed()) {
903 last_error = std::move(ret);
904 if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
905 if (fail_on_obstacle) return last_error;
906 if (had_success) break; // Keep going if we haven't constructed any rail yet, skipping the start of the drag
907 }
908
909 /* Ownership errors are more important. */
910 if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
911 } else {
912 had_success = true;
913 total_cost.AddCost(ret.GetCost());
914 }
915
916 if (tile == end_tile) break;
917
918 tile += ToTileIndexDiff(_trackdelta[trackdir]);
919
920 /* toggle railbit for the non-diagonal tracks */
921 if (!IsDiagonalTrackdir(trackdir)) trackdir = GetOtherTrackdir(trackdir);
922 }
923
924 if (had_success) return total_cost;
925 return last_error;
926}
927
941CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
942{
943 return CmdRailTrackHelper(flags, start_tile, end_tile, railtype, track, false, auto_remove_signals, fail_on_obstacle);
944}
945
957{
958 return CmdRailTrackHelper(flags, start_tile, end_tile, INVALID_RAILTYPE, track, true, false, false);
959}
960
970{
971 /* check railtype and valid direction for depot (0 through 3), 4 in total */
972 if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
973
974 Slope tileh = GetTileSlope(tile);
975
977
978 /* Prohibit construction if
979 * The tile is non-flat AND
980 * 1) build-on-slopes is disabled
981 * 2) the tile is steep i.e. spans two height levels
982 * 3) the exit points in the wrong direction
983 */
984
985 if (tileh != SLOPE_FLAT) {
986 if (!_settings_game.construction.build_on_slopes) return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
987 if (!CanBuildDepotByTileh(dir, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
989 }
990
991 /* Allow the user to rotate the depot instead of having to destroy it and build it again */
992 bool rotate_existing_depot = false;
993 if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
995 if (ret.Failed()) return ret;
996
997 if (dir == GetRailDepotDirection(tile)) return CommandCost();
998
999 ret = EnsureNoVehicleOnGround(tile);
1000 if (ret.Failed()) return ret;
1001
1002 rotate_existing_depot = true;
1003 }
1004
1005 if (!rotate_existing_depot) {
1006 cost.AddCost(Command<Commands::LandscapeClear>::Do(flags, tile));
1007 if (cost.Failed()) return cost;
1008
1009 if (IsBridgeAbove(tile)) {
1011 if (height_diff > 0) return CommandCostWithParam(STR_ERROR_BRIDGE_TOO_LOW_FOR_TRAIN_DEPOT, height_diff * TILE_HEIGHT_STEP);
1012 }
1013
1014 if (!Depot::CanAllocateItem()) return CMD_ERROR;
1015 }
1016
1017 if (flags.Test(DoCommandFlag::Execute)) {
1018 if (rotate_existing_depot) {
1019 SetRailDepotExitDirection(tile, dir);
1020 } else {
1021 Depot *d = Depot::Create(tile);
1022
1023 MakeRailDepot(tile, _current_company, d->index, dir, railtype);
1024 MakeDefaultName(d);
1025
1026 Company::Get(_current_company)->infrastructure.rail[railtype]++;
1028 }
1029
1030 MarkTileDirtyByTile(tile);
1033 }
1034
1036 cost.AddCost(RailBuildCost(railtype));
1037 return cost;
1038}
1039
1058CommandCost 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)
1059{
1060 if (sigtype >= SignalType::End || sigvar >= SignalVariant::End) return CMD_ERROR;
1061 if (cycle_start > cycle_stop || cycle_stop >= SignalType::End) return CMD_ERROR;
1062
1063 if (ctrl_pressed) sigvar = (sigvar == SignalVariant::Electric ? SignalVariant::Semaphore : SignalVariant::Electric);
1064
1065 /* You can only build signals on plain rail tiles, and the selected track must exist */
1066 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
1067 !HasTrack(tile, track)) {
1068 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1069 }
1070 /* Protect against invalid signal copying */
1071 if (signals_copy != 0 && (signals_copy & SignalOnTrack(track)) == 0) return CMD_ERROR;
1072
1073 CommandCost ret = CheckTileOwnership(tile);
1074 if (ret.Failed()) return ret;
1075
1076 /* See if this is a valid track combination for signals (no overlap) */
1077 if (TracksOverlap(GetTrackBits(tile))) return CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
1078
1079 /* In case we don't want to change an existing signal, return without error. */
1080 if (skip_existing_signals && HasSignalOnTrack(tile, track)) return CommandCost();
1081
1082 /* you can not convert a signal if no signal is on track */
1083 if (convert_signal && !HasSignalOnTrack(tile, track)) return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1084
1085 CommandCost cost;
1086 if (!HasSignalOnTrack(tile, track)) {
1087 /* build new signals */
1089 } else {
1090 if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
1091 /* convert signals <-> semaphores */
1093
1094 } else if (convert_signal) {
1095 /* convert button pressed */
1096 if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
1097 /* it costs money to change signal variant (light or semaphore) */
1099 } else {
1100 /* it is free to change signal type (block, exit, entry, combo, path, etc) */
1101 cost = CommandCost();
1102 }
1103
1104 } else {
1105 /* it is free to change orientation or number of signals on the tile (for block/presignals which allow signals in both directions) */
1106 cost = CommandCost();
1107 }
1108 }
1109
1110 if (flags.Test(DoCommandFlag::Execute)) {
1111 Train *v = nullptr;
1112 /* The new/changed signal could block our path. As this can lead to
1113 * stale reservations, we clear the path reservation here and try
1114 * to redo it later on. */
1115 if (HasReservedTracks(tile, track)) {
1116 v = GetTrainForReservation(tile, track);
1117 if (v != nullptr) FreeTrainTrackReservation(v);
1118 }
1119
1120 if (!HasSignals(tile)) {
1121 /* there are no signals at all on this tile yet */
1122 SetHasSignals(tile, true);
1123 SetSignalStates(tile, 0xF); // all signals are on
1124 SetPresentSignals(tile, 0); // no signals built by default
1125 SetSignalType(tile, track, sigtype);
1126 SetSignalVariant(tile, track, sigvar);
1127 }
1128
1129 /* Subtract old signal infrastructure count. */
1130 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1131
1132 if (signals_copy == 0) {
1133 if (!HasSignalOnTrack(tile, track)) {
1134 /* build new signals */
1135 SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
1136 SetSignalType(tile, track, sigtype);
1137 SetSignalVariant(tile, track, sigvar);
1138 while (num_dir_cycle-- > 0) CycleSignalSide(tile, track);
1139 } else {
1140 if (convert_signal) {
1141 /* convert signal button pressed */
1142 if (ctrl_pressed) {
1143 /* toggle the present signal variant: SignalVariant::Electric <-> SignalVariant::Semaphore */
1145 /* Query current signal type so the check for PBS signals below works. */
1146 sigtype = GetSignalType(tile, track);
1147 } else {
1148 /* convert the present signal to the chosen type and variant */
1149 SetSignalType(tile, track, sigtype);
1150 SetSignalVariant(tile, track, sigvar);
1151 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1153 }
1154 }
1155
1156 } else if (ctrl_pressed) {
1157 /* cycle between cycle_start and cycle_end */
1158 sigtype = static_cast<SignalType>(to_underlying(GetSignalType(tile, track)) + 1);
1159
1160 if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
1161
1162 SetSignalType(tile, track, sigtype);
1163 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1165 }
1166 } else {
1167 /* cycle the signal side: both -> left -> right -> both -> ... */
1168 CycleSignalSide(tile, track);
1169 /* Query current signal type so the check for PBS signals below works. */
1170 sigtype = GetSignalType(tile, track);
1171 }
1172 }
1173 } else {
1174 /* If CmdBuildManySignals is called with copying signals, just copy the
1175 * direction of the first signal given as parameter by CmdBuildManySignals */
1176 SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (signals_copy & SignalOnTrack(track)));
1177 SetSignalVariant(tile, track, sigvar);
1178 SetSignalType(tile, track, sigtype);
1179 }
1180
1181 /* Add new signal infrastructure count. */
1182 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1184
1185 if (IsPbsSignal(sigtype)) {
1186 /* PBS signals should show red unless they are on reserved tiles without a train. */
1187 uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
1188 SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((GetRailReservationTrackBits(tile).Test(track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask));
1189 }
1190 MarkTileDirtyByTile(tile);
1192 YapfNotifyTrackLayoutChange(tile, track);
1193 if (v != nullptr && v->track != Track::Depot) {
1194 Train *moving_front = v->GetMovingFront();
1195 /* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1196 if (!((v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) ||
1197 !IsSafeWaitingPosition(v, moving_front->tile, moving_front->GetVehicleTrackdir(), true, _settings_game.pf.forbid_90_deg)) {
1198 TryPathReserve(v, true);
1199 }
1200 }
1201 }
1202
1203 return cost;
1204}
1205
1206static bool AdvanceSignalAutoFill(TileIndex &tile, Trackdir &trackdir, bool remove)
1207{
1208 /* We only process starting tiles of tunnels or bridges so jump to the other end before moving further. */
1210
1211 tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
1212 if (tile == INVALID_TILE) return false;
1213
1214 /* Check for track bits on the new tile */
1216
1217 if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
1218 trackdirbits &= TrackdirReachesTrackdirs(trackdir);
1219
1220 /* No track bits, must stop */
1221 if (trackdirbits.None()) return false;
1222
1223 /* Get the first track dir */
1224 trackdir = RemoveFirstTrackdir(trackdirbits);
1225
1226 /* Any left? It's a junction so we stop */
1227 if (trackdirbits.Any()) return false;
1228
1229 switch (GetTileType(tile)) {
1230 case TileType::Railway:
1231 if (IsRailDepot(tile)) return false;
1232 if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1233 break;
1234
1235 case TileType::Road:
1236 if (!IsLevelCrossing(tile)) return false;
1237 break;
1238
1240 if (GetTunnelBridgeTransportType(tile) != TransportType::Rail) return false;
1241 if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
1242 break;
1243 }
1244
1245 default: return false;
1246 }
1247 return true;
1248}
1249
1265static 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)
1266{
1268
1269 if (end_tile >= Map::Size() || !ValParamTrackOrientation(track)) return CMD_ERROR;
1270 if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
1271 if (!remove && (sigtype >= SignalType::End || sigvar >= SignalVariant::End)) return CMD_ERROR;
1272
1273 if (!IsPlainRailTile(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1274 TileIndex start_tile = tile;
1275
1276 /* Interpret signal_density as the logical length of said amount of tiles in X/Y direction. */
1277 signal_density *= TILE_AXIAL_DISTANCE;
1278
1279 Trackdir trackdir = TrackToTrackdir(track);
1280 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
1281 if (ret.Failed()) return ret;
1282
1283 track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
1284 Trackdir start_trackdir = trackdir;
1285
1286 /* Must start on a valid track to be able to avoid loops */
1287 if (!HasTrack(tile, track)) return CMD_ERROR;
1288
1289 uint8_t signals;
1290 /* copy the signal-style of the first rail-piece if existing */
1291 if (HasSignalOnTrack(tile, track)) {
1292 signals = GetPresentSignals(tile) & SignalOnTrack(track);
1293 assert(signals != 0);
1294
1295 /* copy signal/semaphores style (independent of CTRL) */
1296 sigvar = GetSignalVariant(tile, track);
1297
1298 sigtype = GetSignalType(tile, track);
1299 /* Don't but copy entry or exit-signal type */
1300 if (sigtype == SignalType::Entry || sigtype == SignalType::Exit) sigtype = SignalType::Block;
1301 } else { // no signals exist, drag a two-way signal stretch
1302 signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
1303 }
1304
1305 uint8_t signal_dir = 0;
1306 if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0);
1307 if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
1308
1309 /* signal_ctr - amount of tiles already processed
1310 * last_used_ctr - amount of tiles before previously placed signal
1311 * signals_density - setting to put signal on every Nth tile (double space on |, -- tracks)
1312 * last_suitable_ctr - amount of tiles before last possible signal place
1313 * last_suitable_tile - last tile where it is possible to place a signal
1314 * last_suitable_trackdir - trackdir of the last tile
1315 **********
1316 * trackdir - trackdir to build with autorail
1317 * semaphores - semaphores or signals
1318 * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1319 * and convert all others to semaphore/signal
1320 * remove - 1 remove signals, 0 build signals */
1321 int signal_ctr = 0;
1322 int last_used_ctr = -signal_density; // to force signal at first tile
1323 int last_suitable_ctr = 0;
1324 TileIndex last_suitable_tile = INVALID_TILE;
1325 Trackdir last_suitable_trackdir = Trackdir::Invalid;
1326 CommandCost last_error = CMD_ERROR;
1327 bool had_success = false;
1328 auto build_signal = [&](TileIndex tile, Trackdir trackdir, bool test_only) {
1329 /* Pick the correct orientation for the track direction */
1330 uint8_t signals = 0;
1331 if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
1332 if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
1333
1334 DoCommandFlags do_flags = test_only ? DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) : flags;
1335 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);
1336
1337 if (test_only) return ret.Succeeded();
1338
1339 if (ret.Succeeded()) {
1340 had_success = true;
1341 total_cost.AddCost(ret.GetCost());
1342 } else {
1343 /* The "No railway" error is the least important one. */
1344 if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
1345 last_error.GetErrorMessage() == INVALID_STRING_ID) {
1346 last_error = ret;
1347 }
1348 }
1349 return ret.Succeeded();
1350 };
1351
1352 for (;;) {
1353 if (remove) {
1354 /* In remove mode last_* stuff doesn't matter, we simply try to clear every tile. */
1355 build_signal(tile, trackdir, false);
1356 } else if (minimise_gaps) {
1357 /* We're trying to minimize gaps wherever possible, so keep track of last suitable
1358 * position and use it if current gap exceeds required signal density. */
1359
1360 if (signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1361 /* We overshot so build a signal in last good location. */
1362 if (build_signal(last_suitable_tile, last_suitable_trackdir, false)) {
1363 last_suitable_tile = INVALID_TILE;
1364 last_used_ctr = last_suitable_ctr;
1365 }
1366 }
1367
1368 if (signal_ctr == last_used_ctr + signal_density) {
1369 /* Current gap matches the required density, build a signal. */
1370 if (build_signal(tile, trackdir, false)) {
1371 last_used_ctr = signal_ctr;
1372 last_suitable_tile = INVALID_TILE;
1373 }
1374 } else {
1375 /* Test tile for a potential signal spot. */
1376 if (build_signal(tile, trackdir, true)) {
1377 last_suitable_tile = tile;
1378 last_suitable_ctr = signal_ctr;
1379 last_suitable_trackdir = trackdir;
1380 }
1381 }
1382 } else if (signal_ctr >= last_used_ctr + signal_density) {
1383 /* We're always keeping regular interval between signals so doesn't matter whether we succeed or not. */
1384 build_signal(tile, trackdir, false);
1385 last_used_ctr = signal_ctr;
1386 }
1387
1388 if (autofill) {
1389 switch (GetTileType(tile)) {
1390 case TileType::Railway:
1391 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1392 break;
1393
1394 case TileType::Road:
1395 signal_ctr += TILE_AXIAL_DISTANCE;
1396 break;
1397
1400 if (remove || minimise_gaps) {
1401 signal_ctr += len;
1402 } else {
1403 /* To keep regular interval we need to emulate placing signals on a bridge.
1404 * We start with TILE_AXIAL_DISTANCE as one bridge tile gets processed in the main loop. */
1405 signal_ctr += TILE_AXIAL_DISTANCE;
1406 for (uint i = TILE_AXIAL_DISTANCE; i < len; i += TILE_AXIAL_DISTANCE) {
1407 if (signal_ctr >= last_used_ctr + signal_density) last_used_ctr = signal_ctr;
1408 signal_ctr += TILE_AXIAL_DISTANCE;
1409 }
1410 }
1411 break;
1412 }
1413
1414 default: break;
1415 }
1416
1417 if (!AdvanceSignalAutoFill(tile, trackdir, remove)) break;
1418
1419 /* Prevent possible loops */
1420 if (tile == start_tile && trackdir == start_trackdir) break;
1421 } else {
1422 if (tile == end_tile) break;
1423
1424 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1425 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1426
1427 tile += ToTileIndexDiff(_trackdelta[trackdir]);
1428 if (!IsDiagonalTrackdir(trackdir)) trackdir = GetOtherTrackdir(trackdir);
1429 }
1430 }
1431
1432 /* We may end up with the current gap exceeding the signal density so fix that if needed. */
1433 if (!remove && minimise_gaps && signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1434 build_signal(last_suitable_tile, last_suitable_trackdir, false);
1435 }
1436
1437 return had_success ? total_cost : last_error;
1438}
1439
1456CommandCost 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)
1457{
1458 return CmdSignalTrackHelper(flags, tile, end_tile, track, sigtype, sigvar, mode, false, autofill, minimise_gaps, signal_density);
1459}
1460
1469{
1470 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
1471 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1472 }
1473 if (!HasSignalOnTrack(tile, track)) {
1474 return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1475 }
1476
1477 /* Only water can remove signals from anyone */
1479 CommandCost ret = CheckTileOwnership(tile);
1480 if (ret.Failed()) return ret;
1481 }
1482
1483 /* Do it? */
1484 if (flags.Test(DoCommandFlag::Execute)) {
1485 Train *v = nullptr;
1486 if (HasReservedTracks(tile, track)) {
1487 v = GetTrainForReservation(tile, track);
1488 } else if (IsPbsSignal(GetSignalType(tile, track))) {
1489 /* PBS signal, might be the end of a path reservation. */
1490 Trackdir td = TrackToTrackdir(track);
1491 for (int i = 0; v == nullptr && i < 2; i++, td = ReverseTrackdir(td)) {
1492 /* Only test the active signal side. */
1493 if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
1496 if (HasReservedTracks(next, tracks)) {
1498 }
1499 }
1500 }
1501 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1502 SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
1503 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1505
1506 /* removed last signal from tile? */
1507 if (GetPresentSignals(tile) == 0) {
1508 SetSignalStates(tile, 0);
1509 SetHasSignals(tile, false);
1510 SetSignalVariant(tile, Track::Invalid, SignalVariant::Electric); // remove any possible semaphores
1511 }
1512
1513 AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
1514 YapfNotifyTrackLayoutChange(tile, track);
1515 if (v != nullptr) TryPathReserve(v, false);
1516
1517 MarkTileDirtyByTile(tile);
1518 }
1519
1521}
1522
1534CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
1535{
1536 return CmdSignalTrackHelper(flags, tile, end_tile, track, SignalType::Block, SignalVariant::Electric, false, true, autofill, false, 1); // bit 5 is remove bit
1537}
1538
1549CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
1550{
1551 TileIndex area_end = tile;
1552
1553 if (!ValParamRailType(totype)) return CMD_ERROR;
1554 if (area_start >= Map::Size()) return CMD_ERROR;
1555
1556 TrainList affected_trains;
1557
1559 CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
1560 bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
1561
1562 std::unique_ptr<TileIterator> iter = TileIterator::Create(area_start, area_end, diagonal);
1563 for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
1564 TileType tt = GetTileType(tile);
1565
1566 /* Check if there is any track on tile */
1567 switch (tt) {
1568 case TileType::Railway:
1569 break;
1570 case TileType::Station:
1571 if (!HasStationRail(tile)) continue;
1572 break;
1573 case TileType::Road:
1574 if (!IsLevelCrossing(tile)) continue;
1575 if (RailNoLevelCrossings(totype)) {
1576 error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
1577 continue;
1578 }
1579 break;
1582 break;
1583 default: continue;
1584 }
1585
1586 /* Original railtype we are converting from */
1587 RailType type = GetRailType(tile);
1588
1589 /* Converting to the same type or converting 'hidden' elrail -> rail */
1590 if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
1591
1592 /* Trying to convert other's rail */
1593 CommandCost ret = CheckTileOwnership(tile);
1594 if (ret.Failed()) {
1595 error = std::move(ret);
1596 continue;
1597 }
1598
1599 std::vector<Train *> vehicles_affected;
1600
1601 /* Vehicle on the tile when not converting Rail <-> ElRail
1602 * Tunnels and bridges have special check later */
1603 if (tt != TileType::TunnelBridge) {
1604 if (!IsCompatibleRail(type, totype)) {
1606 if (ret.Failed()) {
1607 error = std::move(ret);
1608 continue;
1609 }
1610 }
1611 if (flags.Test(DoCommandFlag::Execute)) { // we can safely convert, too
1612 for (Track track : GetReservedTrackbits(tile)) {
1613 Train *v = GetTrainForReservation(tile, track);
1614 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1615 /* No power on new rail type, reroute. */
1617 vehicles_affected.push_back(v);
1618 }
1619 }
1620
1621 /* Update the company infrastructure counters. */
1622 if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
1623 Company *c = Company::Get(GetTileOwner(tile));
1624 uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
1625 if (IsPlainRailTile(tile)) {
1626 TrackBits bits = GetTrackBits(tile);
1627 num_pieces = bits.Count();
1628 if (TracksOverlap(bits)) num_pieces *= num_pieces;
1629 }
1630 c->infrastructure.rail[type] -= num_pieces;
1631 c->infrastructure.rail[totype] += num_pieces;
1633 }
1634
1635 SetRailType(tile, totype);
1636 MarkTileDirtyByTile(tile);
1637 /* update power of train on this tile */
1638 for (Vehicle *v : VehiclesOnTile(tile)) {
1639 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1640 }
1641 }
1642 }
1643
1644 switch (tt) {
1645 default: NOT_REACHED();
1646 case TileType::Railway:
1647 switch (GetRailTileType(tile)) {
1649 if (flags.Test(DoCommandFlag::Execute)) {
1650 /* notify YAPF about the track layout change */
1652
1653 /* Update build vehicle window related to this depot */
1654 InvalidateWindowData(WindowClass::VehicleDepot, tile);
1655 InvalidateWindowData(WindowClass::BuildVehicle, tile);
1656 }
1657 found_convertible_track = true;
1658 cost.AddCost(RailConvertCost(type, totype));
1659 break;
1660
1661 default: // RailTileType::Normal, RailTileType::Signals
1662 if (flags.Test(DoCommandFlag::Execute)) {
1663 /* notify YAPF about the track layout change */
1664 for (Track track : GetTrackBits(tile)) {
1665 YapfNotifyTrackLayoutChange(tile, track);
1666 }
1667 }
1668 found_convertible_track = true;
1669 cost.AddCost(RailConvertCost(type, totype) * GetTrackBits(tile).Count());
1670 break;
1671 }
1672 break;
1673
1675 TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
1676
1677 /* If both ends of tunnel/bridge are in the range, do not try to convert twice -
1678 * it would cause assert because of different test and exec runs */
1679 if (endtile < tile) {
1680 if (diagonal) {
1681 if (DiagonalTileArea(area_start, area_end).Contains(endtile)) continue;
1682 } else {
1683 if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
1684 }
1685 }
1686
1687 /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
1688 if (!IsCompatibleRail(GetRailType(tile), totype)) {
1689 ret = TunnelBridgeIsFree(tile, endtile);
1690 if (ret.Failed()) {
1691 error = std::move(ret);
1692 continue;
1693 }
1694 }
1695
1696 if (flags.Test(DoCommandFlag::Execute)) {
1698 if (HasTunnelBridgeReservation(tile)) {
1699 Train *v = GetTrainForReservation(tile, track);
1700 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1701 /* No power on new rail type, reroute. */
1703 vehicles_affected.push_back(v);
1704 }
1705 }
1706
1707 /* Update the company infrastructure counters. */
1708 uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
1709 Company *c = Company::Get(GetTileOwner(tile));
1710 c->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1711 c->infrastructure.rail[totype] += num_pieces;
1713
1714 SetRailType(tile, totype);
1715 SetRailType(endtile, totype);
1716
1717 for (Vehicle *v : VehiclesOnTile(tile)) {
1718 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1719 }
1720 for (Vehicle *v : VehiclesOnTile(endtile)) {
1721 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1722 }
1723
1724 YapfNotifyTrackLayoutChange(tile, track);
1725 YapfNotifyTrackLayoutChange(endtile, track);
1726
1727 if (IsBridge(tile)) {
1728 MarkBridgeDirty(tile);
1729 } else {
1730 MarkTileDirtyByTile(tile);
1731 MarkTileDirtyByTile(endtile);
1732 }
1733 }
1734
1735 found_convertible_track = true;
1736 cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
1737 break;
1738 }
1739
1740 case TileType::Station:
1741 case TileType::Road:
1742 if (flags.Test(DoCommandFlag::Execute)) {
1743 Track track = ((tt == TileType::Station) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile));
1744 YapfNotifyTrackLayoutChange(tile, track);
1745 }
1746
1747 found_convertible_track = true;
1748 cost.AddCost(RailConvertCost(type, totype));
1749 break;
1750 }
1751
1752 for (uint i = 0; i < vehicles_affected.size(); ++i) {
1753 TryPathReserve(vehicles_affected[i], true);
1754 }
1755 }
1756
1757 if (flags.Test(DoCommandFlag::Execute)) {
1758 /* Railtype changed, update trains as when entering different track */
1759 for (Train *v : affected_trains) {
1760 v->ConsistChanged(CCF_TRACK);
1761 }
1762 }
1763
1764 return found_convertible_track ? cost : error;
1765}
1766
1767static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlags flags)
1768{
1770 CommandCost ret = CheckTileOwnership(tile);
1771 if (ret.Failed()) return ret;
1772 }
1773
1775 if (ret.Failed()) return ret;
1776
1777 if (flags.Test(DoCommandFlag::Execute)) {
1778 /* read variables before the depot is removed */
1780 Owner owner = GetTileOwner(tile);
1781 Train *v = nullptr;
1782
1783 if (HasDepotReservation(tile)) {
1785 if (v != nullptr) FreeTrainTrackReservation(v);
1786 }
1787
1788 Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
1790
1791 delete Depot::GetByTile(tile);
1792 DoClearSquare(tile);
1793 AddSideToSignalBuffer(tile, dir, owner);
1795 if (v != nullptr) TryPathReserve(v, true);
1796 }
1797
1799}
1800
1803{
1805
1806 if (flags.Test(DoCommandFlag::Auto)) {
1807 if (!IsTileOwner(tile, _current_company)) {
1808 return CommandCost(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
1809 }
1810
1811 if (IsPlainRail(tile)) {
1812 return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
1813 } else {
1814 return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
1815 }
1816 }
1817
1818 switch (GetRailTileType(tile)) {
1820 case RailTileType::Normal: {
1821 Slope tileh = GetTileSlope(tile);
1822 /* Is there flat water on the lower halftile that gets cleared expensively? */
1823 bool water_ground = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh));
1824
1825 for (Track track : GetTrackBits(tile)) {
1826 CommandCost ret = Command<Commands::RemoveRail>::Do(flags, tile, track);
1827 if (ret.Failed()) return ret;
1828 cost.AddCost(ret.GetCost());
1829 }
1830
1831 /* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
1832 * Same holds for non-companies clearing the tile, e.g. disasters. */
1833 if (water_ground && !flags.Test(DoCommandFlag::Bankrupt) && Company::IsValidID(_current_company)) {
1835 if (ret.Failed()) return ret;
1836
1837 /* The track was removed, and left a coast tile. Now also clear the water. */
1838 if (flags.Test(DoCommandFlag::Execute)) {
1839 DoClearSquare(tile);
1840 }
1842 }
1843
1844 return cost;
1845 }
1846
1848 return RemoveTrainDepot(tile, flags);
1849
1850 default:
1851 return CMD_ERROR;
1852 }
1853}
1854
1863static uint GetSafeSlopeZ(uint x, uint y, Track track)
1864{
1865 switch (track) {
1866 case Track::Upper: x &= ~0xF; y &= ~0xF; break;
1867 case Track::Lower: x |= 0xF; y |= 0xF; break;
1868 case Track::Left: x |= 0xF; y &= ~0xF; break;
1869 case Track::Right: x &= ~0xF; y |= 0xF; break;
1870 default: break;
1871 }
1872 return GetSlopePixelZ(x, y);
1873}
1874
1880{
1881 switch (_settings_game.construction.train_signal_side) {
1883 return false;
1885 return true;
1887 return _settings_game.vehicle.road_side == RoadVehicleDrivingSide::Right;
1888 default:
1889 NOT_REACHED();
1890 }
1891}
1892
1893static void DrawSingleSignal(TileIndex tile, const RailTypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos)
1894{
1895 static const Point SignalPositions[2][12] = {
1896 { // Signals on the left side
1897 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1898 { 8, 5}, {14, 1}, { 1, 14}, { 9, 11}, { 1, 0}, { 3, 10},
1899 /* LOWER LOWER X X Y Y */
1900 {11, 4}, {14, 14}, {11, 3}, { 4, 13}, { 3, 4}, {11, 13}
1901 }, { // Signals on the right side
1902 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1903 {14, 1}, {12, 10}, { 4, 6}, { 1, 14}, {10, 4}, { 0, 1},
1904 /* LOWER LOWER X X Y Y */
1905 {14, 14}, { 5, 12}, {11, 13}, { 4, 3}, {13, 4}, { 3, 11}
1906 }
1907 };
1908
1909 bool signal_on_right = IsTrainSignalSideRight();
1910 uint x = TileX(tile) * TILE_SIZE + SignalPositions[signal_on_right][pos].x;
1911 uint y = TileY(tile) * TILE_SIZE + SignalPositions[signal_on_right][pos].y;
1912
1913 SignalType type = GetSignalType(tile, track);
1914 SignalVariant variant = GetSignalVariant(tile, track);
1915
1916 SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition);
1917 if (sprite != 0) {
1918 sprite += image;
1919 } else {
1920 /* Normal electric signals are stored in a different sprite block than all other signals. */
1921 sprite = (type == SignalType::Block && variant == SignalVariant::Electric) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
1922 sprite += to_underlying(type) * 16 + to_underlying(variant) * 64 + image * 2 + to_underlying(condition) + (type >= SignalType::Path ? 64 : 0);
1923 }
1924
1925 AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, GetSafeSlopeZ(x, y, track), {{}, {1, 1, BB_HEIGHT_UNDER_BRIDGE}, {}});
1926}
1927
1929struct FenceOffset : SpriteBounds {
1931
1932 constexpr FenceOffset(Corner height_ref, int8_t origin_x, int8_t origin_y, uint8_t extent_x, uint8_t extent_y) :
1933 SpriteBounds({origin_x, origin_y, 0}, {extent_x, extent_y, 4}, {}), height_ref(height_ref) {}
1934};
1935
1937static const FenceOffset _fence_offsets[] = {
1938 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_FLAT_X_NW
1939 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_FLAT_Y_NE
1940 { CORNER_W, 8, 8, 1, 1 }, // RFO_FLAT_LEFT
1941 { CORNER_N, 8, 8, 1, 1 }, // RFO_FLAT_UPPER
1942 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_SW_NW
1943 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_SE_NE
1944 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_NE_NW
1945 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_NW_NE
1946 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_FLAT_X_SE
1947 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_FLAT_Y_SW
1948 { CORNER_E, 8, 8, 1, 1 }, // RFO_FLAT_RIGHT
1949 { CORNER_S, 8, 8, 1, 1 }, // RFO_FLAT_LOWER
1950 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_SW_SE
1951 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_SE_SW
1952 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_NE_SE
1953 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_NW_SW
1954};
1955
1963static void DrawTrackFence(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites, RailFenceOffset rfo)
1964{
1965 int z = ti->z;
1966 if (IsValidCorner(_fence_offsets[rfo].height_ref)) {
1968 }
1969 AddSortableSpriteToDraw(psid.sprite + (rfo % num_sprites), psid.pal, ti->x, ti->y, z, _fence_offsets[rfo]);
1970}
1971
1978static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1979{
1981 if (ti->tileh & SLOPE_NW) rfo = (ti->tileh & SLOPE_W) ? RFO_SLOPE_SW_NW : RFO_SLOPE_NE_NW;
1982 DrawTrackFence(ti, psid, num_sprites, rfo);
1983}
1984
1991static void DrawTrackFence_SE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1992{
1994 if (ti->tileh & SLOPE_SE) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SW_SE : RFO_SLOPE_NE_SE;
1995 DrawTrackFence(ti, psid, num_sprites, rfo);
1996}
1997
2004static void DrawTrackFence_NE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
2005{
2007 if (ti->tileh & SLOPE_NE) rfo = (ti->tileh & SLOPE_E) ? RFO_SLOPE_SE_NE : RFO_SLOPE_NW_NE;
2008 DrawTrackFence(ti, psid, num_sprites, rfo);
2009}
2010
2017static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
2018{
2020 if (ti->tileh & SLOPE_SW) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SE_SW : RFO_SLOPE_NW_SW;
2021 DrawTrackFence(ti, psid, num_sprites, rfo);
2022}
2023
2030static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
2031{
2032 /* Base sprite for track fences.
2033 * Note: Halftile slopes only have fences on the upper part. */
2034 uint num_sprites = 0;
2035 PalSpriteID psid{
2037 .pal = pal,
2038 };
2039 if (psid.sprite == 0) {
2041 num_sprites = 8;
2042 }
2043
2044 assert(num_sprites > 0);
2045
2046 switch (GetRailGroundType(ti->tile)) {
2047 case RailGroundType::FenceNW: DrawTrackFence_NW(ti, psid, num_sprites); break;
2048 case RailGroundType::FenceSE: DrawTrackFence_SE(ti, psid, num_sprites); break;
2049 case RailGroundType::FenceSENW: DrawTrackFence_NW(ti, psid, num_sprites);
2050 DrawTrackFence_SE(ti, psid, num_sprites); break;
2051 case RailGroundType::FenceNE: DrawTrackFence_NE(ti, psid, num_sprites); break;
2052 case RailGroundType::FenceSW: DrawTrackFence_SW(ti, psid, num_sprites); break;
2053 case RailGroundType::FenceNESW: DrawTrackFence_NE(ti, psid, num_sprites);
2054 DrawTrackFence_SW(ti, psid, num_sprites); break;
2055 case RailGroundType::FenceVert1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2056 case RailGroundType::FenceVert2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2057 case RailGroundType::FenceHoriz1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2058 case RailGroundType::FenceHoriz2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2060 Corner track_corner;
2061 if (IsHalftileSlope(ti->tileh)) {
2062 /* Steep slope or one-corner-raised slope with halftile foundation */
2063 track_corner = GetHalftileSlopeCorner(ti->tileh);
2064 } else {
2065 /* Three-corner-raised slope */
2067 }
2068 switch (track_corner) {
2069 case CORNER_W: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2070 case CORNER_S: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2071 case CORNER_E: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2072 case CORNER_N: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2073 default: NOT_REACHED();
2074 }
2075 break;
2076 }
2077 default: break;
2078 }
2079}
2080
2081static const int INF = 1000;
2084 { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right
2085 { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top
2086 { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left
2087 { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2088}}};
2089
2090static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
2091{
2092 DrawGroundSprite(sprite, pal, nullptr, 0, (ti->tileh & s) ? -8 : 0);
2093}
2094
2095static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti)
2096{
2098 Foundation f = GetRailFoundation(ti->tileh, track);
2099 Corner halftile_corner = CORNER_INVALID;
2100
2102 /* Save halftile corner */
2104 /* Draw lower part first */
2105 track.Reset(CornerToTrackBits(halftile_corner));
2107 }
2108
2109 DrawFoundation(ti, f);
2110 /* DrawFoundation modifies ti */
2111
2112 /* Draw ground */
2113 if (rgt == RailGroundType::HalfTileWater) {
2114 if (track.Any() || IsSteepSlope(ti->tileh)) {
2115 /* three-corner-raised slope or steep slope with track on upper part */
2116 DrawShoreTile(ti->tileh);
2117 } else {
2118 /* single-corner-raised slope with track on upper part */
2120 }
2121 } else {
2122 SpriteID image;
2123
2124 switch (rgt) {
2125 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2127 default: image = SPR_FLAT_GRASS_TILE; break;
2128 }
2129
2130 image += SlopeToSpriteOffset(ti->tileh);
2131
2132 DrawGroundSprite(image, PAL_NONE);
2133 }
2134
2135 bool no_combine = ti->tileh == SLOPE_FLAT && rti->flags.Test(RailTypeFlag::NoSpriteCombine);
2138 TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TrackBits{};
2139
2140 if (track.None()) {
2141 /* Half-tile foundation, no track here? */
2142 } else if (no_combine) {
2143 /* Use trackbits as direct index from ground sprite, subtract 1
2144 * because there is no sprite for no bits. */
2145 DrawGroundSprite(ground + track.base() - 1, PAL_NONE);
2146
2147 /* Draw reserved track bits */
2148 if (pbs.Test(Track::X)) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2149 if (pbs.Test(Track::Y)) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2150 if (pbs.Test(Track::Upper)) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2151 if (pbs.Test(Track::Lower)) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2152 if (pbs.Test(Track::Right)) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2153 if (pbs.Test(Track::Left)) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2154 } else if (ti->tileh == SLOPE_NW && track == Track::Y) {
2155 DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
2156 if (pbs.Any()) DrawGroundSprite(overlay + RTO_SLOPE_NW, PALETTE_CRASH);
2157 } else if (ti->tileh == SLOPE_NE && track == Track::X) {
2158 DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
2159 if (pbs.Any()) DrawGroundSprite(overlay + RTO_SLOPE_NE, PALETTE_CRASH);
2160 } else if (ti->tileh == SLOPE_SE && track == Track::Y) {
2161 DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
2162 if (pbs.Any()) DrawGroundSprite(overlay + RTO_SLOPE_SE, PALETTE_CRASH);
2163 } else if (ti->tileh == SLOPE_SW && track == Track::X) {
2164 DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
2165 if (pbs.Any()) DrawGroundSprite(overlay + RTO_SLOPE_SW, PALETTE_CRASH);
2166 } else {
2167 switch (track.base()) {
2168 /* Draw single ground sprite when not overlapping. No track overlay
2169 * is necessary for these sprites. */
2170 case TrackBits{Track::X}.base(): DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
2171 case TrackBits{Track::Y}.base(): DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
2172 case TrackBits{Track::Upper}.base(): DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
2173 case TrackBits{Track::Lower}.base(): DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2174 case TrackBits{Track::Right}.base(): DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
2175 case TrackBits{Track::Left}.base(): DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2176 case TRACK_BIT_CROSS.base(): DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
2177 case TRACK_BIT_HORZ.base():
2178 DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
2179 DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S);
2180 break;
2181 case TRACK_BIT_VERT.base():
2182 DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
2183 DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W);
2184 break;
2185
2186 default:
2187 /* We're drawing a junction tile */
2188 if (!track.Any(TRACK_BIT_3WAY_NE)) {
2189 DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
2190 } else if (!track.Any(TRACK_BIT_3WAY_SW)) {
2191 DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
2192 } else if (!track.Any(TRACK_BIT_3WAY_NW)) {
2193 DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
2194 } else if (!track.Any(TRACK_BIT_3WAY_SE)) {
2195 DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
2196 } else {
2197 DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2198 }
2199
2200 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2201 track.Reset(pbs);
2202
2203 /* Draw regular track bits */
2204 if (track.Test(Track::X)) DrawGroundSprite(overlay + RTO_X, PAL_NONE);
2205 if (track.Test(Track::Y)) DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
2206 if (track.Test(Track::Upper)) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
2207 if (track.Test(Track::Lower)) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
2208 if (track.Test(Track::Right)) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
2209 if (track.Test(Track::Left)) DrawGroundSprite(overlay + RTO_W, PAL_NONE);
2210 }
2211
2212 /* Draw reserved track bits */
2213 if (pbs.Test(Track::X)) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2214 if (pbs.Test(Track::Y)) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2215 if (pbs.Test(Track::Upper)) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2216 if (pbs.Test(Track::Lower)) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2217 if (pbs.Test(Track::Right)) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2218 if (pbs.Test(Track::Left)) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2219 }
2220
2221 if (IsValidCorner(halftile_corner)) {
2222 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2225
2226 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2227 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2228
2229 SpriteID image;
2230 switch (rgt) {
2231 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2234 default: image = SPR_FLAT_GRASS_TILE; break;
2235 }
2236
2237 image += SlopeToSpriteOffset(fake_slope);
2238
2239 DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
2240
2241 track = CornerToTrackBits(halftile_corner);
2242
2243 int offset;
2244 switch (FindFirstTrack(track)) {
2245 default: NOT_REACHED();
2246 case Track::Upper: offset = RTO_N; break;
2247 case Track::Lower: offset = RTO_S; break;
2248 case Track::Right: offset = RTO_E; break;
2249 case Track::Left: offset = RTO_W; break;
2250 }
2251
2252 DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
2253 if (_settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, track)) {
2254 DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
2255 }
2256 }
2257}
2258
2267{
2268 /* If none of the tracks end up in the NE corner, return the ground sprite
2269 * where the NE of the tile is not covered. Repeat for the other directions.
2270 * What remains are junctions where all directions are covered. */
2271 if (!track.Any(TRACK_BIT_3WAY_NE)) return 0;
2272 if (!track.Any(TRACK_BIT_3WAY_SW)) return 1;
2273 if (!track.Any(TRACK_BIT_3WAY_NW)) return 2;
2274 if (!track.Any(TRACK_BIT_3WAY_SE)) return 3;
2275 return 4;
2276}
2277
2283static void DrawTrackBits(TileInfo *ti, TrackBits track)
2284{
2285 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2286
2287 if (rti->UsesOverlay()) {
2288 DrawTrackBitsOverlay(ti, track, rti);
2289 return;
2290 }
2291
2293 Foundation f = GetRailFoundation(ti->tileh, track);
2294 Corner halftile_corner = CORNER_INVALID;
2295
2297 /* Save halftile corner */
2299 /* Draw lower part first */
2300 track.Reset(CornerToTrackBits(halftile_corner));
2302 }
2303
2304 DrawFoundation(ti, f);
2305 /* DrawFoundation modifies ti */
2306
2307 SpriteID image;
2308 PaletteID pal = PAL_NONE;
2309 const SubSprite *sub = nullptr;
2310 bool junction = false;
2311
2312 /* Select the sprite to use. */
2313 if (track.None()) {
2314 /* Clear ground (only track on halftile foundation) */
2315 if (rgt == RailGroundType::HalfTileWater) {
2316 if (IsSteepSlope(ti->tileh)) {
2317 DrawShoreTile(ti->tileh);
2318 image = 0;
2319 } else {
2320 image = SPR_FLAT_WATER_TILE;
2321 }
2322 } else {
2323 switch (rgt) {
2324 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2326 default: image = SPR_FLAT_GRASS_TILE; break;
2327 }
2328 image += SlopeToSpriteOffset(ti->tileh);
2329 }
2330 } else {
2331 if (ti->tileh != SLOPE_FLAT) {
2332 /* track on non-flat ground */
2333 image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
2334 } else {
2335 /* track on flat ground */
2336 switch (track.base()) {
2337 /* single track, select combined track + ground sprite*/
2338 case TrackBits{Track::Y}.base(): image = rti->base_sprites.track_y; break;
2339 case TrackBits{Track::X}.base(): image = rti->base_sprites.track_y + 1; break;
2340 case TrackBits{Track::Upper}.base(): image = rti->base_sprites.track_y + 2; break;
2341 case TrackBits{Track::Lower}.base(): image = rti->base_sprites.track_y + 3; break;
2342 case TrackBits{Track::Right}.base(): image = rti->base_sprites.track_y + 4; break;
2343 case TrackBits{Track::Left}.base(): image = rti->base_sprites.track_y + 5; break;
2344 case TRACK_BIT_CROSS.base(): image = rti->base_sprites.track_y + 6; break;
2345
2346 /* double diagonal track, select combined track + ground sprite*/
2347 case TRACK_BIT_HORZ.base(): image = rti->base_sprites.track_ns; break;
2348 case TRACK_BIT_VERT.base(): image = rti->base_sprites.track_ns + 1; break;
2349
2350 /* junction, select only ground sprite, handle track sprite later */
2351 default:
2352 junction = true;
2354 break;
2355 }
2356 }
2357
2358 switch (rgt) {
2360 case RailGroundType::SnowOrDesert: image += rti->snow_offset; break;
2362 /* three-corner-raised slope */
2363 DrawShoreTile(ti->tileh);
2365 sub = &(_halftile_sub_sprite[track_corner]);
2366 break;
2367 }
2368 default: break;
2369 }
2370 }
2371
2372 if (image != 0) DrawGroundSprite(image, pal, sub);
2373
2374 /* Draw track pieces individually for junction tiles */
2375 if (junction) {
2376 if (track.Test(Track::X)) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE);
2377 if (track.Test(Track::Y)) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE);
2378 if (track.Test(Track::Upper)) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE);
2379 if (track.Test(Track::Lower)) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE);
2380 if (track.Test(Track::Left)) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
2381 if (track.Test(Track::Right)) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
2382 }
2383
2384 /* PBS debugging, draw reserved tracks darker */
2385 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation) {
2386 /* Get reservation, but mask track on halftile slope */
2387 TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
2388 if (pbs.Test(Track::X)) {
2389 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2391 } else {
2392 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2393 }
2394 }
2395 if (pbs.Test(Track::Y)) {
2396 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2398 } else {
2399 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2400 }
2401 }
2402 if (pbs.Test(Track::Upper)) DrawGroundSprite(rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_N ? -(int)TILE_HEIGHT : 0);
2403 if (pbs.Test(Track::Lower)) DrawGroundSprite(rti->base_sprites.single_s, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_S ? -(int)TILE_HEIGHT : 0);
2404 if (pbs.Test(Track::Left)) DrawGroundSprite(rti->base_sprites.single_w, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_W ? -(int)TILE_HEIGHT : 0);
2405 if (pbs.Test(Track::Right)) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
2406 }
2407
2408 if (IsValidCorner(halftile_corner)) {
2409 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2410
2411 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2412 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2413 image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y;
2414 pal = PAL_NONE;
2415 switch (rgt) {
2418 case RailGroundType::HalfTileSnow: image += rti->snow_offset; break; // higher part has snow in this case too
2419 default: break;
2420 }
2421 DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
2422
2423 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) {
2424 static constexpr CornerIndexArray<uint8_t> corner_to_track_sprite = {3, 1, 2, 0};
2425 DrawGroundSprite(corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, -static_cast<int>(TILE_HEIGHT));
2426 }
2427 }
2428}
2429
2430static void DrawSignals(TileIndex tile, TrackBits rails, const RailTypeInfo *rti)
2431{
2432 auto MAYBE_DRAW_SIGNAL = [&](uint8_t signalbit, SignalOffsets image, uint pos, Track track) {
2433 if (IsSignalPresent(tile, signalbit)) DrawSingleSignal(tile, rti, track, GetSingleSignalState(tile, signalbit), image, pos);
2434 };
2435
2436 if (!rails.Test(Track::Y)) {
2437 if (!rails.Test(Track::X)) {
2438 if (rails.Test(Track::Left)) {
2439 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, Track::Left);
2440 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, Track::Left);
2441 }
2442 if (rails.Test(Track::Right)) {
2443 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, Track::Right);
2444 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, Track::Right);
2445 }
2446 if (rails.Test(Track::Upper)) {
2447 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, Track::Upper);
2448 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, Track::Upper);
2449 }
2450 if (rails.Test(Track::Lower)) {
2451 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, Track::Lower);
2452 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, Track::Lower);
2453 }
2454 } else {
2455 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, Track::X);
2456 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, Track::X);
2457 }
2458 } else {
2459 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, Track::Y);
2460 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 11, Track::Y);
2461 }
2462}
2463
2465static void DrawTile_Rail(TileInfo *ti)
2466{
2467 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2468 BridgePillarFlags blocked_pillars{};
2470
2471 if (IsPlainRail(ti->tile)) {
2472 TrackBits rails = GetTrackBits(ti->tile);
2473
2474 DrawTrackBits(ti, rails);
2475
2477
2479
2480 if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
2481
2482 if (IsBridgeAbove(ti->tile)) {
2483 if (rails.Any(TRACK_BIT_3WAY_NE)) blocked_pillars.Set(BridgePillarFlag::EdgeNE);
2484 if (rails.Any(TRACK_BIT_3WAY_SE)) blocked_pillars.Set(BridgePillarFlag::EdgeSE);
2485 if (rails.Any(TRACK_BIT_3WAY_SW)) blocked_pillars.Set(BridgePillarFlag::EdgeSW);
2486 if (rails.Any(TRACK_BIT_3WAY_NW)) blocked_pillars.Set(BridgePillarFlag::EdgeNW);
2487 }
2488 } else {
2489 /* draw depot */
2490 const DrawTileSprites *dts;
2492
2494
2496 /* Draw rail instead of depot */
2497 dts = &_depot_invisible_gfx_table[dir];
2498 } else {
2499 dts = &_depot_gfx_table[dir];
2500 }
2501
2502 SpriteID image;
2503 if (rti->UsesOverlay()) {
2504 image = SPR_FLAT_GRASS_TILE;
2505 } else {
2506 image = dts->ground.sprite;
2507 if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
2508 }
2509
2510 /* Adjust ground tile for desert and snow. */
2511 if (IsSnowOrDesertRailGround(ti->tile)) {
2512 if (image != SPR_FLAT_GRASS_TILE) {
2513 image += rti->snow_offset; // tile with tracks
2514 } else {
2515 image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
2516 }
2517 }
2518
2519 DrawGroundSprite(image, GroundSpritePaletteTransform(image, PAL_NONE, pal));
2520
2521 if (rti->UsesOverlay()) {
2523
2524 switch (GetRailDepotDirection(ti->tile)) {
2525 case DiagDirection::NE:
2527 [[fallthrough]];
2528 case DiagDirection::SW:
2529 DrawGroundSprite(ground + RTO_X, PAL_NONE);
2530 break;
2531 case DiagDirection::NW:
2533 [[fallthrough]];
2534 case DiagDirection::SE:
2535 DrawGroundSprite(ground + RTO_Y, PAL_NONE);
2536 break;
2537 default:
2538 break;
2539 }
2540
2541 if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2543
2544 switch (GetRailDepotDirection(ti->tile)) {
2545 case DiagDirection::NE:
2547 [[fallthrough]];
2548 case DiagDirection::SW:
2550 break;
2551 case DiagDirection::NW:
2553 [[fallthrough]];
2554 case DiagDirection::SE:
2556 break;
2557 default:
2558 break;
2559 }
2560 }
2561 } else {
2562 /* PBS debugging, draw reserved tracks darker */
2563 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2564 switch (GetRailDepotDirection(ti->tile)) {
2565 case DiagDirection::NE:
2567 [[fallthrough]];
2568 case DiagDirection::SW:
2570 break;
2571 case DiagDirection::NW:
2573 [[fallthrough]];
2574 case DiagDirection::SE:
2576 break;
2577 default:
2578 break;
2579 }
2580 }
2581 }
2582 int depot_sprite = GetCustomRailSprite(rti, ti->tile, RailSpriteType::Depot);
2583 int relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
2584
2586
2587 DrawRailTileSeq(ti, dts, TransparencyOption::Buildings, relocation, 0, pal);
2588 blocked_pillars = BRIDGEPILLARFLAGS_ALL;
2589 }
2590 DrawBridgeMiddle(ti, blocked_pillars);
2591}
2592
2600void DrawTrainDepotSprite(int x, int y, DiagDirection dir, RailType railtype)
2601{
2602 const DrawTileSprites *dts = &_depot_gfx_table[dir];
2603 const RailTypeInfo *rti = GetRailTypeInfo(railtype);
2604 SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
2605 uint32_t offset = rti->GetRailtypeSpriteOffset();
2606
2607 if (image != SPR_FLAT_GRASS_TILE) image += offset;
2609
2610 DrawSprite(image, PAL_NONE, x, y);
2611
2612 if (rti->UsesOverlay()) {
2614
2615 switch (dir) {
2616 case DiagDirection::SW: DrawSprite(ground + RTO_X, PAL_NONE, x, y); break;
2617 case DiagDirection::SE: DrawSprite(ground + RTO_Y, PAL_NONE, x, y); break;
2618 default: break;
2619 }
2620 }
2621 int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RailSpriteType::Depot);
2622 if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
2623
2624 DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette);
2625}
2626
2628static int GetSlopePixelZ_Rail(TileIndex tile, uint x, uint y, [[maybe_unused]] bool ground_vehicle)
2629{
2630 if (IsPlainRail(tile)) {
2631 auto [tileh, z] = GetTilePixelSlope(tile);
2632 if (tileh == SLOPE_FLAT) return z;
2633
2635 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
2636 } else {
2637 return GetTileMaxPixelZ(tile);
2638 }
2639}
2640
2643{
2644 return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
2645}
2646
2648static void TileLoop_Rail(TileIndex tile)
2649{
2650 RailGroundType old_ground = GetRailGroundType(tile);
2651 RailGroundType new_ground;
2652
2653 if (old_ground == RailGroundType::HalfTileWater) {
2654 TileLoop_Water(tile);
2655 return;
2656 }
2657
2658 switch (_settings_game.game_creation.landscape) {
2659 case LandscapeType::Arctic: {
2660 auto [slope, z] = GetTileSlopeZ(tile);
2661 bool half = false;
2662
2663 /* for non-flat track, use lower part of track
2664 * in other cases, use the highest part with track */
2665 if (IsPlainRail(tile)) {
2666 TrackBits track = GetTrackBits(tile);
2667 Foundation f = GetRailFoundation(slope, track);
2668
2669 switch (f) {
2670 case Foundation::None:
2671 /* no foundation - is the track on the upper side of three corners raised tile? */
2672 if (IsSlopeWithThreeCornersRaised(slope)) z++;
2673 break;
2674
2677 /* sloped track - is it on a steep slope? */
2678 if (IsSteepSlope(slope)) z++;
2679 break;
2680
2682 /* only lower part of steep slope */
2683 z++;
2684 break;
2685
2686 default:
2687 /* if it is a steep slope, then there is a track on higher part */
2688 if (IsSteepSlope(slope)) z++;
2689 z++;
2690 break;
2691 }
2692
2693 half = IsNonContinuousFoundation(f);
2694 } else {
2695 /* is the depot on a non-flat tile? */
2696 if (slope != SLOPE_FLAT) z++;
2697 }
2698
2699 /* 'z' is now the lowest part of the highest track bit -
2700 * for sloped track, it is 'z' of lower part
2701 * for two track bits, it is 'z' of higher track bit
2702 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
2703 if (z > GetSnowLine()) {
2704 if (half && z - GetSnowLine() == 1) {
2705 /* track on non-continuous foundation, lower part is not under snow */
2706 new_ground = RailGroundType::HalfTileSnow;
2707 } else {
2708 new_ground = RailGroundType::SnowOrDesert;
2709 }
2710 goto set_ground;
2711 }
2712 break;
2713 }
2714
2716 if (GetTropicZone(tile) == TropicZone::Desert) {
2717 new_ground = RailGroundType::SnowOrDesert;
2718 goto set_ground;
2719 }
2720 break;
2721
2722 default:
2723 break;
2724 }
2725
2726 new_ground = RailGroundType::Grass;
2727
2728 if (IsPlainRail(tile) && old_ground != RailGroundType::Barren) { // wait until bottom is green
2729 /* determine direction of fence */
2730 TrackBits rail = GetTrackBits(tile);
2731
2732 Owner owner = GetTileOwner(tile);
2733 DiagDirections fences{};
2734
2737
2738 /* Track bit on this edge => no fence. */
2739 if (rail.Any(dir_to_trackbits[d])) continue;
2740
2741 TileIndex tile2 = tile + TileOffsByDiagDir(d);
2742
2743 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
2744 if (!IsValidTile(tile2) || IsTileType(tile2, TileType::House) || IsTileType(tile2, TileType::Industry) ||
2746 fences.Set(d);
2747 }
2748 }
2749
2750 switch (fences.base()) {
2751 case DiagDirections{}.base(): break;
2752 case DiagDirections{DiagDirection::NE}.base(): new_ground = RailGroundType::FenceNE; break;
2753 case DiagDirections{DiagDirection::SE}.base(): new_ground = RailGroundType::FenceSE; break;
2754 case DiagDirections{DiagDirection::SW}.base(): new_ground = RailGroundType::FenceSW; break;
2755 case DiagDirections{DiagDirection::NW}.base(): new_ground = RailGroundType::FenceNW; break;
2762 default: NOT_REACHED();
2763 }
2764 }
2765
2766set_ground:
2767 if (old_ground != new_ground) {
2768 SetRailGroundType(tile, new_ground);
2769 MarkTileDirtyByTile(tile);
2770 }
2771}
2772
2773
2776{
2777 /* Case of half tile slope with water. */
2779 TrackBits tb = GetTrackBits(tile);
2780 switch (tb.base()) {
2781 default: NOT_REACHED();
2782 case TrackBits{Track::Upper}.base(): tb = Track::Lower; break;
2783 case TrackBits{Track::Lower}.base(): tb = Track::Upper; break;
2784 case TrackBits{Track::Left}.base(): tb = Track::Right; break;
2785 case TrackBits{Track::Right}.base(): tb = Track::Left; break;
2786 }
2787 return {TrackBitsToTrackdirBits(tb), {}};
2788 }
2789
2790 if (mode != TransportType::Rail) return {};
2791
2792 TrackBits trackbits{};
2793 TrackdirBits red_signals{};
2794
2795 switch (GetRailTileType(tile)) {
2796 default: NOT_REACHED();
2798 trackbits = GetTrackBits(tile);
2799 break;
2800
2801 case RailTileType::Signals: {
2802 trackbits = GetTrackBits(tile);
2803 uint8_t a = GetPresentSignals(tile);
2804 uint b = GetSignalStates(tile);
2805
2806 b &= a;
2807
2808 /* When signals are not present (in neither direction),
2809 * we pretend them to be green. Otherwise, it depends on
2810 * the signal type. For signals that are only active from
2811 * one side, we set the missing signals explicitly to
2812 * `green'. Otherwise, they implicitly become `red'. */
2813 if (!IsOnewaySignal(tile, Track::Upper) || (a & SignalOnTrack(Track::Upper)) == 0) b |= ~a & SignalOnTrack(Track::Upper);
2814 if (!IsOnewaySignal(tile, Track::Lower) || (a & SignalOnTrack(Track::Lower)) == 0) b |= ~a & SignalOnTrack(Track::Lower);
2815
2816 if ((b & 0x8) == 0) red_signals.Set({Trackdir::Left_N, Trackdir::X_NE, Trackdir::Y_SE, Trackdir::Upper_E});
2817 if ((b & 0x4) == 0) red_signals.Set({Trackdir::Left_S, Trackdir::X_SW, Trackdir::Y_NW, Trackdir::Upper_W});
2818 if ((b & 0x2) == 0) red_signals.Set({Trackdir::Right_N, Trackdir::Lower_E});
2819 if ((b & 0x1) == 0) red_signals.Set({Trackdir::Right_S, Trackdir::Lower_W});
2820
2821 break;
2822 }
2823
2824 case RailTileType::Depot: {
2826
2827 if (side != DiagDirection::Invalid && side != dir) break;
2828
2829 trackbits = DiagDirToDiagTrack(dir);
2830 break;
2831 }
2832 }
2833
2834 return {TrackBitsToTrackdirBits(trackbits), red_signals};
2835}
2836
2838static bool ClickTile_Rail(TileIndex tile)
2839{
2840 if (!IsRailDepot(tile)) return false;
2841
2843 return true;
2844}
2845
2848{
2849 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
2850 td.rail_speed = rti->max_speed;
2851 td.railtype = rti->strings.name;
2852 td.owner[0] = GetTileOwner(tile);
2853 switch (GetRailTileType(tile)) {
2855 td.str = STR_LAI_RAIL_DESCRIPTION_TRACK;
2856 break;
2857
2858 case RailTileType::Signals: {
2860 {
2861 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
2862 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2863 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2864 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2865 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2866 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
2867 },
2868 {
2869 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2870 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS,
2871 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2872 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2873 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2874 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
2875 },
2876 {
2877 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2878 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2879 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
2880 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2881 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2882 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
2883 },
2884 {
2885 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2886 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2887 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2888 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
2889 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2890 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
2891 },
2892 {
2893 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2894 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2895 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2896 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2897 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
2898 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
2899 },
2900 {
2901 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
2902 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
2903 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
2904 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
2905 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
2906 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
2907 },
2908 }}};
2909
2910 SignalType primary_signal;
2911 SignalType secondary_signal;
2912 if (HasSignalOnTrack(tile, Track::Upper)) {
2913 primary_signal = GetSignalType(tile, Track::Upper);
2914 secondary_signal = HasSignalOnTrack(tile, Track::Lower) ? GetSignalType(tile, Track::Lower) : primary_signal;
2915 } else {
2916 secondary_signal = primary_signal = GetSignalType(tile, Track::Lower);
2917 }
2918
2919 td.str = signal_type[secondary_signal][primary_signal];
2920 break;
2921 }
2922
2924 td.str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
2925 if (_settings_game.vehicle.train_acceleration_model != AccelerationModel::Original) {
2926 if (td.rail_speed > 0) {
2927 td.rail_speed = std::min<uint16_t>(td.rail_speed, 61);
2928 } else {
2929 td.rail_speed = 61;
2930 }
2931 }
2932 td.build_date = Depot::GetByTile(tile)->build_date;
2933 break;
2934
2935 default:
2936 NOT_REACHED();
2937 }
2938}
2939
2941static void ChangeTileOwner_Rail(TileIndex tile, Owner old_owner, Owner new_owner)
2942{
2943 if (!IsTileOwner(tile, old_owner)) return;
2944
2945 if (new_owner != INVALID_OWNER) {
2946 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
2947 uint num_pieces = 1;
2948 if (IsPlainRail(tile)) {
2949 TrackBits bits = GetTrackBits(tile);
2950 num_pieces = bits.Count();
2951 if (TracksOverlap(bits)) num_pieces *= num_pieces;
2952 }
2953 RailType rt = GetRailType(tile);
2954 Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;
2955 Company::Get(new_owner)->infrastructure.rail[rt] += num_pieces;
2956
2957 if (HasSignals(tile)) {
2958 uint num_sigs = CountBits(GetPresentSignals(tile));
2959 Company::Get(old_owner)->infrastructure.signal -= num_sigs;
2960 Company::Get(new_owner)->infrastructure.signal += num_sigs;
2961 }
2962
2963 SetTileOwner(tile, new_owner);
2964 } else {
2965 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
2966 }
2967}
2968
2971 {15, 8}, // NE
2972 {8, 0}, // SE
2973 {0, 8}, // SW
2974 {8, 15}, // NW
2975}}};
2976
2979 {10, 8}, // NE
2980 {8, 4}, // SE
2981 {4, 8}, // SW
2982 {8, 10}, // NW
2983}}};
2984
2990 {-1, 0}, // NE
2991 {0, 1}, // SE
2992 {1, 0}, // SW
2993 {0, -1}, // NW
2994}}};
2995
3003{
3005 int length = v->CalcNextVehicleOffset() + 1;
3006
3007 switch (dir) {
3008 case DiagDirection::NE: return (static_cast<int>(v->x_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].x - length));
3009 case DiagDirection::SE: return -(static_cast<int>(v->y_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].y + length));
3010 case DiagDirection::SW: return -(static_cast<int>(v->x_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].x + length));
3011 case DiagDirection::NW: return (static_cast<int>(v->y_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].y - length));
3012 default: NOT_REACHED();
3013 }
3014}
3015
3018{
3019 /* This routine applies only to trains in depot tiles. */
3020 if (v->type != VehicleType::Train || !IsRailDepotTile(tile)) return {};
3021
3022 /* Depot direction. */
3024
3025 Coord2D<uint8_t> fract_coord{
3026 static_cast<uint8_t>(x & TILE_UNIT_MASK),
3027 static_cast<uint8_t>(y & TILE_UNIT_MASK)};
3028
3029 /* Make sure a train is not entering the tile from behind. */
3030 if (_fractcoords_behind[dir] == fract_coord) return VehicleEnterTileState::CannotEnter;
3031
3032 /* Leaving depot? */
3033 if (v->GetMovingDirection() == DiagDirToDir(dir)) {
3034 /* Calculate the point where the following wagon should be activated. */
3035 int length = Train::From(v)->CalcNextVehicleOffset() + 1;
3036
3037 Coord2D<uint8_t> fract_coord_leave{
3038 static_cast<uint8_t>(_fractcoords_enter[dir].x + length * _deltacoord_leaveoffset[dir].x),
3039 static_cast<uint8_t>(_fractcoords_enter[dir].y + length * _deltacoord_leaveoffset[dir].y)};
3040
3041 if (fract_coord_leave == fract_coord) {
3042 /* Leave the depot. */
3043 if ((v = v->GetMovingNext()) != nullptr) {
3046 }
3047 }
3048 } else if (_fractcoords_enter[dir] == fract_coord) {
3049 /* Entering depot. */
3050 assert(DiagDirToDir(ReverseDiagDir(dir)) == v->GetMovingDirection());
3053 if (v->GetMovingNext() == nullptr) {
3054 Train *consist = Train::From(v)->First();
3056 /* Trains always drive forwards out of a depot.
3057 * This allows a player to easily reset a confused train,
3058 * and matches the behaviour of the \c VehicleRailFlag::Reversed variable. */
3060 } else {
3061 for (Train *u = consist; u != nullptr; u = u->Next()) {
3062 u->direction = ReverseDir(u->direction);
3063 }
3064 }
3065 VehicleEnterDepot(consist);
3066 }
3067 v->tile = tile;
3068
3069 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
3071 }
3072
3073 return {};
3074}
3075
3088static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
3089{
3090 if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3091
3092 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3093 if (CheckRailSlope(tileh_new, rail_bits, {}, tile).Failed()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3094
3095 /* Get the slopes on top of the foundations */
3096 z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), tileh_old);
3097 z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), tileh_new);
3098
3099 Corner track_corner;
3100 switch (rail_bits.base()) {
3101 case TrackBits{Track::Left}.base(): track_corner = CORNER_W; break;
3102 case TrackBits{Track::Lower}.base(): track_corner = CORNER_S; break;
3103 case TrackBits{Track::Right}.base(): track_corner = CORNER_E; break;
3104 case TrackBits{Track::Upper}.base(): track_corner = CORNER_N; break;
3105
3106 /* Surface slope must not be changed */
3107 default:
3108 if (z_old != z_new || tileh_old != tileh_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3110 }
3111
3112 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3113 z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
3114 z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
3115 if (z_old != z_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3116
3118 /* Make the ground dirty, if surface slope has changed */
3119 if (tileh_old != tileh_new) {
3120 /* If there is flat water on the lower halftile add the cost for clearing it */
3123 }
3124 return cost;
3125}
3126
3128static CommandCost TerraformTile_Rail(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
3129{
3130 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
3131 if (IsPlainRail(tile)) {
3132 TrackBits rail_bits = GetTrackBits(tile);
3133 /* Is there flat water on the lower halftile that must be cleared expensively? */
3134 bool was_water = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh_old));
3135
3136 /* Allow clearing the water only if there is no ship */
3137 if (was_water && HasVehicleOnTile(tile, [](const Vehicle *v) {
3138 return v->type == VehicleType::Ship;
3139 })) return CommandCost(STR_ERROR_SHIP_IN_THE_WAY);
3140
3141 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3142 CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
3143
3144 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3145 Corner allowed_corner;
3146 switch (rail_bits.base()) {
3147 case TrackBits{Track::Right}.base(): allowed_corner = CORNER_W; break;
3148 case TrackBits{Track::Upper}.base(): allowed_corner = CORNER_S; break;
3149 case TrackBits{Track::Left}.base(): allowed_corner = CORNER_E; break;
3150 case TrackBits{Track::Lower}.base(): allowed_corner = CORNER_N; break;
3151 default: return autoslope_result;
3152 }
3153
3154 Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
3155
3156 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3157 if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
3158
3159 /* Everything is valid, which only changes allowed_corner */
3160 for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
3161 if (allowed_corner == corner) continue;
3162 if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
3163 }
3164
3165 /* Make the ground dirty */
3167
3168 /* allow terraforming */
3169 return CommandCost(ExpensesType::Construction, was_water ? _price[Price::ClearWater] : (Money)0);
3170 } else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
3171 AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
3173 }
3174 return Command<Commands::LandscapeClear>::Do(flags, tile);
3175}
3176
3178static CommandCost CheckBuildAbove_Rail(TileIndex tile, [[maybe_unused]] DoCommandFlags flags, [[maybe_unused]] Axis axis, int height)
3179{
3180 if (IsPlainRail(tile)) return CommandCost();
3181
3182 int height_diff = GetTileMaxZ(tile) + MINIMAL_DEPOT_BRIDGE_HEIGHT - height;
3183 if (height_diff <= 0) return CommandCost();
3184 return CommandCostWithParam(STR_ERROR_BRIDGE_TOO_LOW_FOR_TRAIN_DEPOT, height_diff * TILE_HEIGHT_STEP);
3185}
3186
3188extern const TileTypeProcs _tile_type_rail_procs = {
3189 .draw_tile_proc = DrawTile_Rail,
3190 .get_slope_pixel_z_proc = GetSlopePixelZ_Rail,
3191 .clear_tile_proc = ClearTile_Rail,
3192 .get_tile_desc_proc = GetTileDesc_Rail,
3193 .get_tile_track_status_proc = GetTileTrackStatus_Rail,
3194 .click_tile_proc = ClickTile_Rail,
3195 .tile_loop_proc = TileLoop_Rail,
3196 .change_tile_owner_proc = ChangeTileOwner_Rail,
3197 .vehicle_enter_tile_proc = VehicleEnterTile_Rail,
3198 .get_foundation_proc = GetFoundation_Rail,
3199 .terraform_tile_proc = TerraformTile_Rail,
3200 .check_build_above_proc = CheckBuildAbove_Rail,
3201};
@ 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:451
static const SpriteID SPR_ORIGINAL_SIGNALS_BASE
Sprite number of the first block sginall.
Definition sprites.h:434
static const SpriteID SPR_TRACK_FENCE_FLAT_X
Definition sprites.h:513
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:239
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:884
static Trackdir GetOtherTrackdir(Trackdir trackdir)
Get the other Trackdir for a non-diagonal Trackdir.
Definition rail_cmd.cpp:820
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:969
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:941
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:759
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:390
CommandCost CmdRemoveSingleRail(DoCommandFlags flags, TileIndex tile, Track track)
Remove a single piece of track.
Definition rail_cmd.cpp:613
CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
Remove signals.
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 const TrackBits _valid_tracks_without_foundation[15]
Valid TrackBits on a specific (non-steep)-slope without foundation.
Definition rail_cmd.cpp:260
static const TrackBits _valid_tracks_on_leveled_foundation[15]
Valid TrackBits on a specific (non-steep)-slope with leveled foundation.
Definition rail_cmd.cpp:282
static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
Tests if a vehicle interacts with the specified track.
Definition rail_cmd.cpp:228
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:956
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:424
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:805
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 int GetJunctionGroundSpriteOffset(TrackBits track)
Returns which of the 5 junction-'Rail underlays' to use for the given track bits.
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:310
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
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:36
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ RAILTYPE_END
Used for iterations.
Definition rail_type.h:31
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
@ RAILTYPE_ELECTRIC
Electric rails.
Definition rail_type.h:28
@ RAILTYPE_RAIL
Standard non-electric rails.
Definition rail_type.h:27
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:66
EnumBitSet< RoadBit, uint8_t > RoadBits
Bitset of RoadBit elements.
Definition road_type.h:64
static constexpr RoadBits ROAD_Y
Full road along the y-axis (north-west + south-east).
Definition road_type.h:67
RoadType
The different roadtypes we support.
Definition road_type.h:23
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:28
RoadTramType
The different types of road type.
Definition road_type.h:37
@ Invalid
Invalid marker.
Definition road_type.h:41
@ Tram
Tram type.
Definition road_type.h:39
@ Road
Road type.
Definition road_type.h:38
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:98
@ Leveled
The tile is leveled up to a flat slope.
Definition slope_type.h:100
@ SteepLower
The tile has a steep slope. The lowest corner is raised by a foundation to allow building railroad on...
Definition slope_type.h:103
@ Invalid
Used inside "rail_cmd.cpp" to indicate invalid slope/track combination.
Definition slope_type.h:120
@ None
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:99
@ InclinedY
The tile has an along Y-axis inclined foundation.
Definition slope_type.h:102
@ 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:106
@ InclinedX
The tile has an along X-axis inclined foundation.
Definition slope_type.h:101
static const uint32_t VALID_LEVEL_CROSSING_SLOPES
Constant bitset with safe slopes for building a level crossing.
Definition slope_type.h:91
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:1523
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY
Definition sprites.h:1524
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_NORM
Definition sprites.h:1519
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PBS
Definition sprites.h:1517
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_COMBO
Definition sprites.h:1516
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_ENTRY
Definition sprites.h:1520
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_ENTRY
Definition sprites.h:1514
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1788
static const SpriteID SPR_FLAT_BARE_LAND
Definition sprites.h:668
static const SpriteID SPR_FLAT_WATER_TILE
Definition sprites.h:679
static const SpriteID SPR_FLAT_SNOW_DESERT_TILE
Definition sprites.h:683
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_NORM
Definition sprites.h:1513
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_COMBO
Definition sprites.h:1522
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_EXIT
Definition sprites.h:1515
static const SpriteID SPR_FLAT_GRASS_TILE
Definition sprites.h:671
static const PaletteID PALETTE_TO_BARE_LAND
sets colour to bare land stuff for rail, road and crossings
Definition sprites.h:1776
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_EXIT
Definition sprites.h:1521
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY
Definition sprites.h:1518
@ 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 (.
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.