OpenTTD Source 20260421-master-gc2fbc6fdeb
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. */
98 const SpriteID _signal_lookup[2][SIGTYPE_END] = {
99 {SPR_IMG_SIGNAL_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_EXIT,
100 SPR_IMG_SIGNAL_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_PBS, SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY},
101
102 {SPR_IMG_SIGNAL_SEMAPHORE_NORM, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_EXIT,
103 SPR_IMG_SIGNAL_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_PBS, SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY},
104 };
105
106 for (SignalType type = SIGTYPE_BLOCK; type < SIGTYPE_END; type = (SignalType)(type + 1)) {
107 for (SignalVariant var = SIG_ELECTRIC; var <= SIG_SEMAPHORE; var = (SignalVariant)(var + 1)) {
108 SpriteID red = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_RED, true);
109 SpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_GREEN, true);
110 rti->gui_sprites.signals[type][var][0] = (red != 0) ? red + SIGNAL_TO_SOUTH : _signal_lookup[var][type];
111 rti->gui_sprites.signals[type][var][1] = (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 TrackBits rail_bits = TrackToTrackBits(track);
231 return EnsureNoTrainOnTrackBits(tile, rail_bits);
232}
233
241{
242 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
243
244 /* So, we have a tile with tracks on it (and possibly signals). Let's see
245 * what tracks first */
246 TrackBits current = GetTrackBits(tile); // The current track layout.
247 TrackBits future = current | to_build; // The track layout we want to build.
248
249 /* Are we really building something new? */
250 if (current == future) {
251 /* Nothing new is being built */
252 return CommandCost(STR_ERROR_ALREADY_BUILT);
253 }
254
255 /* Normally, we may overlap and any combination is valid */
256 return CommandCost();
257}
258
259
281
303
312{
313 if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE;
314
315 if (IsSteepSlope(tileh)) {
316 /* Test for inclined foundations */
317 if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X;
318 if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y;
319
320 /* Get higher track */
321 Corner highest_corner = GetHighestSlopeCorner(tileh);
322 TrackBits higher_track = CornerToTrackBits(highest_corner);
323
324 /* Only higher track? */
325 if (bits == higher_track) return HalftileFoundation(highest_corner);
326
327 /* Overlap with higher track? */
328 if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID;
329
330 /* either lower track or both higher and lower track */
331 return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER);
332 } else {
333 if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE;
334
335 bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0);
336
337 Corner track_corner;
338 switch (bits) {
339 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
340 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
341 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
342 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
343
344 case TRACK_BIT_HORZ:
345 if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
346 if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
347 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
348
349 case TRACK_BIT_VERT:
350 if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
351 if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
352 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
353
354 case TRACK_BIT_X:
356 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
357
358 case TRACK_BIT_Y:
360 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
361
362 default:
363 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
364 }
365 /* Single diagonal track */
366
367 /* Track must be at least valid on leveled foundation */
368 if (!valid_on_leveled) return FOUNDATION_INVALID;
369
370 /* If slope has three raised corners, build leveled foundation */
372
373 /* If neighboured corners of track_corner are lowered, build halftile foundation */
374 if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
375
376 /* else special anti-zig-zag foundation */
377 return SpecialRailFoundation(track_corner);
378 }
379}
380
381
391static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
392{
393 /* don't allow building on the lower side of a coast */
394 if (GetFloodingBehaviour(tile) != FLOOD_NONE) {
395 if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
396 }
397
398 Foundation f_new = GetRailFoundation(tileh, rail_bits | existing);
399
400 /* check track/slope combination */
401 if ((f_new == FOUNDATION_INVALID) ||
402 ((f_new != FOUNDATION_NONE) && (!_settings_game.construction.build_on_slopes))) {
403 return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
404 }
405
406 Foundation f_old = GetRailFoundation(tileh, existing);
407 return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price[Price::BuildFoundation] : (Money)0);
408}
409
410/* Validate functions for rail building */
411static inline bool ValParamTrackOrientation(Track track)
412{
413 return IsValidTrack(track);
414}
415
425CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
426{
428
429 if (!ValParamRailType(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
430
431 Slope tileh = GetTileSlope(tile);
432 TrackBits trackbit = TrackToTrackBits(track);
433
434 switch (GetTileType(tile)) {
435 case TileType::Railway: {
437 if (ret.Failed()) return ret;
438
439 if (!IsPlainRail(tile)) return Command<Commands::LandscapeClear>::Do(flags, tile); // just get appropriate error message
440
441 if (!IsCompatibleRail(GetRailType(tile), railtype)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
442
443 ret = CheckTrackCombination(tile, trackbit);
444 if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
445 if (ret.Failed()) return ret;
446
447 ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
448 if (ret.Failed()) return ret;
449 cost.AddCost(ret.GetCost());
450
451 if (HasSignals(tile) && TracksOverlap(GetTrackBits(tile) | TrackToTrackBits(track))) {
452 /* If adding the new track causes any overlap, all signals must be removed first */
453 if (!auto_remove_signals) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
454
455 for (Track track_it = TRACK_BEGIN; track_it < TRACK_END; track_it++) {
456 if (HasTrack(tile, track_it) && HasSignalOnTrack(tile, track_it)) {
457 CommandCost ret_remove_signals = Command<Commands::RemoveSignal>::Do(flags, tile, track_it);
458 if (ret_remove_signals.Failed()) return ret_remove_signals;
459 cost.AddCost(ret_remove_signals.GetCost());
460 }
461 }
462 }
463
464 /* If the rail types don't match, try to convert only if engines of
465 * the new rail type are not powered on the present rail type and engines of
466 * the present rail type are powered on the new rail type. */
467 if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
468 if (HasPowerOnRail(GetRailType(tile), railtype)) {
469 ret = Command<Commands::ConvertRail>::Do(flags, tile, tile, railtype, false);
470 if (ret.Failed()) return ret;
471 cost.AddCost(ret.GetCost());
472 } else {
473 return CMD_ERROR;
474 }
475 }
476
477 if (flags.Test(DoCommandFlag::Execute)) {
479 TrackBits bits = GetTrackBits(tile);
480 SetTrackBits(tile, bits | trackbit);
481 /* Subtract old infrastructure count. */
482 uint pieces = CountBits(bits);
483 if (TracksOverlap(bits)) pieces *= pieces;
484 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
485 /* Add new infrastructure count. */
486 pieces = CountBits(bits | trackbit);
487 if (TracksOverlap(bits | trackbit)) pieces *= pieces;
488 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
490 }
491 break;
492 }
493
494 case TileType::Road: {
495 /* Level crossings may only be built on these slopes */
496 if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
497
498 if (!_settings_game.construction.crossing_with_competitor && _current_company != OWNER_DEITY) {
500 if (ret.Failed()) return ret;
501 }
502
504 if (ret.Failed()) return ret;
505
506 if (IsNormalRoad(tile)) {
507 if (HasRoadWorks(tile)) return CommandCost(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
508
509 if (GetDisallowedRoadDirections(tile).Any()) return CommandCost(STR_ERROR_CROSSING_ON_ONEWAY_ROAD);
510
511 if (RailNoLevelCrossings(railtype)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_RAIL);
512
513 RoadType roadtype_road = GetRoadTypeRoad(tile);
514 RoadType roadtype_tram = GetRoadTypeTram(tile);
515
516 if (roadtype_road != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_road)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
517 if (roadtype_tram != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_tram)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
518
521 if ((track == TRACK_X && !(road | tram).Any(ROAD_X)) ||
522 (track == TRACK_Y && !(road | tram).Any(ROAD_Y))) {
523 Owner road_owner = GetRoadOwner(tile, RoadTramType::Road);
524 Owner tram_owner = GetRoadOwner(tile, RoadTramType::Tram);
525 /* Disallow breaking end-of-line of someone else
526 * so trams can still reverse on this tile. */
527 if (Company::IsValidID(tram_owner) && tram.Count() == 1) {
528 ret = CheckOwnership(tram_owner);
529 if (ret.Failed()) return ret;
530 }
531
532 uint num_new_road_pieces = road.Any() ? 2 - road.Count() : 0;
533 if (num_new_road_pieces > 0) {
534 cost.AddCost(num_new_road_pieces * RoadBuildCost(roadtype_road));
535 }
536
537 uint num_new_tram_pieces = tram.Any() ? 2 - tram.Count() : 0;
538 if (num_new_tram_pieces > 0) {
539 cost.AddCost(num_new_tram_pieces * RoadBuildCost(roadtype_tram));
540 }
541
542 if (flags.Test(DoCommandFlag::Execute)) {
543 MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile));
544 UpdateLevelCrossing(tile, false);
546 Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR;
548 if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) {
549 Company::Get(road_owner)->infrastructure.road[roadtype_road] += num_new_road_pieces;
551 }
552 if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) {
553 Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces;
555 }
556 }
557 break;
558 }
559 }
560
561 if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) {
562 return CommandCost(STR_ERROR_ALREADY_BUILT);
563 }
564 [[fallthrough]];
565 }
566
567 default: {
568 /* Will there be flat water on the lower halftile? */
569 bool water_ground = IsTileType(tile, TileType::Water) && IsSlopeWithOneCornerRaised(tileh);
570
571 CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile);
572 if (ret.Failed()) return ret;
573 cost.AddCost(ret.GetCost());
574
575 ret = Command<Commands::LandscapeClear>::Do(flags, tile);
576 if (ret.Failed()) return ret;
577 cost.AddCost(ret.GetCost());
578
579 if (water_ground) {
582 }
583
584 if (flags.Test(DoCommandFlag::Execute)) {
585 MakeRailNormal(tile, _current_company, trackbit, railtype);
586 if (water_ground) {
589 }
590 Company::Get(_current_company)->infrastructure.rail[railtype]++;
592 }
593 break;
594 }
595 }
596
597 if (flags.Test(DoCommandFlag::Execute)) {
600 YapfNotifyTrackLayoutChange(tile, track);
601 }
602
603 cost.AddCost(RailBuildCost(railtype));
604 return cost;
605}
606
614CommandCost CmdRemoveSingleRail(DoCommandFlags flags, TileIndex tile, Track track)
615{
617 bool crossing = false;
618
619 if (!ValParamTrackOrientation(track)) return CMD_ERROR;
620 TrackBits trackbit = TrackToTrackBits(track);
621
622 /* Need to read tile owner now because it may change when the rail is removed
623 * Also, in case of floods, _current_company != owner
624 * There may be invalid tiletype even in exec run (when removing long track),
625 * so do not call GetTileOwner(tile) in any case here */
626 Owner owner = INVALID_OWNER;
627
628 Train *v = nullptr;
629
630 switch (GetTileType(tile)) {
631 case TileType::Road: {
632 if (!IsLevelCrossing(tile) || GetCrossingRailBits(tile) != trackbit) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
633
636 if (ret.Failed()) return ret;
637 }
638
639 if (!flags.Test(DoCommandFlag::Bankrupt)) {
641 if (ret.Failed()) return ret;
642 }
643
644 cost.AddCost(RailClearCost(GetRailType(tile)));
645
646 if (flags.Test(DoCommandFlag::Execute)) {
648
649 if (HasReservedTracks(tile, trackbit)) {
650 v = GetTrainForReservation(tile, track);
651 if (v != nullptr) FreeTrainTrackReservation(v);
652 }
653
654 owner = GetTileOwner(tile);
655 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR;
659 }
660 break;
661 }
662
663 case TileType::Railway: {
664 TrackBits present;
665 /* There are no rails present at depots. */
666 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
667
670 if (ret.Failed()) return ret;
671 }
672
673 CommandCost ret = EnsureNoTrainOnTrack(tile, track);
674 if (ret.Failed()) return ret;
675
676 present = GetTrackBits(tile);
677 if ((present & trackbit) == 0) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
678 if (present == (TRACK_BIT_X | TRACK_BIT_Y)) crossing = true;
679
680 cost.AddCost(RailClearCost(GetRailType(tile)));
681
682 /* Charge extra to remove signals on the track, if they are there */
683 if (HasSignalOnTrack(tile, track)) {
684 cost.AddCost(Command<Commands::RemoveSignal>::Do(flags, tile, track));
685 }
686
687 if (flags.Test(DoCommandFlag::Execute)) {
688 if (HasReservedTracks(tile, trackbit)) {
689 v = GetTrainForReservation(tile, track);
690 if (v != nullptr) FreeTrainTrackReservation(v);
691 }
692
693 owner = GetTileOwner(tile);
694
695 /* Subtract old infrastructure count. */
696 uint pieces = CountBits(present);
697 if (TracksOverlap(present)) pieces *= pieces;
698 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= pieces;
699 /* Add new infrastructure count. */
700 present ^= trackbit;
701 pieces = CountBits(present);
702 if (TracksOverlap(present)) pieces *= pieces;
703 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] += pieces;
705
706 if (present == 0) {
707 Slope tileh = GetTileSlope(tile);
708 /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */
710 bool docking = IsDockingTile(tile);
711 MakeShore(tile);
712 SetDockingTile(tile, docking);
713 } else {
714 DoClearSquare(tile);
715 }
717 } else {
718 SetTrackBits(tile, present);
720 }
721 }
722 break;
723 }
724
725 default: return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
726 }
727
728 if (flags.Test(DoCommandFlag::Execute)) {
729 /* if we got that far, 'owner' variable is set correctly */
730 assert(Company::IsValidID(owner));
731
733 if (crossing) {
734 /* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
735 * are removing one of these pieces, we'll need to update signals for
736 * both directions explicitly, as after the track is removed it won't
737 * 'connect' with the other piece. */
738 AddTrackToSignalBuffer(tile, TRACK_X, owner);
739 AddTrackToSignalBuffer(tile, TRACK_Y, owner);
742 } else {
743 AddTrackToSignalBuffer(tile, track, owner);
744 YapfNotifyTrackLayoutChange(tile, track);
745 }
746
747 if (v != nullptr) TryPathReserve(v, true);
748 }
749
750 return cost;
751}
752
753
762{
763 assert(IsPlainRailTile(t));
764
765 bool flooded = false;
766 if (GetRailGroundType(t) == RailGroundType::HalfTileWater) return flooded;
767
768 Slope tileh = GetTileSlope(t);
769 TrackBits rail_bits = GetTrackBits(t);
770
771 if (IsSlopeWithOneCornerRaised(tileh)) {
773
774 TrackBits to_remove = lower_track & rail_bits;
775 if (to_remove != TRACK_BIT_NONE) {
777 flooded = Command<Commands::RemoveRail>::Do(DoCommandFlag::Execute, t, FindFirstTrack(to_remove)).Succeeded();
778 cur_company.Restore();
779 if (!flooded) return flooded; // not yet floodable
780 rail_bits = rail_bits & ~to_remove;
781 if (rail_bits == TRACK_BIT_NONE) {
782 MakeShore(t);
784 return flooded;
785 }
786 }
787
788 if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
789 flooded = true;
792 }
793 } else {
794 /* Make shore on steep slopes and 'three-corners-raised'-slopes. */
795 if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), tileh) == 0) {
796 if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) {
797 flooded = true;
800 }
801 }
802 }
803 return flooded;
804}
805
806static const TileIndexDiffC _trackdelta[] = {
807 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
808 { 0, 0 },
809 { 0, 0 },
810 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
811 { 0, 0 },
812 { 0, 0 }
813};
814
815
816static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
817{
818 int x = TileX(start);
819 int y = TileY(start);
820 int ex = TileX(end);
821 int ey = TileY(end);
822
823 if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR;
824
825 /* calculate delta x,y from start to end tile */
826 int dx = ex - x;
827 int dy = ey - y;
828
829 /* calculate delta x,y for the first direction */
830 int trdx = _trackdelta[*trackdir].x;
831 int trdy = _trackdelta[*trackdir].y;
832
833 if (!IsDiagonalTrackdir(*trackdir)) {
834 trdx += _trackdelta[*trackdir ^ 1].x;
835 trdy += _trackdelta[*trackdir ^ 1].y;
836 }
837
838 /* validate the direction */
839 while ((trdx <= 0 && dx > 0) ||
840 (trdx >= 0 && dx < 0) ||
841 (trdy <= 0 && dy > 0) ||
842 (trdy >= 0 && dy < 0)) {
843 if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other
844 SetBit(*trackdir, 3); // reverse the direction
845 trdx = -trdx;
846 trdy = -trdy;
847 } else { // other direction is invalid too, invalid drag
848 return CMD_ERROR;
849 }
850 }
851
852 /* (for diagonal tracks, this is already made sure of by above test), but:
853 * for non-diagonal tracks, check if the start and end tile are on 1 line */
854 if (!IsDiagonalTrackdir(*trackdir)) {
855 trdx = _trackdelta[*trackdir].x;
856 trdy = _trackdelta[*trackdir].y;
857 if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) return CMD_ERROR;
858 }
859
860 return CommandCost();
861}
862
875static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
876{
878
879 if ((!remove && !ValParamRailType(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
880 if (end_tile >= Map::Size() || tile >= Map::Size()) return CMD_ERROR;
881
882 Trackdir trackdir = TrackToTrackdir(track);
883
884 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
885 if (ret.Failed()) return ret;
886
887 bool had_success = false;
888 CommandCost last_error = CMD_ERROR;
889 for (;;) {
890 ret = remove ? Command<Commands::RemoveRail>::Do(flags, tile, TrackdirToTrack(trackdir)) : Command<Commands::BuildRail>::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals);
891 if (!remove && !fail_on_obstacle && last_error.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) had_success = true;
892
893 if (ret.Failed()) {
894 last_error = std::move(ret);
895 if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
896 if (fail_on_obstacle) return last_error;
897 if (had_success) break; // Keep going if we haven't constructed any rail yet, skipping the start of the drag
898 }
899
900 /* Ownership errors are more important. */
901 if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
902 } else {
903 had_success = true;
904 total_cost.AddCost(ret.GetCost());
905 }
906
907 if (tile == end_tile) break;
908
909 tile += ToTileIndexDiff(_trackdelta[trackdir]);
910
911 /* toggle railbit for the non-diagonal tracks */
912 if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
913 }
914
915 if (had_success) return total_cost;
916 return last_error;
917}
918
932CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
933{
934 return CmdRailTrackHelper(flags, start_tile, end_tile, railtype, track, false, auto_remove_signals, fail_on_obstacle);
935}
936
947CommandCost CmdRemoveRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, Track track)
948{
949 return CmdRailTrackHelper(flags, start_tile, end_tile, INVALID_RAILTYPE, track, true, false, false);
950}
951
960CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
961{
962 /* check railtype and valid direction for depot (0 through 3), 4 in total */
963 if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
964
965 Slope tileh = GetTileSlope(tile);
966
968
969 /* Prohibit construction if
970 * The tile is non-flat AND
971 * 1) build-on-slopes is disabled
972 * 2) the tile is steep i.e. spans two height levels
973 * 3) the exit points in the wrong direction
974 */
975
976 if (tileh != SLOPE_FLAT) {
977 if (!_settings_game.construction.build_on_slopes) return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
978 if (!CanBuildDepotByTileh(dir, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
980 }
981
982 /* Allow the user to rotate the depot instead of having to destroy it and build it again */
983 bool rotate_existing_depot = false;
984 if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
986 if (ret.Failed()) return ret;
987
988 if (dir == GetRailDepotDirection(tile)) return CommandCost();
989
990 ret = EnsureNoVehicleOnGround(tile);
991 if (ret.Failed()) return ret;
992
993 rotate_existing_depot = true;
994 }
995
996 if (!rotate_existing_depot) {
997 cost.AddCost(Command<Commands::LandscapeClear>::Do(flags, tile));
998 if (cost.Failed()) return cost;
999
1000 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
1001
1002 if (!Depot::CanAllocateItem()) return CMD_ERROR;
1003 }
1004
1005 if (flags.Test(DoCommandFlag::Execute)) {
1006 if (rotate_existing_depot) {
1007 SetRailDepotExitDirection(tile, dir);
1008 } else {
1009 Depot *d = Depot::Create(tile);
1010
1011 MakeRailDepot(tile, _current_company, d->index, dir, railtype);
1012 MakeDefaultName(d);
1013
1014 Company::Get(_current_company)->infrastructure.rail[railtype]++;
1016 }
1017
1018 MarkTileDirtyByTile(tile);
1021 }
1022
1024 cost.AddCost(RailBuildCost(railtype));
1025 return cost;
1026}
1027
1046CommandCost 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)
1047{
1048 if (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE) return CMD_ERROR;
1049 if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
1050
1051 if (ctrl_pressed) sigvar = (SignalVariant)(sigvar ^ SIG_SEMAPHORE);
1052
1053 /* You can only build signals on plain rail tiles, and the selected track must exist */
1054 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
1055 !HasTrack(tile, track)) {
1056 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1057 }
1058 /* Protect against invalid signal copying */
1059 if (signals_copy != 0 && (signals_copy & SignalOnTrack(track)) == 0) return CMD_ERROR;
1060
1061 CommandCost ret = CheckTileOwnership(tile);
1062 if (ret.Failed()) return ret;
1063
1064 /* See if this is a valid track combination for signals (no overlap) */
1065 if (TracksOverlap(GetTrackBits(tile))) return CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
1066
1067 /* In case we don't want to change an existing signal, return without error. */
1068 if (skip_existing_signals && HasSignalOnTrack(tile, track)) return CommandCost();
1069
1070 /* you can not convert a signal if no signal is on track */
1071 if (convert_signal && !HasSignalOnTrack(tile, track)) return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1072
1073 CommandCost cost;
1074 if (!HasSignalOnTrack(tile, track)) {
1075 /* build new signals */
1077 } else {
1078 if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
1079 /* convert signals <-> semaphores */
1081
1082 } else if (convert_signal) {
1083 /* convert button pressed */
1084 if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
1085 /* it costs money to change signal variant (light or semaphore) */
1087 } else {
1088 /* it is free to change signal type (block, exit, entry, combo, path, etc) */
1089 cost = CommandCost();
1090 }
1091
1092 } else {
1093 /* it is free to change orientation or number of signals on the tile (for block/presignals which allow signals in both directions) */
1094 cost = CommandCost();
1095 }
1096 }
1097
1098 if (flags.Test(DoCommandFlag::Execute)) {
1099 Train *v = nullptr;
1100 /* The new/changed signal could block our path. As this can lead to
1101 * stale reservations, we clear the path reservation here and try
1102 * to redo it later on. */
1103 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1104 v = GetTrainForReservation(tile, track);
1105 if (v != nullptr) FreeTrainTrackReservation(v);
1106 }
1107
1108 if (!HasSignals(tile)) {
1109 /* there are no signals at all on this tile yet */
1110 SetHasSignals(tile, true);
1111 SetSignalStates(tile, 0xF); // all signals are on
1112 SetPresentSignals(tile, 0); // no signals built by default
1113 SetSignalType(tile, track, sigtype);
1114 SetSignalVariant(tile, track, sigvar);
1115 }
1116
1117 /* Subtract old signal infrastructure count. */
1118 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1119
1120 if (signals_copy == 0) {
1121 if (!HasSignalOnTrack(tile, track)) {
1122 /* build new signals */
1123 SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
1124 SetSignalType(tile, track, sigtype);
1125 SetSignalVariant(tile, track, sigvar);
1126 while (num_dir_cycle-- > 0) CycleSignalSide(tile, track);
1127 } else {
1128 if (convert_signal) {
1129 /* convert signal button pressed */
1130 if (ctrl_pressed) {
1131 /* toggle the present signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */
1132 SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC);
1133 /* Query current signal type so the check for PBS signals below works. */
1134 sigtype = GetSignalType(tile, track);
1135 } else {
1136 /* convert the present signal to the chosen type and variant */
1137 SetSignalType(tile, track, sigtype);
1138 SetSignalVariant(tile, track, sigvar);
1139 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1141 }
1142 }
1143
1144 } else if (ctrl_pressed) {
1145 /* cycle between cycle_start and cycle_end */
1146 sigtype = (SignalType)(GetSignalType(tile, track) + 1);
1147
1148 if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
1149
1150 SetSignalType(tile, track, sigtype);
1151 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1153 }
1154 } else {
1155 /* cycle the signal side: both -> left -> right -> both -> ... */
1156 CycleSignalSide(tile, track);
1157 /* Query current signal type so the check for PBS signals below works. */
1158 sigtype = GetSignalType(tile, track);
1159 }
1160 }
1161 } else {
1162 /* If CmdBuildManySignals is called with copying signals, just copy the
1163 * direction of the first signal given as parameter by CmdBuildManySignals */
1164 SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (signals_copy & SignalOnTrack(track)));
1165 SetSignalVariant(tile, track, sigvar);
1166 SetSignalType(tile, track, sigtype);
1167 }
1168
1169 /* Add new signal infrastructure count. */
1170 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1172
1173 if (IsPbsSignal(sigtype)) {
1174 /* PBS signals should show red unless they are on reserved tiles without a train. */
1175 uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
1176 SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetRailReservationTrackBits(tile), track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask));
1177 }
1178 MarkTileDirtyByTile(tile);
1180 YapfNotifyTrackLayoutChange(tile, track);
1181 if (v != nullptr && v->track != TRACK_BIT_DEPOT) {
1182 Train *moving_front = v->GetMovingFront();
1183 /* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1184 if (!((v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) ||
1185 !IsSafeWaitingPosition(v, moving_front->tile, moving_front->GetVehicleTrackdir(), true, _settings_game.pf.forbid_90_deg)) {
1186 TryPathReserve(v, true);
1187 }
1188 }
1189 }
1190
1191 return cost;
1192}
1193
1194static bool AdvanceSignalAutoFill(TileIndex &tile, Trackdir &trackdir, bool remove)
1195{
1196 /* We only process starting tiles of tunnels or bridges so jump to the other end before moving further. */
1198
1199 tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
1200 if (tile == INVALID_TILE) return false;
1201
1202 /* Check for track bits on the new tile */
1204
1205 if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
1206 trackdirbits &= TrackdirReachesTrackdirs(trackdir);
1207
1208 /* No track bits, must stop */
1209 if (trackdirbits == TRACKDIR_BIT_NONE) return false;
1210
1211 /* Get the first track dir */
1212 trackdir = RemoveFirstTrackdir(&trackdirbits);
1213
1214 /* Any left? It's a junction so we stop */
1215 if (trackdirbits != TRACKDIR_BIT_NONE) return false;
1216
1217 switch (GetTileType(tile)) {
1218 case TileType::Railway:
1219 if (IsRailDepot(tile)) return false;
1220 if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1221 break;
1222
1223 case TileType::Road:
1224 if (!IsLevelCrossing(tile)) return false;
1225 break;
1226
1228 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
1229 if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
1230 break;
1231 }
1232
1233 default: return false;
1234 }
1235 return true;
1236}
1237
1253static 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)
1254{
1256
1257 if (end_tile >= Map::Size() || !ValParamTrackOrientation(track)) return CMD_ERROR;
1258 if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
1259 if (!remove && (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE)) return CMD_ERROR;
1260
1261 if (!IsPlainRailTile(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1262 TileIndex start_tile = tile;
1263
1264 /* Interpret signal_density as the logical length of said amount of tiles in X/Y direction. */
1265 signal_density *= TILE_AXIAL_DISTANCE;
1266
1267 Trackdir trackdir = TrackToTrackdir(track);
1268 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
1269 if (ret.Failed()) return ret;
1270
1271 track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
1272 Trackdir start_trackdir = trackdir;
1273
1274 /* Must start on a valid track to be able to avoid loops */
1275 if (!HasTrack(tile, track)) return CMD_ERROR;
1276
1277 uint8_t signals;
1278 /* copy the signal-style of the first rail-piece if existing */
1279 if (HasSignalOnTrack(tile, track)) {
1280 signals = GetPresentSignals(tile) & SignalOnTrack(track);
1281 assert(signals != 0);
1282
1283 /* copy signal/semaphores style (independent of CTRL) */
1284 sigvar = GetSignalVariant(tile, track);
1285
1286 sigtype = GetSignalType(tile, track);
1287 /* Don't but copy entry or exit-signal type */
1288 if (sigtype == SIGTYPE_ENTRY || sigtype == SIGTYPE_EXIT) sigtype = SIGTYPE_BLOCK;
1289 } else { // no signals exist, drag a two-way signal stretch
1290 signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
1291 }
1292
1293 uint8_t signal_dir = 0;
1294 if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0);
1295 if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
1296
1297 /* signal_ctr - amount of tiles already processed
1298 * last_used_ctr - amount of tiles before previously placed signal
1299 * signals_density - setting to put signal on every Nth tile (double space on |, -- tracks)
1300 * last_suitable_ctr - amount of tiles before last possible signal place
1301 * last_suitable_tile - last tile where it is possible to place a signal
1302 * last_suitable_trackdir - trackdir of the last tile
1303 **********
1304 * trackdir - trackdir to build with autorail
1305 * semaphores - semaphores or signals
1306 * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1307 * and convert all others to semaphore/signal
1308 * remove - 1 remove signals, 0 build signals */
1309 int signal_ctr = 0;
1310 int last_used_ctr = -signal_density; // to force signal at first tile
1311 int last_suitable_ctr = 0;
1312 TileIndex last_suitable_tile = INVALID_TILE;
1313 Trackdir last_suitable_trackdir = INVALID_TRACKDIR;
1314 CommandCost last_error = CMD_ERROR;
1315 bool had_success = false;
1316 auto build_signal = [&](TileIndex tile, Trackdir trackdir, bool test_only) {
1317 /* Pick the correct orientation for the track direction */
1318 uint8_t signals = 0;
1319 if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
1320 if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
1321
1322 DoCommandFlags do_flags = test_only ? DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) : flags;
1323 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, SIGTYPE_BLOCK, SIGTYPE_BLOCK, 0, signals);
1324
1325 if (test_only) return ret.Succeeded();
1326
1327 if (ret.Succeeded()) {
1328 had_success = true;
1329 total_cost.AddCost(ret.GetCost());
1330 } else {
1331 /* The "No railway" error is the least important one. */
1332 if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
1333 last_error.GetErrorMessage() == INVALID_STRING_ID) {
1334 last_error = ret;
1335 }
1336 }
1337 return ret.Succeeded();
1338 };
1339
1340 for (;;) {
1341 if (remove) {
1342 /* In remove mode last_* stuff doesn't matter, we simply try to clear every tile. */
1343 build_signal(tile, trackdir, false);
1344 } else if (minimise_gaps) {
1345 /* We're trying to minimize gaps wherever possible, so keep track of last suitable
1346 * position and use it if current gap exceeds required signal density. */
1347
1348 if (signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1349 /* We overshot so build a signal in last good location. */
1350 if (build_signal(last_suitable_tile, last_suitable_trackdir, false)) {
1351 last_suitable_tile = INVALID_TILE;
1352 last_used_ctr = last_suitable_ctr;
1353 }
1354 }
1355
1356 if (signal_ctr == last_used_ctr + signal_density) {
1357 /* Current gap matches the required density, build a signal. */
1358 if (build_signal(tile, trackdir, false)) {
1359 last_used_ctr = signal_ctr;
1360 last_suitable_tile = INVALID_TILE;
1361 }
1362 } else {
1363 /* Test tile for a potential signal spot. */
1364 if (build_signal(tile, trackdir, true)) {
1365 last_suitable_tile = tile;
1366 last_suitable_ctr = signal_ctr;
1367 last_suitable_trackdir = trackdir;
1368 }
1369 }
1370 } else if (signal_ctr >= last_used_ctr + signal_density) {
1371 /* We're always keeping regular interval between signals so doesn't matter whether we succeed or not. */
1372 build_signal(tile, trackdir, false);
1373 last_used_ctr = signal_ctr;
1374 }
1375
1376 if (autofill) {
1377 switch (GetTileType(tile)) {
1378 case TileType::Railway:
1379 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1380 break;
1381
1382 case TileType::Road:
1383 signal_ctr += TILE_AXIAL_DISTANCE;
1384 break;
1385
1388 if (remove || minimise_gaps) {
1389 signal_ctr += len;
1390 } else {
1391 /* To keep regular interval we need to emulate placing signals on a bridge.
1392 * We start with TILE_AXIAL_DISTANCE as one bridge tile gets processed in the main loop. */
1393 signal_ctr += TILE_AXIAL_DISTANCE;
1394 for (uint i = TILE_AXIAL_DISTANCE; i < len; i += TILE_AXIAL_DISTANCE) {
1395 if (signal_ctr >= last_used_ctr + signal_density) last_used_ctr = signal_ctr;
1396 signal_ctr += TILE_AXIAL_DISTANCE;
1397 }
1398 }
1399 break;
1400 }
1401
1402 default: break;
1403 }
1404
1405 if (!AdvanceSignalAutoFill(tile, trackdir, remove)) break;
1406
1407 /* Prevent possible loops */
1408 if (tile == start_tile && trackdir == start_trackdir) break;
1409 } else {
1410 if (tile == end_tile) break;
1411
1412 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1413 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1414
1415 tile += ToTileIndexDiff(_trackdelta[trackdir]);
1416 if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
1417 }
1418 }
1419
1420 /* We may end up with the current gap exceeding the signal density so fix that if needed. */
1421 if (!remove && minimise_gaps && signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1422 build_signal(last_suitable_tile, last_suitable_trackdir, false);
1423 }
1424
1425 return had_success ? total_cost : last_error;
1426}
1427
1444CommandCost 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)
1445{
1446 return CmdSignalTrackHelper(flags, tile, end_tile, track, sigtype, sigvar, mode, false, autofill, minimise_gaps, signal_density);
1447}
1448
1456CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
1457{
1458 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
1459 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1460 }
1461 if (!HasSignalOnTrack(tile, track)) {
1462 return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1463 }
1464
1465 /* Only water can remove signals from anyone */
1467 CommandCost ret = CheckTileOwnership(tile);
1468 if (ret.Failed()) return ret;
1469 }
1470
1471 /* Do it? */
1472 if (flags.Test(DoCommandFlag::Execute)) {
1473 Train *v = nullptr;
1474 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1475 v = GetTrainForReservation(tile, track);
1476 } else if (IsPbsSignal(GetSignalType(tile, track))) {
1477 /* PBS signal, might be the end of a path reservation. */
1478 Trackdir td = TrackToTrackdir(track);
1479 for (int i = 0; v == nullptr && i < 2; i++, td = ReverseTrackdir(td)) {
1480 /* Only test the active signal side. */
1481 if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
1484 if (HasReservedTracks(next, tracks)) {
1486 }
1487 }
1488 }
1489 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1490 SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
1491 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1493
1494 /* removed last signal from tile? */
1495 if (GetPresentSignals(tile) == 0) {
1496 SetSignalStates(tile, 0);
1497 SetHasSignals(tile, false);
1498 SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores
1499 }
1500
1501 AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
1502 YapfNotifyTrackLayoutChange(tile, track);
1503 if (v != nullptr) TryPathReserve(v, false);
1504
1505 MarkTileDirtyByTile(tile);
1506 }
1507
1509}
1510
1522CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
1523{
1524 return CmdSignalTrackHelper(flags, tile, end_tile, track, SIGTYPE_BLOCK, SIG_ELECTRIC, false, true, autofill, false, 1); // bit 5 is remove bit
1525}
1526
1537CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
1538{
1539 TileIndex area_end = tile;
1540
1541 if (!ValParamRailType(totype)) return CMD_ERROR;
1542 if (area_start >= Map::Size()) return CMD_ERROR;
1543
1544 TrainList affected_trains;
1545
1547 CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
1548 bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
1549
1550 std::unique_ptr<TileIterator> iter = TileIterator::Create(area_start, area_end, diagonal);
1551 for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
1552 TileType tt = GetTileType(tile);
1553
1554 /* Check if there is any track on tile */
1555 switch (tt) {
1556 case TileType::Railway:
1557 break;
1558 case TileType::Station:
1559 if (!HasStationRail(tile)) continue;
1560 break;
1561 case TileType::Road:
1562 if (!IsLevelCrossing(tile)) continue;
1563 if (RailNoLevelCrossings(totype)) {
1564 error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
1565 continue;
1566 }
1567 break;
1569 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
1570 break;
1571 default: continue;
1572 }
1573
1574 /* Original railtype we are converting from */
1575 RailType type = GetRailType(tile);
1576
1577 /* Converting to the same type or converting 'hidden' elrail -> rail */
1578 if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
1579
1580 /* Trying to convert other's rail */
1581 CommandCost ret = CheckTileOwnership(tile);
1582 if (ret.Failed()) {
1583 error = std::move(ret);
1584 continue;
1585 }
1586
1587 std::vector<Train *> vehicles_affected;
1588
1589 /* Vehicle on the tile when not converting Rail <-> ElRail
1590 * Tunnels and bridges have special check later */
1591 if (tt != TileType::TunnelBridge) {
1592 if (!IsCompatibleRail(type, totype)) {
1594 if (ret.Failed()) {
1595 error = std::move(ret);
1596 continue;
1597 }
1598 }
1599 if (flags.Test(DoCommandFlag::Execute)) { // we can safely convert, too
1600 TrackBits reserved = GetReservedTrackbits(tile);
1601 Track track;
1602 while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
1603 Train *v = GetTrainForReservation(tile, track);
1604 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1605 /* No power on new rail type, reroute. */
1607 vehicles_affected.push_back(v);
1608 }
1609 }
1610
1611 /* Update the company infrastructure counters. */
1612 if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
1613 Company *c = Company::Get(GetTileOwner(tile));
1614 uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
1615 if (IsPlainRailTile(tile)) {
1616 TrackBits bits = GetTrackBits(tile);
1617 num_pieces = CountBits(bits);
1618 if (TracksOverlap(bits)) num_pieces *= num_pieces;
1619 }
1620 c->infrastructure.rail[type] -= num_pieces;
1621 c->infrastructure.rail[totype] += num_pieces;
1623 }
1624
1625 SetRailType(tile, totype);
1626 MarkTileDirtyByTile(tile);
1627 /* update power of train on this tile */
1628 for (Vehicle *v : VehiclesOnTile(tile)) {
1629 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1630 }
1631 }
1632 }
1633
1634 switch (tt) {
1635 default: NOT_REACHED();
1636 case TileType::Railway:
1637 switch (GetRailTileType(tile)) {
1639 if (flags.Test(DoCommandFlag::Execute)) {
1640 /* notify YAPF about the track layout change */
1642
1643 /* Update build vehicle window related to this depot */
1646 }
1647 found_convertible_track = true;
1648 cost.AddCost(RailConvertCost(type, totype));
1649 break;
1650
1651 default: // RailTileType::Normal, RailTileType::Signals
1652 if (flags.Test(DoCommandFlag::Execute)) {
1653 /* notify YAPF about the track layout change */
1654 TrackBits tracks = GetTrackBits(tile);
1655 while (tracks != TRACK_BIT_NONE) {
1657 }
1658 }
1659 found_convertible_track = true;
1660 cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
1661 break;
1662 }
1663 break;
1664
1666 TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
1667
1668 /* If both ends of tunnel/bridge are in the range, do not try to convert twice -
1669 * it would cause assert because of different test and exec runs */
1670 if (endtile < tile) {
1671 if (diagonal) {
1672 if (DiagonalTileArea(area_start, area_end).Contains(endtile)) continue;
1673 } else {
1674 if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
1675 }
1676 }
1677
1678 /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
1679 if (!IsCompatibleRail(GetRailType(tile), totype)) {
1680 ret = TunnelBridgeIsFree(tile, endtile);
1681 if (ret.Failed()) {
1682 error = std::move(ret);
1683 continue;
1684 }
1685 }
1686
1687 if (flags.Test(DoCommandFlag::Execute)) {
1689 if (HasTunnelBridgeReservation(tile)) {
1690 Train *v = GetTrainForReservation(tile, track);
1691 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1692 /* No power on new rail type, reroute. */
1694 vehicles_affected.push_back(v);
1695 }
1696 }
1697
1698 /* Update the company infrastructure counters. */
1699 uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
1700 Company *c = Company::Get(GetTileOwner(tile));
1701 c->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1702 c->infrastructure.rail[totype] += num_pieces;
1704
1705 SetRailType(tile, totype);
1706 SetRailType(endtile, totype);
1707
1708 for (Vehicle *v : VehiclesOnTile(tile)) {
1709 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1710 }
1711 for (Vehicle *v : VehiclesOnTile(endtile)) {
1712 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1713 }
1714
1715 YapfNotifyTrackLayoutChange(tile, track);
1716 YapfNotifyTrackLayoutChange(endtile, track);
1717
1718 if (IsBridge(tile)) {
1719 MarkBridgeDirty(tile);
1720 } else {
1721 MarkTileDirtyByTile(tile);
1722 MarkTileDirtyByTile(endtile);
1723 }
1724 }
1725
1726 found_convertible_track = true;
1727 cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
1728 break;
1729 }
1730
1731 case TileType::Station:
1732 case TileType::Road:
1733 if (flags.Test(DoCommandFlag::Execute)) {
1734 Track track = ((tt == TileType::Station) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile));
1735 YapfNotifyTrackLayoutChange(tile, track);
1736 }
1737
1738 found_convertible_track = true;
1739 cost.AddCost(RailConvertCost(type, totype));
1740 break;
1741 }
1742
1743 for (uint i = 0; i < vehicles_affected.size(); ++i) {
1744 TryPathReserve(vehicles_affected[i], true);
1745 }
1746 }
1747
1748 if (flags.Test(DoCommandFlag::Execute)) {
1749 /* Railtype changed, update trains as when entering different track */
1750 for (Train *v : affected_trains) {
1751 v->ConsistChanged(CCF_TRACK);
1752 }
1753 }
1754
1755 return found_convertible_track ? cost : error;
1756}
1757
1758static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlags flags)
1759{
1761 CommandCost ret = CheckTileOwnership(tile);
1762 if (ret.Failed()) return ret;
1763 }
1764
1766 if (ret.Failed()) return ret;
1767
1768 if (flags.Test(DoCommandFlag::Execute)) {
1769 /* read variables before the depot is removed */
1771 Owner owner = GetTileOwner(tile);
1772 Train *v = nullptr;
1773
1774 if (HasDepotReservation(tile)) {
1776 if (v != nullptr) FreeTrainTrackReservation(v);
1777 }
1778
1779 Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
1781
1782 delete Depot::GetByTile(tile);
1783 DoClearSquare(tile);
1784 AddSideToSignalBuffer(tile, dir, owner);
1786 if (v != nullptr) TryPathReserve(v, true);
1787 }
1788
1790}
1791
1793static CommandCost ClearTile_Rail(TileIndex tile, DoCommandFlags flags)
1794{
1796
1797 if (flags.Test(DoCommandFlag::Auto)) {
1798 if (!IsTileOwner(tile, _current_company)) {
1799 return CommandCost(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
1800 }
1801
1802 if (IsPlainRail(tile)) {
1803 return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
1804 } else {
1805 return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
1806 }
1807 }
1808
1809 switch (GetRailTileType(tile)) {
1811 case RailTileType::Normal: {
1812 Slope tileh = GetTileSlope(tile);
1813 /* Is there flat water on the lower halftile that gets cleared expensively? */
1814 bool water_ground = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh));
1815
1816 TrackBits tracks = GetTrackBits(tile);
1817 while (tracks != TRACK_BIT_NONE) {
1818 Track track = RemoveFirstTrack(&tracks);
1819 CommandCost ret = Command<Commands::RemoveRail>::Do(flags, tile, track);
1820 if (ret.Failed()) return ret;
1821 cost.AddCost(ret.GetCost());
1822 }
1823
1824 /* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
1825 * Same holds for non-companies clearing the tile, e.g. disasters. */
1826 if (water_ground && !flags.Test(DoCommandFlag::Bankrupt) && Company::IsValidID(_current_company)) {
1828 if (ret.Failed()) return ret;
1829
1830 /* The track was removed, and left a coast tile. Now also clear the water. */
1831 if (flags.Test(DoCommandFlag::Execute)) {
1832 DoClearSquare(tile);
1833 }
1835 }
1836
1837 return cost;
1838 }
1839
1841 return RemoveTrainDepot(tile, flags);
1842
1843 default:
1844 return CMD_ERROR;
1845 }
1846}
1847
1856static uint GetSafeSlopeZ(uint x, uint y, Track track)
1857{
1858 switch (track) {
1859 case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break;
1860 case TRACK_LOWER: x |= 0xF; y |= 0xF; break;
1861 case TRACK_LEFT: x |= 0xF; y &= ~0xF; break;
1862 case TRACK_RIGHT: x &= ~0xF; y |= 0xF; break;
1863 default: break;
1864 }
1865 return GetSlopePixelZ(x, y);
1866}
1867
1868static void DrawSingleSignal(TileIndex tile, const RailTypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos)
1869{
1870 bool side;
1871 switch (_settings_game.construction.train_signal_side) {
1872 case 0: side = false; break; // left
1873 case 2: side = true; break; // right
1874 default: side = _settings_game.vehicle.road_side != 0; break; // driving side
1875 }
1876 static const Point SignalPositions[2][12] = {
1877 { // Signals on the left side
1878 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1879 { 8, 5}, {14, 1}, { 1, 14}, { 9, 11}, { 1, 0}, { 3, 10},
1880 /* LOWER LOWER X X Y Y */
1881 {11, 4}, {14, 14}, {11, 3}, { 4, 13}, { 3, 4}, {11, 13}
1882 }, { // Signals on the right side
1883 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1884 {14, 1}, {12, 10}, { 4, 6}, { 1, 14}, {10, 4}, { 0, 1},
1885 /* LOWER LOWER X X Y Y */
1886 {14, 14}, { 5, 12}, {11, 13}, { 4, 3}, {13, 4}, { 3, 11}
1887 }
1888 };
1889
1890 uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x;
1891 uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y;
1892
1893 SignalType type = GetSignalType(tile, track);
1894 SignalVariant variant = GetSignalVariant(tile, track);
1895
1896 SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition);
1897 if (sprite != 0) {
1898 sprite += image;
1899 } else {
1900 /* Normal electric signals are stored in a different sprite block than all other signals. */
1901 sprite = (type == SIGTYPE_BLOCK && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
1902 sprite += type * 16 + variant * 64 + image * 2 + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0);
1903 }
1904
1905 AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, GetSafeSlopeZ(x, y, track), {{}, {1, 1, BB_HEIGHT_UNDER_BRIDGE}, {}});
1906}
1907
1909struct FenceOffset : SpriteBounds {
1911
1912 constexpr FenceOffset(Corner height_ref, int8_t origin_x, int8_t origin_y, uint8_t extent_x, uint8_t extent_y) :
1913 SpriteBounds({origin_x, origin_y, 0}, {extent_x, extent_y, 4}, {}), height_ref(height_ref) {}
1914};
1915
1917static const FenceOffset _fence_offsets[] = {
1918 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_FLAT_X_NW
1919 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_FLAT_Y_NE
1920 { CORNER_W, 8, 8, 1, 1 }, // RFO_FLAT_LEFT
1921 { CORNER_N, 8, 8, 1, 1 }, // RFO_FLAT_UPPER
1922 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_SW_NW
1923 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_SE_NE
1924 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_NE_NW
1925 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_NW_NE
1926 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_FLAT_X_SE
1927 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_FLAT_Y_SW
1928 { CORNER_E, 8, 8, 1, 1 }, // RFO_FLAT_RIGHT
1929 { CORNER_S, 8, 8, 1, 1 }, // RFO_FLAT_LOWER
1930 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_SW_SE
1931 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_SE_SW
1932 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_NE_SE
1933 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_NW_SW
1934};
1935
1943static void DrawTrackFence(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites, RailFenceOffset rfo)
1944{
1945 int z = ti->z;
1946 if (_fence_offsets[rfo].height_ref != CORNER_INVALID) {
1948 }
1949 AddSortableSpriteToDraw(psid.sprite + (rfo % num_sprites), psid.pal, ti->x, ti->y, z, _fence_offsets[rfo]);
1950}
1951
1958static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1959{
1961 if (ti->tileh & SLOPE_NW) rfo = (ti->tileh & SLOPE_W) ? RFO_SLOPE_SW_NW : RFO_SLOPE_NE_NW;
1962 DrawTrackFence(ti, psid, num_sprites, rfo);
1963}
1964
1971static void DrawTrackFence_SE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1972{
1974 if (ti->tileh & SLOPE_SE) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SW_SE : RFO_SLOPE_NE_SE;
1975 DrawTrackFence(ti, psid, num_sprites, rfo);
1976}
1977
1984static void DrawTrackFence_NE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1985{
1987 if (ti->tileh & SLOPE_NE) rfo = (ti->tileh & SLOPE_E) ? RFO_SLOPE_SE_NE : RFO_SLOPE_NW_NE;
1988 DrawTrackFence(ti, psid, num_sprites, rfo);
1989}
1990
1997static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1998{
2000 if (ti->tileh & SLOPE_SW) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SE_SW : RFO_SLOPE_NW_SW;
2001 DrawTrackFence(ti, psid, num_sprites, rfo);
2002}
2003
2010static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
2011{
2012 /* Base sprite for track fences.
2013 * Note: Halftile slopes only have fences on the upper part. */
2014 uint num_sprites = 0;
2015 PalSpriteID psid{
2017 .pal = pal,
2018 };
2019 if (psid.sprite == 0) {
2020 psid.sprite = SPR_TRACK_FENCE_FLAT_X;
2021 num_sprites = 8;
2022 }
2023
2024 assert(num_sprites > 0);
2025
2026 switch (GetRailGroundType(ti->tile)) {
2027 case RailGroundType::FenceNW: DrawTrackFence_NW(ti, psid, num_sprites); break;
2028 case RailGroundType::FenceSE: DrawTrackFence_SE(ti, psid, num_sprites); break;
2029 case RailGroundType::FenceSENW: DrawTrackFence_NW(ti, psid, num_sprites);
2030 DrawTrackFence_SE(ti, psid, num_sprites); break;
2031 case RailGroundType::FenceNE: DrawTrackFence_NE(ti, psid, num_sprites); break;
2032 case RailGroundType::FenceSW: DrawTrackFence_SW(ti, psid, num_sprites); break;
2033 case RailGroundType::FenceNESW: DrawTrackFence_NE(ti, psid, num_sprites);
2034 DrawTrackFence_SW(ti, psid, num_sprites); break;
2035 case RailGroundType::FenceVert1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2036 case RailGroundType::FenceVert2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2037 case RailGroundType::FenceHoriz1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2038 case RailGroundType::FenceHoriz2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2040 Corner track_corner;
2041 if (IsHalftileSlope(ti->tileh)) {
2042 /* Steep slope or one-corner-raised slope with halftile foundation */
2043 track_corner = GetHalftileSlopeCorner(ti->tileh);
2044 } else {
2045 /* Three-corner-raised slope */
2047 }
2048 switch (track_corner) {
2049 case CORNER_W: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2050 case CORNER_S: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2051 case CORNER_E: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2052 case CORNER_N: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2053 default: NOT_REACHED();
2054 }
2055 break;
2056 }
2057 default: break;
2058 }
2059}
2060
2061/* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */
2062static const int INF = 1000;
2063static const SubSprite _halftile_sub_sprite[4] = {
2064 { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right
2065 { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top
2066 { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left
2067 { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2068};
2069
2070static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
2071{
2072 DrawGroundSprite(sprite, pal, nullptr, 0, (ti->tileh & s) ? -8 : 0);
2073}
2074
2075static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti)
2076{
2078 Foundation f = GetRailFoundation(ti->tileh, track);
2079 Corner halftile_corner = CORNER_INVALID;
2080
2082 /* Save halftile corner */
2084 /* Draw lower part first */
2085 track &= ~CornerToTrackBits(halftile_corner);
2087 }
2088
2089 DrawFoundation(ti, f);
2090 /* DrawFoundation modifies ti */
2091
2092 /* Draw ground */
2093 if (rgt == RailGroundType::HalfTileWater) {
2094 if (track != TRACK_BIT_NONE || IsSteepSlope(ti->tileh)) {
2095 /* three-corner-raised slope or steep slope with track on upper part */
2096 DrawShoreTile(ti->tileh);
2097 } else {
2098 /* single-corner-raised slope with track on upper part */
2099 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
2100 }
2101 } else {
2102 SpriteID image;
2103
2104 switch (rgt) {
2105 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2106 case RailGroundType::SnowOrDesert: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2107 default: image = SPR_FLAT_GRASS_TILE; break;
2108 }
2109
2110 image += SlopeToSpriteOffset(ti->tileh);
2111
2112 DrawGroundSprite(image, PAL_NONE);
2113 }
2114
2115 bool no_combine = ti->tileh == SLOPE_FLAT && rti->flags.Test(RailTypeFlag::NoSpriteCombine);
2118 TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE;
2119
2120 if (track == TRACK_BIT_NONE) {
2121 /* Half-tile foundation, no track here? */
2122 } else if (no_combine) {
2123 /* Use trackbits as direct index from ground sprite, subtract 1
2124 * because there is no sprite for no bits. */
2125 DrawGroundSprite(ground + track - 1, PAL_NONE);
2126
2127 /* Draw reserved track bits */
2128 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2129 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2130 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2131 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2132 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2133 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2134 } else if (ti->tileh == SLOPE_NW && track == TRACK_BIT_Y) {
2135 DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
2137 } else if (ti->tileh == SLOPE_NE && track == TRACK_BIT_X) {
2138 DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
2140 } else if (ti->tileh == SLOPE_SE && track == TRACK_BIT_Y) {
2141 DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
2143 } else if (ti->tileh == SLOPE_SW && track == TRACK_BIT_X) {
2144 DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
2146 } else {
2147 switch (track) {
2148 /* Draw single ground sprite when not overlapping. No track overlay
2149 * is necessary for these sprites. */
2150 case TRACK_BIT_X: DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
2151 case TRACK_BIT_Y: DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
2152 case TRACK_BIT_UPPER: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
2153 case TRACK_BIT_LOWER: DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2154 case TRACK_BIT_RIGHT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
2155 case TRACK_BIT_LEFT: DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2156 case TRACK_BIT_CROSS: DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
2157 case TRACK_BIT_HORZ: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
2158 DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2159 case TRACK_BIT_VERT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
2160 DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2161
2162 default:
2163 /* We're drawing a junction tile */
2164 if ((track & TRACK_BIT_3WAY_NE) == 0) {
2165 DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
2166 } else if ((track & TRACK_BIT_3WAY_SW) == 0) {
2167 DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
2168 } else if ((track & TRACK_BIT_3WAY_NW) == 0) {
2169 DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
2170 } else if ((track & TRACK_BIT_3WAY_SE) == 0) {
2171 DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
2172 } else {
2173 DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2174 }
2175
2176 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2177 track &= ~pbs;
2178
2179 /* Draw regular track bits */
2180 if (track & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PAL_NONE);
2181 if (track & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
2182 if (track & TRACK_BIT_UPPER) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
2183 if (track & TRACK_BIT_LOWER) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
2184 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
2185 if (track & TRACK_BIT_LEFT) DrawGroundSprite(overlay + RTO_W, PAL_NONE);
2186 }
2187
2188 /* Draw reserved track bits */
2189 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2190 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2191 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2192 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2193 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2194 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2195 }
2196
2197 if (IsValidCorner(halftile_corner)) {
2198 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2201
2202 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2203 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2204
2205 SpriteID image;
2206 switch (rgt) {
2207 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2209 case RailGroundType::HalfTileSnow: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2210 default: image = SPR_FLAT_GRASS_TILE; break;
2211 }
2212
2213 image += SlopeToSpriteOffset(fake_slope);
2214
2215 DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
2216
2217 track = CornerToTrackBits(halftile_corner);
2218
2219 int offset;
2220 switch (track) {
2221 default: NOT_REACHED();
2222 case TRACK_BIT_UPPER: offset = RTO_N; break;
2223 case TRACK_BIT_LOWER: offset = RTO_S; break;
2224 case TRACK_BIT_RIGHT: offset = RTO_E; break;
2225 case TRACK_BIT_LEFT: offset = RTO_W; break;
2226 }
2227
2228 DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
2229 if (_settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, track)) {
2230 DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
2231 }
2232 }
2233}
2234
2243{
2244 /* If none of the tracks end up in the NE corner, return the ground sprite
2245 * where the NE of the tile is not covered. Repeat for the other directions.
2246 * What remains are junctions where all directions are covered. */
2247 if ((track & TRACK_BIT_3WAY_NE) == 0) return 0;
2248 if ((track & TRACK_BIT_3WAY_SW) == 0) return 1;
2249 if ((track & TRACK_BIT_3WAY_NW) == 0) return 2;
2250 if ((track & TRACK_BIT_3WAY_SE) == 0) return 3;
2251 return 4;
2252}
2253
2259static void DrawTrackBits(TileInfo *ti, TrackBits track)
2260{
2261 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2262
2263 if (rti->UsesOverlay()) {
2264 DrawTrackBitsOverlay(ti, track, rti);
2265 return;
2266 }
2267
2269 Foundation f = GetRailFoundation(ti->tileh, track);
2270 Corner halftile_corner = CORNER_INVALID;
2271
2273 /* Save halftile corner */
2275 /* Draw lower part first */
2276 track &= ~CornerToTrackBits(halftile_corner);
2278 }
2279
2280 DrawFoundation(ti, f);
2281 /* DrawFoundation modifies ti */
2282
2283 SpriteID image;
2284 PaletteID pal = PAL_NONE;
2285 const SubSprite *sub = nullptr;
2286 bool junction = false;
2287
2288 /* Select the sprite to use. */
2289 if (track == 0) {
2290 /* Clear ground (only track on halftile foundation) */
2291 if (rgt == RailGroundType::HalfTileWater) {
2292 if (IsSteepSlope(ti->tileh)) {
2293 DrawShoreTile(ti->tileh);
2294 image = 0;
2295 } else {
2296 image = SPR_FLAT_WATER_TILE;
2297 }
2298 } else {
2299 switch (rgt) {
2300 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2301 case RailGroundType::SnowOrDesert: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2302 default: image = SPR_FLAT_GRASS_TILE; break;
2303 }
2304 image += SlopeToSpriteOffset(ti->tileh);
2305 }
2306 } else {
2307 if (ti->tileh != SLOPE_FLAT) {
2308 /* track on non-flat ground */
2309 image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
2310 } else {
2311 /* track on flat ground */
2312 switch (track) {
2313 /* single track, select combined track + ground sprite*/
2314 case TRACK_BIT_Y: image = rti->base_sprites.track_y; break;
2315 case TRACK_BIT_X: image = rti->base_sprites.track_y + 1; break;
2316 case TRACK_BIT_UPPER: image = rti->base_sprites.track_y + 2; break;
2317 case TRACK_BIT_LOWER: image = rti->base_sprites.track_y + 3; break;
2318 case TRACK_BIT_RIGHT: image = rti->base_sprites.track_y + 4; break;
2319 case TRACK_BIT_LEFT: image = rti->base_sprites.track_y + 5; break;
2320 case TRACK_BIT_CROSS: image = rti->base_sprites.track_y + 6; break;
2321
2322 /* double diagonal track, select combined track + ground sprite*/
2323 case TRACK_BIT_HORZ: image = rti->base_sprites.track_ns; break;
2324 case TRACK_BIT_VERT: image = rti->base_sprites.track_ns + 1; break;
2325
2326 /* junction, select only ground sprite, handle track sprite later */
2327 default:
2328 junction = true;
2330 break;
2331 }
2332 }
2333
2334 switch (rgt) {
2336 case RailGroundType::SnowOrDesert: image += rti->snow_offset; break;
2338 /* three-corner-raised slope */
2339 DrawShoreTile(ti->tileh);
2341 sub = &(_halftile_sub_sprite[track_corner]);
2342 break;
2343 }
2344 default: break;
2345 }
2346 }
2347
2348 if (image != 0) DrawGroundSprite(image, pal, sub);
2349
2350 /* Draw track pieces individually for junction tiles */
2351 if (junction) {
2352 if (track & TRACK_BIT_X) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE);
2353 if (track & TRACK_BIT_Y) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE);
2354 if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE);
2355 if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE);
2356 if (track & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
2357 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
2358 }
2359
2360 /* PBS debugging, draw reserved tracks darker */
2361 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) {
2362 /* Get reservation, but mask track on halftile slope */
2363 TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
2364 if (pbs & TRACK_BIT_X) {
2365 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2367 } else {
2368 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2369 }
2370 }
2371 if (pbs & TRACK_BIT_Y) {
2372 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2374 } else {
2375 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2376 }
2377 }
2378 if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_N ? -(int)TILE_HEIGHT : 0);
2379 if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_S ? -(int)TILE_HEIGHT : 0);
2380 if (pbs & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_W ? -(int)TILE_HEIGHT : 0);
2381 if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
2382 }
2383
2384 if (IsValidCorner(halftile_corner)) {
2385 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2386
2387 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2388 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2389 image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y;
2390 pal = PAL_NONE;
2391 switch (rgt) {
2394 case RailGroundType::HalfTileSnow: image += rti->snow_offset; break; // higher part has snow in this case too
2395 default: break;
2396 }
2397 DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
2398
2399 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) {
2400 static const uint8_t _corner_to_track_sprite[] = {3, 1, 2, 0};
2401 DrawGroundSprite(_corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, -(int)TILE_HEIGHT);
2402 }
2403 }
2404}
2405
2406static void DrawSignals(TileIndex tile, TrackBits rails, const RailTypeInfo *rti)
2407{
2408 auto MAYBE_DRAW_SIGNAL = [&](uint8_t signalbit, SignalOffsets image, uint pos, Track track) {
2409 if (IsSignalPresent(tile, signalbit)) DrawSingleSignal(tile, rti, track, GetSingleSignalState(tile, signalbit), image, pos);
2410 };
2411
2412 if (!(rails & TRACK_BIT_Y)) {
2413 if (!(rails & TRACK_BIT_X)) {
2414 if (rails & TRACK_BIT_LEFT) {
2415 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, TRACK_LEFT);
2416 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, TRACK_LEFT);
2417 }
2418 if (rails & TRACK_BIT_RIGHT) {
2419 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, TRACK_RIGHT);
2420 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, TRACK_RIGHT);
2421 }
2422 if (rails & TRACK_BIT_UPPER) {
2423 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, TRACK_UPPER);
2424 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, TRACK_UPPER);
2425 }
2426 if (rails & TRACK_BIT_LOWER) {
2427 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, TRACK_LOWER);
2428 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, TRACK_LOWER);
2429 }
2430 } else {
2431 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, TRACK_X);
2432 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, TRACK_X);
2433 }
2434 } else {
2435 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, TRACK_Y);
2436 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 11, TRACK_Y);
2437 }
2438}
2439
2441static void DrawTile_Rail(TileInfo *ti)
2442{
2443 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2444 BridgePillarFlags blocked_pillars{};
2446
2447 if (IsPlainRail(ti->tile)) {
2448 TrackBits rails = GetTrackBits(ti->tile);
2449
2450 DrawTrackBits(ti, rails);
2451
2453
2455
2456 if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
2457
2458 if (IsBridgeAbove(ti->tile)) {
2459 if ((rails & TRACK_BIT_3WAY_NE) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeNE);
2460 if ((rails & TRACK_BIT_3WAY_SE) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeSE);
2461 if ((rails & TRACK_BIT_3WAY_SW) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeSW);
2462 if ((rails & TRACK_BIT_3WAY_NW) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeNW);
2463 }
2464 } else {
2465 /* draw depot */
2466 const DrawTileSprites *dts;
2468
2470
2472 /* Draw rail instead of depot */
2473 dts = &_depot_invisible_gfx_table[dir];
2474 } else {
2475 dts = &_depot_gfx_table[dir];
2476 }
2477
2478 SpriteID image;
2479 if (rti->UsesOverlay()) {
2480 image = SPR_FLAT_GRASS_TILE;
2481 } else {
2482 image = dts->ground.sprite;
2483 if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
2484 }
2485
2486 /* Adjust ground tile for desert and snow. */
2487 if (IsSnowOrDesertRailGround(ti->tile)) {
2488 if (image != SPR_FLAT_GRASS_TILE) {
2489 image += rti->snow_offset; // tile with tracks
2490 } else {
2491 image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
2492 }
2493 }
2494
2495 DrawGroundSprite(image, GroundSpritePaletteTransform(image, PAL_NONE, pal));
2496
2497 if (rti->UsesOverlay()) {
2499
2500 switch (GetRailDepotDirection(ti->tile)) {
2501 case DIAGDIR_NE:
2502 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2503 [[fallthrough]];
2504 case DIAGDIR_SW:
2505 DrawGroundSprite(ground + RTO_X, PAL_NONE);
2506 break;
2507 case DIAGDIR_NW:
2508 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2509 [[fallthrough]];
2510 case DIAGDIR_SE:
2511 DrawGroundSprite(ground + RTO_Y, PAL_NONE);
2512 break;
2513 default:
2514 break;
2515 }
2516
2517 if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2519
2520 switch (GetRailDepotDirection(ti->tile)) {
2521 case DIAGDIR_NE:
2522 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2523 [[fallthrough]];
2524 case DIAGDIR_SW:
2526 break;
2527 case DIAGDIR_NW:
2528 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2529 [[fallthrough]];
2530 case DIAGDIR_SE:
2532 break;
2533 default:
2534 break;
2535 }
2536 }
2537 } else {
2538 /* PBS debugging, draw reserved tracks darker */
2539 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2540 switch (GetRailDepotDirection(ti->tile)) {
2541 case DIAGDIR_NE:
2542 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2543 [[fallthrough]];
2544 case DIAGDIR_SW:
2546 break;
2547 case DIAGDIR_NW:
2548 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2549 [[fallthrough]];
2550 case DIAGDIR_SE:
2552 break;
2553 default:
2554 break;
2555 }
2556 }
2557 }
2558 int depot_sprite = GetCustomRailSprite(rti, ti->tile, RailSpriteType::Depot);
2559 int relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
2560
2562
2563 DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, pal);
2564 /* Depots can't have bridges above so no blocked pillars. */
2565 }
2566 DrawBridgeMiddle(ti, blocked_pillars);
2567}
2568
2569void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype)
2570{
2571 const DrawTileSprites *dts = &_depot_gfx_table[dir];
2572 const RailTypeInfo *rti = GetRailTypeInfo(railtype);
2573 SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
2574 uint32_t offset = rti->GetRailtypeSpriteOffset();
2575
2576 if (image != SPR_FLAT_GRASS_TILE) image += offset;
2578
2579 DrawSprite(image, PAL_NONE, x, y);
2580
2581 if (rti->UsesOverlay()) {
2583
2584 switch (dir) {
2585 case DIAGDIR_SW: DrawSprite(ground + RTO_X, PAL_NONE, x, y); break;
2586 case DIAGDIR_SE: DrawSprite(ground + RTO_Y, PAL_NONE, x, y); break;
2587 default: break;
2588 }
2589 }
2590 int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RailSpriteType::Depot);
2591 if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
2592
2593 DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette);
2594}
2595
2597static int GetSlopePixelZ_Rail(TileIndex tile, uint x, uint y, [[maybe_unused]] bool ground_vehicle)
2598{
2599 if (IsPlainRail(tile)) {
2600 auto [tileh, z] = GetTilePixelSlope(tile);
2601 if (tileh == SLOPE_FLAT) return z;
2602
2604 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
2605 } else {
2606 return GetTileMaxPixelZ(tile);
2607 }
2608}
2609
2612{
2613 return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
2614}
2615
2617static void TileLoop_Rail(TileIndex tile)
2618{
2619 RailGroundType old_ground = GetRailGroundType(tile);
2620 RailGroundType new_ground;
2621
2622 if (old_ground == RailGroundType::HalfTileWater) {
2623 TileLoop_Water(tile);
2624 return;
2625 }
2626
2627 switch (_settings_game.game_creation.landscape) {
2628 case LandscapeType::Arctic: {
2629 auto [slope, z] = GetTileSlopeZ(tile);
2630 bool half = false;
2631
2632 /* for non-flat track, use lower part of track
2633 * in other cases, use the highest part with track */
2634 if (IsPlainRail(tile)) {
2635 TrackBits track = GetTrackBits(tile);
2636 Foundation f = GetRailFoundation(slope, track);
2637
2638 switch (f) {
2639 case FOUNDATION_NONE:
2640 /* no foundation - is the track on the upper side of three corners raised tile? */
2641 if (IsSlopeWithThreeCornersRaised(slope)) z++;
2642 break;
2643
2646 /* sloped track - is it on a steep slope? */
2647 if (IsSteepSlope(slope)) z++;
2648 break;
2649
2651 /* only lower part of steep slope */
2652 z++;
2653 break;
2654
2655 default:
2656 /* if it is a steep slope, then there is a track on higher part */
2657 if (IsSteepSlope(slope)) z++;
2658 z++;
2659 break;
2660 }
2661
2663 } else {
2664 /* is the depot on a non-flat tile? */
2665 if (slope != SLOPE_FLAT) z++;
2666 }
2667
2668 /* 'z' is now the lowest part of the highest track bit -
2669 * for sloped track, it is 'z' of lower part
2670 * for two track bits, it is 'z' of higher track bit
2671 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
2672 if (z > GetSnowLine()) {
2673 if (half && z - GetSnowLine() == 1) {
2674 /* track on non-continuous foundation, lower part is not under snow */
2675 new_ground = RailGroundType::HalfTileSnow;
2676 } else {
2677 new_ground = RailGroundType::SnowOrDesert;
2678 }
2679 goto set_ground;
2680 }
2681 break;
2682 }
2683
2685 if (GetTropicZone(tile) == TropicZone::Desert) {
2686 new_ground = RailGroundType::SnowOrDesert;
2687 goto set_ground;
2688 }
2689 break;
2690
2691 default:
2692 break;
2693 }
2694
2695 new_ground = RailGroundType::Grass;
2696
2697 if (IsPlainRail(tile) && old_ground != RailGroundType::Barren) { // wait until bottom is green
2698 /* determine direction of fence */
2699 TrackBits rail = GetTrackBits(tile);
2700
2701 Owner owner = GetTileOwner(tile);
2702 DiagDirections fences{};
2703
2704 for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
2706
2707 /* Track bit on this edge => no fence. */
2708 if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue;
2709
2710 TileIndex tile2 = tile + TileOffsByDiagDir(d);
2711
2712 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
2713 if (!IsValidTile(tile2) || IsTileType(tile2, TileType::House) || IsTileType(tile2, TileType::Industry) ||
2715 fences.Set(d);
2716 }
2717 }
2718
2719 switch (fences.base()) {
2720 case DiagDirections{}.base(): break;
2721 case DiagDirections{DIAGDIR_NE}.base(): new_ground = RailGroundType::FenceNE; break;
2722 case DiagDirections{DIAGDIR_SE}.base(): new_ground = RailGroundType::FenceSE; break;
2723 case DiagDirections{DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceSW; break;
2724 case DiagDirections{DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceNW; break;
2725 case DiagDirections{DIAGDIR_NE, DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceNESW; break;
2726 case DiagDirections{DIAGDIR_SE, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceSENW; break;
2727 case DiagDirections{DIAGDIR_NE, DIAGDIR_SE}.base(): new_ground = RailGroundType::FenceVert1; break;
2728 case DiagDirections{DIAGDIR_NE, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceHoriz2; break;
2729 case DiagDirections{DIAGDIR_SE, DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceHoriz1; break;
2730 case DiagDirections{DIAGDIR_SW, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceVert2; break;
2731 default: NOT_REACHED();
2732 }
2733 }
2734
2735set_ground:
2736 if (old_ground != new_ground) {
2737 SetRailGroundType(tile, new_ground);
2738 MarkTileDirtyByTile(tile);
2739 }
2740}
2741
2742
2744static TrackStatus GetTileTrackStatus_Rail(TileIndex tile, TransportType mode, [[maybe_unused]] RoadTramType sub_mode, DiagDirection side)
2745{
2746 /* Case of half tile slope with water. */
2748 TrackBits tb = GetTrackBits(tile);
2749 switch (tb) {
2750 default: NOT_REACHED();
2751 case TRACK_BIT_UPPER: tb = TRACK_BIT_LOWER; break;
2752 case TRACK_BIT_LOWER: tb = TRACK_BIT_UPPER; break;
2753 case TRACK_BIT_LEFT: tb = TRACK_BIT_RIGHT; break;
2754 case TRACK_BIT_RIGHT: tb = TRACK_BIT_LEFT; break;
2755 }
2757 }
2758
2759 if (mode != TRANSPORT_RAIL) return 0;
2760
2761 TrackBits trackbits = TRACK_BIT_NONE;
2762 TrackdirBits red_signals = TRACKDIR_BIT_NONE;
2763
2764 switch (GetRailTileType(tile)) {
2765 default: NOT_REACHED();
2767 trackbits = GetTrackBits(tile);
2768 break;
2769
2770 case RailTileType::Signals: {
2771 trackbits = GetTrackBits(tile);
2772 uint8_t a = GetPresentSignals(tile);
2773 uint b = GetSignalStates(tile);
2774
2775 b &= a;
2776
2777 /* When signals are not present (in neither direction),
2778 * we pretend them to be green. Otherwise, it depends on
2779 * the signal type. For signals that are only active from
2780 * one side, we set the missing signals explicitly to
2781 * `green'. Otherwise, they implicitly become `red'. */
2782 if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER);
2783 if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER);
2784
2785 if ((b & 0x8) == 0) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E);
2786 if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W);
2787 if ((b & 0x2) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_E);
2788 if ((b & 0x1) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_W);
2789
2790 break;
2791 }
2792
2793 case RailTileType::Depot: {
2795
2796 if (side != INVALID_DIAGDIR && side != dir) break;
2797
2798 trackbits = DiagDirToDiagTrackBits(dir);
2799 break;
2800 }
2801 }
2802
2803 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), red_signals);
2804}
2805
2807static bool ClickTile_Rail(TileIndex tile)
2808{
2809 if (!IsRailDepot(tile)) return false;
2810
2812 return true;
2813}
2814
2817{
2818 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
2819 td.rail_speed = rti->max_speed;
2820 td.railtype = rti->strings.name;
2821 td.owner[0] = GetTileOwner(tile);
2822 switch (GetRailTileType(tile)) {
2824 td.str = STR_LAI_RAIL_DESCRIPTION_TRACK;
2825 break;
2826
2827 case RailTileType::Signals: {
2828 static const StringID signal_type[6][6] = {
2829 {
2830 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
2831 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2832 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2833 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2834 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2835 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
2836 },
2837 {
2838 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2839 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS,
2840 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2841 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2842 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2843 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
2844 },
2845 {
2846 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2847 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2848 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
2849 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2850 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2851 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
2852 },
2853 {
2854 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2855 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2856 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2857 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
2858 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2859 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
2860 },
2861 {
2862 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2863 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2864 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2865 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2866 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
2867 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
2868 },
2869 {
2870 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
2871 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
2872 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
2873 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
2874 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
2875 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
2876 }
2877 };
2878
2879 SignalType primary_signal;
2880 SignalType secondary_signal;
2881 if (HasSignalOnTrack(tile, TRACK_UPPER)) {
2882 primary_signal = GetSignalType(tile, TRACK_UPPER);
2883 secondary_signal = HasSignalOnTrack(tile, TRACK_LOWER) ? GetSignalType(tile, TRACK_LOWER) : primary_signal;
2884 } else {
2885 secondary_signal = primary_signal = GetSignalType(tile, TRACK_LOWER);
2886 }
2887
2888 td.str = signal_type[secondary_signal][primary_signal];
2889 break;
2890 }
2891
2893 td.str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
2894 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) {
2895 if (td.rail_speed > 0) {
2896 td.rail_speed = std::min<uint16_t>(td.rail_speed, 61);
2897 } else {
2898 td.rail_speed = 61;
2899 }
2900 }
2901 td.build_date = Depot::GetByTile(tile)->build_date;
2902 break;
2903
2904 default:
2905 NOT_REACHED();
2906 }
2907}
2908
2910static void ChangeTileOwner_Rail(TileIndex tile, Owner old_owner, Owner new_owner)
2911{
2912 if (!IsTileOwner(tile, old_owner)) return;
2913
2914 if (new_owner != INVALID_OWNER) {
2915 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
2916 uint num_pieces = 1;
2917 if (IsPlainRail(tile)) {
2918 TrackBits bits = GetTrackBits(tile);
2919 num_pieces = CountBits(bits);
2920 if (TracksOverlap(bits)) num_pieces *= num_pieces;
2921 }
2922 RailType rt = GetRailType(tile);
2923 Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;
2924 Company::Get(new_owner)->infrastructure.rail[rt] += num_pieces;
2925
2926 if (HasSignals(tile)) {
2927 uint num_sigs = CountBits(GetPresentSignals(tile));
2928 Company::Get(old_owner)->infrastructure.signal -= num_sigs;
2929 Company::Get(new_owner)->infrastructure.signal += num_sigs;
2930 }
2931
2932 SetTileOwner(tile, new_owner);
2933 } else {
2934 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
2935 }
2936}
2937
2938static const uint8_t _fractcoords_behind[4] = { 0x8F, 0x8, 0x80, 0xF8 };
2939static const uint8_t _fractcoords_enter[4] = { 0x8A, 0x48, 0x84, 0xA8 };
2940static const int8_t _deltacoord_leaveoffset[8] = {
2941 -1, 0, 1, 0, /* x */
2942 0, 1, 0, -1 /* y */
2943};
2944
2945
2953{
2955 int length = v->CalcNextVehicleOffset();
2956
2957 switch (dir) {
2958 case DIAGDIR_NE: return ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1)));
2959 case DIAGDIR_SE: return -((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) + (length + 1)));
2960 case DIAGDIR_SW: return -((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) + (length + 1)));
2961 case DIAGDIR_NW: return ((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) - (length + 1)));
2962 default: NOT_REACHED();
2963 }
2964}
2965
2967static VehicleEnterTileStates VehicleEnterTile_Rail(Vehicle *v, TileIndex tile, int x, int y)
2968{
2969 /* This routine applies only to trains in depot tiles. */
2970 if (v->type != VEH_TRAIN || !IsRailDepotTile(tile)) return {};
2971
2972 /* Depot direction. */
2974
2975 uint8_t fract_coord = (x & 0xF) + ((y & 0xF) << 4);
2976
2977 /* Make sure a train is not entering the tile from behind. */
2978 if (_fractcoords_behind[dir] == fract_coord) return VehicleEnterTileState::CannotEnter;
2979
2980 /* Leaving depot? */
2981 if (v->GetMovingDirection() == DiagDirToDir(dir)) {
2982 /* Calculate the point where the following wagon should be activated. */
2983 int length = Train::From(v)->CalcNextVehicleOffset();
2984
2985 uint8_t fract_coord_leave =
2986 ((_fractcoords_enter[dir] & 0x0F) + // x
2987 (length + 1) * _deltacoord_leaveoffset[dir]) +
2988 (((_fractcoords_enter[dir] >> 4) + // y
2989 ((length + 1) * _deltacoord_leaveoffset[dir + 4])) << 4);
2990
2991 if (fract_coord_leave == fract_coord) {
2992 /* Leave the depot. */
2993 if ((v = v->GetMovingNext()) != nullptr) {
2996 }
2997 }
2998 } else if (_fractcoords_enter[dir] == fract_coord) {
2999 /* Entering depot. */
3000 assert(DiagDirToDir(ReverseDiagDir(dir)) == v->GetMovingDirection());
3003 if (v->GetMovingNext() == nullptr) {
3004 Train *consist = Train::From(v)->First();
3006 /* Trains always drive forwards out of a depot.
3007 * This allows a player to easily reset a confused train,
3008 * and matches the behaviour of the \c VehicleRailFlag::Reversed variable. */
3010 } else {
3011 for (Train *u = consist; u != nullptr; u = u->Next()) {
3012 u->direction = ReverseDir(u->direction);
3013 }
3014 }
3015 VehicleEnterDepot(consist);
3016 }
3017 v->tile = tile;
3018
3021 }
3022
3023 return {};
3024}
3025
3038static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
3039{
3040 if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3041
3042 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3043 if (CheckRailSlope(tileh_new, rail_bits, TRACK_BIT_NONE, tile).Failed()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3044
3045 /* Get the slopes on top of the foundations */
3046 z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), tileh_old);
3047 z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), tileh_new);
3048
3049 Corner track_corner;
3050 switch (rail_bits) {
3051 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
3052 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
3053 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
3054 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
3055
3056 /* Surface slope must not be changed */
3057 default:
3058 if (z_old != z_new || tileh_old != tileh_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3060 }
3061
3062 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3063 z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
3064 z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
3065 if (z_old != z_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3066
3068 /* Make the ground dirty, if surface slope has changed */
3069 if (tileh_old != tileh_new) {
3070 /* If there is flat water on the lower halftile add the cost for clearing it */
3073 }
3074 return cost;
3075}
3076
3078static CommandCost TerraformTile_Rail(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
3079{
3080 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
3081 if (IsPlainRail(tile)) {
3082 TrackBits rail_bits = GetTrackBits(tile);
3083 /* Is there flat water on the lower halftile that must be cleared expensively? */
3084 bool was_water = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh_old));
3085
3086 /* Allow clearing the water only if there is no ship */
3087 if (was_water && HasVehicleOnTile(tile, [](const Vehicle *v) {
3088 return v->type == VEH_SHIP;
3089 })) return CommandCost(STR_ERROR_SHIP_IN_THE_WAY);
3090
3091 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3092 CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
3093
3094 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3095 Corner allowed_corner;
3096 switch (rail_bits) {
3097 case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break;
3098 case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break;
3099 case TRACK_BIT_LEFT: allowed_corner = CORNER_E; break;
3100 case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break;
3101 default: return autoslope_result;
3102 }
3103
3104 Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
3105
3106 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3107 if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
3108
3109 /* Everything is valid, which only changes allowed_corner */
3110 for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
3111 if (allowed_corner == corner) continue;
3112 if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
3113 }
3114
3115 /* Make the ground dirty */
3117
3118 /* allow terraforming */
3119 return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price[Price::ClearWater] : (Money)0);
3120 } else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
3121 AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
3123 }
3124 return Command<Commands::LandscapeClear>::Do(flags, tile);
3125}
3126
3128static CommandCost CheckBuildAbove_Rail(TileIndex tile, DoCommandFlags flags, [[maybe_unused]] Axis axis, [[maybe_unused]] int height)
3129{
3130 if (IsPlainRail(tile)) return CommandCost();
3131 return Command<Commands::LandscapeClear>::Do(flags, tile);
3132}
3133
3135extern const TileTypeProcs _tile_type_rail_procs = {
3136 .draw_tile_proc = DrawTile_Rail,
3137 .get_slope_pixel_z_proc = GetSlopePixelZ_Rail,
3138 .clear_tile_proc = ClearTile_Rail,
3139 .get_tile_desc_proc = GetTileDesc_Rail,
3140 .get_tile_track_status_proc = GetTileTrackStatus_Rail,
3141 .click_tile_proc = ClickTile_Rail,
3142 .tile_loop_proc = TileLoop_Rail,
3143 .change_tile_owner_proc = ChangeTileOwner_Rail,
3144 .vehicle_enter_tile_proc = VehicleEnterTile_Rail,
3145 .get_foundation_proc = GetFoundation_Rail,
3146 .terraform_tile_proc = TerraformTile_Rail,
3147 .check_build_above_proc = CheckBuildAbove_Rail,
3148};
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 ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
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.
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
@ 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 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.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:115
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:125
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:220
SpriteID build_tunnel
button for building a tunnel
Definition rail.h:148
CursorID rail_swne
Cursor for building rail in X direction.
Definition rail.h:155
SpriteID convert_rail
button for converting rail
Definition rail.h:149
struct RailTypeInfo::@157247141350136173143103254227157213063052244122 strings
Strings associated with the rail type.
CursorID convert
Cursor for converting track.
Definition rail.h:161
CursorID depot
Cursor for building a depot.
Definition rail.h:159
RailTypes powered_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype generates power
Definition rail.h:177
RailTypes introduces_railtypes
Bitmask of which other railtypes are introduced when this railtype is introduced.
Definition rail.h:255
SpriteID ground
ground sprite for a 3-way switch
Definition rail.h:124
CursorID rail_nwse
Cursor for building rail in Y direction.
Definition rail.h:157
SpriteID build_x_rail
button for building single rail in X direction
Definition rail.h:143
uint8_t sorting_order
The sorting order of this railtype for the toolbar dropdown.
Definition rail.h:260
RailTypeLabel label
Unique 32 bit rail type identifier.
Definition rail.h:225
SpriteID single_n
single piece of rail in the northern corner
Definition rail.h:127
CursorID rail_ew
Cursor for building rail in E-W direction.
Definition rail.h:156
SpriteID auto_rail
button for the autorail construction
Definition rail.h:146
CursorID autorail
Cursor for autorail tool.
Definition rail.h:158
SpriteID single_y
single piece of rail in Y direction, without ground
Definition rail.h:126
StringID name
Name of this rail type.
Definition rail.h:165
RailTypes compatible_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype can physically travel
Definition rail.h:180
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:287
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:128
RailTypeFlags flags
Bit mask of rail type flags.
Definition rail.h:200
SpriteID signals[SIGTYPE_END][2][2]
signal GUI sprites (type, variant, state)
Definition rail.h:150
SpriteID build_ew_rail
button for building single rail in E-W direction
Definition rail.h:144
SpriteID build_y_rail
button for building single rail in Y direction
Definition rail.h:145
FlatSet< RailTypeLabel > alternate_labels
Rail type labels this type provides in addition to the main label.
Definition rail.h:230
SpriteID track_ns
two pieces of rail in North and South corner (East-West direction)
Definition rail.h:123
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:174
SpriteID track_y
single piece of rail in Y direction, with ground
Definition rail.h:122
SpriteID build_depot
button for building depots
Definition rail.h:147
SpriteID single_w
single piece of rail in the western corner
Definition rail.h:130
SpriteID single_e
single piece of rail in the eastern corner
Definition rail.h:129
SpriteID build_ns_rail
button for building single rail in N-S direction
Definition rail.h:142
CursorID rail_ns
Cursor for building rail in N-S direction.
Definition rail.h:154
SpriteID tunnel
tunnel sprites base
Definition rail.h:133
SpriteID single_sloped
single piece of rail for slopes
Definition rail.h:131
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.
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
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.
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.
Axis
Allow incrementing of DiagDirDiff variables.
@ AXIS_X
The X axis.
@ AXIS_Y
The y axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_END
Used for iterations.
@ DIAGDIR_BEGIN
Used for iterations.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
@ DIAGDIR_SW
Southwest.
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.
@ EXPENSES_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:559
Header file for electrified rail specific functions.
bool HasRailCatenaryDrawn(RailType rt)
Test if we should draw rail catenary.
Definition elrail_func.h:32
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:1038
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.
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 bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
@ RailTypes
Rail types feature.
Definition newgrf.h:89
@ TCX_UPPER_HALFTILE
Querying information about the upper part of a tile with halftile foundation.
@ TCX_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
@ DO_FULL_DETAIL
Also draw details of track and roads.
Definition openttd.h:50
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:457
@ Ground
Main group of ground images.
Definition rail.h:43
@ GroundComplete
Complete ground images.
Definition rail.h:53
@ Overlay
Images for overlaying track.
Definition rail.h:42
@ UI
Cursor and toolbar icon images.
Definition rail.h:41
@ Depot
Depot images.
Definition rail.h:49
@ Fences
Fence images.
Definition rail.h:50
Money RailClearCost(RailType railtype)
Returns the 'cost' of clearing the specified railtype.
Definition rail.h:440
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:429
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:93
@ RFO_FLAT_RIGHT
Slope FLAT, Track RIGHT, Fence W.
Definition rail.h:104
@ RFO_FLAT_Y_NE
Slope FLAT, Track Y, Fence NE.
Definition rail.h:95
@ RFO_SLOPE_NW_SW
Slope NW, Track Y, Fence SW.
Definition rail.h:109
@ RFO_FLAT_X_SE
Slope FLAT, Track X, Fence SE.
Definition rail.h:102
@ RFO_SLOPE_NW_NE
Slope NW, Track Y, Fence NE.
Definition rail.h:101
@ RFO_SLOPE_SE_SW
Slope SE, Track Y, Fence SW.
Definition rail.h:107
@ RFO_SLOPE_NE_SE
Slope NE, Track X, Fence SE.
Definition rail.h:108
@ RFO_FLAT_LOWER
Slope FLAT, Track LOWER, Fence N.
Definition rail.h:105
@ RFO_SLOPE_SW_SE
Slope SW, Track X, Fence SE.
Definition rail.h:106
@ RFO_FLAT_UPPER
Slope FLAT, Track UPPER, Fence S.
Definition rail.h:97
@ RFO_SLOPE_SE_NE
Slope SE, Track Y, Fence NE.
Definition rail.h:99
@ RFO_FLAT_Y_SW
Slope FLAT, Track Y, Fence SW.
Definition rail.h:103
@ RFO_FLAT_LEFT
Slope FLAT, Track LEFT, Fence E.
Definition rail.h:96
@ RFO_SLOPE_NE_NW
Slope NE, Track X, Fence NW.
Definition rail.h:100
@ RFO_FLAT_X_NW
Slope FLAT, Track X, Fence NW.
Definition rail.h:94
@ RFO_SLOPE_SW_NW
Slope SW, Track X, Fence NW.
Definition rail.h:98
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:352
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:377
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:301
@ 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:63
@ RTO_S
Piece of rail in southern corner.
Definition rail.h:65
@ RTO_JUNCTION_NE
Ballast for junction 'pointing' NE.
Definition rail.h:74
@ RTO_JUNCTION_NSEW
Ballast for full junction.
Definition rail.h:77
@ RTO_JUNCTION_SW
Ballast for junction 'pointing' SW.
Definition rail.h:73
@ RTO_SLOPE_SE
Piece of rail on slope with south-east raised.
Definition rail.h:69
@ RTO_E
Piece of rail in eastern corner.
Definition rail.h:66
@ RTO_W
Piece of rail in western corner.
Definition rail.h:67
@ RTO_CROSSING_XY
Crossing of X and Y rail, with ballast.
Definition rail.h:72
@ RTO_SLOPE_SW
Piece of rail on slope with south-west raised.
Definition rail.h:70
@ RTO_SLOPE_NW
Piece of rail on slope with north-west raised.
Definition rail.h:71
@ RTO_JUNCTION_NW
Ballast for junction 'pointing' NW.
Definition rail.h:76
@ RTO_JUNCTION_SE
Ballast for junction 'pointing' SE.
Definition rail.h:75
@ RTO_SLOPE_NE
Piece of rail on slope with north-east raised.
Definition rail.h:68
@ RTO_X
Piece of rail in X direction.
Definition rail.h:62
@ RTO_N
Piece of rail in northern corner.
Definition rail.h:64
bool RailNoLevelCrossings(RailType rt)
Test if a RailType disallows build of level crossings.
Definition rail.h:399
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 CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build)
Check that the new track bits may be built.
Definition rail_cmd.cpp:240
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:875
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:960
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:932
CommandCost CmdBuildSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool autofill, bool minimise_gaps, uint8_t signal_density)
Build signals on a stretch of track.
static CommandCost ClearTile_Rail(TileIndex tile, DoCommandFlags flags)
Tile callback function signature for clearing a tile.
bool FloodHalftile(TileIndex t)
Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
Definition rail_cmd.cpp:761
RailType AllocateRailType(RailTypeLabel label)
Allocate a new rail type label.
Definition rail_cmd.cpp:150
static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
Tests if a track can be build on a tile.
Definition rail_cmd.cpp:391
CommandCost CmdRemoveSingleRail(DoCommandFlags flags, TileIndex tile, Track track)
Remove a single piece of track.
Definition rail_cmd.cpp:614
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:261
static const TrackBits _valid_tracks_on_leveled_foundation[15]
Valid TrackBits on a specific (non-steep)-slope with leveled foundation.
Definition rail_cmd.cpp:283
static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
Tests if a vehicle interacts with the specified track.
Definition rail_cmd.cpp:228
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:947
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:425
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 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 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 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:311
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:421
void MakeRailDepot(Tile tile, Owner owner, DepotID depot_id, DiagDirection dir, RailType rail_type)
Make a rail depot.
Definition rail_map.h:647
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:491
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:386
RailGroundType GetRailGroundType(Tile t)
Get the ground type for rail tiles.
Definition rail_map.h:591
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:292
void CycleSignalSide(Tile t, Track track)
Cycle to the next signal side at the given track on a tile.
Definition rail_map.h:369
void MakeRailNormal(Tile t, Owner o, TrackBits b, RailType r)
Make the given tile a normal rail.
Definition rail_map.h:614
@ 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:463
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:558
@ FenceSENW
Grass with a fence at the NW and SE edges.
Definition rail_map.h:563
@ FenceVert2
Grass with a fence at the western side.
Definition rail_map.h:568
@ HalfTileSnow
Snow only on higher part of slope (steep or one corner raised).
Definition rail_map.h:573
@ Barren
Nothing (dirt).
Definition rail_map.h:559
@ FenceNESW
Grass with a fence at the NE and SW edges.
Definition rail_map.h:566
@ FenceHoriz2
Grass with a fence at the northern side.
Definition rail_map.h:570
@ SnowOrDesert
Icy or sandy.
Definition rail_map.h:571
@ HalfTileWater
Grass with a fence and shore or water on the free halftile.
Definition rail_map.h:572
@ FenceVert1
Grass with a fence at the eastern side.
Definition rail_map.h:567
@ FenceNW
Grass with a fence at the NW edge.
Definition rail_map.h:561
@ FenceSE
Grass with a fence at the SE edge.
Definition rail_map.h:562
@ FenceNE
Grass with a fence at the NE edge.
Definition rail_map.h:564
@ Grass
Grassy.
Definition rail_map.h:560
@ FenceSW
Grass with a fence at the SW edge.
Definition rail_map.h:565
@ FenceHoriz1
Grass with a fence at the southern side.
Definition rail_map.h:569
uint GetPresentSignals(Tile tile)
Get whether the given signals are present (Along/AgainstTrackDir).
Definition rail_map.h:452
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:475
bool IsOnewaySignal(Tile t, Track track)
Is the signal at the given track on a tile a one way signal?
Definition rail_map.h:358
void SetPresentSignals(Tile tile, uint signals)
Set whether the given signals are present (Along/AgainstTrackDir).
Definition rail_map.h:442
void SetRailGroundType(Tile t, RailGroundType rgt)
Set the ground type for rail tiles.
Definition rail_map.h:581
void SetRailDepotExitDirection(Tile tile, DiagDirection dir)
Sets the exit direction of a rail depot.
Definition rail_map.h:633
SignalType GetSignalType(Tile t, Track track)
Get the signal type for a track on a tile.
Definition rail_map.h:304
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:318
bool HasDepotReservation(Tile t)
Get the reservation state of the depot.
Definition rail_map.h:257
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:601
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:399
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:432
void SetSignalStates(Tile tile, uint state)
Set the states of the signals (Along/AgainstTrackDir).
Definition rail_map.h:411
void SetRailType(Tile t, RailType r)
Sets the rail type of the given tile.
Definition rail_map.h:125
EnumBitSet< RailType, uint64_t > RailTypes
Allow incrementing of Track variables.
Definition rail_type.h:38
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:283
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:240
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
TrackBits GetCrossingRailBits(Tile tile)
Get the rail track bits of a level crossing.
Definition road_map.h:378
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:646
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:672
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:519
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
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:596
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:628
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:23
@ SIGTYPE_ENTRY
presignal block entry
Definition signal_type.h:25
@ SIGTYPE_EXIT
presignal block exit
Definition signal_type.h:26
@ SIGTYPE_BLOCK
block signal
Definition signal_type.h:24
SignalState
These are states in which a signal can be.
Definition signal_type.h:42
@ SIGNAL_STATE_RED
The signal is red.
Definition signal_type.h:43
@ SIGNAL_STATE_GREEN
The signal is green.
Definition signal_type.h:44
SignalVariant
Variant of the signal, i.e.
Definition signal_type.h:16
@ SIG_SEMAPHORE
Old-fashioned semaphore signal.
Definition signal_type.h:18
@ SIG_ELECTRIC
Light signal.
Definition signal_type.h:17
bool IsSpecialRailFoundation(Foundation f)
Tests if a foundation is a special rail foundation for single horizontal/vertical track.
Definition slope_func.h:345
Slope SlopeWithThreeCornersRaised(Corner corner)
Returns the slope with all except one corner raised.
Definition slope_func.h:206
Corner OppositeCorner(Corner corner)
Returns the opposite corner.
Definition slope_func.h:184
static constexpr Corner GetHalftileSlopeCorner(Slope s)
Returns the leveled halftile of a halftile slope.
Definition slope_func.h:148
static constexpr Slope RemoveHalftileSlope(Slope s)
Removes a halftile slope from a slope.
Definition slope_func.h:60
bool IsSlopeWithOneCornerRaised(Slope s)
Tests if a specific slope has exactly one corner raised.
Definition slope_func.h:88
uint SlopeToSpriteOffset(Slope s)
Returns the Sprite offset for a given Slope.
Definition slope_func.h:415
bool IsNonContinuousFoundation(Foundation f)
Tests if a foundation is a non-continuous foundation, i.e.
Definition slope_func.h:320
Corner GetHighestSlopeCorner(Slope s)
Returns the highest corner of a slope (one corner raised or a steep slope).
Definition slope_func.h:126
Corner GetHalftileFoundationCorner(Foundation f)
Returns the halftile corner of a halftile-foundation.
Definition slope_func.h:333
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:195
Foundation FlatteningFoundation(Slope s)
Returns the foundation needed to flatten a slope.
Definition slope_func.h:369
static constexpr bool IsHalftileSlope(Slope s)
Checks for non-continuous slope on halftile foundations.
Definition slope_func.h:47
Foundation HalftileFoundation(Corner corner)
Returns the halftile foundation for single horizontal/vertical track.
Definition slope_func.h:391
Foundation SpecialRailFoundation(Corner corner)
Returns the special rail foundation for single horizontal/vertical track.
Definition slope_func.h:403
Slope ComplementSlope(Slope s)
Return the complement of a slope.
Definition slope_func.h:76
Slope SlopeWithOneCornerRaised(Corner corner)
Returns the slope with a specific corner raised.
Definition slope_func.h:99
Corner
Enumeration of tile corners.
Definition slope_type.h:21
Slope
Enumeration for the slope-type.
Definition slope_type.h:47
@ SLOPE_W
the west corner of the tile is raised
Definition slope_type.h:49
@ SLOPE_ELEVATED
bit mask containing all 'simple' slopes
Definition slope_type.h:60
@ SLOPE_NS
north and south corner are raised
Definition slope_type.h:59
@ SLOPE_E
the east corner of the tile is raised
Definition slope_type.h:51
@ SLOPE_S
the south corner of the tile is raised
Definition slope_type.h:50
@ SLOPE_N
the north corner of the tile is raised
Definition slope_type.h:52
@ SLOPE_SW
south and west corner are raised
Definition slope_type.h:55
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:48
@ SLOPE_NE
north and east corner are raised
Definition slope_type.h:57
@ SLOPE_SE
south and east corner are raised
Definition slope_type.h:56
@ SLOPE_NW
north and west corner are raised
Definition slope_type.h:54
@ SLOPE_EW
east and west corner are raised
Definition slope_type.h:58
Foundation
Enumeration for Foundations.
Definition slope_type.h:92
@ FOUNDATION_LEVELED
The tile is leveled up to a flat slope.
Definition slope_type.h:94
@ FOUNDATION_NONE
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:93
@ FOUNDATION_INCLINED_X
The tile has an along X-axis inclined foundation.
Definition slope_type.h:95
@ FOUNDATION_STEEP_BOTH
The tile has a steep slope. The lowest corner is raised by a foundation and the upper halftile is lev...
Definition slope_type.h:100
@ FOUNDATION_INCLINED_Y
The tile has an along Y-axis inclined foundation.
Definition slope_type.h:96
@ FOUNDATION_STEEP_LOWER
The tile has a steep slope. The lowest corner is raised by a foundation to allow building railroad on...
Definition slope_type.h:97
@ FOUNDATION_HALFTILE_N
Level north halftile non-continuously.
Definition slope_type.h:104
@ FOUNDATION_INVALID
Used inside "rail_cmd.cpp" to indicate invalid slope/track combination.
Definition slope_type.h:112
static const uint32_t VALID_LEVEL_CROSSING_SLOPES
Constant bitset with safe slopes for building a level crossing.
Definition slope_type.h:85
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 PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
static const PaletteID PALETTE_TO_BARE_LAND
sets colour to bare land stuff for rail, road and crossings
Definition sprites.h:1607
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.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
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 later.
void Restore()
Restore the variable.
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.
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 Company * Get(auto index)
static bool CanAllocateItem(size_t n=1)
static T * Create(Targs &&... args)
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:39
uint16_t rail_speed
Speed limit of rail (bridges and track).
Definition tile_cmd.h:52
StringID str
Description of the tile.
Definition tile_cmd.h:40
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:44
std::array< Owner, 4 > owner
Name of the owner(s).
Definition tile_cmd.h:42
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:51
A pair-construct of a TileIndexDiff.
Definition map_type.h:31
Tile information, used while rendering the tile.
Definition tile_cmd.h:33
Slope tileh
Slope of the tile.
Definition tile_cmd.h:34
TileIndex tile
Tile index.
Definition tile_cmd.h:35
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:213
'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
std::tuple< Slope, int > GetTileSlopeZ(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.cpp:55
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
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:333
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:279
Trackdir RemoveFirstTrackdir(TrackdirBits *trackdirs)
Removes first Trackdir from TrackdirBits and returns it.
Definition track_func.h:156
Track TrackdirToTrack(Trackdir trackdir)
Returns the Track that a given Trackdir represents.
Definition track_func.h:262
TrackBits CornerToTrackBits(Corner corner)
Returns a single horizontal/vertical trackbit that is in a specific tile corner.
Definition track_func.h:99
TrackBits TrackToTrackBits(Track track)
Maps a Track to the corresponding TrackBits value.
Definition track_func.h:77
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
TrackdirBits TrackStatusToTrackdirBits(TrackStatus ts)
Returns the present-trackdir-information of a TrackStatus.
Definition track_func.h:354
Track TrackBitsToTrack(TrackBits tracks)
Converts TrackBits to Track.
Definition track_func.h:193
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:247
TrackStatus CombineTrackStatus(TrackdirBits trackdirbits, TrackdirBits red_signals)
Builds a TrackStatus.
Definition track_func.h:390
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:650
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:589
bool IsValidTrack(Track track)
Checks if a Track is valid.
Definition track_func.h:28
Track FindFirstTrack(TrackBits tracks)
Returns first Track from TrackBits or INVALID_TRACK.
Definition track_func.h:177
bool IsDiagonalTrackdir(Trackdir trackdir)
Checks if a given Trackdir is diagonal.
Definition track_func.h:636
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:529
Track RemoveFirstTrack(TrackBits *tracks)
Removes first Track from TrackBits and returns it.
Definition track_func.h:131
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:441
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track incidating with that diagdir.
Definition track_func.h:517
TrackBits TrackdirBitsToTrackBits(TrackdirBits bits)
Discards all directional information from a TrackdirBits value.
Definition track_func.h:308
Sprites to use and how to display them for train depot tiles.
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:39
@ TRACK_BIT_DEPOT
Bitflag for a depot.
Definition track_type.h:53
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:41
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:38
@ TRACK_BIT_CROSS
X-Y-axis cross.
Definition track_type.h:43
@ TRACK_BIT_HORZ
Upper and lower track.
Definition track_type.h:44
@ TRACK_BIT_NONE
No track.
Definition track_type.h:36
@ TRACK_BIT_3WAY_NW
"Arrow" to the north-west
Definition track_type.h:49
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:37
@ TRACK_BIT_LOWER
Lower track.
Definition track_type.h:40
@ TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:50
@ TRACK_BIT_3WAY_NE
"Arrow" to the north-east
Definition track_type.h:46
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:42
@ TRACK_BIT_3WAY_SW
"Arrow" to the south-west
Definition track_type.h:48
@ TRACK_BIT_VERT
Left and right track.
Definition track_type.h:45
@ TRACK_BIT_3WAY_SE
"Arrow" to the south-east
Definition track_type.h:47
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
@ INVALID_TRACKDIR
Flag for an invalid trackdir.
Definition track_type.h:85
TrackdirBits
Allow incrementing of Trackdir variables.
Definition track_type.h:97
@ TRACKDIR_BIT_LEFT_S
Track left, direction south.
Definition track_type.h:103
@ TRACKDIR_BIT_Y_NW
Track y-axis, direction north-west.
Definition track_type.h:107
@ TRACKDIR_BIT_UPPER_E
Track upper, direction east.
Definition track_type.h:101
@ TRACKDIR_BIT_X_NE
Track x-axis, direction north-east.
Definition track_type.h:99
@ TRACKDIR_BIT_LOWER_E
Track lower, direction east.
Definition track_type.h:102
@ TRACKDIR_BIT_LEFT_N
Track left, direction north.
Definition track_type.h:110
@ TRACKDIR_BIT_RIGHT_S
Track right, direction south.
Definition track_type.h:104
@ TRACKDIR_BIT_Y_SE
Track y-axis, direction south-east.
Definition track_type.h:100
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
@ TRACKDIR_BIT_RIGHT_N
Track right, direction north.
Definition track_type.h:111
@ TRACKDIR_BIT_UPPER_W
Track upper, direction west.
Definition track_type.h:108
@ TRACKDIR_BIT_LOWER_W
Track lower, direction west.
Definition track_type.h:109
@ TRACKDIR_BIT_X_SW
Track x-axis, direction south-west.
Definition track_type.h:106
Track
These are used to specify a single track.
Definition track_type.h:19
@ INVALID_TRACK
Flag for an invalid track.
Definition track_type.h:28
@ TRACK_Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ TRACK_LOWER
Track in the lower corner of the tile (south).
Definition track_type.h:24
@ TRACK_END
Used for iterations.
Definition track_type.h:27
@ TRACK_LEFT
Track in the left corner of the tile (west).
Definition track_type.h:25
@ TRACK_RIGHT
Track in the right corner of the tile (east).
Definition track_type.h:26
@ TRACK_BEGIN
Used for iterations.
Definition track_type.h:20
@ TRACK_X
Track along the x-axis (north-east to south-west).
Definition track_type.h:21
@ TRACK_UPPER
Track in the upper corner of the tile (north).
Definition track_type.h:23
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.
uint8_t _display_opt
What do we want to draw/do?
bool IsInvisibilitySet(TransparencyOption to)
Check if the invisibility option bit is set and if we aren't in the game menu (there's never transpar...
@ TO_BUILDINGS
company buildings - depots, stations, HQ, ...
TransportType
Available types of transport.
@ TRANSPORT_RAIL
Transport by train.
@ TRANSPORT_WATER
Transport over water.
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:1565
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.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
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:658
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:579
Functions related to (drawing on) viewports.
static constexpr int BB_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.
@ FLOOD_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:3322
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers:
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.