OpenTTD Source 20260604-master-ga892d8e848
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 */
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(ExpensesType::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 if (!flooded) return flooded; // not yet floodable
779
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
808 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
809 { 0, 0 },
810 { 0, 0 },
811 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
812 { 0, 0 },
813 { 0, 0 }
814}}};
815
823{
824 return static_cast<Trackdir>(to_underlying(trackdir) ^ 1);
825}
826
827static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
828{
829 int x = TileX(start);
830 int y = TileY(start);
831 int ex = TileX(end);
832 int ey = TileY(end);
833
834 if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR;
835
836 /* calculate delta x,y from start to end tile */
837 int dx = ex - x;
838 int dy = ey - y;
839
840 /* calculate delta x,y for the first direction */
841 int trdx = _trackdelta[*trackdir].x;
842 int trdy = _trackdelta[*trackdir].y;
843
844 if (!IsDiagonalTrackdir(*trackdir)) {
845 trdx += _trackdelta[GetOtherTrackdir(*trackdir)].x;
846 trdy += _trackdelta[GetOtherTrackdir(*trackdir)].y;
847 }
848
849 /* validate the direction */
850 while ((trdx <= 0 && dx > 0) ||
851 (trdx >= 0 && dx < 0) ||
852 (trdy <= 0 && dy > 0) ||
853 (trdy >= 0 && dy < 0)) {
854 if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other
855 SetBit(*trackdir, 3); // reverse the direction
856 trdx = -trdx;
857 trdy = -trdy;
858 } else { // other direction is invalid too, invalid drag
859 return CMD_ERROR;
860 }
861 }
862
863 /* (for diagonal tracks, this is already made sure of by above test), but:
864 * for non-diagonal tracks, check if the start and end tile are on 1 line */
865 if (!IsDiagonalTrackdir(*trackdir)) {
866 trdx = _trackdelta[*trackdir].x;
867 trdy = _trackdelta[*trackdir].y;
868 if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) return CMD_ERROR;
869 }
870
871 return CommandCost();
872}
873
886static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
887{
889
890 if ((!remove && !ValParamRailType(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
891 if (end_tile >= Map::Size() || tile >= Map::Size()) return CMD_ERROR;
892
893 Trackdir trackdir = TrackToTrackdir(track);
894
895 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
896 if (ret.Failed()) return ret;
897
898 bool had_success = false;
899 CommandCost last_error = CMD_ERROR;
900 for (;;) {
901 ret = remove ? Command<Commands::RemoveRail>::Do(flags, tile, TrackdirToTrack(trackdir)) : Command<Commands::BuildRail>::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals);
902 if (!remove && !fail_on_obstacle && last_error.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) had_success = true;
903
904 if (ret.Failed()) {
905 last_error = std::move(ret);
906 if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
907 if (fail_on_obstacle) return last_error;
908 if (had_success) break; // Keep going if we haven't constructed any rail yet, skipping the start of the drag
909 }
910
911 /* Ownership errors are more important. */
912 if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
913 } else {
914 had_success = true;
915 total_cost.AddCost(ret.GetCost());
916 }
917
918 if (tile == end_tile) break;
919
920 tile += ToTileIndexDiff(_trackdelta[trackdir]);
921
922 /* toggle railbit for the non-diagonal tracks */
923 if (!IsDiagonalTrackdir(trackdir)) trackdir = GetOtherTrackdir(trackdir);
924 }
925
926 if (had_success) return total_cost;
927 return last_error;
928}
929
943CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
944{
945 return CmdRailTrackHelper(flags, start_tile, end_tile, railtype, track, false, auto_remove_signals, fail_on_obstacle);
946}
947
958CommandCost CmdRemoveRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, Track track)
959{
960 return CmdRailTrackHelper(flags, start_tile, end_tile, INVALID_RAILTYPE, track, true, false, false);
961}
962
971CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
972{
973 /* check railtype and valid direction for depot (0 through 3), 4 in total */
974 if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
975
976 Slope tileh = GetTileSlope(tile);
977
979
980 /* Prohibit construction if
981 * The tile is non-flat AND
982 * 1) build-on-slopes is disabled
983 * 2) the tile is steep i.e. spans two height levels
984 * 3) the exit points in the wrong direction
985 */
986
987 if (tileh != SLOPE_FLAT) {
988 if (!_settings_game.construction.build_on_slopes) return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
989 if (!CanBuildDepotByTileh(dir, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
991 }
992
993 /* Allow the user to rotate the depot instead of having to destroy it and build it again */
994 bool rotate_existing_depot = false;
995 if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
997 if (ret.Failed()) return ret;
998
999 if (dir == GetRailDepotDirection(tile)) return CommandCost();
1000
1001 ret = EnsureNoVehicleOnGround(tile);
1002 if (ret.Failed()) return ret;
1003
1004 rotate_existing_depot = true;
1005 }
1006
1007 if (!rotate_existing_depot) {
1008 cost.AddCost(Command<Commands::LandscapeClear>::Do(flags, tile));
1009 if (cost.Failed()) return cost;
1010
1011 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
1012
1013 if (!Depot::CanAllocateItem()) return CMD_ERROR;
1014 }
1015
1016 if (flags.Test(DoCommandFlag::Execute)) {
1017 if (rotate_existing_depot) {
1018 SetRailDepotExitDirection(tile, dir);
1019 } else {
1020 Depot *d = Depot::Create(tile);
1021
1022 MakeRailDepot(tile, _current_company, d->index, dir, railtype);
1023 MakeDefaultName(d);
1024
1025 Company::Get(_current_company)->infrastructure.rail[railtype]++;
1027 }
1028
1029 MarkTileDirtyByTile(tile);
1032 }
1033
1035 cost.AddCost(RailBuildCost(railtype));
1036 return cost;
1037}
1038
1057CommandCost 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)
1058{
1059 if (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE) return CMD_ERROR;
1060 if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
1061
1062 if (ctrl_pressed) sigvar = (SignalVariant)(sigvar ^ SIG_SEMAPHORE);
1063
1064 /* You can only build signals on plain rail tiles, and the selected track must exist */
1065 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
1066 !HasTrack(tile, track)) {
1067 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1068 }
1069 /* Protect against invalid signal copying */
1070 if (signals_copy != 0 && (signals_copy & SignalOnTrack(track)) == 0) return CMD_ERROR;
1071
1072 CommandCost ret = CheckTileOwnership(tile);
1073 if (ret.Failed()) return ret;
1074
1075 /* See if this is a valid track combination for signals (no overlap) */
1076 if (TracksOverlap(GetTrackBits(tile))) return CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
1077
1078 /* In case we don't want to change an existing signal, return without error. */
1079 if (skip_existing_signals && HasSignalOnTrack(tile, track)) return CommandCost();
1080
1081 /* you can not convert a signal if no signal is on track */
1082 if (convert_signal && !HasSignalOnTrack(tile, track)) return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1083
1084 CommandCost cost;
1085 if (!HasSignalOnTrack(tile, track)) {
1086 /* build new signals */
1088 } else {
1089 if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
1090 /* convert signals <-> semaphores */
1092
1093 } else if (convert_signal) {
1094 /* convert button pressed */
1095 if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
1096 /* it costs money to change signal variant (light or semaphore) */
1098 } else {
1099 /* it is free to change signal type (block, exit, entry, combo, path, etc) */
1100 cost = CommandCost();
1101 }
1102
1103 } else {
1104 /* it is free to change orientation or number of signals on the tile (for block/presignals which allow signals in both directions) */
1105 cost = CommandCost();
1106 }
1107 }
1108
1109 if (flags.Test(DoCommandFlag::Execute)) {
1110 Train *v = nullptr;
1111 /* The new/changed signal could block our path. As this can lead to
1112 * stale reservations, we clear the path reservation here and try
1113 * to redo it later on. */
1114 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1115 v = GetTrainForReservation(tile, track);
1116 if (v != nullptr) FreeTrainTrackReservation(v);
1117 }
1118
1119 if (!HasSignals(tile)) {
1120 /* there are no signals at all on this tile yet */
1121 SetHasSignals(tile, true);
1122 SetSignalStates(tile, 0xF); // all signals are on
1123 SetPresentSignals(tile, 0); // no signals built by default
1124 SetSignalType(tile, track, sigtype);
1125 SetSignalVariant(tile, track, sigvar);
1126 }
1127
1128 /* Subtract old signal infrastructure count. */
1129 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1130
1131 if (signals_copy == 0) {
1132 if (!HasSignalOnTrack(tile, track)) {
1133 /* build new signals */
1134 SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
1135 SetSignalType(tile, track, sigtype);
1136 SetSignalVariant(tile, track, sigvar);
1137 while (num_dir_cycle-- > 0) CycleSignalSide(tile, track);
1138 } else {
1139 if (convert_signal) {
1140 /* convert signal button pressed */
1141 if (ctrl_pressed) {
1142 /* toggle the present signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */
1143 SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC);
1144 /* Query current signal type so the check for PBS signals below works. */
1145 sigtype = GetSignalType(tile, track);
1146 } else {
1147 /* convert the present signal to the chosen type and variant */
1148 SetSignalType(tile, track, sigtype);
1149 SetSignalVariant(tile, track, sigvar);
1150 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1152 }
1153 }
1154
1155 } else if (ctrl_pressed) {
1156 /* cycle between cycle_start and cycle_end */
1157 sigtype = (SignalType)(GetSignalType(tile, track) + 1);
1158
1159 if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
1160
1161 SetSignalType(tile, track, sigtype);
1162 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1164 }
1165 } else {
1166 /* cycle the signal side: both -> left -> right -> both -> ... */
1167 CycleSignalSide(tile, track);
1168 /* Query current signal type so the check for PBS signals below works. */
1169 sigtype = GetSignalType(tile, track);
1170 }
1171 }
1172 } else {
1173 /* If CmdBuildManySignals is called with copying signals, just copy the
1174 * direction of the first signal given as parameter by CmdBuildManySignals */
1175 SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (signals_copy & SignalOnTrack(track)));
1176 SetSignalVariant(tile, track, sigvar);
1177 SetSignalType(tile, track, sigtype);
1178 }
1179
1180 /* Add new signal infrastructure count. */
1181 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1183
1184 if (IsPbsSignal(sigtype)) {
1185 /* PBS signals should show red unless they are on reserved tiles without a train. */
1186 uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
1187 SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetRailReservationTrackBits(tile), track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask));
1188 }
1189 MarkTileDirtyByTile(tile);
1191 YapfNotifyTrackLayoutChange(tile, track);
1192 if (v != nullptr && v->track != TRACK_BIT_DEPOT) {
1193 Train *moving_front = v->GetMovingFront();
1194 /* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1195 if (!((v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) ||
1196 !IsSafeWaitingPosition(v, moving_front->tile, moving_front->GetVehicleTrackdir(), true, _settings_game.pf.forbid_90_deg)) {
1197 TryPathReserve(v, true);
1198 }
1199 }
1200 }
1201
1202 return cost;
1203}
1204
1205static bool AdvanceSignalAutoFill(TileIndex &tile, Trackdir &trackdir, bool remove)
1206{
1207 /* We only process starting tiles of tunnels or bridges so jump to the other end before moving further. */
1209
1210 tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
1211 if (tile == INVALID_TILE) return false;
1212
1213 /* Check for track bits on the new tile */
1215
1216 if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
1217 trackdirbits &= TrackdirReachesTrackdirs(trackdir);
1218
1219 /* No track bits, must stop */
1220 if (trackdirbits == TRACKDIR_BIT_NONE) return false;
1221
1222 /* Get the first track dir */
1223 trackdir = RemoveFirstTrackdir(&trackdirbits);
1224
1225 /* Any left? It's a junction so we stop */
1226 if (trackdirbits != TRACKDIR_BIT_NONE) return false;
1227
1228 switch (GetTileType(tile)) {
1229 case TileType::Railway:
1230 if (IsRailDepot(tile)) return false;
1231 if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1232 break;
1233
1234 case TileType::Road:
1235 if (!IsLevelCrossing(tile)) return false;
1236 break;
1237
1239 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
1240 if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
1241 break;
1242 }
1243
1244 default: return false;
1245 }
1246 return true;
1247}
1248
1264static 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)
1265{
1267
1268 if (end_tile >= Map::Size() || !ValParamTrackOrientation(track)) return CMD_ERROR;
1269 if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
1270 if (!remove && (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE)) return CMD_ERROR;
1271
1272 if (!IsPlainRailTile(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1273 TileIndex start_tile = tile;
1274
1275 /* Interpret signal_density as the logical length of said amount of tiles in X/Y direction. */
1276 signal_density *= TILE_AXIAL_DISTANCE;
1277
1278 Trackdir trackdir = TrackToTrackdir(track);
1279 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
1280 if (ret.Failed()) return ret;
1281
1282 track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
1283 Trackdir start_trackdir = trackdir;
1284
1285 /* Must start on a valid track to be able to avoid loops */
1286 if (!HasTrack(tile, track)) return CMD_ERROR;
1287
1288 uint8_t signals;
1289 /* copy the signal-style of the first rail-piece if existing */
1290 if (HasSignalOnTrack(tile, track)) {
1291 signals = GetPresentSignals(tile) & SignalOnTrack(track);
1292 assert(signals != 0);
1293
1294 /* copy signal/semaphores style (independent of CTRL) */
1295 sigvar = GetSignalVariant(tile, track);
1296
1297 sigtype = GetSignalType(tile, track);
1298 /* Don't but copy entry or exit-signal type */
1299 if (sigtype == SIGTYPE_ENTRY || sigtype == SIGTYPE_EXIT) sigtype = SIGTYPE_BLOCK;
1300 } else { // no signals exist, drag a two-way signal stretch
1301 signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
1302 }
1303
1304 uint8_t signal_dir = 0;
1305 if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0);
1306 if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
1307
1308 /* signal_ctr - amount of tiles already processed
1309 * last_used_ctr - amount of tiles before previously placed signal
1310 * signals_density - setting to put signal on every Nth tile (double space on |, -- tracks)
1311 * last_suitable_ctr - amount of tiles before last possible signal place
1312 * last_suitable_tile - last tile where it is possible to place a signal
1313 * last_suitable_trackdir - trackdir of the last tile
1314 **********
1315 * trackdir - trackdir to build with autorail
1316 * semaphores - semaphores or signals
1317 * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1318 * and convert all others to semaphore/signal
1319 * remove - 1 remove signals, 0 build signals */
1320 int signal_ctr = 0;
1321 int last_used_ctr = -signal_density; // to force signal at first tile
1322 int last_suitable_ctr = 0;
1323 TileIndex last_suitable_tile = INVALID_TILE;
1324 Trackdir last_suitable_trackdir = INVALID_TRACKDIR;
1325 CommandCost last_error = CMD_ERROR;
1326 bool had_success = false;
1327 auto build_signal = [&](TileIndex tile, Trackdir trackdir, bool test_only) {
1328 /* Pick the correct orientation for the track direction */
1329 uint8_t signals = 0;
1330 if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
1331 if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
1332
1333 DoCommandFlags do_flags = test_only ? DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) : flags;
1334 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);
1335
1336 if (test_only) return ret.Succeeded();
1337
1338 if (ret.Succeeded()) {
1339 had_success = true;
1340 total_cost.AddCost(ret.GetCost());
1341 } else {
1342 /* The "No railway" error is the least important one. */
1343 if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
1344 last_error.GetErrorMessage() == INVALID_STRING_ID) {
1345 last_error = ret;
1346 }
1347 }
1348 return ret.Succeeded();
1349 };
1350
1351 for (;;) {
1352 if (remove) {
1353 /* In remove mode last_* stuff doesn't matter, we simply try to clear every tile. */
1354 build_signal(tile, trackdir, false);
1355 } else if (minimise_gaps) {
1356 /* We're trying to minimize gaps wherever possible, so keep track of last suitable
1357 * position and use it if current gap exceeds required signal density. */
1358
1359 if (signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1360 /* We overshot so build a signal in last good location. */
1361 if (build_signal(last_suitable_tile, last_suitable_trackdir, false)) {
1362 last_suitable_tile = INVALID_TILE;
1363 last_used_ctr = last_suitable_ctr;
1364 }
1365 }
1366
1367 if (signal_ctr == last_used_ctr + signal_density) {
1368 /* Current gap matches the required density, build a signal. */
1369 if (build_signal(tile, trackdir, false)) {
1370 last_used_ctr = signal_ctr;
1371 last_suitable_tile = INVALID_TILE;
1372 }
1373 } else {
1374 /* Test tile for a potential signal spot. */
1375 if (build_signal(tile, trackdir, true)) {
1376 last_suitable_tile = tile;
1377 last_suitable_ctr = signal_ctr;
1378 last_suitable_trackdir = trackdir;
1379 }
1380 }
1381 } else if (signal_ctr >= last_used_ctr + signal_density) {
1382 /* We're always keeping regular interval between signals so doesn't matter whether we succeed or not. */
1383 build_signal(tile, trackdir, false);
1384 last_used_ctr = signal_ctr;
1385 }
1386
1387 if (autofill) {
1388 switch (GetTileType(tile)) {
1389 case TileType::Railway:
1390 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1391 break;
1392
1393 case TileType::Road:
1394 signal_ctr += TILE_AXIAL_DISTANCE;
1395 break;
1396
1399 if (remove || minimise_gaps) {
1400 signal_ctr += len;
1401 } else {
1402 /* To keep regular interval we need to emulate placing signals on a bridge.
1403 * We start with TILE_AXIAL_DISTANCE as one bridge tile gets processed in the main loop. */
1404 signal_ctr += TILE_AXIAL_DISTANCE;
1405 for (uint i = TILE_AXIAL_DISTANCE; i < len; i += TILE_AXIAL_DISTANCE) {
1406 if (signal_ctr >= last_used_ctr + signal_density) last_used_ctr = signal_ctr;
1407 signal_ctr += TILE_AXIAL_DISTANCE;
1408 }
1409 }
1410 break;
1411 }
1412
1413 default: break;
1414 }
1415
1416 if (!AdvanceSignalAutoFill(tile, trackdir, remove)) break;
1417
1418 /* Prevent possible loops */
1419 if (tile == start_tile && trackdir == start_trackdir) break;
1420 } else {
1421 if (tile == end_tile) break;
1422
1423 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1424 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1425
1426 tile += ToTileIndexDiff(_trackdelta[trackdir]);
1427 if (!IsDiagonalTrackdir(trackdir)) trackdir = GetOtherTrackdir(trackdir);
1428 }
1429 }
1430
1431 /* We may end up with the current gap exceeding the signal density so fix that if needed. */
1432 if (!remove && minimise_gaps && signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1433 build_signal(last_suitable_tile, last_suitable_trackdir, false);
1434 }
1435
1436 return had_success ? total_cost : last_error;
1437}
1438
1455CommandCost 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)
1456{
1457 return CmdSignalTrackHelper(flags, tile, end_tile, track, sigtype, sigvar, mode, false, autofill, minimise_gaps, signal_density);
1458}
1459
1467CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
1468{
1469 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
1470 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1471 }
1472 if (!HasSignalOnTrack(tile, track)) {
1473 return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1474 }
1475
1476 /* Only water can remove signals from anyone */
1478 CommandCost ret = CheckTileOwnership(tile);
1479 if (ret.Failed()) return ret;
1480 }
1481
1482 /* Do it? */
1483 if (flags.Test(DoCommandFlag::Execute)) {
1484 Train *v = nullptr;
1485 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1486 v = GetTrainForReservation(tile, track);
1487 } else if (IsPbsSignal(GetSignalType(tile, track))) {
1488 /* PBS signal, might be the end of a path reservation. */
1489 Trackdir td = TrackToTrackdir(track);
1490 for (int i = 0; v == nullptr && i < 2; i++, td = ReverseTrackdir(td)) {
1491 /* Only test the active signal side. */
1492 if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
1495 if (HasReservedTracks(next, tracks)) {
1497 }
1498 }
1499 }
1500 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1501 SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
1502 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1504
1505 /* removed last signal from tile? */
1506 if (GetPresentSignals(tile) == 0) {
1507 SetSignalStates(tile, 0);
1508 SetHasSignals(tile, false);
1509 SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores
1510 }
1511
1512 AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
1513 YapfNotifyTrackLayoutChange(tile, track);
1514 if (v != nullptr) TryPathReserve(v, false);
1515
1516 MarkTileDirtyByTile(tile);
1517 }
1518
1520}
1521
1533CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
1534{
1535 return CmdSignalTrackHelper(flags, tile, end_tile, track, SIGTYPE_BLOCK, SIG_ELECTRIC, false, true, autofill, false, 1); // bit 5 is remove bit
1536}
1537
1548CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
1549{
1550 TileIndex area_end = tile;
1551
1552 if (!ValParamRailType(totype)) return CMD_ERROR;
1553 if (area_start >= Map::Size()) return CMD_ERROR;
1554
1555 TrainList affected_trains;
1556
1558 CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
1559 bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
1560
1561 std::unique_ptr<TileIterator> iter = TileIterator::Create(area_start, area_end, diagonal);
1562 for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
1563 TileType tt = GetTileType(tile);
1564
1565 /* Check if there is any track on tile */
1566 switch (tt) {
1567 case TileType::Railway:
1568 break;
1569 case TileType::Station:
1570 if (!HasStationRail(tile)) continue;
1571 break;
1572 case TileType::Road:
1573 if (!IsLevelCrossing(tile)) continue;
1574 if (RailNoLevelCrossings(totype)) {
1575 error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
1576 continue;
1577 }
1578 break;
1580 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
1581 break;
1582 default: continue;
1583 }
1584
1585 /* Original railtype we are converting from */
1586 RailType type = GetRailType(tile);
1587
1588 /* Converting to the same type or converting 'hidden' elrail -> rail */
1589 if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
1590
1591 /* Trying to convert other's rail */
1592 CommandCost ret = CheckTileOwnership(tile);
1593 if (ret.Failed()) {
1594 error = std::move(ret);
1595 continue;
1596 }
1597
1598 std::vector<Train *> vehicles_affected;
1599
1600 /* Vehicle on the tile when not converting Rail <-> ElRail
1601 * Tunnels and bridges have special check later */
1602 if (tt != TileType::TunnelBridge) {
1603 if (!IsCompatibleRail(type, totype)) {
1605 if (ret.Failed()) {
1606 error = std::move(ret);
1607 continue;
1608 }
1609 }
1610 if (flags.Test(DoCommandFlag::Execute)) { // we can safely convert, too
1611 for (Track track : SetTrackBitIterator(GetReservedTrackbits(tile))) {
1612 Train *v = GetTrainForReservation(tile, track);
1613 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1614 /* No power on new rail type, reroute. */
1616 vehicles_affected.push_back(v);
1617 }
1618 }
1619
1620 /* Update the company infrastructure counters. */
1621 if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
1622 Company *c = Company::Get(GetTileOwner(tile));
1623 uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
1624 if (IsPlainRailTile(tile)) {
1625 TrackBits bits = GetTrackBits(tile);
1626 num_pieces = CountBits(bits);
1627 if (TracksOverlap(bits)) num_pieces *= num_pieces;
1628 }
1629 c->infrastructure.rail[type] -= num_pieces;
1630 c->infrastructure.rail[totype] += num_pieces;
1632 }
1633
1634 SetRailType(tile, totype);
1635 MarkTileDirtyByTile(tile);
1636 /* update power of train on this tile */
1637 for (Vehicle *v : VehiclesOnTile(tile)) {
1638 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1639 }
1640 }
1641 }
1642
1643 switch (tt) {
1644 default: NOT_REACHED();
1645 case TileType::Railway:
1646 switch (GetRailTileType(tile)) {
1648 if (flags.Test(DoCommandFlag::Execute)) {
1649 /* notify YAPF about the track layout change */
1651
1652 /* Update build vehicle window related to this depot */
1653 InvalidateWindowData(WindowClass::VehicleDepot, tile);
1654 InvalidateWindowData(WindowClass::BuildVehicle, tile);
1655 }
1656 found_convertible_track = true;
1657 cost.AddCost(RailConvertCost(type, totype));
1658 break;
1659
1660 default: // RailTileType::Normal, RailTileType::Signals
1661 if (flags.Test(DoCommandFlag::Execute)) {
1662 /* notify YAPF about the track layout change */
1663 for (Track track : SetTrackBitIterator(GetTrackBits(tile))) {
1664 YapfNotifyTrackLayoutChange(tile, track);
1665 }
1666 }
1667 found_convertible_track = true;
1668 cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
1669 break;
1670 }
1671 break;
1672
1674 TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
1675
1676 /* If both ends of tunnel/bridge are in the range, do not try to convert twice -
1677 * it would cause assert because of different test and exec runs */
1678 if (endtile < tile) {
1679 if (diagonal) {
1680 if (DiagonalTileArea(area_start, area_end).Contains(endtile)) continue;
1681 } else {
1682 if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
1683 }
1684 }
1685
1686 /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
1687 if (!IsCompatibleRail(GetRailType(tile), totype)) {
1688 ret = TunnelBridgeIsFree(tile, endtile);
1689 if (ret.Failed()) {
1690 error = std::move(ret);
1691 continue;
1692 }
1693 }
1694
1695 if (flags.Test(DoCommandFlag::Execute)) {
1697 if (HasTunnelBridgeReservation(tile)) {
1698 Train *v = GetTrainForReservation(tile, track);
1699 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1700 /* No power on new rail type, reroute. */
1702 vehicles_affected.push_back(v);
1703 }
1704 }
1705
1706 /* Update the company infrastructure counters. */
1707 uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
1708 Company *c = Company::Get(GetTileOwner(tile));
1709 c->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1710 c->infrastructure.rail[totype] += num_pieces;
1712
1713 SetRailType(tile, totype);
1714 SetRailType(endtile, totype);
1715
1716 for (Vehicle *v : VehiclesOnTile(tile)) {
1717 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1718 }
1719 for (Vehicle *v : VehiclesOnTile(endtile)) {
1720 if (v->type == VehicleType::Train) include(affected_trains, Train::From(v)->First());
1721 }
1722
1723 YapfNotifyTrackLayoutChange(tile, track);
1724 YapfNotifyTrackLayoutChange(endtile, track);
1725
1726 if (IsBridge(tile)) {
1727 MarkBridgeDirty(tile);
1728 } else {
1729 MarkTileDirtyByTile(tile);
1730 MarkTileDirtyByTile(endtile);
1731 }
1732 }
1733
1734 found_convertible_track = true;
1735 cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
1736 break;
1737 }
1738
1739 case TileType::Station:
1740 case TileType::Road:
1741 if (flags.Test(DoCommandFlag::Execute)) {
1742 Track track = ((tt == TileType::Station) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile));
1743 YapfNotifyTrackLayoutChange(tile, track);
1744 }
1745
1746 found_convertible_track = true;
1747 cost.AddCost(RailConvertCost(type, totype));
1748 break;
1749 }
1750
1751 for (uint i = 0; i < vehicles_affected.size(); ++i) {
1752 TryPathReserve(vehicles_affected[i], true);
1753 }
1754 }
1755
1756 if (flags.Test(DoCommandFlag::Execute)) {
1757 /* Railtype changed, update trains as when entering different track */
1758 for (Train *v : affected_trains) {
1759 v->ConsistChanged(CCF_TRACK);
1760 }
1761 }
1762
1763 return found_convertible_track ? cost : error;
1764}
1765
1766static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlags flags)
1767{
1769 CommandCost ret = CheckTileOwnership(tile);
1770 if (ret.Failed()) return ret;
1771 }
1772
1774 if (ret.Failed()) return ret;
1775
1776 if (flags.Test(DoCommandFlag::Execute)) {
1777 /* read variables before the depot is removed */
1779 Owner owner = GetTileOwner(tile);
1780 Train *v = nullptr;
1781
1782 if (HasDepotReservation(tile)) {
1784 if (v != nullptr) FreeTrainTrackReservation(v);
1785 }
1786
1787 Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
1789
1790 delete Depot::GetByTile(tile);
1791 DoClearSquare(tile);
1792 AddSideToSignalBuffer(tile, dir, owner);
1794 if (v != nullptr) TryPathReserve(v, true);
1795 }
1796
1798}
1799
1801static CommandCost ClearTile_Rail(TileIndex tile, DoCommandFlags flags)
1802{
1804
1805 if (flags.Test(DoCommandFlag::Auto)) {
1806 if (!IsTileOwner(tile, _current_company)) {
1807 return CommandCost(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
1808 }
1809
1810 if (IsPlainRail(tile)) {
1811 return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
1812 } else {
1813 return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
1814 }
1815 }
1816
1817 switch (GetRailTileType(tile)) {
1819 case RailTileType::Normal: {
1820 Slope tileh = GetTileSlope(tile);
1821 /* Is there flat water on the lower halftile that gets cleared expensively? */
1822 bool water_ground = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh));
1823
1824 for (Track track : SetTrackBitIterator(GetTrackBits(tile))) {
1825 CommandCost ret = Command<Commands::RemoveRail>::Do(flags, tile, track);
1826 if (ret.Failed()) return ret;
1827 cost.AddCost(ret.GetCost());
1828 }
1829
1830 /* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
1831 * Same holds for non-companies clearing the tile, e.g. disasters. */
1832 if (water_ground && !flags.Test(DoCommandFlag::Bankrupt) && Company::IsValidID(_current_company)) {
1834 if (ret.Failed()) return ret;
1835
1836 /* The track was removed, and left a coast tile. Now also clear the water. */
1837 if (flags.Test(DoCommandFlag::Execute)) {
1838 DoClearSquare(tile);
1839 }
1841 }
1842
1843 return cost;
1844 }
1845
1847 return RemoveTrainDepot(tile, flags);
1848
1849 default:
1850 return CMD_ERROR;
1851 }
1852}
1853
1862static uint GetSafeSlopeZ(uint x, uint y, Track track)
1863{
1864 switch (track) {
1865 case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break;
1866 case TRACK_LOWER: x |= 0xF; y |= 0xF; break;
1867 case TRACK_LEFT: x |= 0xF; y &= ~0xF; break;
1868 case TRACK_RIGHT: x &= ~0xF; y |= 0xF; break;
1869 default: break;
1870 }
1871 return GetSlopePixelZ(x, y);
1872}
1873
1874static void DrawSingleSignal(TileIndex tile, const RailTypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos)
1875{
1876 bool side;
1877 switch (_settings_game.construction.train_signal_side) {
1878 case 0: side = false; break; // left
1879 case 2: side = true; break; // right
1880 default: side = _settings_game.vehicle.road_side != 0; break; // driving side
1881 }
1882 static const Point SignalPositions[2][12] = {
1883 { // Signals on the left side
1884 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1885 { 8, 5}, {14, 1}, { 1, 14}, { 9, 11}, { 1, 0}, { 3, 10},
1886 /* LOWER LOWER X X Y Y */
1887 {11, 4}, {14, 14}, {11, 3}, { 4, 13}, { 3, 4}, {11, 13}
1888 }, { // Signals on the right side
1889 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1890 {14, 1}, {12, 10}, { 4, 6}, { 1, 14}, {10, 4}, { 0, 1},
1891 /* LOWER LOWER X X Y Y */
1892 {14, 14}, { 5, 12}, {11, 13}, { 4, 3}, {13, 4}, { 3, 11}
1893 }
1894 };
1895
1896 uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x;
1897 uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y;
1898
1899 SignalType type = GetSignalType(tile, track);
1900 SignalVariant variant = GetSignalVariant(tile, track);
1901
1902 SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition);
1903 if (sprite != 0) {
1904 sprite += image;
1905 } else {
1906 /* Normal electric signals are stored in a different sprite block than all other signals. */
1907 sprite = (type == SIGTYPE_BLOCK && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
1908 sprite += type * 16 + variant * 64 + image * 2 + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0);
1909 }
1910
1911 AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, GetSafeSlopeZ(x, y, track), {{}, {1, 1, BB_HEIGHT_UNDER_BRIDGE}, {}});
1912}
1913
1915struct FenceOffset : SpriteBounds {
1917
1918 constexpr FenceOffset(Corner height_ref, int8_t origin_x, int8_t origin_y, uint8_t extent_x, uint8_t extent_y) :
1919 SpriteBounds({origin_x, origin_y, 0}, {extent_x, extent_y, 4}, {}), height_ref(height_ref) {}
1920};
1921
1923static const FenceOffset _fence_offsets[] = {
1924 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_FLAT_X_NW
1925 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_FLAT_Y_NE
1926 { CORNER_W, 8, 8, 1, 1 }, // RFO_FLAT_LEFT
1927 { CORNER_N, 8, 8, 1, 1 }, // RFO_FLAT_UPPER
1928 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_SW_NW
1929 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_SE_NE
1930 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_NE_NW
1931 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_NW_NE
1932 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_FLAT_X_SE
1933 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_FLAT_Y_SW
1934 { CORNER_E, 8, 8, 1, 1 }, // RFO_FLAT_RIGHT
1935 { CORNER_S, 8, 8, 1, 1 }, // RFO_FLAT_LOWER
1936 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_SW_SE
1937 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_SE_SW
1938 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_NE_SE
1939 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_NW_SW
1940};
1941
1949static void DrawTrackFence(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites, RailFenceOffset rfo)
1950{
1951 int z = ti->z;
1952 if (_fence_offsets[rfo].height_ref != CORNER_INVALID) {
1954 }
1955 AddSortableSpriteToDraw(psid.sprite + (rfo % num_sprites), psid.pal, ti->x, ti->y, z, _fence_offsets[rfo]);
1956}
1957
1964static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1965{
1967 if (ti->tileh & SLOPE_NW) rfo = (ti->tileh & SLOPE_W) ? RFO_SLOPE_SW_NW : RFO_SLOPE_NE_NW;
1968 DrawTrackFence(ti, psid, num_sprites, rfo);
1969}
1970
1977static void DrawTrackFence_SE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1978{
1980 if (ti->tileh & SLOPE_SE) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SW_SE : RFO_SLOPE_NE_SE;
1981 DrawTrackFence(ti, psid, num_sprites, rfo);
1982}
1983
1990static void DrawTrackFence_NE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1991{
1993 if (ti->tileh & SLOPE_NE) rfo = (ti->tileh & SLOPE_E) ? RFO_SLOPE_SE_NE : RFO_SLOPE_NW_NE;
1994 DrawTrackFence(ti, psid, num_sprites, rfo);
1995}
1996
2003static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
2004{
2006 if (ti->tileh & SLOPE_SW) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SE_SW : RFO_SLOPE_NW_SW;
2007 DrawTrackFence(ti, psid, num_sprites, rfo);
2008}
2009
2016static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
2017{
2018 /* Base sprite for track fences.
2019 * Note: Halftile slopes only have fences on the upper part. */
2020 uint num_sprites = 0;
2021 PalSpriteID psid{
2023 .pal = pal,
2024 };
2025 if (psid.sprite == 0) {
2026 psid.sprite = SPR_TRACK_FENCE_FLAT_X;
2027 num_sprites = 8;
2028 }
2029
2030 assert(num_sprites > 0);
2031
2032 switch (GetRailGroundType(ti->tile)) {
2033 case RailGroundType::FenceNW: DrawTrackFence_NW(ti, psid, num_sprites); break;
2034 case RailGroundType::FenceSE: DrawTrackFence_SE(ti, psid, num_sprites); break;
2035 case RailGroundType::FenceSENW: DrawTrackFence_NW(ti, psid, num_sprites);
2036 DrawTrackFence_SE(ti, psid, num_sprites); break;
2037 case RailGroundType::FenceNE: DrawTrackFence_NE(ti, psid, num_sprites); break;
2038 case RailGroundType::FenceSW: DrawTrackFence_SW(ti, psid, num_sprites); break;
2039 case RailGroundType::FenceNESW: DrawTrackFence_NE(ti, psid, num_sprites);
2040 DrawTrackFence_SW(ti, psid, num_sprites); break;
2041 case RailGroundType::FenceVert1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2042 case RailGroundType::FenceVert2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2043 case RailGroundType::FenceHoriz1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2044 case RailGroundType::FenceHoriz2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2046 Corner track_corner;
2047 if (IsHalftileSlope(ti->tileh)) {
2048 /* Steep slope or one-corner-raised slope with halftile foundation */
2049 track_corner = GetHalftileSlopeCorner(ti->tileh);
2050 } else {
2051 /* Three-corner-raised slope */
2053 }
2054 switch (track_corner) {
2055 case CORNER_W: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2056 case CORNER_S: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2057 case CORNER_E: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2058 case CORNER_N: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2059 default: NOT_REACHED();
2060 }
2061 break;
2062 }
2063 default: break;
2064 }
2065}
2066
2067/* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */
2068static const int INF = 1000;
2069static const SubSprite _halftile_sub_sprite[4] = {
2070 { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right
2071 { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top
2072 { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left
2073 { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2074};
2075
2076static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
2077{
2078 DrawGroundSprite(sprite, pal, nullptr, 0, (ti->tileh & s) ? -8 : 0);
2079}
2080
2081static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti)
2082{
2084 Foundation f = GetRailFoundation(ti->tileh, track);
2085 Corner halftile_corner = CORNER_INVALID;
2086
2088 /* Save halftile corner */
2090 /* Draw lower part first */
2091 track &= ~CornerToTrackBits(halftile_corner);
2093 }
2094
2095 DrawFoundation(ti, f);
2096 /* DrawFoundation modifies ti */
2097
2098 /* Draw ground */
2099 if (rgt == RailGroundType::HalfTileWater) {
2100 if (track != TRACK_BIT_NONE || IsSteepSlope(ti->tileh)) {
2101 /* three-corner-raised slope or steep slope with track on upper part */
2102 DrawShoreTile(ti->tileh);
2103 } else {
2104 /* single-corner-raised slope with track on upper part */
2105 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
2106 }
2107 } else {
2108 SpriteID image;
2109
2110 switch (rgt) {
2111 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2112 case RailGroundType::SnowOrDesert: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2113 default: image = SPR_FLAT_GRASS_TILE; break;
2114 }
2115
2116 image += SlopeToSpriteOffset(ti->tileh);
2117
2118 DrawGroundSprite(image, PAL_NONE);
2119 }
2120
2121 bool no_combine = ti->tileh == SLOPE_FLAT && rti->flags.Test(RailTypeFlag::NoSpriteCombine);
2124 TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE;
2125
2126 if (track == TRACK_BIT_NONE) {
2127 /* Half-tile foundation, no track here? */
2128 } else if (no_combine) {
2129 /* Use trackbits as direct index from ground sprite, subtract 1
2130 * because there is no sprite for no bits. */
2131 DrawGroundSprite(ground + track - 1, PAL_NONE);
2132
2133 /* Draw reserved track bits */
2134 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2135 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2136 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2137 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2138 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2139 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2140 } else if (ti->tileh == SLOPE_NW && track == TRACK_BIT_Y) {
2141 DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
2143 } else if (ti->tileh == SLOPE_NE && track == TRACK_BIT_X) {
2144 DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
2146 } else if (ti->tileh == SLOPE_SE && track == TRACK_BIT_Y) {
2147 DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
2149 } else if (ti->tileh == SLOPE_SW && track == TRACK_BIT_X) {
2150 DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
2152 } else {
2153 switch (track) {
2154 /* Draw single ground sprite when not overlapping. No track overlay
2155 * is necessary for these sprites. */
2156 case TRACK_BIT_X: DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
2157 case TRACK_BIT_Y: DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
2158 case TRACK_BIT_UPPER: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
2159 case TRACK_BIT_LOWER: DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2160 case TRACK_BIT_RIGHT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
2161 case TRACK_BIT_LEFT: DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2162 case TRACK_BIT_CROSS: DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
2163 case TRACK_BIT_HORZ: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
2164 DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2165 case TRACK_BIT_VERT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
2166 DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2167
2168 default:
2169 /* We're drawing a junction tile */
2170 if ((track & TRACK_BIT_3WAY_NE) == 0) {
2171 DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
2172 } else if ((track & TRACK_BIT_3WAY_SW) == 0) {
2173 DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
2174 } else if ((track & TRACK_BIT_3WAY_NW) == 0) {
2175 DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
2176 } else if ((track & TRACK_BIT_3WAY_SE) == 0) {
2177 DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
2178 } else {
2179 DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2180 }
2181
2182 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2183 track &= ~pbs;
2184
2185 /* Draw regular track bits */
2186 if (track & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PAL_NONE);
2187 if (track & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
2188 if (track & TRACK_BIT_UPPER) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
2189 if (track & TRACK_BIT_LOWER) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
2190 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
2191 if (track & TRACK_BIT_LEFT) DrawGroundSprite(overlay + RTO_W, PAL_NONE);
2192 }
2193
2194 /* Draw reserved track bits */
2195 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2196 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2197 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2198 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2199 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2200 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2201 }
2202
2203 if (IsValidCorner(halftile_corner)) {
2204 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2207
2208 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2209 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2210
2211 SpriteID image;
2212 switch (rgt) {
2213 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2215 case RailGroundType::HalfTileSnow: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2216 default: image = SPR_FLAT_GRASS_TILE; break;
2217 }
2218
2219 image += SlopeToSpriteOffset(fake_slope);
2220
2221 DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
2222
2223 track = CornerToTrackBits(halftile_corner);
2224
2225 int offset;
2226 switch (track) {
2227 default: NOT_REACHED();
2228 case TRACK_BIT_UPPER: offset = RTO_N; break;
2229 case TRACK_BIT_LOWER: offset = RTO_S; break;
2230 case TRACK_BIT_RIGHT: offset = RTO_E; break;
2231 case TRACK_BIT_LEFT: offset = RTO_W; break;
2232 }
2233
2234 DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
2235 if (_settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, track)) {
2236 DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
2237 }
2238 }
2239}
2240
2249{
2250 /* If none of the tracks end up in the NE corner, return the ground sprite
2251 * where the NE of the tile is not covered. Repeat for the other directions.
2252 * What remains are junctions where all directions are covered. */
2253 if ((track & TRACK_BIT_3WAY_NE) == 0) return 0;
2254 if ((track & TRACK_BIT_3WAY_SW) == 0) return 1;
2255 if ((track & TRACK_BIT_3WAY_NW) == 0) return 2;
2256 if ((track & TRACK_BIT_3WAY_SE) == 0) return 3;
2257 return 4;
2258}
2259
2265static void DrawTrackBits(TileInfo *ti, TrackBits track)
2266{
2267 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2268
2269 if (rti->UsesOverlay()) {
2270 DrawTrackBitsOverlay(ti, track, rti);
2271 return;
2272 }
2273
2275 Foundation f = GetRailFoundation(ti->tileh, track);
2276 Corner halftile_corner = CORNER_INVALID;
2277
2279 /* Save halftile corner */
2281 /* Draw lower part first */
2282 track &= ~CornerToTrackBits(halftile_corner);
2284 }
2285
2286 DrawFoundation(ti, f);
2287 /* DrawFoundation modifies ti */
2288
2289 SpriteID image;
2290 PaletteID pal = PAL_NONE;
2291 const SubSprite *sub = nullptr;
2292 bool junction = false;
2293
2294 /* Select the sprite to use. */
2295 if (track == 0) {
2296 /* Clear ground (only track on halftile foundation) */
2297 if (rgt == RailGroundType::HalfTileWater) {
2298 if (IsSteepSlope(ti->tileh)) {
2299 DrawShoreTile(ti->tileh);
2300 image = 0;
2301 } else {
2302 image = SPR_FLAT_WATER_TILE;
2303 }
2304 } else {
2305 switch (rgt) {
2306 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2307 case RailGroundType::SnowOrDesert: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2308 default: image = SPR_FLAT_GRASS_TILE; break;
2309 }
2310 image += SlopeToSpriteOffset(ti->tileh);
2311 }
2312 } else {
2313 if (ti->tileh != SLOPE_FLAT) {
2314 /* track on non-flat ground */
2315 image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
2316 } else {
2317 /* track on flat ground */
2318 switch (track) {
2319 /* single track, select combined track + ground sprite*/
2320 case TRACK_BIT_Y: image = rti->base_sprites.track_y; break;
2321 case TRACK_BIT_X: image = rti->base_sprites.track_y + 1; break;
2322 case TRACK_BIT_UPPER: image = rti->base_sprites.track_y + 2; break;
2323 case TRACK_BIT_LOWER: image = rti->base_sprites.track_y + 3; break;
2324 case TRACK_BIT_RIGHT: image = rti->base_sprites.track_y + 4; break;
2325 case TRACK_BIT_LEFT: image = rti->base_sprites.track_y + 5; break;
2326 case TRACK_BIT_CROSS: image = rti->base_sprites.track_y + 6; break;
2327
2328 /* double diagonal track, select combined track + ground sprite*/
2329 case TRACK_BIT_HORZ: image = rti->base_sprites.track_ns; break;
2330 case TRACK_BIT_VERT: image = rti->base_sprites.track_ns + 1; break;
2331
2332 /* junction, select only ground sprite, handle track sprite later */
2333 default:
2334 junction = true;
2336 break;
2337 }
2338 }
2339
2340 switch (rgt) {
2342 case RailGroundType::SnowOrDesert: image += rti->snow_offset; break;
2344 /* three-corner-raised slope */
2345 DrawShoreTile(ti->tileh);
2347 sub = &(_halftile_sub_sprite[track_corner]);
2348 break;
2349 }
2350 default: break;
2351 }
2352 }
2353
2354 if (image != 0) DrawGroundSprite(image, pal, sub);
2355
2356 /* Draw track pieces individually for junction tiles */
2357 if (junction) {
2358 if (track & TRACK_BIT_X) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE);
2359 if (track & TRACK_BIT_Y) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE);
2360 if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE);
2361 if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE);
2362 if (track & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
2363 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
2364 }
2365
2366 /* PBS debugging, draw reserved tracks darker */
2367 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation) {
2368 /* Get reservation, but mask track on halftile slope */
2369 TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
2370 if (pbs & TRACK_BIT_X) {
2371 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2373 } else {
2374 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2375 }
2376 }
2377 if (pbs & TRACK_BIT_Y) {
2378 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2380 } else {
2381 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2382 }
2383 }
2384 if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_N ? -(int)TILE_HEIGHT : 0);
2385 if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_S ? -(int)TILE_HEIGHT : 0);
2386 if (pbs & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_W ? -(int)TILE_HEIGHT : 0);
2387 if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
2388 }
2389
2390 if (IsValidCorner(halftile_corner)) {
2391 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2392
2393 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2394 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2395 image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y;
2396 pal = PAL_NONE;
2397 switch (rgt) {
2400 case RailGroundType::HalfTileSnow: image += rti->snow_offset; break; // higher part has snow in this case too
2401 default: break;
2402 }
2403 DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
2404
2405 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) {
2406 static const uint8_t _corner_to_track_sprite[] = {3, 1, 2, 0};
2407 DrawGroundSprite(_corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, -(int)TILE_HEIGHT);
2408 }
2409 }
2410}
2411
2412static void DrawSignals(TileIndex tile, TrackBits rails, const RailTypeInfo *rti)
2413{
2414 auto MAYBE_DRAW_SIGNAL = [&](uint8_t signalbit, SignalOffsets image, uint pos, Track track) {
2415 if (IsSignalPresent(tile, signalbit)) DrawSingleSignal(tile, rti, track, GetSingleSignalState(tile, signalbit), image, pos);
2416 };
2417
2418 if (!(rails & TRACK_BIT_Y)) {
2419 if (!(rails & TRACK_BIT_X)) {
2420 if (rails & TRACK_BIT_LEFT) {
2421 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, TRACK_LEFT);
2422 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, TRACK_LEFT);
2423 }
2424 if (rails & TRACK_BIT_RIGHT) {
2425 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, TRACK_RIGHT);
2426 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, TRACK_RIGHT);
2427 }
2428 if (rails & TRACK_BIT_UPPER) {
2429 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, TRACK_UPPER);
2430 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, TRACK_UPPER);
2431 }
2432 if (rails & TRACK_BIT_LOWER) {
2433 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, TRACK_LOWER);
2434 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, TRACK_LOWER);
2435 }
2436 } else {
2437 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, TRACK_X);
2438 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, TRACK_X);
2439 }
2440 } else {
2441 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, TRACK_Y);
2442 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 11, TRACK_Y);
2443 }
2444}
2445
2447static void DrawTile_Rail(TileInfo *ti)
2448{
2449 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2450 BridgePillarFlags blocked_pillars{};
2452
2453 if (IsPlainRail(ti->tile)) {
2454 TrackBits rails = GetTrackBits(ti->tile);
2455
2456 DrawTrackBits(ti, rails);
2457
2459
2461
2462 if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
2463
2464 if (IsBridgeAbove(ti->tile)) {
2465 if ((rails & TRACK_BIT_3WAY_NE) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeNE);
2466 if ((rails & TRACK_BIT_3WAY_SE) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeSE);
2467 if ((rails & TRACK_BIT_3WAY_SW) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeSW);
2468 if ((rails & TRACK_BIT_3WAY_NW) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeNW);
2469 }
2470 } else {
2471 /* draw depot */
2472 const DrawTileSprites *dts;
2474
2476
2478 /* Draw rail instead of depot */
2479 dts = &_depot_invisible_gfx_table[dir];
2480 } else {
2481 dts = &_depot_gfx_table[dir];
2482 }
2483
2484 SpriteID image;
2485 if (rti->UsesOverlay()) {
2486 image = SPR_FLAT_GRASS_TILE;
2487 } else {
2488 image = dts->ground.sprite;
2489 if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
2490 }
2491
2492 /* Adjust ground tile for desert and snow. */
2493 if (IsSnowOrDesertRailGround(ti->tile)) {
2494 if (image != SPR_FLAT_GRASS_TILE) {
2495 image += rti->snow_offset; // tile with tracks
2496 } else {
2497 image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
2498 }
2499 }
2500
2501 DrawGroundSprite(image, GroundSpritePaletteTransform(image, PAL_NONE, pal));
2502
2503 if (rti->UsesOverlay()) {
2505
2506 switch (GetRailDepotDirection(ti->tile)) {
2507 case DiagDirection::NE:
2509 [[fallthrough]];
2510 case DiagDirection::SW:
2511 DrawGroundSprite(ground + RTO_X, PAL_NONE);
2512 break;
2513 case DiagDirection::NW:
2515 [[fallthrough]];
2516 case DiagDirection::SE:
2517 DrawGroundSprite(ground + RTO_Y, PAL_NONE);
2518 break;
2519 default:
2520 break;
2521 }
2522
2523 if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2525
2526 switch (GetRailDepotDirection(ti->tile)) {
2527 case DiagDirection::NE:
2529 [[fallthrough]];
2530 case DiagDirection::SW:
2532 break;
2533 case DiagDirection::NW:
2535 [[fallthrough]];
2536 case DiagDirection::SE:
2538 break;
2539 default:
2540 break;
2541 }
2542 }
2543 } else {
2544 /* PBS debugging, draw reserved tracks darker */
2545 if (_game_mode != GameMode::Menu && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2546 switch (GetRailDepotDirection(ti->tile)) {
2547 case DiagDirection::NE:
2549 [[fallthrough]];
2550 case DiagDirection::SW:
2552 break;
2553 case DiagDirection::NW:
2555 [[fallthrough]];
2556 case DiagDirection::SE:
2558 break;
2559 default:
2560 break;
2561 }
2562 }
2563 }
2564 int depot_sprite = GetCustomRailSprite(rti, ti->tile, RailSpriteType::Depot);
2565 int relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
2566
2568
2569 DrawRailTileSeq(ti, dts, TransparencyOption::Buildings, relocation, 0, pal);
2570 /* Depots can't have bridges above so no blocked pillars. */
2571 }
2572 DrawBridgeMiddle(ti, blocked_pillars);
2573}
2574
2582void DrawTrainDepotSprite(int x, int y, DiagDirection dir, RailType railtype)
2583{
2584 const DrawTileSprites *dts = &_depot_gfx_table[dir];
2585 const RailTypeInfo *rti = GetRailTypeInfo(railtype);
2586 SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
2587 uint32_t offset = rti->GetRailtypeSpriteOffset();
2588
2589 if (image != SPR_FLAT_GRASS_TILE) image += offset;
2591
2592 DrawSprite(image, PAL_NONE, x, y);
2593
2594 if (rti->UsesOverlay()) {
2596
2597 switch (dir) {
2598 case DiagDirection::SW: DrawSprite(ground + RTO_X, PAL_NONE, x, y); break;
2599 case DiagDirection::SE: DrawSprite(ground + RTO_Y, PAL_NONE, x, y); break;
2600 default: break;
2601 }
2602 }
2603 int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RailSpriteType::Depot);
2604 if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
2605
2606 DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette);
2607}
2608
2610static int GetSlopePixelZ_Rail(TileIndex tile, uint x, uint y, [[maybe_unused]] bool ground_vehicle)
2611{
2612 if (IsPlainRail(tile)) {
2613 auto [tileh, z] = GetTilePixelSlope(tile);
2614 if (tileh == SLOPE_FLAT) return z;
2615
2617 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
2618 } else {
2619 return GetTileMaxPixelZ(tile);
2620 }
2621}
2622
2625{
2626 return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
2627}
2628
2630static void TileLoop_Rail(TileIndex tile)
2631{
2632 RailGroundType old_ground = GetRailGroundType(tile);
2633 RailGroundType new_ground;
2634
2635 if (old_ground == RailGroundType::HalfTileWater) {
2636 TileLoop_Water(tile);
2637 return;
2638 }
2639
2640 switch (_settings_game.game_creation.landscape) {
2641 case LandscapeType::Arctic: {
2642 auto [slope, z] = GetTileSlopeZ(tile);
2643 bool half = false;
2644
2645 /* for non-flat track, use lower part of track
2646 * in other cases, use the highest part with track */
2647 if (IsPlainRail(tile)) {
2648 TrackBits track = GetTrackBits(tile);
2649 Foundation f = GetRailFoundation(slope, track);
2650
2651 switch (f) {
2652 case FOUNDATION_NONE:
2653 /* no foundation - is the track on the upper side of three corners raised tile? */
2654 if (IsSlopeWithThreeCornersRaised(slope)) z++;
2655 break;
2656
2659 /* sloped track - is it on a steep slope? */
2660 if (IsSteepSlope(slope)) z++;
2661 break;
2662
2664 /* only lower part of steep slope */
2665 z++;
2666 break;
2667
2668 default:
2669 /* if it is a steep slope, then there is a track on higher part */
2670 if (IsSteepSlope(slope)) z++;
2671 z++;
2672 break;
2673 }
2674
2676 } else {
2677 /* is the depot on a non-flat tile? */
2678 if (slope != SLOPE_FLAT) z++;
2679 }
2680
2681 /* 'z' is now the lowest part of the highest track bit -
2682 * for sloped track, it is 'z' of lower part
2683 * for two track bits, it is 'z' of higher track bit
2684 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
2685 if (z > GetSnowLine()) {
2686 if (half && z - GetSnowLine() == 1) {
2687 /* track on non-continuous foundation, lower part is not under snow */
2688 new_ground = RailGroundType::HalfTileSnow;
2689 } else {
2690 new_ground = RailGroundType::SnowOrDesert;
2691 }
2692 goto set_ground;
2693 }
2694 break;
2695 }
2696
2698 if (GetTropicZone(tile) == TropicZone::Desert) {
2699 new_ground = RailGroundType::SnowOrDesert;
2700 goto set_ground;
2701 }
2702 break;
2703
2704 default:
2705 break;
2706 }
2707
2708 new_ground = RailGroundType::Grass;
2709
2710 if (IsPlainRail(tile) && old_ground != RailGroundType::Barren) { // wait until bottom is green
2711 /* determine direction of fence */
2712 TrackBits rail = GetTrackBits(tile);
2713
2714 Owner owner = GetTileOwner(tile);
2715 DiagDirections fences{};
2716
2719
2720 /* Track bit on this edge => no fence. */
2721 if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue;
2722
2723 TileIndex tile2 = tile + TileOffsByDiagDir(d);
2724
2725 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
2726 if (!IsValidTile(tile2) || IsTileType(tile2, TileType::House) || IsTileType(tile2, TileType::Industry) ||
2728 fences.Set(d);
2729 }
2730 }
2731
2732 switch (fences.base()) {
2733 case DiagDirections{}.base(): break;
2734 case DiagDirections{DiagDirection::NE}.base(): new_ground = RailGroundType::FenceNE; break;
2735 case DiagDirections{DiagDirection::SE}.base(): new_ground = RailGroundType::FenceSE; break;
2736 case DiagDirections{DiagDirection::SW}.base(): new_ground = RailGroundType::FenceSW; break;
2737 case DiagDirections{DiagDirection::NW}.base(): new_ground = RailGroundType::FenceNW; break;
2744 default: NOT_REACHED();
2745 }
2746 }
2747
2748set_ground:
2749 if (old_ground != new_ground) {
2750 SetRailGroundType(tile, new_ground);
2751 MarkTileDirtyByTile(tile);
2752 }
2753}
2754
2755
2758{
2759 /* Case of half tile slope with water. */
2761 TrackBits tb = GetTrackBits(tile);
2762 switch (tb) {
2763 default: NOT_REACHED();
2764 case TRACK_BIT_UPPER: tb = TRACK_BIT_LOWER; break;
2765 case TRACK_BIT_LOWER: tb = TRACK_BIT_UPPER; break;
2766 case TRACK_BIT_LEFT: tb = TRACK_BIT_RIGHT; break;
2767 case TRACK_BIT_RIGHT: tb = TRACK_BIT_LEFT; break;
2768 }
2770 }
2771
2772 if (mode != TRANSPORT_RAIL) return {};
2773
2774 TrackBits trackbits = TRACK_BIT_NONE;
2775 TrackdirBits red_signals = TRACKDIR_BIT_NONE;
2776
2777 switch (GetRailTileType(tile)) {
2778 default: NOT_REACHED();
2780 trackbits = GetTrackBits(tile);
2781 break;
2782
2783 case RailTileType::Signals: {
2784 trackbits = GetTrackBits(tile);
2785 uint8_t a = GetPresentSignals(tile);
2786 uint b = GetSignalStates(tile);
2787
2788 b &= a;
2789
2790 /* When signals are not present (in neither direction),
2791 * we pretend them to be green. Otherwise, it depends on
2792 * the signal type. For signals that are only active from
2793 * one side, we set the missing signals explicitly to
2794 * `green'. Otherwise, they implicitly become `red'. */
2795 if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER);
2796 if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER);
2797
2798 if ((b & 0x8) == 0) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E);
2799 if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W);
2800 if ((b & 0x2) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_E);
2801 if ((b & 0x1) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_W);
2802
2803 break;
2804 }
2805
2806 case RailTileType::Depot: {
2808
2809 if (side != DiagDirection::Invalid && side != dir) break;
2810
2811 trackbits = DiagDirToDiagTrackBits(dir);
2812 break;
2813 }
2814 }
2815
2816 return {TrackBitsToTrackdirBits(trackbits), red_signals};
2817}
2818
2820static bool ClickTile_Rail(TileIndex tile)
2821{
2822 if (!IsRailDepot(tile)) return false;
2823
2825 return true;
2826}
2827
2830{
2831 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
2832 td.rail_speed = rti->max_speed;
2833 td.railtype = rti->strings.name;
2834 td.owner[0] = GetTileOwner(tile);
2835 switch (GetRailTileType(tile)) {
2837 td.str = STR_LAI_RAIL_DESCRIPTION_TRACK;
2838 break;
2839
2840 case RailTileType::Signals: {
2841 static const StringID signal_type[6][6] = {
2842 {
2843 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
2844 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2845 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2846 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2847 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2848 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
2849 },
2850 {
2851 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2852 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS,
2853 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2854 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2855 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2856 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
2857 },
2858 {
2859 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2860 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2861 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
2862 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2863 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2864 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
2865 },
2866 {
2867 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2868 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2869 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2870 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
2871 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2872 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
2873 },
2874 {
2875 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2876 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2877 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2878 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2879 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
2880 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
2881 },
2882 {
2883 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
2884 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
2885 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
2886 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
2887 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
2888 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
2889 }
2890 };
2891
2892 SignalType primary_signal;
2893 SignalType secondary_signal;
2894 if (HasSignalOnTrack(tile, TRACK_UPPER)) {
2895 primary_signal = GetSignalType(tile, TRACK_UPPER);
2896 secondary_signal = HasSignalOnTrack(tile, TRACK_LOWER) ? GetSignalType(tile, TRACK_LOWER) : primary_signal;
2897 } else {
2898 secondary_signal = primary_signal = GetSignalType(tile, TRACK_LOWER);
2899 }
2900
2901 td.str = signal_type[secondary_signal][primary_signal];
2902 break;
2903 }
2904
2906 td.str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
2907 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) {
2908 if (td.rail_speed > 0) {
2909 td.rail_speed = std::min<uint16_t>(td.rail_speed, 61);
2910 } else {
2911 td.rail_speed = 61;
2912 }
2913 }
2914 td.build_date = Depot::GetByTile(tile)->build_date;
2915 break;
2916
2917 default:
2918 NOT_REACHED();
2919 }
2920}
2921
2923static void ChangeTileOwner_Rail(TileIndex tile, Owner old_owner, Owner new_owner)
2924{
2925 if (!IsTileOwner(tile, old_owner)) return;
2926
2927 if (new_owner != INVALID_OWNER) {
2928 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
2929 uint num_pieces = 1;
2930 if (IsPlainRail(tile)) {
2931 TrackBits bits = GetTrackBits(tile);
2932 num_pieces = CountBits(bits);
2933 if (TracksOverlap(bits)) num_pieces *= num_pieces;
2934 }
2935 RailType rt = GetRailType(tile);
2936 Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;
2937 Company::Get(new_owner)->infrastructure.rail[rt] += num_pieces;
2938
2939 if (HasSignals(tile)) {
2940 uint num_sigs = CountBits(GetPresentSignals(tile));
2941 Company::Get(old_owner)->infrastructure.signal -= num_sigs;
2942 Company::Get(new_owner)->infrastructure.signal += num_sigs;
2943 }
2944
2945 SetTileOwner(tile, new_owner);
2946 } else {
2947 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
2948 }
2949}
2950
2953 {15, 8}, // NE
2954 {8, 0}, // SE
2955 {0, 8}, // SW
2956 {8, 15}, // NW
2957}}};
2958
2961 {10, 8}, // NE
2962 {8, 4}, // SE
2963 {4, 8}, // SW
2964 {8, 10}, // NW
2965}}};
2966
2972 {-1, 0}, // NE
2973 {0, 1}, // SE
2974 {1, 0}, // SW
2975 {0, -1}, // NW
2976}}};
2977
2985{
2987 int length = v->CalcNextVehicleOffset() + 1;
2988
2989 switch (dir) {
2990 case DiagDirection::NE: return (static_cast<int>(v->x_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].x - length));
2991 case DiagDirection::SE: return -(static_cast<int>(v->y_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].y + length));
2992 case DiagDirection::SW: return -(static_cast<int>(v->x_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].x + length));
2993 case DiagDirection::NW: return (static_cast<int>(v->y_pos & TILE_UNIT_MASK) - (_fractcoords_enter[dir].y - length));
2994 default: NOT_REACHED();
2995 }
2996}
2997
2999static VehicleEnterTileStates VehicleEnterTile_Rail(Vehicle *v, TileIndex tile, int x, int y)
3000{
3001 /* This routine applies only to trains in depot tiles. */
3002 if (v->type != VehicleType::Train || !IsRailDepotTile(tile)) return {};
3003
3004 /* Depot direction. */
3006
3007 Coord2D<uint8_t> fract_coord{
3008 static_cast<uint8_t>(x & TILE_UNIT_MASK),
3009 static_cast<uint8_t>(y & TILE_UNIT_MASK)};
3010
3011 /* Make sure a train is not entering the tile from behind. */
3012 if (_fractcoords_behind[dir] == fract_coord) return VehicleEnterTileState::CannotEnter;
3013
3014 /* Leaving depot? */
3015 if (v->GetMovingDirection() == DiagDirToDir(dir)) {
3016 /* Calculate the point where the following wagon should be activated. */
3017 int length = Train::From(v)->CalcNextVehicleOffset() + 1;
3018
3019 Coord2D<uint8_t> fract_coord_leave{
3020 static_cast<uint8_t>(_fractcoords_enter[dir].x + length * _deltacoord_leaveoffset[dir].x),
3021 static_cast<uint8_t>(_fractcoords_enter[dir].y + length * _deltacoord_leaveoffset[dir].y)};
3022
3023 if (fract_coord_leave == fract_coord) {
3024 /* Leave the depot. */
3025 if ((v = v->GetMovingNext()) != nullptr) {
3028 }
3029 }
3030 } else if (_fractcoords_enter[dir] == fract_coord) {
3031 /* Entering depot. */
3032 assert(DiagDirToDir(ReverseDiagDir(dir)) == v->GetMovingDirection());
3035 if (v->GetMovingNext() == nullptr) {
3036 Train *consist = Train::From(v)->First();
3038 /* Trains always drive forwards out of a depot.
3039 * This allows a player to easily reset a confused train,
3040 * and matches the behaviour of the \c VehicleRailFlag::Reversed variable. */
3042 } else {
3043 for (Train *u = consist; u != nullptr; u = u->Next()) {
3044 u->direction = ReverseDir(u->direction);
3045 }
3046 }
3047 VehicleEnterDepot(consist);
3048 }
3049 v->tile = tile;
3050
3051 InvalidateWindowData(WindowClass::VehicleDepot, v->tile);
3053 }
3054
3055 return {};
3056}
3057
3070static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
3071{
3072 if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3073
3074 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3075 if (CheckRailSlope(tileh_new, rail_bits, TRACK_BIT_NONE, tile).Failed()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3076
3077 /* Get the slopes on top of the foundations */
3078 z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), tileh_old);
3079 z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), tileh_new);
3080
3081 Corner track_corner;
3082 switch (rail_bits) {
3083 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
3084 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
3085 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
3086 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
3087
3088 /* Surface slope must not be changed */
3089 default:
3090 if (z_old != z_new || tileh_old != tileh_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3092 }
3093
3094 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3095 z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
3096 z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
3097 if (z_old != z_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3098
3100 /* Make the ground dirty, if surface slope has changed */
3101 if (tileh_old != tileh_new) {
3102 /* If there is flat water on the lower halftile add the cost for clearing it */
3105 }
3106 return cost;
3107}
3108
3110static CommandCost TerraformTile_Rail(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
3111{
3112 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
3113 if (IsPlainRail(tile)) {
3114 TrackBits rail_bits = GetTrackBits(tile);
3115 /* Is there flat water on the lower halftile that must be cleared expensively? */
3116 bool was_water = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh_old));
3117
3118 /* Allow clearing the water only if there is no ship */
3119 if (was_water && HasVehicleOnTile(tile, [](const Vehicle *v) {
3120 return v->type == VehicleType::Ship;
3121 })) return CommandCost(STR_ERROR_SHIP_IN_THE_WAY);
3122
3123 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3124 CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
3125
3126 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3127 Corner allowed_corner;
3128 switch (rail_bits) {
3129 case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break;
3130 case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break;
3131 case TRACK_BIT_LEFT: allowed_corner = CORNER_E; break;
3132 case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break;
3133 default: return autoslope_result;
3134 }
3135
3136 Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
3137
3138 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3139 if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
3140
3141 /* Everything is valid, which only changes allowed_corner */
3142 for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
3143 if (allowed_corner == corner) continue;
3144 if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
3145 }
3146
3147 /* Make the ground dirty */
3149
3150 /* allow terraforming */
3151 return CommandCost(ExpensesType::Construction, was_water ? _price[Price::ClearWater] : (Money)0);
3152 } else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
3153 AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
3155 }
3156 return Command<Commands::LandscapeClear>::Do(flags, tile);
3157}
3158
3160static CommandCost CheckBuildAbove_Rail(TileIndex tile, DoCommandFlags flags, [[maybe_unused]] Axis axis, [[maybe_unused]] int height)
3161{
3162 if (IsPlainRail(tile)) return CommandCost();
3163 return Command<Commands::LandscapeClear>::Do(flags, tile);
3164}
3165
3167extern const TileTypeProcs _tile_type_rail_procs = {
3168 .draw_tile_proc = DrawTile_Rail,
3169 .get_slope_pixel_z_proc = GetSlopePixelZ_Rail,
3170 .clear_tile_proc = ClearTile_Rail,
3171 .get_tile_desc_proc = GetTileDesc_Rail,
3172 .get_tile_track_status_proc = GetTileTrackStatus_Rail,
3173 .click_tile_proc = ClickTile_Rail,
3174 .tile_loop_proc = TileLoop_Rail,
3175 .change_tile_owner_proc = ChangeTileOwner_Rail,
3176 .vehicle_enter_tile_proc = VehicleEnterTile_Rail,
3177 .get_foundation_proc = GetFoundation_Rail,
3178 .terraform_tile_proc = TerraformTile_Rail,
3179 .check_build_above_proc = CheckBuildAbove_Rail,
3180};
Functions related to autoslope.
bool AutoslopeCheckForEntranceEdge(TileIndex tile, int z_new, Slope tileh_new, DiagDirection entrance)
Autoslope check for tiles with an entrance on an edge.
Definition autoslope.h:31
bool AutoslopeEnabled()
Tests if autoslope is enabled for _current_company.
Definition autoslope.h:65
Class for backupping variables and making sure they are restored later.
@ DrivingBackwards
Vehicle is driving backwards.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
Draw the middle bits of a bridge.
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.
EnumIndexArray< T, DiagDirection, DiagDirection::End > DiagDirectionIndexArray
Array with DiagDirection as index.
Axis
Enumeration for the two axis X and Y.
@ X
The X axis.
@ Y
The y axis.
DiagDirection
Enumeration for diagonal directions.
@ Begin
Used for iterations.
@ Invalid
Flag for an invalid DiagDirection.
@ SW
Southwest.
@ NW
Northwest.
@ End
Used for iterations.
@ NE
Northeast, upper right on your monitor.
@ SE
Southeast.
EnumBitSet< DiagDirection, uint8_t > DiagDirections
Bitset of DiagDirection elements.
Prices _price
Prices and also the fractional part.
Definition economy.cpp:106
static const uint LEVELCROSSING_TRACKBIT_FACTOR
Multiplier for how many regular track bits a level crossing counts.
@ Construction
Construction costs.
static const uint TUNNELBRIDGE_TRACKBIT_FACTOR
Multiplier for how many regular track bits a tunnel/bridge counts.
@ ClearRough
Price for destroying rough land.
@ BuildSignals
Price for building rail signals.
@ BuildFoundation
Price for building foundation under other constructions e.g. roads, rails, depots,...
@ ClearWater
Price for destroying water e.g. see, rives.
@ ClearSignals
Price for destroying rail signals.
@ ClearDepotTrain
Price for destroying train depots.
@ BuildDepotTrain
Price for building train depots.
void DrawRailCatenary(const TileInfo *ti)
Draws overhead wires and pylons for electric railways.
Definition elrail.cpp:552
Header file for electrified rail specific functions.
bool HasRailCatenaryDrawn(RailType rt)
Test if we should draw rail catenary.
Definition elrail_func.h:32
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1037
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
uint8_t GetSnowLine()
Get the current snow line, either variable or static.
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
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:90
@ 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
@ FullDetail
Also draw details of track and roads.
Definition openttd.h:50
@ Menu
In the main menu.
Definition openttd.h:19
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:345
TrackBits GetReservedTrackbits(TileIndex t)
Get the reserved trackbits for any tile, regardless of type.
Definition pbs.cpp:24
bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
Determine whether a certain track on a tile is a safe position to end a path.
Definition pbs.cpp:395
PBS support routines.
bool HasReservedTracks(TileIndex tile, TrackBits tracks)
Check whether some of tracks is reserved on a tile.
Definition pbs.h:58
bool ValParamRailType(const RailType rail)
Validate functions for rail building.
Definition rail.cpp:92
Money RailConvertCost(RailType from, RailType to)
Calculates the cost of rail conversion.
Definition rail.h: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
void DrawTrainDepotSprite(int x, int y, DiagDirection dir, RailType railtype)
Draw train depot sprite in the UI.
static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
Build or remove a stretch of railroad tracks.
Definition rail_cmd.cpp:886
static Trackdir GetOtherTrackdir(Trackdir trackdir)
Get the other Trackdir for a non-diagonal Trackdir.
Definition rail_cmd.cpp:822
static const int INF
Big number compared to tilesprite size.
CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
Build a train depot.
Definition rail_cmd.cpp:971
static void DrawTile_Rail(TileInfo *ti)
Tile callback function signature for drawing a tile and its contents to the screen.
CommandCost CmdBuildSingleSignal(DoCommandFlags flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy)
Build signals, alternate between double/single, signal/semaphore, pre/exit/combo-signals,...
CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
Build rail on a stretch of track.
Definition rail_cmd.cpp:943
CommandCost CmdBuildSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool autofill, bool minimise_gaps, uint8_t signal_density)
Build signals on a stretch of track.
static CommandCost ClearTile_Rail(TileIndex tile, DoCommandFlags flags)
Tile callback function signature for clearing a tile.
bool FloodHalftile(TileIndex t)
Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
Definition rail_cmd.cpp:761
RailType AllocateRailType(RailTypeLabel label)
Allocate a new rail type label.
Definition rail_cmd.cpp:150
static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
Tests if a track can be build on a tile.
Definition rail_cmd.cpp: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 constexpr DiagDirectionIndexArray< Coord2D< uint8_t > > _fractcoords_enter
Coordinates where a train should enter a depot for each depot direction.
static CommandCost CmdSignalTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool remove, bool autofill, bool minimise_gaps, int signal_density)
Build many signals by dragging; AutoSignals.
static int GetSlopePixelZ_Rail(TileIndex tile, uint x, uint y, bool ground_vehicle)
Tile callback function signature for obtaining the world Z coordinate of a given point of a tile.
static void DrawTrackBits(TileInfo *ti, TrackBits track)
Draw ground sprite and track bits.
static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
Draw track fences.
SignalOffsets
Enum holding the signal offset in the sprite sheet according to the side it is representing.
Definition rail_cmd.cpp:51
CommandCost CmdRemoveRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, Track track)
Build rail on a stretch of track.
Definition rail_cmd.cpp:958
static VehicleEnterTileStates VehicleEnterTile_Rail(Vehicle *v, TileIndex tile, int x, int y)
Tile callback function for a vehicle entering a tile.
CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
Build a single piece of rail.
Definition rail_cmd.cpp:425
static constexpr DiagDirectionIndexArray< Coord2D< uint8_t > > _fractcoords_behind
Coordinates to detect when a train is approaching a depot from behind for each depot direction.
CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
Convert one rail type to the other.
void InitRailTypes()
Resolve sprites of custom rail types.
Definition rail_cmd.cpp:130
static TrackStatus GetTileTrackStatus_Rail(TileIndex tile, TransportType mode, RoadTramType sub_mode, DiagDirection side)
Tile callback function signature for getting the possible tracks that can be taken on a given tile by...
static void ChangeTileOwner_Rail(TileIndex tile, Owner old_owner, Owner new_owner)
Tile callback function signature for changing the owner of a tile.
static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at SW border matching the tile slope.
static bool ClickTile_Rail(TileIndex tile)
Tile callback function signature for clicking a tile.
static void GetTileDesc_Rail(TileIndex tile, TileDesc &td)
Tile callback function signature for obtaining a tile description.
void ResetRailTypes()
Reset all rail type information to its default values.
Definition rail_cmd.cpp:65
static const TrackdirIndexArray< TileIndexDiffC > _trackdelta
Tile deltas for each trackdir.
Definition rail_cmd.cpp:807
static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
Tests if autoslope is allowed.
static const FenceOffset _fence_offsets[]
Offsets for drawing fences.
static int GetJunctionGroundSpriteOffset(TrackBits track)
Returns which of the 5 junction-'Rail underlays' to use for the given track bits.
static bool CompareRailTypes(const RailType &first, const RailType &second)
Compare railtypes based on their sorting order.
Definition rail_cmd.cpp:122
static Foundation GetFoundation_Rail(TileIndex tile, Slope tileh)
Tile callback function signature for getting the foundation of a tile.
static constexpr DiagDirectionIndexArray< Coord2D< int8_t > > _deltacoord_leaveoffset
Offsets (to be multiplied by length) from the depot enter coordinates where a train should appear whe...
static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at NW border matching the tile slope.
Foundation GetRailFoundation(Slope tileh, TrackBits bits)
Checks if a track combination is valid on a specific slope and returns the needed foundation.
Definition rail_cmd.cpp: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:658
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:602
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:625
@ 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:569
@ FenceSENW
Grass with a fence at the NW and SE edges.
Definition rail_map.h:574
@ FenceVert2
Grass with a fence at the western side.
Definition rail_map.h:579
@ HalfTileSnow
Snow only on higher part of slope (steep or one corner raised).
Definition rail_map.h:584
@ Barren
Nothing (dirt).
Definition rail_map.h:570
@ FenceNESW
Grass with a fence at the NE and SW edges.
Definition rail_map.h:577
@ FenceHoriz2
Grass with a fence at the northern side.
Definition rail_map.h:581
@ SnowOrDesert
Icy or sandy.
Definition rail_map.h:582
@ HalfTileWater
Grass with a fence and shore or water on the free halftile.
Definition rail_map.h:583
@ FenceVert1
Grass with a fence at the eastern side.
Definition rail_map.h:578
@ FenceNW
Grass with a fence at the NW edge.
Definition rail_map.h:572
@ FenceSE
Grass with a fence at the SE edge.
Definition rail_map.h:573
@ FenceNE
Grass with a fence at the NE edge.
Definition rail_map.h:575
@ Grass
Grassy.
Definition rail_map.h:571
@ FenceSW
Grass with a fence at the SW edge.
Definition rail_map.h:576
@ FenceHoriz1
Grass with a fence at the southern side.
Definition rail_map.h:580
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:592
void SetRailDepotExitDirection(Tile tile, DiagDirection dir)
Sets the exit direction of a rail depot.
Definition rail_map.h:644
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:612
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:53
@ SLOPE_W
the west corner of the tile is raised
Definition slope_type.h:55
@ SLOPE_ELEVATED
bit mask containing all 'simple' slopes
Definition slope_type.h:66
@ SLOPE_NS
north and south corner are raised
Definition slope_type.h:65
@ SLOPE_E
the east corner of the tile is raised
Definition slope_type.h:57
@ SLOPE_S
the south corner of the tile is raised
Definition slope_type.h:56
@ SLOPE_N
the north corner of the tile is raised
Definition slope_type.h:58
@ SLOPE_SW
south and west corner are raised
Definition slope_type.h:61
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:54
@ SLOPE_NE
north and east corner are raised
Definition slope_type.h:63
@ SLOPE_SE
south and east corner are raised
Definition slope_type.h:62
@ SLOPE_NW
north and west corner are raised
Definition slope_type.h:60
@ SLOPE_EW
east and west corner are raised
Definition slope_type.h:64
Foundation
Enumeration for Foundations.
Definition slope_type.h:98
@ FOUNDATION_LEVELED
The tile is leveled up to a flat slope.
Definition slope_type.h:100
@ FOUNDATION_NONE
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:99
@ FOUNDATION_INCLINED_X
The tile has an along X-axis inclined foundation.
Definition slope_type.h:101
@ 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:106
@ FOUNDATION_INCLINED_Y
The tile has an along Y-axis inclined foundation.
Definition slope_type.h:102
@ 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:103
@ FOUNDATION_HALFTILE_N
Level north halftile non-continuously.
Definition slope_type.h:110
@ FOUNDATION_INVALID
Used inside "rail_cmd.cpp" to indicate invalid slope/track combination.
Definition slope_type.h:118
static const uint32_t VALID_LEVEL_CROSSING_SLOPES
Constant bitset with safe slopes for building a level crossing.
Definition slope_type.h:91
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 upon destruction of this object to prevent stack v...
VehicleFlags vehicle_flags
Used for gradual loading and other miscellaneous things (.
VehicleType type
Type of vehicle.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
A coordinate with two dimensions.
T y
Y coordinate.
T x
X coordinate.
T x
X coordinate.
T y
Y coordinate.
T z
Z coordinate.
Represents a diagonal tile area.
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:55
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:56
Offsets for drawing fences.
Corner height_ref
Corner to use height offset from.
static uint Size()
Get the size of the map.
Definition map_func.h:280
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:67
Represents the covered area of e.g.
Combination of a palette sprite and a 'real' sprite.
Definition gfx_type.h:22
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed).
Definition gfx_type.h:24
static 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
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
Track status of a tile.
Definition track_type.h:132
TrackdirBits trackdirs
Trackdirs present on the tile.
Definition track_type.h:133
'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
static constexpr uint TILE_UNIT_MASK
For masking in/out the inner-tile world coordinate units.
Definition tile_type.h:16
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
@ Desert
Tile is desert.
Definition tile_type.h:83
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
static constexpr uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
TileType
The different types of tiles.
Definition tile_type.h:48
@ TunnelBridge
Tunnel entry/exit and bridge heads.
Definition tile_type.h:58
@ Water
Water tile.
Definition tile_type.h:55
@ Station
A tile of a station or airport.
Definition tile_type.h:54
@ Object
Contains objects such as transmitters and owned land.
Definition tile_type.h:59
@ Industry
Part of an industry.
Definition tile_type.h:57
@ Railway
A tile with railway.
Definition tile_type.h:50
@ House
A house by a town.
Definition tile_type.h:52
@ Road
A tile with road and/or tram tracks.
Definition tile_type.h:51
Definition of the game-calendar-timer.
Base of the town class.
void MakeDefaultName(T *obj)
Set the default name for a depot/waypoint.
Definition town.h: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
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
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:603
TrackBits AxisToTrackBits(Axis a)
Maps an Axis to the corresponding TrackBits value.
Definition track_func.h:88
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:542
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:589
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a DiagDirection to the associated diagonal TrackBits.
Definition track_func.h:482
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:394
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a DiagDirection to the associated diagonal Track.
Definition track_func.h:470
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.
static const DiagDirectionIndexArray< DrawTileSpriteSpan > _depot_gfx_table
Sprite layout of a depot for each direction.
Definition track_land.h:32
static const DiagDirectionIndexArray< DrawTileSpriteSpan > _depot_invisible_gfx_table
Sprite layout of an invisible depot with for each direction.
Definition track_land.h:40
TrackBits
Bitfield corresponding to Track.
Definition track_type.h:42
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:46
@ TRACK_BIT_DEPOT
Bitflag for a depot.
Definition track_type.h:60
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:48
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:45
@ TRACK_BIT_CROSS
X-Y-axis cross.
Definition track_type.h:50
@ TRACK_BIT_HORZ
Upper and lower track.
Definition track_type.h:51
@ TRACK_BIT_NONE
No track.
Definition track_type.h:43
@ TRACK_BIT_3WAY_NW
"Arrow" to the north-west
Definition track_type.h:56
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:44
@ TRACK_BIT_LOWER
Lower track.
Definition track_type.h:47
@ TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:57
@ TRACK_BIT_3WAY_NE
"Arrow" to the north-east
Definition track_type.h:53
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:49
@ TRACK_BIT_3WAY_SW
"Arrow" to the south-west
Definition track_type.h:55
@ TRACK_BIT_VERT
Left and right track.
Definition track_type.h:52
@ TRACK_BIT_3WAY_SE
"Arrow" to the south-east
Definition track_type.h:54
EnumIndexArray< T, Trackdir, TRACKDIR_END > TrackdirIndexArray
Allow incrementing of Trackdir variables.
Definition track_type.h:103
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:73
@ INVALID_TRACKDIR
Flag for an invalid trackdir.
Definition track_type.h:92
TrackdirBits
Enumeration of bitmasks for the TrackDirs.
Definition track_type.h:111
@ TRACKDIR_BIT_LEFT_S
Track left, direction south.
Definition track_type.h:117
@ TRACKDIR_BIT_Y_NW
Track y-axis, direction north-west.
Definition track_type.h:121
@ TRACKDIR_BIT_UPPER_E
Track upper, direction east.
Definition track_type.h:115
@ TRACKDIR_BIT_X_NE
Track x-axis, direction north-east.
Definition track_type.h:113
@ TRACKDIR_BIT_LOWER_E
Track lower, direction east.
Definition track_type.h:116
@ TRACKDIR_BIT_LEFT_N
Track left, direction north.
Definition track_type.h:124
@ TRACKDIR_BIT_RIGHT_S
Track right, direction south.
Definition track_type.h:118
@ TRACKDIR_BIT_Y_SE
Track y-axis, direction south-east.
Definition track_type.h:114
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:112
@ TRACKDIR_BIT_RIGHT_N
Track right, direction north.
Definition track_type.h:125
@ TRACKDIR_BIT_UPPER_W
Track upper, direction west.
Definition track_type.h:122
@ TRACKDIR_BIT_LOWER_W
Track lower, direction west.
Definition track_type.h:123
@ TRACKDIR_BIT_X_SW
Track x-axis, direction south-west.
Definition track_type.h:120
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.
bool IsInvisibilitySet(TransparencyOption to)
Check if the invisibility option bit is set and if we aren't in the game menu (there's never transpar...
DisplayOptions _display_opt
What do we want to draw/do?
@ Buildings
company buildings - depots, stations, HQ, ...
TransportType
Available types of transport.
@ 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:1562
CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
Tests if a vehicle interacts with the specified track bits.
Definition vehicle.cpp:605
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:581
const uint TILE_AXIAL_DISTANCE
Logical length of the tile in any DiagDirection used in vehicle movement.
const uint TILE_CORNER_DISTANCE
Logical length of the tile corner crossing in any non-diagonal direction used in vehicle movement.
@ Hidden
Vehicle is not visible.
@ Stopped
Vehicle is stopped by the player.
Functions related to vehicles.
bool HasVehicleOnTile(TileIndex tile, UnaryPred &&predicate)
Loop over vehicles on a tile, and check whether a predicate is true for any of them.
@ Ship
Ship vehicle type.
@ Train
Train vehicle type.
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.
@ 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
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.