OpenTTD Source 20260311-master-g511d3794ce
rail_cmd.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
11#include "viewport_func.h"
12#include "command_func.h"
13#include "depot_base.h"
15#include "newgrf_debug.h"
16#include "newgrf_railtype.h"
17#include "train.h"
18#include "autoslope.h"
19#include "water.h"
20#include "tunnelbridge_map.h"
21#include "vehicle_func.h"
22#include "sound_func.h"
23#include "tunnelbridge.h"
24#include "elrail_func.h"
25#include "town.h"
26#include "pbs.h"
27#include "company_base.h"
28#include "core/backup_type.hpp"
31#include "strings_func.h"
32#include "company_gui.h"
33#include "object_map.h"
34#include "rail_cmd.h"
35#include "landscape_cmd.h"
36
37#include "table/strings.h"
38#include "table/railtypes.h"
39#include "table/track_land.h"
40
41#include "safeguards.h"
42
44typedef std::vector<Train *> TrainList;
45
46RailTypeInfo _railtypes[RAILTYPE_END];
47std::vector<RailType> _sorted_railtypes;
48RailTypes _railtypes_hidden_mask;
49
52 SIGNAL_TO_SOUTHWEST,
53 SIGNAL_TO_NORTHEAST,
54 SIGNAL_TO_SOUTHEAST,
55 SIGNAL_TO_NORTHWEST,
56 SIGNAL_TO_EAST,
57 SIGNAL_TO_WEST,
58 SIGNAL_TO_SOUTH,
59 SIGNAL_TO_NORTH,
60};
61
66{
67 static_assert(std::size(_original_railtypes) <= std::size(_railtypes));
68
69 auto insert = std::copy(std::begin(_original_railtypes), std::end(_original_railtypes), std::begin(_railtypes));
70 std::fill(insert, std::end(_railtypes), RailTypeInfo{});
71
72 _railtypes_hidden_mask = {};
73}
74
75void ResolveRailTypeGUISprites(RailTypeInfo *rti)
76{
78 if (cursors_base != 0) {
79 rti->gui_sprites.build_ns_rail = cursors_base + 0;
80 rti->gui_sprites.build_x_rail = cursors_base + 1;
81 rti->gui_sprites.build_ew_rail = cursors_base + 2;
82 rti->gui_sprites.build_y_rail = cursors_base + 3;
83 rti->gui_sprites.auto_rail = cursors_base + 4;
84 rti->gui_sprites.build_depot = cursors_base + 5;
85 rti->gui_sprites.build_tunnel = cursors_base + 6;
86 rti->gui_sprites.convert_rail = cursors_base + 7;
87 rti->cursor.rail_ns = cursors_base + 8;
88 rti->cursor.rail_swne = cursors_base + 9;
89 rti->cursor.rail_ew = cursors_base + 10;
90 rti->cursor.rail_nwse = cursors_base + 11;
91 rti->cursor.autorail = cursors_base + 12;
92 rti->cursor.depot = cursors_base + 13;
93 rti->cursor.tunnel = cursors_base + 14;
94 rti->cursor.convert = cursors_base + 15;
95 }
96
97 /* Array of default GUI signal sprite numbers. */
98 const SpriteID _signal_lookup[2][SIGTYPE_END] = {
99 {SPR_IMG_SIGNAL_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_EXIT,
100 SPR_IMG_SIGNAL_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_PBS, SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY},
101
102 {SPR_IMG_SIGNAL_SEMAPHORE_NORM, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_EXIT,
103 SPR_IMG_SIGNAL_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_PBS, SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY},
104 };
105
106 for (SignalType type = SIGTYPE_BLOCK; type < SIGTYPE_END; type = (SignalType)(type + 1)) {
107 for (SignalVariant var = SIG_ELECTRIC; var <= SIG_SEMAPHORE; var = (SignalVariant)(var + 1)) {
108 SpriteID red = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_RED, true);
109 SpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_GREEN, true);
110 rti->gui_sprites.signals[type][var][0] = (red != 0) ? red + SIGNAL_TO_SOUTH : _signal_lookup[var][type];
111 rti->gui_sprites.signals[type][var][1] = (green != 0) ? green + SIGNAL_TO_SOUTH : _signal_lookup[var][type] + 1;
112 }
113 }
114}
115
122static bool CompareRailTypes(const RailType &first, const RailType &second)
123{
125}
126
131{
132 _sorted_railtypes.clear();
133 for (RailTypeInfo &rti : _railtypes) {
134 RailType rt = rti.Index();
135
136 ResolveRailTypeGUISprites(&rti);
137 _railtypes_hidden_mask.Set(rt, rti.flags.Test(RailTypeFlag::Hidden));
138
139 if (rti.label == 0) continue;
140 _sorted_railtypes.push_back(rt);
141 }
142 std::sort(_sorted_railtypes.begin(), _sorted_railtypes.end(), CompareRailTypes);
143}
144
150RailType AllocateRailType(RailTypeLabel label)
151{
152 auto it = std::ranges::find(_railtypes, 0, &RailTypeInfo::label);
153 if (it == std::end(_railtypes)) return INVALID_RAILTYPE;
154
155 RailTypeInfo &rti = *it;
156 RailType rt = rti.Index();
157
158 /* Set up new rail type based on default rail. */
160 rti.label = label;
161 rti.alternate_labels.clear();
162
163 /* Make us compatible with ourself. */
164 rti.powered_railtypes = rt;
165 rti.compatible_railtypes = rt;
166
167 /* We also introduce ourself. */
168 rti.introduces_railtypes = rt;
169
170 /* Default sort order; order of allocation, but with some
171 * offsets so it's easier for NewGRF to pick a spot without
172 * changing the order of other (original) rail types.
173 * The << is so you can place other railtypes in between the
174 * other railtypes, the 7 is to be able to place something
175 * before the first (default) rail type. */
176 rti.sorting_order = rt << 4 | 7;
177
178 return rt;
179}
180
181static const uint8_t _track_sloped_sprites[14] = {
182 14, 15, 22, 13,
183 0, 21, 17, 12,
184 23, 0, 18, 20,
185 19, 16
186};
187
188
189/* 4
190 * ---------
191 * |\ /|
192 * | \ 1/ |
193 * | \ / |
194 * | \ / |
195 * 16| \ |32
196 * | / \2 |
197 * | / \ |
198 * | / \ |
199 * |/ \|
200 * ---------
201 * 8
202 */
203
204
205
206/* MAP2 byte: abcd???? => Signal On? Same coding as map3lo
207 * MAP3LO byte: abcd???? => Signal Exists?
208 * a and b are for diagonals, upper and left,
209 * one for each direction. (ie a == NE->SW, b ==
210 * SW->NE, or v.v., I don't know. b and c are
211 * similar for lower and right.
212 * MAP2 byte: ????abcd => Type of ground.
213 * MAP3LO byte: ????abcd => Type of rail.
214 * MAP5: 00abcdef => rail
215 * 01abcdef => rail w/ signals
216 * 10uuuuuu => unused
217 * 11uuuudd => rail depot
218 */
219
229{
230 TrackBits rail_bits = TrackToTrackBits(track);
231 return EnsureNoTrainOnTrackBits(tile, rail_bits);
232}
233
241{
242 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
243
244 /* So, we have a tile with tracks on it (and possibly signals). Let's see
245 * what tracks first */
246 TrackBits current = GetTrackBits(tile); // The current track layout.
247 TrackBits future = current | to_build; // The track layout we want to build.
248
249 /* Are we really building something new? */
250 if (current == future) {
251 /* Nothing new is being built */
252 return CommandCost(STR_ERROR_ALREADY_BUILT);
253 }
254
255 /* Normally, we may overlap and any combination is valid */
256 return CommandCost();
257}
258
259
281
303
312{
313 if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE;
314
315 if (IsSteepSlope(tileh)) {
316 /* Test for inclined foundations */
317 if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X;
318 if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y;
319
320 /* Get higher track */
321 Corner highest_corner = GetHighestSlopeCorner(tileh);
322 TrackBits higher_track = CornerToTrackBits(highest_corner);
323
324 /* Only higher track? */
325 if (bits == higher_track) return HalftileFoundation(highest_corner);
326
327 /* Overlap with higher track? */
328 if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID;
329
330 /* either lower track or both higher and lower track */
331 return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER);
332 } else {
333 if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE;
334
335 bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0);
336
337 Corner track_corner;
338 switch (bits) {
339 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
340 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
341 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
342 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
343
344 case TRACK_BIT_HORZ:
345 if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
346 if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
347 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
348
349 case TRACK_BIT_VERT:
350 if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
351 if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
352 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
353
354 case TRACK_BIT_X:
356 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
357
358 case TRACK_BIT_Y:
360 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
361
362 default:
363 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
364 }
365 /* Single diagonal track */
366
367 /* Track must be at least valid on leveled foundation */
368 if (!valid_on_leveled) return FOUNDATION_INVALID;
369
370 /* If slope has three raised corners, build leveled foundation */
372
373 /* If neighboured corners of track_corner are lowered, build halftile foundation */
374 if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
375
376 /* else special anti-zig-zag foundation */
377 return SpecialRailFoundation(track_corner);
378 }
379}
380
381
391static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
392{
393 /* don't allow building on the lower side of a coast */
394 if (GetFloodingBehaviour(tile) != FLOOD_NONE) {
395 if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
396 }
397
398 Foundation f_new = GetRailFoundation(tileh, rail_bits | existing);
399
400 /* check track/slope combination */
401 if ((f_new == FOUNDATION_INVALID) ||
402 ((f_new != FOUNDATION_NONE) && (!_settings_game.construction.build_on_slopes))) {
403 return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
404 }
405
406 Foundation f_old = GetRailFoundation(tileh, existing);
407 return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price[Price::BuildFoundation] : (Money)0);
408}
409
410/* Validate functions for rail building */
411static inline bool ValParamTrackOrientation(Track track)
412{
413 return IsValidTrack(track);
414}
415
425CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
426{
428
429 if (!ValParamRailType(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
430
431 Slope tileh = GetTileSlope(tile);
432 TrackBits trackbit = TrackToTrackBits(track);
433
434 switch (GetTileType(tile)) {
435 case TileType::Railway: {
437 if (ret.Failed()) return ret;
438
439 if (!IsPlainRail(tile)) return Command<Commands::LandscapeClear>::Do(flags, tile); // just get appropriate error message
440
441 if (!IsCompatibleRail(GetRailType(tile), railtype)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
442
443 ret = CheckTrackCombination(tile, trackbit);
444 if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
445 if (ret.Failed()) return ret;
446
447 ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
448 if (ret.Failed()) return ret;
449 cost.AddCost(ret.GetCost());
450
451 if (HasSignals(tile) && TracksOverlap(GetTrackBits(tile) | TrackToTrackBits(track))) {
452 /* If adding the new track causes any overlap, all signals must be removed first */
453 if (!auto_remove_signals) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
454
455 for (Track track_it = TRACK_BEGIN; track_it < TRACK_END; track_it++) {
456 if (HasTrack(tile, track_it) && HasSignalOnTrack(tile, track_it)) {
457 CommandCost ret_remove_signals = Command<Commands::RemoveSignal>::Do(flags, tile, track_it);
458 if (ret_remove_signals.Failed()) return ret_remove_signals;
459 cost.AddCost(ret_remove_signals.GetCost());
460 }
461 }
462 }
463
464 /* If the rail types don't match, try to convert only if engines of
465 * the new rail type are not powered on the present rail type and engines of
466 * the present rail type are powered on the new rail type. */
467 if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
468 if (HasPowerOnRail(GetRailType(tile), railtype)) {
469 ret = Command<Commands::ConvertRail>::Do(flags, tile, tile, railtype, false);
470 if (ret.Failed()) return ret;
471 cost.AddCost(ret.GetCost());
472 } else {
473 return CMD_ERROR;
474 }
475 }
476
477 if (flags.Test(DoCommandFlag::Execute)) {
479 TrackBits bits = GetTrackBits(tile);
480 SetTrackBits(tile, bits | trackbit);
481 /* Subtract old infrastructure count. */
482 uint pieces = CountBits(bits);
483 if (TracksOverlap(bits)) pieces *= pieces;
484 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
485 /* Add new infrastructure count. */
486 pieces = CountBits(bits | trackbit);
487 if (TracksOverlap(bits | trackbit)) pieces *= pieces;
488 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
490 }
491 break;
492 }
493
494 case TileType::Road: {
495 /* Level crossings may only be built on these slopes */
496 if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
497
498 if (!_settings_game.construction.crossing_with_competitor && _current_company != OWNER_DEITY) {
500 if (ret.Failed()) return ret;
501 }
502
504 if (ret.Failed()) return ret;
505
506 if (IsNormalRoad(tile)) {
507 if (HasRoadWorks(tile)) return CommandCost(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
508
509 if (GetDisallowedRoadDirections(tile) != DRD_NONE) 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
519 RoadBits road = GetRoadBits(tile, RTT_ROAD);
520 RoadBits tram = GetRoadBits(tile, RTT_TRAM);
521 if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) ||
522 (track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) {
523 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
524 Owner tram_owner = GetRoadOwner(tile, RTT_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) && HasExactlyOneBit(tram)) {
528 ret = CheckOwnership(tram_owner);
529 if (ret.Failed()) return ret;
530 }
531
532 uint num_new_road_pieces = (road != ROAD_NONE) ? 2 - CountBits(road) : 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 != ROAD_NONE) ? 2 - CountBits(tram) : 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;
658 DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile.base());
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 }
716 DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile.base());
717 } else {
718 SetTrackBits(tile, present);
720 }
721 }
722 break;
723 }
724
725 default: return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
726 }
727
728 if (flags.Test(DoCommandFlag::Execute)) {
729 /* if we got that far, 'owner' variable is set correctly */
730 assert(Company::IsValidID(owner));
731
733 if (crossing) {
734 /* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
735 * are removing one of these pieces, we'll need to update signals for
736 * both directions explicitly, as after the track is removed it won't
737 * 'connect' with the other piece. */
738 AddTrackToSignalBuffer(tile, TRACK_X, owner);
739 AddTrackToSignalBuffer(tile, TRACK_Y, owner);
742 } else {
743 AddTrackToSignalBuffer(tile, track, owner);
744 YapfNotifyTrackLayoutChange(tile, track);
745 }
746
747 if (v != nullptr) TryPathReserve(v, true);
748 }
749
750 return cost;
751}
752
753
762{
763 assert(IsPlainRailTile(t));
764
765 bool flooded = false;
766 if (GetRailGroundType(t) == RailGroundType::HalfTileWater) return flooded;
767
768 Slope tileh = GetTileSlope(t);
769 TrackBits rail_bits = GetTrackBits(t);
770
771 if (IsSlopeWithOneCornerRaised(tileh)) {
773
774 TrackBits to_remove = lower_track & rail_bits;
775 if (to_remove != TRACK_BIT_NONE) {
777 flooded = Command<Commands::RemoveRail>::Do(DoCommandFlag::Execute, t, FindFirstTrack(to_remove)).Succeeded();
778 cur_company.Restore();
779 if (!flooded) return flooded; // not yet floodable
780 rail_bits = rail_bits & ~to_remove;
781 if (rail_bits == TRACK_BIT_NONE) {
782 MakeShore(t);
784 return flooded;
785 }
786 }
787
788 if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
789 flooded = true;
792 }
793 } else {
794 /* Make shore on steep slopes and 'three-corners-raised'-slopes. */
795 if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), tileh) == 0) {
796 if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) {
797 flooded = true;
800 }
801 }
802 }
803 return flooded;
804}
805
806static const TileIndexDiffC _trackdelta[] = {
807 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
808 { 0, 0 },
809 { 0, 0 },
810 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
811 { 0, 0 },
812 { 0, 0 }
813};
814
815
816static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
817{
818 int x = TileX(start);
819 int y = TileY(start);
820 int ex = TileX(end);
821 int ey = TileY(end);
822
823 if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR;
824
825 /* calculate delta x,y from start to end tile */
826 int dx = ex - x;
827 int dy = ey - y;
828
829 /* calculate delta x,y for the first direction */
830 int trdx = _trackdelta[*trackdir].x;
831 int trdy = _trackdelta[*trackdir].y;
832
833 if (!IsDiagonalTrackdir(*trackdir)) {
834 trdx += _trackdelta[*trackdir ^ 1].x;
835 trdy += _trackdelta[*trackdir ^ 1].y;
836 }
837
838 /* validate the direction */
839 while ((trdx <= 0 && dx > 0) ||
840 (trdx >= 0 && dx < 0) ||
841 (trdy <= 0 && dy > 0) ||
842 (trdy >= 0 && dy < 0)) {
843 if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other
844 SetBit(*trackdir, 3); // reverse the direction
845 trdx = -trdx;
846 trdy = -trdy;
847 } else { // other direction is invalid too, invalid drag
848 return CMD_ERROR;
849 }
850 }
851
852 /* (for diagonal tracks, this is already made sure of by above test), but:
853 * for non-diagonal tracks, check if the start and end tile are on 1 line */
854 if (!IsDiagonalTrackdir(*trackdir)) {
855 trdx = _trackdelta[*trackdir].x;
856 trdy = _trackdelta[*trackdir].y;
857 if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) return CMD_ERROR;
858 }
859
860 return CommandCost();
861}
862
875static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
876{
878
879 if ((!remove && !ValParamRailType(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
880 if (end_tile >= Map::Size() || tile >= Map::Size()) return CMD_ERROR;
881
882 Trackdir trackdir = TrackToTrackdir(track);
883
884 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
885 if (ret.Failed()) return ret;
886
887 bool had_success = false;
888 CommandCost last_error = CMD_ERROR;
889 for (;;) {
890 ret = remove ? Command<Commands::RemoveRail>::Do(flags, tile, TrackdirToTrack(trackdir)) : Command<Commands::BuildRail>::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals);
891 if (!remove && !fail_on_obstacle && last_error.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) had_success = true;
892
893 if (ret.Failed()) {
894 last_error = std::move(ret);
895 if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
896 if (fail_on_obstacle) return last_error;
897 if (had_success) break; // Keep going if we haven't constructed any rail yet, skipping the start of the drag
898 }
899
900 /* Ownership errors are more important. */
901 if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
902 } else {
903 had_success = true;
904 total_cost.AddCost(ret.GetCost());
905 }
906
907 if (tile == end_tile) break;
908
909 tile += ToTileIndexDiff(_trackdelta[trackdir]);
910
911 /* toggle railbit for the non-diagonal tracks */
912 if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
913 }
914
915 if (had_success) return total_cost;
916 return last_error;
917}
918
932CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
933{
934 return CmdRailTrackHelper(flags, start_tile, end_tile, railtype, track, false, auto_remove_signals, fail_on_obstacle);
935}
936
947CommandCost CmdRemoveRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, Track track)
948{
949 return CmdRailTrackHelper(flags, start_tile, end_tile, INVALID_RAILTYPE, track, true, false, false);
950}
951
960CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
961{
962 /* check railtype and valid direction for depot (0 through 3), 4 in total */
963 if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
964
965 Slope tileh = GetTileSlope(tile);
966
968
969 /* Prohibit construction if
970 * The tile is non-flat AND
971 * 1) build-on-slopes is disabled
972 * 2) the tile is steep i.e. spans two height levels
973 * 3) the exit points in the wrong direction
974 */
975
976 if (tileh != SLOPE_FLAT) {
977 if (!_settings_game.construction.build_on_slopes) return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
978 if (!CanBuildDepotByTileh(dir, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
980 }
981
982 /* Allow the user to rotate the depot instead of having to destroy it and build it again */
983 bool rotate_existing_depot = false;
984 if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
986 if (ret.Failed()) return ret;
987
988 if (dir == GetRailDepotDirection(tile)) return CommandCost();
989
990 ret = EnsureNoVehicleOnGround(tile);
991 if (ret.Failed()) return ret;
992
993 rotate_existing_depot = true;
994 }
995
996 if (!rotate_existing_depot) {
997 cost.AddCost(Command<Commands::LandscapeClear>::Do(flags, tile));
998 if (cost.Failed()) return cost;
999
1000 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
1001
1002 if (!Depot::CanAllocateItem()) return CMD_ERROR;
1003 }
1004
1005 if (flags.Test(DoCommandFlag::Execute)) {
1006 if (rotate_existing_depot) {
1007 SetRailDepotExitDirection(tile, dir);
1008 } else {
1009 Depot *d = Depot::Create(tile);
1010
1011 MakeRailDepot(tile, _current_company, d->index, dir, railtype);
1012 MakeDefaultName(d);
1013
1014 Company::Get(_current_company)->infrastructure.rail[railtype]++;
1016 }
1017
1018 MarkTileDirtyByTile(tile);
1021 }
1022
1024 cost.AddCost(RailBuildCost(railtype));
1025 return cost;
1026}
1027
1046CommandCost CmdBuildSingleSignal(DoCommandFlags flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy)
1047{
1048 if (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE) return CMD_ERROR;
1049 if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
1050
1051 if (ctrl_pressed) sigvar = (SignalVariant)(sigvar ^ SIG_SEMAPHORE);
1052
1053 /* You can only build signals on plain rail tiles, and the selected track must exist */
1054 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
1055 !HasTrack(tile, track)) {
1056 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1057 }
1058 /* Protect against invalid signal copying */
1059 if (signals_copy != 0 && (signals_copy & SignalOnTrack(track)) == 0) return CMD_ERROR;
1060
1061 CommandCost ret = CheckTileOwnership(tile);
1062 if (ret.Failed()) return ret;
1063
1064 /* See if this is a valid track combination for signals (no overlap) */
1065 if (TracksOverlap(GetTrackBits(tile))) return CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
1066
1067 /* In case we don't want to change an existing signal, return without error. */
1068 if (skip_existing_signals && HasSignalOnTrack(tile, track)) return CommandCost();
1069
1070 /* you can not convert a signal if no signal is on track */
1071 if (convert_signal && !HasSignalOnTrack(tile, track)) return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1072
1073 CommandCost cost;
1074 if (!HasSignalOnTrack(tile, track)) {
1075 /* build new signals */
1077 } else {
1078 if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
1079 /* convert signals <-> semaphores */
1081
1082 } else if (convert_signal) {
1083 /* convert button pressed */
1084 if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
1085 /* it costs money to change signal variant (light or semaphore) */
1087 } else {
1088 /* it is free to change signal type (block, exit, entry, combo, path, etc) */
1089 cost = CommandCost();
1090 }
1091
1092 } else {
1093 /* it is free to change orientation or number of signals on the tile (for block/presignals which allow signals in both directions) */
1094 cost = CommandCost();
1095 }
1096 }
1097
1098 if (flags.Test(DoCommandFlag::Execute)) {
1099 Train *v = nullptr;
1100 /* The new/changed signal could block our path. As this can lead to
1101 * stale reservations, we clear the path reservation here and try
1102 * to redo it later on. */
1103 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1104 v = GetTrainForReservation(tile, track);
1105 if (v != nullptr) FreeTrainTrackReservation(v);
1106 }
1107
1108 if (!HasSignals(tile)) {
1109 /* there are no signals at all on this tile yet */
1110 SetHasSignals(tile, true);
1111 SetSignalStates(tile, 0xF); // all signals are on
1112 SetPresentSignals(tile, 0); // no signals built by default
1113 SetSignalType(tile, track, sigtype);
1114 SetSignalVariant(tile, track, sigvar);
1115 }
1116
1117 /* Subtract old signal infrastructure count. */
1118 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1119
1120 if (signals_copy == 0) {
1121 if (!HasSignalOnTrack(tile, track)) {
1122 /* build new signals */
1123 SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
1124 SetSignalType(tile, track, sigtype);
1125 SetSignalVariant(tile, track, sigvar);
1126 while (num_dir_cycle-- > 0) CycleSignalSide(tile, track);
1127 } else {
1128 if (convert_signal) {
1129 /* convert signal button pressed */
1130 if (ctrl_pressed) {
1131 /* toggle the present signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */
1132 SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC);
1133 /* Query current signal type so the check for PBS signals below works. */
1134 sigtype = GetSignalType(tile, track);
1135 } else {
1136 /* convert the present signal to the chosen type and variant */
1137 SetSignalType(tile, track, sigtype);
1138 SetSignalVariant(tile, track, sigvar);
1139 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1141 }
1142 }
1143
1144 } else if (ctrl_pressed) {
1145 /* cycle between cycle_start and cycle_end */
1146 sigtype = (SignalType)(GetSignalType(tile, track) + 1);
1147
1148 if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
1149
1150 SetSignalType(tile, track, sigtype);
1151 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1153 }
1154 } else {
1155 /* cycle the signal side: both -> left -> right -> both -> ... */
1156 CycleSignalSide(tile, track);
1157 /* Query current signal type so the check for PBS signals below works. */
1158 sigtype = GetSignalType(tile, track);
1159 }
1160 }
1161 } else {
1162 /* If CmdBuildManySignals is called with copying signals, just copy the
1163 * direction of the first signal given as parameter by CmdBuildManySignals */
1164 SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (signals_copy & SignalOnTrack(track)));
1165 SetSignalVariant(tile, track, sigvar);
1166 SetSignalType(tile, track, sigtype);
1167 }
1168
1169 /* Add new signal infrastructure count. */
1170 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1172
1173 if (IsPbsSignal(sigtype)) {
1174 /* PBS signals should show red unless they are on reserved tiles without a train. */
1175 uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
1176 SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetRailReservationTrackBits(tile), track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask));
1177 }
1178 MarkTileDirtyByTile(tile);
1180 YapfNotifyTrackLayoutChange(tile, track);
1181 if (v != nullptr && v->track != TRACK_BIT_DEPOT) {
1182 /* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1183 if (!((v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) ||
1184 !IsSafeWaitingPosition(v, v->tile, v->GetVehicleTrackdir(), true, _settings_game.pf.forbid_90_deg)) {
1185 TryPathReserve(v, true);
1186 }
1187 }
1188 }
1189
1190 return cost;
1191}
1192
1193static bool AdvanceSignalAutoFill(TileIndex &tile, Trackdir &trackdir, bool remove)
1194{
1195 /* We only process starting tiles of tunnels or bridges so jump to the other end before moving further. */
1197
1198 tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
1199 if (tile == INVALID_TILE) return false;
1200
1201 /* Check for track bits on the new tile */
1203
1204 if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
1205 trackdirbits &= TrackdirReachesTrackdirs(trackdir);
1206
1207 /* No track bits, must stop */
1208 if (trackdirbits == TRACKDIR_BIT_NONE) return false;
1209
1210 /* Get the first track dir */
1211 trackdir = RemoveFirstTrackdir(&trackdirbits);
1212
1213 /* Any left? It's a junction so we stop */
1214 if (trackdirbits != TRACKDIR_BIT_NONE) return false;
1215
1216 switch (GetTileType(tile)) {
1217 case TileType::Railway:
1218 if (IsRailDepot(tile)) return false;
1219 if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1220 break;
1221
1222 case TileType::Road:
1223 if (!IsLevelCrossing(tile)) return false;
1224 break;
1225
1227 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
1228 if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
1229 break;
1230 }
1231
1232 default: return false;
1233 }
1234 return true;
1235}
1236
1252static 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)
1253{
1255
1256 if (end_tile >= Map::Size() || !ValParamTrackOrientation(track)) return CMD_ERROR;
1257 if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
1258 if (!remove && (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE)) return CMD_ERROR;
1259
1260 if (!IsPlainRailTile(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1261 TileIndex start_tile = tile;
1262
1263 /* Interpret signal_density as the logical length of said amount of tiles in X/Y direction. */
1264 signal_density *= TILE_AXIAL_DISTANCE;
1265
1266 Trackdir trackdir = TrackToTrackdir(track);
1267 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
1268 if (ret.Failed()) return ret;
1269
1270 track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
1271 Trackdir start_trackdir = trackdir;
1272
1273 /* Must start on a valid track to be able to avoid loops */
1274 if (!HasTrack(tile, track)) return CMD_ERROR;
1275
1276 uint8_t signals;
1277 /* copy the signal-style of the first rail-piece if existing */
1278 if (HasSignalOnTrack(tile, track)) {
1279 signals = GetPresentSignals(tile) & SignalOnTrack(track);
1280 assert(signals != 0);
1281
1282 /* copy signal/semaphores style (independent of CTRL) */
1283 sigvar = GetSignalVariant(tile, track);
1284
1285 sigtype = GetSignalType(tile, track);
1286 /* Don't but copy entry or exit-signal type */
1287 if (sigtype == SIGTYPE_ENTRY || sigtype == SIGTYPE_EXIT) sigtype = SIGTYPE_BLOCK;
1288 } else { // no signals exist, drag a two-way signal stretch
1289 signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
1290 }
1291
1292 uint8_t signal_dir = 0;
1293 if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0);
1294 if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
1295
1296 /* signal_ctr - amount of tiles already processed
1297 * last_used_ctr - amount of tiles before previously placed signal
1298 * signals_density - setting to put signal on every Nth tile (double space on |, -- tracks)
1299 * last_suitable_ctr - amount of tiles before last possible signal place
1300 * last_suitable_tile - last tile where it is possible to place a signal
1301 * last_suitable_trackdir - trackdir of the last tile
1302 **********
1303 * trackdir - trackdir to build with autorail
1304 * semaphores - semaphores or signals
1305 * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1306 * and convert all others to semaphore/signal
1307 * remove - 1 remove signals, 0 build signals */
1308 int signal_ctr = 0;
1309 int last_used_ctr = -signal_density; // to force signal at first tile
1310 int last_suitable_ctr = 0;
1311 TileIndex last_suitable_tile = INVALID_TILE;
1312 Trackdir last_suitable_trackdir = INVALID_TRACKDIR;
1313 CommandCost last_error = CMD_ERROR;
1314 bool had_success = false;
1315 auto build_signal = [&](TileIndex tile, Trackdir trackdir, bool test_only) {
1316 /* Pick the correct orientation for the track direction */
1317 uint8_t signals = 0;
1318 if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
1319 if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
1320
1321 DoCommandFlags do_flags = test_only ? DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) : flags;
1322 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);
1323
1324 if (test_only) return ret.Succeeded();
1325
1326 if (ret.Succeeded()) {
1327 had_success = true;
1328 total_cost.AddCost(ret.GetCost());
1329 } else {
1330 /* The "No railway" error is the least important one. */
1331 if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
1332 last_error.GetErrorMessage() == INVALID_STRING_ID) {
1333 last_error = ret;
1334 }
1335 }
1336 return ret.Succeeded();
1337 };
1338
1339 for (;;) {
1340 if (remove) {
1341 /* In remove mode last_* stuff doesn't matter, we simply try to clear every tile. */
1342 build_signal(tile, trackdir, false);
1343 } else if (minimise_gaps) {
1344 /* We're trying to minimize gaps wherever possible, so keep track of last suitable
1345 * position and use it if current gap exceeds required signal density. */
1346
1347 if (signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1348 /* We overshot so build a signal in last good location. */
1349 if (build_signal(last_suitable_tile, last_suitable_trackdir, false)) {
1350 last_suitable_tile = INVALID_TILE;
1351 last_used_ctr = last_suitable_ctr;
1352 }
1353 }
1354
1355 if (signal_ctr == last_used_ctr + signal_density) {
1356 /* Current gap matches the required density, build a signal. */
1357 if (build_signal(tile, trackdir, false)) {
1358 last_used_ctr = signal_ctr;
1359 last_suitable_tile = INVALID_TILE;
1360 }
1361 } else {
1362 /* Test tile for a potential signal spot. */
1363 if (build_signal(tile, trackdir, true)) {
1364 last_suitable_tile = tile;
1365 last_suitable_ctr = signal_ctr;
1366 last_suitable_trackdir = trackdir;
1367 }
1368 }
1369 } else if (signal_ctr >= last_used_ctr + signal_density) {
1370 /* We're always keeping regular interval between signals so doesn't matter whether we succeed or not. */
1371 build_signal(tile, trackdir, false);
1372 last_used_ctr = signal_ctr;
1373 }
1374
1375 if (autofill) {
1376 switch (GetTileType(tile)) {
1377 case TileType::Railway:
1378 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1379 break;
1380
1381 case TileType::Road:
1382 signal_ctr += TILE_AXIAL_DISTANCE;
1383 break;
1384
1387 if (remove || minimise_gaps) {
1388 signal_ctr += len;
1389 } else {
1390 /* To keep regular interval we need to emulate placing signals on a bridge.
1391 * We start with TILE_AXIAL_DISTANCE as one bridge tile gets processed in the main loop. */
1392 signal_ctr += TILE_AXIAL_DISTANCE;
1393 for (uint i = TILE_AXIAL_DISTANCE; i < len; i += TILE_AXIAL_DISTANCE) {
1394 if (signal_ctr >= last_used_ctr + signal_density) last_used_ctr = signal_ctr;
1395 signal_ctr += TILE_AXIAL_DISTANCE;
1396 }
1397 }
1398 break;
1399 }
1400
1401 default: break;
1402 }
1403
1404 if (!AdvanceSignalAutoFill(tile, trackdir, remove)) break;
1405
1406 /* Prevent possible loops */
1407 if (tile == start_tile && trackdir == start_trackdir) break;
1408 } else {
1409 if (tile == end_tile) break;
1410
1411 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1412 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1413
1414 tile += ToTileIndexDiff(_trackdelta[trackdir]);
1415 if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
1416 }
1417 }
1418
1419 /* We may end up with the current gap exceeding the signal density so fix that if needed. */
1420 if (!remove && minimise_gaps && signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1421 build_signal(last_suitable_tile, last_suitable_trackdir, false);
1422 }
1423
1424 return had_success ? total_cost : last_error;
1425}
1426
1443CommandCost 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)
1444{
1445 return CmdSignalTrackHelper(flags, tile, end_tile, track, sigtype, sigvar, mode, false, autofill, minimise_gaps, signal_density);
1446}
1447
1455CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
1456{
1457 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
1458 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1459 }
1460 if (!HasSignalOnTrack(tile, track)) {
1461 return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1462 }
1463
1464 /* Only water can remove signals from anyone */
1466 CommandCost ret = CheckTileOwnership(tile);
1467 if (ret.Failed()) return ret;
1468 }
1469
1470 /* Do it? */
1471 if (flags.Test(DoCommandFlag::Execute)) {
1472 Train *v = nullptr;
1473 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1474 v = GetTrainForReservation(tile, track);
1475 } else if (IsPbsSignal(GetSignalType(tile, track))) {
1476 /* PBS signal, might be the end of a path reservation. */
1477 Trackdir td = TrackToTrackdir(track);
1478 for (int i = 0; v == nullptr && i < 2; i++, td = ReverseTrackdir(td)) {
1479 /* Only test the active signal side. */
1480 if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
1483 if (HasReservedTracks(next, tracks)) {
1485 }
1486 }
1487 }
1488 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1489 SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
1490 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1492
1493 /* removed last signal from tile? */
1494 if (GetPresentSignals(tile) == 0) {
1495 SetSignalStates(tile, 0);
1496 SetHasSignals(tile, false);
1497 SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores
1498 }
1499
1500 AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
1501 YapfNotifyTrackLayoutChange(tile, track);
1502 if (v != nullptr) TryPathReserve(v, false);
1503
1504 MarkTileDirtyByTile(tile);
1505 }
1506
1508}
1509
1521CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
1522{
1523 return CmdSignalTrackHelper(flags, tile, end_tile, track, SIGTYPE_BLOCK, SIG_ELECTRIC, false, true, autofill, false, 1); // bit 5 is remove bit
1524}
1525
1536CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
1537{
1538 TileIndex area_end = tile;
1539
1540 if (!ValParamRailType(totype)) return CMD_ERROR;
1541 if (area_start >= Map::Size()) return CMD_ERROR;
1542
1543 TrainList affected_trains;
1544
1546 CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
1547 bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
1548
1549 std::unique_ptr<TileIterator> iter = TileIterator::Create(area_start, area_end, diagonal);
1550 for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
1551 TileType tt = GetTileType(tile);
1552
1553 /* Check if there is any track on tile */
1554 switch (tt) {
1555 case TileType::Railway:
1556 break;
1557 case TileType::Station:
1558 if (!HasStationRail(tile)) continue;
1559 break;
1560 case TileType::Road:
1561 if (!IsLevelCrossing(tile)) continue;
1562 if (RailNoLevelCrossings(totype)) {
1563 error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
1564 continue;
1565 }
1566 break;
1568 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
1569 break;
1570 default: continue;
1571 }
1572
1573 /* Original railtype we are converting from */
1574 RailType type = GetRailType(tile);
1575
1576 /* Converting to the same type or converting 'hidden' elrail -> rail */
1577 if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
1578
1579 /* Trying to convert other's rail */
1580 CommandCost ret = CheckTileOwnership(tile);
1581 if (ret.Failed()) {
1582 error = std::move(ret);
1583 continue;
1584 }
1585
1586 std::vector<Train *> vehicles_affected;
1587
1588 /* Vehicle on the tile when not converting Rail <-> ElRail
1589 * Tunnels and bridges have special check later */
1590 if (tt != TileType::TunnelBridge) {
1591 if (!IsCompatibleRail(type, totype)) {
1593 if (ret.Failed()) {
1594 error = std::move(ret);
1595 continue;
1596 }
1597 }
1598 if (flags.Test(DoCommandFlag::Execute)) { // we can safely convert, too
1599 TrackBits reserved = GetReservedTrackbits(tile);
1600 Track track;
1601 while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
1602 Train *v = GetTrainForReservation(tile, track);
1603 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1604 /* No power on new rail type, reroute. */
1606 vehicles_affected.push_back(v);
1607 }
1608 }
1609
1610 /* Update the company infrastructure counters. */
1611 if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
1612 Company *c = Company::Get(GetTileOwner(tile));
1613 uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
1614 if (IsPlainRailTile(tile)) {
1615 TrackBits bits = GetTrackBits(tile);
1616 num_pieces = CountBits(bits);
1617 if (TracksOverlap(bits)) num_pieces *= num_pieces;
1618 }
1619 c->infrastructure.rail[type] -= num_pieces;
1620 c->infrastructure.rail[totype] += num_pieces;
1622 }
1623
1624 SetRailType(tile, totype);
1625 MarkTileDirtyByTile(tile);
1626 /* update power of train on this tile */
1627 for (Vehicle *v : VehiclesOnTile(tile)) {
1628 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1629 }
1630 }
1631 }
1632
1633 switch (tt) {
1634 default: NOT_REACHED();
1635 case TileType::Railway:
1636 switch (GetRailTileType(tile)) {
1638 if (flags.Test(DoCommandFlag::Execute)) {
1639 /* notify YAPF about the track layout change */
1641
1642 /* Update build vehicle window related to this depot */
1645 }
1646 found_convertible_track = true;
1647 cost.AddCost(RailConvertCost(type, totype));
1648 break;
1649
1650 default: // RailTileType::Normal, RailTileType::Signals
1651 if (flags.Test(DoCommandFlag::Execute)) {
1652 /* notify YAPF about the track layout change */
1653 TrackBits tracks = GetTrackBits(tile);
1654 while (tracks != TRACK_BIT_NONE) {
1656 }
1657 }
1658 found_convertible_track = true;
1659 cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
1660 break;
1661 }
1662 break;
1663
1665 TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
1666
1667 /* If both ends of tunnel/bridge are in the range, do not try to convert twice -
1668 * it would cause assert because of different test and exec runs */
1669 if (endtile < tile) {
1670 if (diagonal) {
1671 if (DiagonalTileArea(area_start, area_end).Contains(endtile)) continue;
1672 } else {
1673 if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
1674 }
1675 }
1676
1677 /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
1678 if (!IsCompatibleRail(GetRailType(tile), totype)) {
1679 ret = TunnelBridgeIsFree(tile, endtile);
1680 if (ret.Failed()) {
1681 error = std::move(ret);
1682 continue;
1683 }
1684 }
1685
1686 if (flags.Test(DoCommandFlag::Execute)) {
1688 if (HasTunnelBridgeReservation(tile)) {
1689 Train *v = GetTrainForReservation(tile, track);
1690 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1691 /* No power on new rail type, reroute. */
1693 vehicles_affected.push_back(v);
1694 }
1695 }
1696
1697 /* Update the company infrastructure counters. */
1698 uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
1699 Company *c = Company::Get(GetTileOwner(tile));
1700 c->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1701 c->infrastructure.rail[totype] += num_pieces;
1703
1704 SetRailType(tile, totype);
1705 SetRailType(endtile, totype);
1706
1707 for (Vehicle *v : VehiclesOnTile(tile)) {
1708 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1709 }
1710 for (Vehicle *v : VehiclesOnTile(endtile)) {
1711 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1712 }
1713
1714 YapfNotifyTrackLayoutChange(tile, track);
1715 YapfNotifyTrackLayoutChange(endtile, track);
1716
1717 if (IsBridge(tile)) {
1718 MarkBridgeDirty(tile);
1719 } else {
1720 MarkTileDirtyByTile(tile);
1721 MarkTileDirtyByTile(endtile);
1722 }
1723 }
1724
1725 found_convertible_track = true;
1726 cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
1727 break;
1728 }
1729
1730 case TileType::Station:
1731 case TileType::Road:
1732 if (flags.Test(DoCommandFlag::Execute)) {
1733 Track track = ((tt == TileType::Station) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile));
1734 YapfNotifyTrackLayoutChange(tile, track);
1735 }
1736
1737 found_convertible_track = true;
1738 cost.AddCost(RailConvertCost(type, totype));
1739 break;
1740 }
1741
1742 for (uint i = 0; i < vehicles_affected.size(); ++i) {
1743 TryPathReserve(vehicles_affected[i], true);
1744 }
1745 }
1746
1747 if (flags.Test(DoCommandFlag::Execute)) {
1748 /* Railtype changed, update trains as when entering different track */
1749 for (Train *v : affected_trains) {
1750 v->ConsistChanged(CCF_TRACK);
1751 }
1752 }
1753
1754 return found_convertible_track ? cost : error;
1755}
1756
1757static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlags flags)
1758{
1760 CommandCost ret = CheckTileOwnership(tile);
1761 if (ret.Failed()) return ret;
1762 }
1763
1765 if (ret.Failed()) return ret;
1766
1767 if (flags.Test(DoCommandFlag::Execute)) {
1768 /* read variables before the depot is removed */
1770 Owner owner = GetTileOwner(tile);
1771 Train *v = nullptr;
1772
1773 if (HasDepotReservation(tile)) {
1775 if (v != nullptr) FreeTrainTrackReservation(v);
1776 }
1777
1778 Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
1780
1781 delete Depot::GetByTile(tile);
1782 DoClearSquare(tile);
1783 AddSideToSignalBuffer(tile, dir, owner);
1785 if (v != nullptr) TryPathReserve(v, true);
1786 }
1787
1789}
1790
1792static CommandCost ClearTile_Rail(TileIndex tile, DoCommandFlags flags)
1793{
1795
1796 if (flags.Test(DoCommandFlag::Auto)) {
1797 if (!IsTileOwner(tile, _current_company)) {
1798 return CommandCost(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
1799 }
1800
1801 if (IsPlainRail(tile)) {
1802 return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
1803 } else {
1804 return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
1805 }
1806 }
1807
1808 switch (GetRailTileType(tile)) {
1810 case RailTileType::Normal: {
1811 Slope tileh = GetTileSlope(tile);
1812 /* Is there flat water on the lower halftile that gets cleared expensively? */
1813 bool water_ground = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh));
1814
1815 TrackBits tracks = GetTrackBits(tile);
1816 while (tracks != TRACK_BIT_NONE) {
1817 Track track = RemoveFirstTrack(&tracks);
1818 CommandCost ret = Command<Commands::RemoveRail>::Do(flags, tile, track);
1819 if (ret.Failed()) return ret;
1820 cost.AddCost(ret.GetCost());
1821 }
1822
1823 /* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
1824 * Same holds for non-companies clearing the tile, e.g. disasters. */
1825 if (water_ground && !flags.Test(DoCommandFlag::Bankrupt) && Company::IsValidID(_current_company)) {
1827 if (ret.Failed()) return ret;
1828
1829 /* The track was removed, and left a coast tile. Now also clear the water. */
1830 if (flags.Test(DoCommandFlag::Execute)) {
1831 DoClearSquare(tile);
1832 }
1834 }
1835
1836 return cost;
1837 }
1838
1840 return RemoveTrainDepot(tile, flags);
1841
1842 default:
1843 return CMD_ERROR;
1844 }
1845}
1846
1855static uint GetSafeSlopeZ(uint x, uint y, Track track)
1856{
1857 switch (track) {
1858 case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break;
1859 case TRACK_LOWER: x |= 0xF; y |= 0xF; break;
1860 case TRACK_LEFT: x |= 0xF; y &= ~0xF; break;
1861 case TRACK_RIGHT: x &= ~0xF; y |= 0xF; break;
1862 default: break;
1863 }
1864 return GetSlopePixelZ(x, y);
1865}
1866
1867static void DrawSingleSignal(TileIndex tile, const RailTypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos)
1868{
1869 bool side;
1870 switch (_settings_game.construction.train_signal_side) {
1871 case 0: side = false; break; // left
1872 case 2: side = true; break; // right
1873 default: side = _settings_game.vehicle.road_side != 0; break; // driving side
1874 }
1875 static const Point SignalPositions[2][12] = {
1876 { // Signals on the left side
1877 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1878 { 8, 5}, {14, 1}, { 1, 14}, { 9, 11}, { 1, 0}, { 3, 10},
1879 /* LOWER LOWER X X Y Y */
1880 {11, 4}, {14, 14}, {11, 3}, { 4, 13}, { 3, 4}, {11, 13}
1881 }, { // Signals on the right side
1882 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1883 {14, 1}, {12, 10}, { 4, 6}, { 1, 14}, {10, 4}, { 0, 1},
1884 /* LOWER LOWER X X Y Y */
1885 {14, 14}, { 5, 12}, {11, 13}, { 4, 3}, {13, 4}, { 3, 11}
1886 }
1887 };
1888
1889 uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x;
1890 uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y;
1891
1892 SignalType type = GetSignalType(tile, track);
1893 SignalVariant variant = GetSignalVariant(tile, track);
1894
1895 SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition);
1896 if (sprite != 0) {
1897 sprite += image;
1898 } else {
1899 /* Normal electric signals are stored in a different sprite block than all other signals. */
1900 sprite = (type == SIGTYPE_BLOCK && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
1901 sprite += type * 16 + variant * 64 + image * 2 + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0);
1902 }
1903
1904 AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, GetSafeSlopeZ(x, y, track), {{}, {1, 1, BB_HEIGHT_UNDER_BRIDGE}, {}});
1905}
1906
1908struct FenceOffset : SpriteBounds {
1910
1911 constexpr FenceOffset(Corner height_ref, int8_t origin_x, int8_t origin_y, uint8_t extent_x, uint8_t extent_y) :
1912 SpriteBounds({origin_x, origin_y, 0}, {extent_x, extent_y, 4}, {}), height_ref(height_ref) {}
1913};
1914
1916static const FenceOffset _fence_offsets[] = {
1917 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_FLAT_X_NW
1918 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_FLAT_Y_NE
1919 { CORNER_W, 8, 8, 1, 1 }, // RFO_FLAT_LEFT
1920 { CORNER_N, 8, 8, 1, 1 }, // RFO_FLAT_UPPER
1921 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_SW_NW
1922 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_SE_NE
1923 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_NE_NW
1924 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_NW_NE
1925 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_FLAT_X_SE
1926 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_FLAT_Y_SW
1927 { CORNER_E, 8, 8, 1, 1 }, // RFO_FLAT_RIGHT
1928 { CORNER_S, 8, 8, 1, 1 }, // RFO_FLAT_LOWER
1929 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_SW_SE
1930 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_SE_SW
1931 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_NE_SE
1932 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_NW_SW
1933};
1934
1942static void DrawTrackFence(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites, RailFenceOffset rfo)
1943{
1944 int z = ti->z;
1945 if (_fence_offsets[rfo].height_ref != CORNER_INVALID) {
1947 }
1948 AddSortableSpriteToDraw(psid.sprite + (rfo % num_sprites), psid.pal, ti->x, ti->y, z, _fence_offsets[rfo]);
1949}
1950
1957static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1958{
1960 if (ti->tileh & SLOPE_NW) rfo = (ti->tileh & SLOPE_W) ? RFO_SLOPE_SW_NW : RFO_SLOPE_NE_NW;
1961 DrawTrackFence(ti, psid, num_sprites, rfo);
1962}
1963
1970static void DrawTrackFence_SE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1971{
1973 if (ti->tileh & SLOPE_SE) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SW_SE : RFO_SLOPE_NE_SE;
1974 DrawTrackFence(ti, psid, num_sprites, rfo);
1975}
1976
1983static void DrawTrackFence_NE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1984{
1986 if (ti->tileh & SLOPE_NE) rfo = (ti->tileh & SLOPE_E) ? RFO_SLOPE_SE_NE : RFO_SLOPE_NW_NE;
1987 DrawTrackFence(ti, psid, num_sprites, rfo);
1988}
1989
1996static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1997{
1999 if (ti->tileh & SLOPE_SW) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SE_SW : RFO_SLOPE_NW_SW;
2000 DrawTrackFence(ti, psid, num_sprites, rfo);
2001}
2002
2009static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
2010{
2011 /* Base sprite for track fences.
2012 * Note: Halftile slopes only have fences on the upper part. */
2013 uint num_sprites = 0;
2014 PalSpriteID psid{
2015 .sprite = GetCustomRailSprite(rti, ti->tile, RTSG_FENCES, IsHalftileSlope(ti->tileh) ? TCX_UPPER_HALFTILE : TCX_NORMAL, &num_sprites),
2016 .pal = pal,
2017 };
2018 if (psid.sprite == 0) {
2019 psid.sprite = SPR_TRACK_FENCE_FLAT_X;
2020 num_sprites = 8;
2021 }
2022
2023 assert(num_sprites > 0);
2024
2025 switch (GetRailGroundType(ti->tile)) {
2026 case RailGroundType::FenceNW: DrawTrackFence_NW(ti, psid, num_sprites); break;
2027 case RailGroundType::FenceSE: DrawTrackFence_SE(ti, psid, num_sprites); break;
2028 case RailGroundType::FenceSENW: DrawTrackFence_NW(ti, psid, num_sprites);
2029 DrawTrackFence_SE(ti, psid, num_sprites); break;
2030 case RailGroundType::FenceNE: DrawTrackFence_NE(ti, psid, num_sprites); break;
2031 case RailGroundType::FenceSW: DrawTrackFence_SW(ti, psid, num_sprites); break;
2032 case RailGroundType::FenceNESW: DrawTrackFence_NE(ti, psid, num_sprites);
2033 DrawTrackFence_SW(ti, psid, num_sprites); break;
2034 case RailGroundType::FenceVert1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2035 case RailGroundType::FenceVert2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2036 case RailGroundType::FenceHoriz1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2037 case RailGroundType::FenceHoriz2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2039 Corner track_corner;
2040 if (IsHalftileSlope(ti->tileh)) {
2041 /* Steep slope or one-corner-raised slope with halftile foundation */
2042 track_corner = GetHalftileSlopeCorner(ti->tileh);
2043 } else {
2044 /* Three-corner-raised slope */
2046 }
2047 switch (track_corner) {
2048 case CORNER_W: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2049 case CORNER_S: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2050 case CORNER_E: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2051 case CORNER_N: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2052 default: NOT_REACHED();
2053 }
2054 break;
2055 }
2056 default: break;
2057 }
2058}
2059
2060/* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */
2061static const int INF = 1000;
2062static const SubSprite _halftile_sub_sprite[4] = {
2063 { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right
2064 { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top
2065 { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left
2066 { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2067};
2068
2069static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
2070{
2071 DrawGroundSprite(sprite, pal, nullptr, 0, (ti->tileh & s) ? -8 : 0);
2072}
2073
2074static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti)
2075{
2077 Foundation f = GetRailFoundation(ti->tileh, track);
2078 Corner halftile_corner = CORNER_INVALID;
2079
2081 /* Save halftile corner */
2083 /* Draw lower part first */
2084 track &= ~CornerToTrackBits(halftile_corner);
2086 }
2087
2088 DrawFoundation(ti, f);
2089 /* DrawFoundation modifies ti */
2090
2091 /* Draw ground */
2092 if (rgt == RailGroundType::HalfTileWater) {
2093 if (track != TRACK_BIT_NONE || IsSteepSlope(ti->tileh)) {
2094 /* three-corner-raised slope or steep slope with track on upper part */
2095 DrawShoreTile(ti->tileh);
2096 } else {
2097 /* single-corner-raised slope with track on upper part */
2098 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
2099 }
2100 } else {
2101 SpriteID image;
2102
2103 switch (rgt) {
2104 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2105 case RailGroundType::SnowOrDesert: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2106 default: image = SPR_FLAT_GRASS_TILE; break;
2107 }
2108
2109 image += SlopeToSpriteOffset(ti->tileh);
2110
2111 DrawGroundSprite(image, PAL_NONE);
2112 }
2113
2114 bool no_combine = ti->tileh == SLOPE_FLAT && rti->flags.Test(RailTypeFlag::NoSpriteCombine);
2115 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
2116 SpriteID ground = GetCustomRailSprite(rti, ti->tile, no_combine ? RTSG_GROUND_COMPLETE : RTSG_GROUND);
2117 TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE;
2118
2119 if (track == TRACK_BIT_NONE) {
2120 /* Half-tile foundation, no track here? */
2121 } else if (no_combine) {
2122 /* Use trackbits as direct index from ground sprite, subtract 1
2123 * because there is no sprite for no bits. */
2124 DrawGroundSprite(ground + track - 1, PAL_NONE);
2125
2126 /* Draw reserved track bits */
2127 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2128 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2129 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2130 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2131 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2132 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2133 } else if (ti->tileh == SLOPE_NW && track == TRACK_BIT_Y) {
2134 DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
2136 } else if (ti->tileh == SLOPE_NE && track == TRACK_BIT_X) {
2137 DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
2139 } else if (ti->tileh == SLOPE_SE && track == TRACK_BIT_Y) {
2140 DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
2142 } else if (ti->tileh == SLOPE_SW && track == TRACK_BIT_X) {
2143 DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
2145 } else {
2146 switch (track) {
2147 /* Draw single ground sprite when not overlapping. No track overlay
2148 * is necessary for these sprites. */
2149 case TRACK_BIT_X: DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
2150 case TRACK_BIT_Y: DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
2151 case TRACK_BIT_UPPER: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
2152 case TRACK_BIT_LOWER: DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2153 case TRACK_BIT_RIGHT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
2154 case TRACK_BIT_LEFT: DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2155 case TRACK_BIT_CROSS: DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
2156 case TRACK_BIT_HORZ: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
2157 DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2158 case TRACK_BIT_VERT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
2159 DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2160
2161 default:
2162 /* We're drawing a junction tile */
2163 if ((track & TRACK_BIT_3WAY_NE) == 0) {
2164 DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
2165 } else if ((track & TRACK_BIT_3WAY_SW) == 0) {
2166 DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
2167 } else if ((track & TRACK_BIT_3WAY_NW) == 0) {
2168 DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
2169 } else if ((track & TRACK_BIT_3WAY_SE) == 0) {
2170 DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
2171 } else {
2172 DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2173 }
2174
2175 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2176 track &= ~pbs;
2177
2178 /* Draw regular track bits */
2179 if (track & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PAL_NONE);
2180 if (track & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
2181 if (track & TRACK_BIT_UPPER) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
2182 if (track & TRACK_BIT_LOWER) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
2183 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
2184 if (track & TRACK_BIT_LEFT) DrawGroundSprite(overlay + RTO_W, PAL_NONE);
2185 }
2186
2187 /* Draw reserved track bits */
2188 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2189 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2190 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2191 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2192 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2193 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2194 }
2195
2196 if (IsValidCorner(halftile_corner)) {
2197 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2200
2201 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2202 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2203
2204 SpriteID image;
2205 switch (rgt) {
2206 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2208 case RailGroundType::HalfTileSnow: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2209 default: image = SPR_FLAT_GRASS_TILE; break;
2210 }
2211
2212 image += SlopeToSpriteOffset(fake_slope);
2213
2214 DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
2215
2216 track = CornerToTrackBits(halftile_corner);
2217
2218 int offset;
2219 switch (track) {
2220 default: NOT_REACHED();
2221 case TRACK_BIT_UPPER: offset = RTO_N; break;
2222 case TRACK_BIT_LOWER: offset = RTO_S; break;
2223 case TRACK_BIT_RIGHT: offset = RTO_E; break;
2224 case TRACK_BIT_LEFT: offset = RTO_W; break;
2225 }
2226
2227 DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
2228 if (_settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, track)) {
2229 DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
2230 }
2231 }
2232}
2233
2242{
2243 /* If none of the tracks end up in the NE corner, return the ground sprite
2244 * where the NE of the tile is not covered. Repeat for the other directions.
2245 * What remains are junctions where all directions are covered. */
2246 if ((track & TRACK_BIT_3WAY_NE) == 0) return 0;
2247 if ((track & TRACK_BIT_3WAY_SW) == 0) return 1;
2248 if ((track & TRACK_BIT_3WAY_NW) == 0) return 2;
2249 if ((track & TRACK_BIT_3WAY_SE) == 0) return 3;
2250 return 4;
2251}
2252
2258static void DrawTrackBits(TileInfo *ti, TrackBits track)
2259{
2260 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2261
2262 if (rti->UsesOverlay()) {
2263 DrawTrackBitsOverlay(ti, track, rti);
2264 return;
2265 }
2266
2268 Foundation f = GetRailFoundation(ti->tileh, track);
2269 Corner halftile_corner = CORNER_INVALID;
2270
2272 /* Save halftile corner */
2274 /* Draw lower part first */
2275 track &= ~CornerToTrackBits(halftile_corner);
2277 }
2278
2279 DrawFoundation(ti, f);
2280 /* DrawFoundation modifies ti */
2281
2282 SpriteID image;
2283 PaletteID pal = PAL_NONE;
2284 const SubSprite *sub = nullptr;
2285 bool junction = false;
2286
2287 /* Select the sprite to use. */
2288 if (track == 0) {
2289 /* Clear ground (only track on halftile foundation) */
2290 if (rgt == RailGroundType::HalfTileWater) {
2291 if (IsSteepSlope(ti->tileh)) {
2292 DrawShoreTile(ti->tileh);
2293 image = 0;
2294 } else {
2295 image = SPR_FLAT_WATER_TILE;
2296 }
2297 } else {
2298 switch (rgt) {
2299 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2300 case RailGroundType::SnowOrDesert: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2301 default: image = SPR_FLAT_GRASS_TILE; break;
2302 }
2303 image += SlopeToSpriteOffset(ti->tileh);
2304 }
2305 } else {
2306 if (ti->tileh != SLOPE_FLAT) {
2307 /* track on non-flat ground */
2308 image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
2309 } else {
2310 /* track on flat ground */
2311 switch (track) {
2312 /* single track, select combined track + ground sprite*/
2313 case TRACK_BIT_Y: image = rti->base_sprites.track_y; break;
2314 case TRACK_BIT_X: image = rti->base_sprites.track_y + 1; break;
2315 case TRACK_BIT_UPPER: image = rti->base_sprites.track_y + 2; break;
2316 case TRACK_BIT_LOWER: image = rti->base_sprites.track_y + 3; break;
2317 case TRACK_BIT_RIGHT: image = rti->base_sprites.track_y + 4; break;
2318 case TRACK_BIT_LEFT: image = rti->base_sprites.track_y + 5; break;
2319 case TRACK_BIT_CROSS: image = rti->base_sprites.track_y + 6; break;
2320
2321 /* double diagonal track, select combined track + ground sprite*/
2322 case TRACK_BIT_HORZ: image = rti->base_sprites.track_ns; break;
2323 case TRACK_BIT_VERT: image = rti->base_sprites.track_ns + 1; break;
2324
2325 /* junction, select only ground sprite, handle track sprite later */
2326 default:
2327 junction = true;
2329 break;
2330 }
2331 }
2332
2333 switch (rgt) {
2335 case RailGroundType::SnowOrDesert: image += rti->snow_offset; break;
2337 /* three-corner-raised slope */
2338 DrawShoreTile(ti->tileh);
2340 sub = &(_halftile_sub_sprite[track_corner]);
2341 break;
2342 }
2343 default: break;
2344 }
2345 }
2346
2347 if (image != 0) DrawGroundSprite(image, pal, sub);
2348
2349 /* Draw track pieces individually for junction tiles */
2350 if (junction) {
2351 if (track & TRACK_BIT_X) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE);
2352 if (track & TRACK_BIT_Y) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE);
2353 if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE);
2354 if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE);
2355 if (track & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
2356 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
2357 }
2358
2359 /* PBS debugging, draw reserved tracks darker */
2360 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) {
2361 /* Get reservation, but mask track on halftile slope */
2362 TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
2363 if (pbs & TRACK_BIT_X) {
2364 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2366 } else {
2367 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2368 }
2369 }
2370 if (pbs & TRACK_BIT_Y) {
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_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_N ? -(int)TILE_HEIGHT : 0);
2378 if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_S ? -(int)TILE_HEIGHT : 0);
2379 if (pbs & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_W ? -(int)TILE_HEIGHT : 0);
2380 if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
2381 }
2382
2383 if (IsValidCorner(halftile_corner)) {
2384 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2385
2386 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2387 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2388 image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y;
2389 pal = PAL_NONE;
2390 switch (rgt) {
2393 case RailGroundType::HalfTileSnow: image += rti->snow_offset; break; // higher part has snow in this case too
2394 default: break;
2395 }
2396 DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
2397
2398 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) {
2399 static const uint8_t _corner_to_track_sprite[] = {3, 1, 2, 0};
2400 DrawGroundSprite(_corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, -(int)TILE_HEIGHT);
2401 }
2402 }
2403}
2404
2405static void DrawSignals(TileIndex tile, TrackBits rails, const RailTypeInfo *rti)
2406{
2407 auto MAYBE_DRAW_SIGNAL = [&](uint8_t signalbit, SignalOffsets image, uint pos, Track track) {
2408 if (IsSignalPresent(tile, signalbit)) DrawSingleSignal(tile, rti, track, GetSingleSignalState(tile, signalbit), image, pos);
2409 };
2410
2411 if (!(rails & TRACK_BIT_Y)) {
2412 if (!(rails & TRACK_BIT_X)) {
2413 if (rails & TRACK_BIT_LEFT) {
2414 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, TRACK_LEFT);
2415 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, TRACK_LEFT);
2416 }
2417 if (rails & TRACK_BIT_RIGHT) {
2418 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, TRACK_RIGHT);
2419 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, TRACK_RIGHT);
2420 }
2421 if (rails & TRACK_BIT_UPPER) {
2422 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, TRACK_UPPER);
2423 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, TRACK_UPPER);
2424 }
2425 if (rails & TRACK_BIT_LOWER) {
2426 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, TRACK_LOWER);
2427 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, TRACK_LOWER);
2428 }
2429 } else {
2430 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, TRACK_X);
2431 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, TRACK_X);
2432 }
2433 } else {
2434 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, TRACK_Y);
2435 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 11, TRACK_Y);
2436 }
2437}
2438
2440static void DrawTile_Rail(TileInfo *ti)
2441{
2442 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2443 BridgePillarFlags blocked_pillars{};
2445
2446 if (IsPlainRail(ti->tile)) {
2447 TrackBits rails = GetTrackBits(ti->tile);
2448
2449 DrawTrackBits(ti, rails);
2450
2452
2454
2455 if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
2456
2457 if (IsBridgeAbove(ti->tile)) {
2458 if ((rails & TRACK_BIT_3WAY_NE) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeNE);
2459 if ((rails & TRACK_BIT_3WAY_SE) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeSE);
2460 if ((rails & TRACK_BIT_3WAY_SW) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeSW);
2461 if ((rails & TRACK_BIT_3WAY_NW) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeNW);
2462 }
2463 } else {
2464 /* draw depot */
2465 const DrawTileSprites *dts;
2467
2469
2471 /* Draw rail instead of depot */
2472 dts = &_depot_invisible_gfx_table[dir];
2473 } else {
2474 dts = &_depot_gfx_table[dir];
2475 }
2476
2477 SpriteID image;
2478 if (rti->UsesOverlay()) {
2479 image = SPR_FLAT_GRASS_TILE;
2480 } else {
2481 image = dts->ground.sprite;
2482 if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
2483 }
2484
2485 /* Adjust ground tile for desert and snow. */
2486 if (IsSnowOrDesertRailGround(ti->tile)) {
2487 if (image != SPR_FLAT_GRASS_TILE) {
2488 image += rti->snow_offset; // tile with tracks
2489 } else {
2490 image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
2491 }
2492 }
2493
2494 DrawGroundSprite(image, GroundSpritePaletteTransform(image, PAL_NONE, pal));
2495
2496 if (rti->UsesOverlay()) {
2497 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
2498
2499 switch (GetRailDepotDirection(ti->tile)) {
2500 case DIAGDIR_NE:
2501 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2502 [[fallthrough]];
2503 case DIAGDIR_SW:
2504 DrawGroundSprite(ground + RTO_X, PAL_NONE);
2505 break;
2506 case DIAGDIR_NW:
2507 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2508 [[fallthrough]];
2509 case DIAGDIR_SE:
2510 DrawGroundSprite(ground + RTO_Y, PAL_NONE);
2511 break;
2512 default:
2513 break;
2514 }
2515
2516 if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2517 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
2518
2519 switch (GetRailDepotDirection(ti->tile)) {
2520 case DIAGDIR_NE:
2521 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2522 [[fallthrough]];
2523 case DIAGDIR_SW:
2525 break;
2526 case DIAGDIR_NW:
2527 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2528 [[fallthrough]];
2529 case DIAGDIR_SE:
2531 break;
2532 default:
2533 break;
2534 }
2535 }
2536 } else {
2537 /* PBS debugging, draw reserved tracks darker */
2538 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2539 switch (GetRailDepotDirection(ti->tile)) {
2540 case DIAGDIR_NE:
2541 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2542 [[fallthrough]];
2543 case DIAGDIR_SW:
2545 break;
2546 case DIAGDIR_NW:
2547 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2548 [[fallthrough]];
2549 case DIAGDIR_SE:
2551 break;
2552 default:
2553 break;
2554 }
2555 }
2556 }
2557 int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT);
2558 int relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
2559
2561
2562 DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, pal);
2563 /* Depots can't have bridges above so no blocked pillars. */
2564 }
2565 DrawBridgeMiddle(ti, blocked_pillars);
2566}
2567
2568void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype)
2569{
2570 const DrawTileSprites *dts = &_depot_gfx_table[dir];
2571 const RailTypeInfo *rti = GetRailTypeInfo(railtype);
2572 SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
2573 uint32_t offset = rti->GetRailtypeSpriteOffset();
2574
2575 if (image != SPR_FLAT_GRASS_TILE) image += offset;
2577
2578 DrawSprite(image, PAL_NONE, x, y);
2579
2580 if (rti->UsesOverlay()) {
2582
2583 switch (dir) {
2584 case DIAGDIR_SW: DrawSprite(ground + RTO_X, PAL_NONE, x, y); break;
2585 case DIAGDIR_SE: DrawSprite(ground + RTO_Y, PAL_NONE, x, y); break;
2586 default: break;
2587 }
2588 }
2589 int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RTSG_DEPOT);
2590 if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
2591
2592 DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette);
2593}
2594
2596static int GetSlopePixelZ_Rail(TileIndex tile, uint x, uint y, [[maybe_unused]] bool ground_vehicle)
2597{
2598 if (IsPlainRail(tile)) {
2599 auto [tileh, z] = GetTilePixelSlope(tile);
2600 if (tileh == SLOPE_FLAT) return z;
2601
2603 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
2604 } else {
2605 return GetTileMaxPixelZ(tile);
2606 }
2607}
2608
2611{
2612 return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
2613}
2614
2616static void TileLoop_Rail(TileIndex tile)
2617{
2618 RailGroundType old_ground = GetRailGroundType(tile);
2619 RailGroundType new_ground;
2620
2621 if (old_ground == RailGroundType::HalfTileWater) {
2622 TileLoop_Water(tile);
2623 return;
2624 }
2625
2626 switch (_settings_game.game_creation.landscape) {
2627 case LandscapeType::Arctic: {
2628 auto [slope, z] = GetTileSlopeZ(tile);
2629 bool half = false;
2630
2631 /* for non-flat track, use lower part of track
2632 * in other cases, use the highest part with track */
2633 if (IsPlainRail(tile)) {
2634 TrackBits track = GetTrackBits(tile);
2635 Foundation f = GetRailFoundation(slope, track);
2636
2637 switch (f) {
2638 case FOUNDATION_NONE:
2639 /* no foundation - is the track on the upper side of three corners raised tile? */
2640 if (IsSlopeWithThreeCornersRaised(slope)) z++;
2641 break;
2642
2645 /* sloped track - is it on a steep slope? */
2646 if (IsSteepSlope(slope)) z++;
2647 break;
2648
2650 /* only lower part of steep slope */
2651 z++;
2652 break;
2653
2654 default:
2655 /* if it is a steep slope, then there is a track on higher part */
2656 if (IsSteepSlope(slope)) z++;
2657 z++;
2658 break;
2659 }
2660
2662 } else {
2663 /* is the depot on a non-flat tile? */
2664 if (slope != SLOPE_FLAT) z++;
2665 }
2666
2667 /* 'z' is now the lowest part of the highest track bit -
2668 * for sloped track, it is 'z' of lower part
2669 * for two track bits, it is 'z' of higher track bit
2670 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
2671 if (z > GetSnowLine()) {
2672 if (half && z - GetSnowLine() == 1) {
2673 /* track on non-continuous foundation, lower part is not under snow */
2674 new_ground = RailGroundType::HalfTileSnow;
2675 } else {
2676 new_ground = RailGroundType::SnowOrDesert;
2677 }
2678 goto set_ground;
2679 }
2680 break;
2681 }
2682
2684 if (GetTropicZone(tile) == TROPICZONE_DESERT) {
2685 new_ground = RailGroundType::SnowOrDesert;
2686 goto set_ground;
2687 }
2688 break;
2689
2690 default:
2691 break;
2692 }
2693
2694 new_ground = RailGroundType::Grass;
2695
2696 if (IsPlainRail(tile) && old_ground != RailGroundType::Barren) { // wait until bottom is green
2697 /* determine direction of fence */
2698 TrackBits rail = GetTrackBits(tile);
2699
2700 Owner owner = GetTileOwner(tile);
2701 DiagDirections fences{};
2702
2703 for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
2705
2706 /* Track bit on this edge => no fence. */
2707 if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue;
2708
2709 TileIndex tile2 = tile + TileOffsByDiagDir(d);
2710
2711 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
2712 if (!IsValidTile(tile2) || IsTileType(tile2, TileType::House) || IsTileType(tile2, TileType::Industry) ||
2714 fences.Set(d);
2715 }
2716 }
2717
2718 switch (fences.base()) {
2719 case DiagDirections{}.base(): break;
2720 case DiagDirections{DIAGDIR_NE}.base(): new_ground = RailGroundType::FenceNE; break;
2721 case DiagDirections{DIAGDIR_SE}.base(): new_ground = RailGroundType::FenceSE; break;
2722 case DiagDirections{DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceSW; break;
2723 case DiagDirections{DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceNW; break;
2724 case DiagDirections{DIAGDIR_NE, DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceNESW; break;
2725 case DiagDirections{DIAGDIR_SE, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceSENW; break;
2726 case DiagDirections{DIAGDIR_NE, DIAGDIR_SE}.base(): new_ground = RailGroundType::FenceVert1; break;
2727 case DiagDirections{DIAGDIR_NE, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceHoriz2; break;
2728 case DiagDirections{DIAGDIR_SE, DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceHoriz1; break;
2729 case DiagDirections{DIAGDIR_SW, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceVert2; break;
2730 default: NOT_REACHED();
2731 }
2732 }
2733
2734set_ground:
2735 if (old_ground != new_ground) {
2736 SetRailGroundType(tile, new_ground);
2737 MarkTileDirtyByTile(tile);
2738 }
2739}
2740
2741
2743static TrackStatus GetTileTrackStatus_Rail(TileIndex tile, TransportType mode, [[maybe_unused]] uint sub_mode, DiagDirection side)
2744{
2745 /* Case of half tile slope with water. */
2747 TrackBits tb = GetTrackBits(tile);
2748 switch (tb) {
2749 default: NOT_REACHED();
2750 case TRACK_BIT_UPPER: tb = TRACK_BIT_LOWER; break;
2751 case TRACK_BIT_LOWER: tb = TRACK_BIT_UPPER; break;
2752 case TRACK_BIT_LEFT: tb = TRACK_BIT_RIGHT; break;
2753 case TRACK_BIT_RIGHT: tb = TRACK_BIT_LEFT; break;
2754 }
2756 }
2757
2758 if (mode != TRANSPORT_RAIL) return 0;
2759
2760 TrackBits trackbits = TRACK_BIT_NONE;
2761 TrackdirBits red_signals = TRACKDIR_BIT_NONE;
2762
2763 switch (GetRailTileType(tile)) {
2764 default: NOT_REACHED();
2766 trackbits = GetTrackBits(tile);
2767 break;
2768
2769 case RailTileType::Signals: {
2770 trackbits = GetTrackBits(tile);
2771 uint8_t a = GetPresentSignals(tile);
2772 uint b = GetSignalStates(tile);
2773
2774 b &= a;
2775
2776 /* When signals are not present (in neither direction),
2777 * we pretend them to be green. Otherwise, it depends on
2778 * the signal type. For signals that are only active from
2779 * one side, we set the missing signals explicitly to
2780 * `green'. Otherwise, they implicitly become `red'. */
2781 if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER);
2782 if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER);
2783
2784 if ((b & 0x8) == 0) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E);
2785 if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W);
2786 if ((b & 0x2) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_E);
2787 if ((b & 0x1) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_W);
2788
2789 break;
2790 }
2791
2792 case RailTileType::Depot: {
2794
2795 if (side != INVALID_DIAGDIR && side != dir) break;
2796
2797 trackbits = DiagDirToDiagTrackBits(dir);
2798 break;
2799 }
2800 }
2801
2802 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), red_signals);
2803}
2804
2806static bool ClickTile_Rail(TileIndex tile)
2807{
2808 if (!IsRailDepot(tile)) return false;
2809
2811 return true;
2812}
2813
2816{
2817 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
2818 td.rail_speed = rti->max_speed;
2819 td.railtype = rti->strings.name;
2820 td.owner[0] = GetTileOwner(tile);
2821 switch (GetRailTileType(tile)) {
2823 td.str = STR_LAI_RAIL_DESCRIPTION_TRACK;
2824 break;
2825
2826 case RailTileType::Signals: {
2827 static const StringID signal_type[6][6] = {
2828 {
2829 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
2830 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2831 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2832 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2833 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2834 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
2835 },
2836 {
2837 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2838 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS,
2839 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2840 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2841 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2842 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
2843 },
2844 {
2845 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2846 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2847 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
2848 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2849 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2850 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
2851 },
2852 {
2853 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2854 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2855 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2856 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
2857 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2858 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
2859 },
2860 {
2861 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2862 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2863 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2864 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2865 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
2866 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
2867 },
2868 {
2869 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
2870 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
2871 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
2872 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
2873 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
2874 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
2875 }
2876 };
2877
2878 SignalType primary_signal;
2879 SignalType secondary_signal;
2880 if (HasSignalOnTrack(tile, TRACK_UPPER)) {
2881 primary_signal = GetSignalType(tile, TRACK_UPPER);
2882 secondary_signal = HasSignalOnTrack(tile, TRACK_LOWER) ? GetSignalType(tile, TRACK_LOWER) : primary_signal;
2883 } else {
2884 secondary_signal = primary_signal = GetSignalType(tile, TRACK_LOWER);
2885 }
2886
2887 td.str = signal_type[secondary_signal][primary_signal];
2888 break;
2889 }
2890
2892 td.str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
2893 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) {
2894 if (td.rail_speed > 0) {
2895 td.rail_speed = std::min<uint16_t>(td.rail_speed, 61);
2896 } else {
2897 td.rail_speed = 61;
2898 }
2899 }
2900 td.build_date = Depot::GetByTile(tile)->build_date;
2901 break;
2902
2903 default:
2904 NOT_REACHED();
2905 }
2906}
2907
2909static void ChangeTileOwner_Rail(TileIndex tile, Owner old_owner, Owner new_owner)
2910{
2911 if (!IsTileOwner(tile, old_owner)) return;
2912
2913 if (new_owner != INVALID_OWNER) {
2914 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
2915 uint num_pieces = 1;
2916 if (IsPlainRail(tile)) {
2917 TrackBits bits = GetTrackBits(tile);
2918 num_pieces = CountBits(bits);
2919 if (TracksOverlap(bits)) num_pieces *= num_pieces;
2920 }
2921 RailType rt = GetRailType(tile);
2922 Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;
2923 Company::Get(new_owner)->infrastructure.rail[rt] += num_pieces;
2924
2925 if (HasSignals(tile)) {
2926 uint num_sigs = CountBits(GetPresentSignals(tile));
2927 Company::Get(old_owner)->infrastructure.signal -= num_sigs;
2928 Company::Get(new_owner)->infrastructure.signal += num_sigs;
2929 }
2930
2931 SetTileOwner(tile, new_owner);
2932 } else {
2933 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
2934 }
2935}
2936
2937static const uint8_t _fractcoords_behind[4] = { 0x8F, 0x8, 0x80, 0xF8 };
2938static const uint8_t _fractcoords_enter[4] = { 0x8A, 0x48, 0x84, 0xA8 };
2939static const int8_t _deltacoord_leaveoffset[8] = {
2940 -1, 0, 1, 0, /* x */
2941 0, 1, 0, -1 /* y */
2942};
2943
2944
2952{
2954 int length = v->CalcNextVehicleOffset();
2955
2956 switch (dir) {
2957 case DIAGDIR_NE: return ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1)));
2958 case DIAGDIR_SE: return -((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) + (length + 1)));
2959 case DIAGDIR_SW: return -((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) + (length + 1)));
2960 case DIAGDIR_NW: return ((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) - (length + 1)));
2961 default: NOT_REACHED();
2962 }
2963}
2964
2966static VehicleEnterTileStates VehicleEnterTile_Rail(Vehicle *v, TileIndex tile, int x, int y)
2967{
2968 /* This routine applies only to trains in depot tiles. */
2969 if (v->type != VEH_TRAIN || !IsRailDepotTile(tile)) return {};
2970
2971 /* Depot direction. */
2973
2974 uint8_t fract_coord = (x & 0xF) + ((y & 0xF) << 4);
2975
2976 /* Make sure a train is not entering the tile from behind. */
2977 if (_fractcoords_behind[dir] == fract_coord) return VehicleEnterTileState::CannotEnter;
2978
2979 /* Leaving depot? */
2980 if (v->direction == DiagDirToDir(dir)) {
2981 /* Calculate the point where the following wagon should be activated. */
2982 int length = Train::From(v)->CalcNextVehicleOffset();
2983
2984 uint8_t fract_coord_leave =
2985 ((_fractcoords_enter[dir] & 0x0F) + // x
2986 (length + 1) * _deltacoord_leaveoffset[dir]) +
2987 (((_fractcoords_enter[dir] >> 4) + // y
2988 ((length + 1) * _deltacoord_leaveoffset[dir + 4])) << 4);
2989
2990 if (fract_coord_leave == fract_coord) {
2991 /* Leave the depot. */
2992 if ((v = v->Next()) != nullptr) {
2995 }
2996 }
2997 } else if (_fractcoords_enter[dir] == fract_coord) {
2998 /* Entering depot. */
2999 assert(DiagDirToDir(ReverseDiagDir(dir)) == v->direction);
3003 if (v->Next() == nullptr) VehicleEnterDepot(v->First());
3004 v->tile = tile;
3005
3008 }
3009
3010 return {};
3011}
3012
3025static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
3026{
3027 if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3028
3029 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3030 if (CheckRailSlope(tileh_new, rail_bits, TRACK_BIT_NONE, tile).Failed()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3031
3032 /* Get the slopes on top of the foundations */
3033 z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), tileh_old);
3034 z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), tileh_new);
3035
3036 Corner track_corner;
3037 switch (rail_bits) {
3038 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
3039 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
3040 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
3041 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
3042
3043 /* Surface slope must not be changed */
3044 default:
3045 if (z_old != z_new || tileh_old != tileh_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3047 }
3048
3049 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3050 z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
3051 z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
3052 if (z_old != z_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3053
3055 /* Make the ground dirty, if surface slope has changed */
3056 if (tileh_old != tileh_new) {
3057 /* If there is flat water on the lower halftile add the cost for clearing it */
3060 }
3061 return cost;
3062}
3063
3065static CommandCost TerraformTile_Rail(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
3066{
3067 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
3068 if (IsPlainRail(tile)) {
3069 TrackBits rail_bits = GetTrackBits(tile);
3070 /* Is there flat water on the lower halftile that must be cleared expensively? */
3071 bool was_water = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh_old));
3072
3073 /* Allow clearing the water only if there is no ship */
3074 if (was_water && HasVehicleOnTile(tile, [](const Vehicle *v) {
3075 return v->type == VEH_SHIP;
3076 })) return CommandCost(STR_ERROR_SHIP_IN_THE_WAY);
3077
3078 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3079 CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
3080
3081 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3082 Corner allowed_corner;
3083 switch (rail_bits) {
3084 case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break;
3085 case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break;
3086 case TRACK_BIT_LEFT: allowed_corner = CORNER_E; break;
3087 case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break;
3088 default: return autoslope_result;
3089 }
3090
3091 Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
3092
3093 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3094 if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
3095
3096 /* Everything is valid, which only changes allowed_corner */
3097 for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
3098 if (allowed_corner == corner) continue;
3099 if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
3100 }
3101
3102 /* Make the ground dirty */
3104
3105 /* allow terraforming */
3106 return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price[Price::ClearWater] : (Money)0);
3107 } else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
3108 AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
3110 }
3111 return Command<Commands::LandscapeClear>::Do(flags, tile);
3112}
3113
3115static CommandCost CheckBuildAbove_Rail(TileIndex tile, DoCommandFlags flags, [[maybe_unused]] Axis axis, [[maybe_unused]] int height)
3116{
3117 if (IsPlainRail(tile)) return CommandCost();
3118 return Command<Commands::LandscapeClear>::Do(flags, tile);
3119}
3120
3122extern const TileTypeProcs _tile_type_rail_procs = {
3123 .draw_tile_proc = DrawTile_Rail,
3124 .get_slope_pixel_z_proc = GetSlopePixelZ_Rail,
3125 .clear_tile_proc = ClearTile_Rail,
3126 .get_tile_desc_proc = GetTileDesc_Rail,
3127 .get_tile_track_status_proc = GetTileTrackStatus_Rail,
3128 .click_tile_proc = ClickTile_Rail,
3129 .tile_loop_proc = TileLoop_Rail,
3130 .change_tile_owner_proc = ChangeTileOwner_Rail,
3131 .vehicle_enter_tile_proc = VehicleEnterTile_Rail,
3132 .get_foundation_proc = GetFoundation_Rail,
3133 .terraform_tile_proc = TerraformTile_Rail,
3134 .check_build_above_proc = CheckBuildAbove_Rail,
3135};
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.
constexpr bool HasExactlyOneBit(T value)
Test whether value has exactly 1 bit set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ToggleBit(T &x, const uint8_t y)
Toggles a bit in a variable.
constexpr T KillFirstBit(T value)
Clear the first bit in an integer.
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
Draw the middle bits of a bridge.
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
bool IsBridge(Tile t)
Checks if this is a bridge, instead of a tunnel.
Definition bridge_map.h:24
@ EdgeNE
Northeast edge is obstructed.
Definition bridge_type.h:46
@ EdgeSW
Southwest edge is obstructed.
Definition bridge_type.h:48
@ EdgeNW
Northwest edge is obstructed.
Definition bridge_type.h:49
@ EdgeSE
Southeast edge is obstructed.
Definition bridge_type.h:47
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.
Common return value for all commands.
bool Succeeded() const
Did this command succeed?
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
void MakeError(StringID message)
Makes this CommandCost behave like an error command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
StringID GetErrorMessage() const
Returns the error message of a command.
This struct contains all the info that is needed to draw and construct tracks.
Definition rail.h:115
RailType Index() const
Get the RailType for this RailTypeInfo.
Definition rail.cpp:26
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:125
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:220
SpriteID build_tunnel
button for building a tunnel
Definition rail.h:148
CursorID rail_swne
Cursor for building rail in X direction.
Definition rail.h:155
SpriteID convert_rail
button for converting rail
Definition rail.h:149
struct RailTypeInfo::@157247141350136173143103254227157213063052244122 strings
Strings associated with the rail type.
CursorID convert
Cursor for converting track.
Definition rail.h:161
CursorID depot
Cursor for building a depot.
Definition rail.h:159
RailTypes powered_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype generates power
Definition rail.h:177
RailTypes introduces_railtypes
Bitmask of which other railtypes are introduced when this railtype is introduced.
Definition rail.h:255
SpriteID ground
ground sprite for a 3-way switch
Definition rail.h:124
CursorID rail_nwse
Cursor for building rail in Y direction.
Definition rail.h:157
SpriteID build_x_rail
button for building single rail in X direction
Definition rail.h:143
uint8_t sorting_order
The sorting order of this railtype for the toolbar dropdown.
Definition rail.h:260
RailTypeLabel label
Unique 32 bit rail type identifier.
Definition rail.h:225
SpriteID single_n
single piece of rail in the northern corner
Definition rail.h:127
CursorID rail_ew
Cursor for building rail in E-W direction.
Definition rail.h:156
SpriteID auto_rail
button for the autorail construction
Definition rail.h:146
CursorID autorail
Cursor for autorail tool.
Definition rail.h:158
SpriteID single_y
single piece of rail in Y direction, without ground
Definition rail.h:126
StringID name
Name of this rail type.
Definition rail.h:165
RailTypes compatible_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype can physically travel
Definition rail.h:180
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:287
struct RailTypeInfo::@047376261311064105134233254254216006075341230376 gui_sprites
struct containing the sprites for the rail GUI.
SpriteID single_s
single piece of rail in the southern corner
Definition rail.h:128
RailTypeFlags flags
Bit mask of rail type flags.
Definition rail.h:200
SpriteID signals[SIGTYPE_END][2][2]
signal GUI sprites (type, variant, state)
Definition rail.h:150
SpriteID build_ew_rail
button for building single rail in E-W direction
Definition rail.h:144
SpriteID build_y_rail
button for building single rail in Y direction
Definition rail.h:145
FlatSet< RailTypeLabel > alternate_labels
Rail type labels this type provides in addition to the main label.
Definition rail.h:230
SpriteID track_ns
two pieces of rail in North and South corner (East-West direction)
Definition rail.h:123
struct RailTypeInfo::@332027037331076264023214171276243307073252216167 base_sprites
Struct containing the main sprites.
SpriteID snow_offset
sprite number difference between a piece of track on a snowy ground and the corresponding one on norm...
Definition rail.h:174
SpriteID track_y
single piece of rail in Y direction, with ground
Definition rail.h:122
SpriteID build_depot
button for building depots
Definition rail.h:147
SpriteID single_w
single piece of rail in the western corner
Definition rail.h:130
SpriteID single_e
single piece of rail in the eastern corner
Definition rail.h:129
SpriteID build_ns_rail
button for building single rail in N-S direction
Definition rail.h:142
CursorID rail_ns
Cursor for building rail in N-S direction.
Definition rail.h:154
SpriteID tunnel
tunnel sprites base
Definition rail.h:133
SpriteID single_sloped
single piece of rail for slopes
Definition rail.h:131
struct RailTypeInfo::@057010233226120022371265166156055202241326366156 cursor
Cursors associated with the rail type.
static std::unique_ptr< TileIterator > Create(TileIndex corner1, TileIndex corner2, bool diagonal)
Create either an OrthogonalTileIterator or DiagonalTileIterator given the diagonal parameter.
Definition tilearea.cpp:292
Iterate over all vehicles on a tile.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Auto
don't allow building on structures
@ Execute
execute the given command
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
Definition of stuff that is very close to a company, like the company struct itself.
CommandCost CheckTileOwnership(TileIndex tile)
Check whether the current owner owns the stuff on the given tile.
PaletteID GetCompanyPalette(CompanyID company)
Get the palette for recolouring with a company colour.
CommandCost CheckOwnership(Owner owner, TileIndex tile)
Check whether the current owner owns something.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
void DirtyCompanyInfrastructureWindows(CompanyID company)
Redraw all windows with company infrastructure counts.
GUI Functions related to companies.
static constexpr Owner OWNER_DEITY
The object is owned by a superuser / goal script.
static constexpr Owner INVALID_OWNER
An invalid owner.
static constexpr Owner OWNER_WATER
The tile/execution is done by "water".
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
Base for all depots (except hangars).
bool CanBuildDepotByTileh(DiagDirection direction, Slope tileh)
Find out if the slope of the tile is suitable to build a depot of given direction.
Definition depot_func.h:27
void ShowDepotWindow(TileIndex tile, VehicleType type)
Opens a depot window.
Direction DiagDirToDir(DiagDirection dir)
Convert a DiagDirection to a Direction.
DiagDirection ReverseDiagDir(DiagDirection d)
Returns the reverse direction of the given DiagDirection.
Direction ReverseDir(Direction d)
Return the reverse of a direction.
bool IsValidDiagDirection(DiagDirection d)
Checks if an integer value is a valid DiagDirection.
Axis DiagDirToAxis(DiagDirection d)
Convert a DiagDirection to the axis.
Axis
Allow incrementing of DiagDirDiff variables.
@ AXIS_X
The X axis.
@ AXIS_Y
The y axis.
DiagDirection
Enumeration for diagonal directions.
@ DIAGDIR_NE
Northeast, upper right on your monitor.
@ DIAGDIR_NW
Northwest.
@ DIAGDIR_SE
Southeast.
@ DIAGDIR_END
Used for iterations.
@ DIAGDIR_BEGIN
Used for iterations.
@ INVALID_DIAGDIR
Flag for an invalid DiagDirection.
@ DIAGDIR_SW
Southwest.
Prices _price
Prices and also the fractional part.
Definition economy.cpp:106
static const uint LEVELCROSSING_TRACKBIT_FACTOR
Multiplier for how many regular track bits a level crossing counts.
@ EXPENSES_CONSTRUCTION
Construction costs.
static const uint TUNNELBRIDGE_TRACKBIT_FACTOR
Multiplier for how many regular track bits a tunnel/bridge counts.
@ ClearRough
Price for destroying rough land.
@ BuildSignals
Price for building rail signals.
@ BuildFoundation
Price for building foundation under other constructions e.g. roads, rails, depots,...
@ ClearWater
Price for destroying water e.g. see, rives.
@ ClearSignals
Price for destroying rail signals.
@ ClearDepotTrain
Price for destroying train depots.
@ BuildDepotTrain
Price for building train depots.
void DrawRailCatenary(const TileInfo *ti)
Draws overhead wires and pylons for electric railways.
Definition elrail.cpp:559
Header file for electrified rail specific functions.
bool HasRailCatenaryDrawn(RailType rt)
Test if we should draw rail catenary.
Definition elrail_func.h:32
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1038
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
uint8_t GetSnowLine()
Get the current snow line, either variable or static.
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
uint GetPartialPixelZ(int x, int y, Slope corners)
Determines height at given coordinate of a slope.
TrackStatus GetTileTrackStatus(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
Returns information about trackdirs and signal states.
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.
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
@ TCX_UPPER_HALFTILE
Querying information about the upper part of a tile with halftile foundation.
@ TCX_NORMAL
Nothing special.
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, RailTypeSpriteGroup rtsg, TileContext context, uint *num_results)
Get the sprite to draw for the given tile.
NewGRF handling of rail types.
Map accessors for object tiles.
bool IsObjectType(Tile t, ObjectType type)
Check whether the object on a tile is of a specific type.
Definition object_map.h:25
static const ObjectType OBJECT_OWNED_LAND
Owned land 'flag'.
Definition object_type.h:21
@ DO_FULL_DETAIL
Also draw details of track and roads.
Definition openttd.h:50
Train * GetTrainForReservation(TileIndex tile, Track track)
Find the train which has reserved a specific path.
Definition pbs.cpp:343
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:393
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
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
@ RTSG_GROUND
Main group of ground images.
Definition rail.h:43
@ RTSG_CURSORS
Cursor and toolbar icon images.
Definition rail.h:41
@ RTSG_GROUND_COMPLETE
Complete ground images.
Definition rail.h:53
@ RTSG_OVERLAY
Images for overlaying track.
Definition rail.h:42
@ RTSG_DEPOT
Depot images.
Definition rail.h:49
@ RTSG_FENCES
Fence images.
Definition rail.h:50
@ RTO_Y
Piece of rail in Y direction.
Definition rail.h:63
@ RTO_S
Piece of rail in southern corner.
Definition rail.h:65
@ RTO_JUNCTION_NE
Ballast for junction 'pointing' NE.
Definition rail.h:74
@ RTO_JUNCTION_NSEW
Ballast for full junction.
Definition rail.h:77
@ RTO_JUNCTION_SW
Ballast for junction 'pointing' SW.
Definition rail.h:73
@ RTO_SLOPE_SE
Piece of rail on slope with south-east raised.
Definition rail.h:69
@ RTO_E
Piece of rail in eastern corner.
Definition rail.h:66
@ RTO_W
Piece of rail in western corner.
Definition rail.h:67
@ RTO_CROSSING_XY
Crossing of X and Y rail, with ballast.
Definition rail.h:72
@ RTO_SLOPE_SW
Piece of rail on slope with south-west raised.
Definition rail.h:70
@ RTO_SLOPE_NW
Piece of rail on slope with north-west raised.
Definition rail.h:71
@ RTO_JUNCTION_NW
Ballast for junction 'pointing' NW.
Definition rail.h:76
@ RTO_JUNCTION_SE
Ballast for junction 'pointing' SE.
Definition rail.h:75
@ RTO_SLOPE_NE
Piece of rail on slope with north-east raised.
Definition rail.h:68
@ RTO_X
Piece of rail in X direction.
Definition rail.h:62
@ RTO_N
Piece of rail in northern corner.
Definition rail.h:64
bool RailNoLevelCrossings(RailType rt)
Test if a RailType disallows build of level crossings.
Definition rail.h:399
CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
Remove signals on a stretch of track.
int TicksToLeaveDepot(const Train *v)
Compute number of ticks when next wagon will leave a depot.
static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build)
Check that the new track bits may be built.
Definition rail_cmd.cpp:240
static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
Build or remove a stretch of railroad tracks.
Definition rail_cmd.cpp:875
static const int INF
Big number compared to tilesprite size.
CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
Build a train depot.
Definition rail_cmd.cpp:960
static void DrawTile_Rail(TileInfo *ti)
Tile callback function signature for drawing a tile and its contents to the screen.
CommandCost CmdBuildSingleSignal(DoCommandFlags flags, TileIndex tile, Track track, SignalType sigtype, SignalVariant sigvar, bool convert_signal, bool skip_existing_signals, bool ctrl_pressed, SignalType cycle_start, SignalType cycle_stop, uint8_t num_dir_cycle, uint8_t signals_copy)
Build signals, alternate between double/single, signal/semaphore, pre/exit/combo-signals,...
CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
Build rail on a stretch of track.
Definition rail_cmd.cpp:932
CommandCost CmdBuildSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool autofill, bool minimise_gaps, uint8_t signal_density)
Build signals on a stretch of track.
static CommandCost ClearTile_Rail(TileIndex tile, DoCommandFlags flags)
Tile callback function signature for clearing a tile.
bool FloodHalftile(TileIndex t)
Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
Definition rail_cmd.cpp:761
RailType AllocateRailType(RailTypeLabel label)
Allocate a new rail type label.
Definition rail_cmd.cpp:150
static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
Tests if a track can be build on a tile.
Definition rail_cmd.cpp:391
CommandCost CmdRemoveSingleRail(DoCommandFlags flags, TileIndex tile, Track track)
Remove a single piece of track.
Definition rail_cmd.cpp:614
CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
Remove signals.
static uint GetSafeSlopeZ(uint x, uint y, Track track)
Get surface height in point (x,y) On tiles with halftile foundations move (x,y) to a safe point wrt.
static const TrackBits _valid_tracks_without_foundation[15]
Valid TrackBits on a specific (non-steep)-slope without foundation.
Definition rail_cmd.cpp:261
static const TrackBits _valid_tracks_on_leveled_foundation[15]
Valid TrackBits on a specific (non-steep)-slope with leveled foundation.
Definition rail_cmd.cpp:283
static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
Tests if a vehicle interacts with the specified track.
Definition rail_cmd.cpp:228
static CommandCost CmdSignalTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool remove, bool autofill, bool minimise_gaps, int signal_density)
Build many signals by dragging; AutoSignals.
static int GetSlopePixelZ_Rail(TileIndex tile, uint x, uint y, bool ground_vehicle)
Tile callback function signature for obtaining the world Z coordinate of a given point of a tile.
static void DrawTrackBits(TileInfo *ti, TrackBits track)
Draw ground sprite and track bits.
static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
Draw track fences.
SignalOffsets
Enum holding the signal offset in the sprite sheet according to the side it is representing.
Definition rail_cmd.cpp:51
CommandCost CmdRemoveRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, Track track)
Build rail on a stretch of track.
Definition rail_cmd.cpp:947
static VehicleEnterTileStates VehicleEnterTile_Rail(Vehicle *v, TileIndex tile, int x, int y)
Tile callback function for a vehicle entering a tile.
CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
Build a single piece of rail.
Definition rail_cmd.cpp:425
CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
Convert one rail type to the other.
void InitRailTypes()
Resolve sprites of custom rail types.
Definition rail_cmd.cpp:130
static void ChangeTileOwner_Rail(TileIndex tile, Owner old_owner, Owner new_owner)
Tile callback function signature for changing the owner of a tile.
static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at SW border matching the tile slope.
static bool ClickTile_Rail(TileIndex tile)
Tile callback function signature for clicking a tile.
static void GetTileDesc_Rail(TileIndex tile, TileDesc &td)
Tile callback function signature for obtaining a tile description.
void ResetRailTypes()
Reset all rail type information to its default values.
Definition rail_cmd.cpp:65
static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
Tests if autoslope is allowed.
static const FenceOffset _fence_offsets[]
Offsets for drawing fences.
static TrackStatus GetTileTrackStatus_Rail(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
Tile callback function signature for getting the possible tracks that can be taken on a given tile by...
static int GetJunctionGroundSpriteOffset(TrackBits track)
Returns which of the 5 junction-'Rail underlays' to use for the given track bits.
static bool CompareRailTypes(const RailType &first, const RailType &second)
Compare railtypes based on their sorting order.
Definition rail_cmd.cpp:122
static Foundation GetFoundation_Rail(TileIndex tile, Slope tileh)
Tile callback function signature for getting the foundation of a tile.
static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at NW border matching the tile slope.
Foundation GetRailFoundation(Slope tileh, TrackBits bits)
Checks if a track combination is valid on a specific slope and returns the needed foundation.
Definition rail_cmd.cpp:311
static void DrawTrackFence_NE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at NE border matching the tile slope.
static void DrawTrackFence_SE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at SE border matching the tile slope.
static CommandCost CheckBuildAbove_Rail(TileIndex tile, DoCommandFlags flags, Axis axis, int height)
Tile callback function signature to test if a bridge can be built above a tile.
std::vector< Train * > TrainList
Helper type for lists/vectors of trains.
Definition rail_cmd.cpp:44
static void TileLoop_Rail(TileIndex tile)
Tile callback function signature for running periodic tile updates.
static CommandCost TerraformTile_Rail(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
Tile callback function signature of the terraforming callback.
static void DrawTrackFence(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites, RailFenceOffset rfo)
Draw a track fence.
Command definitions for rail.
static RailTileType GetRailTileType(Tile t)
Returns the RailTileType (normal with or without signals, waypoint or depot).
Definition rail_map.h:36
static bool IsPlainRail(Tile t)
Returns whether this is plain rails, with or without signals.
Definition rail_map.h:49
uint GetSignalStates(Tile tile)
Set the states of the signals (Along/AgainstTrackDir).
Definition rail_map.h:421
void MakeRailDepot(Tile tile, Owner owner, DepotID depot_id, DiagDirection dir, RailType rail_type)
Make a rail depot.
Definition rail_map.h:647
RailType GetRailType(Tile t)
Gets the rail type of the given tile.
Definition rail_map.h:115
bool HasSignalOnTrackdir(Tile tile, Trackdir trackdir)
Checks for the presence of signals along the given trackdir on the given rail tile.
Definition rail_map.h:491
static bool IsRailDepot(Tile t)
Is this rail tile a rail depot?
Definition rail_map.h:95
SignalVariant GetSignalVariant(Tile t, Track track)
Get the signal variant for a track on a tile.
Definition rail_map.h:386
RailGroundType GetRailGroundType(Tile t)
Get the ground type for rail tiles.
Definition rail_map.h:591
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
static bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
bool IsPbsSignal(SignalType s)
Checks whether the given signal is a path based signal.
Definition rail_map.h:292
void CycleSignalSide(Tile t, Track track)
Cycle to the next signal side at the given track on a tile.
Definition rail_map.h:369
void MakeRailNormal(Tile t, Owner o, TrackBits b, RailType r)
Make the given tile a normal rail.
Definition rail_map.h:614
@ Normal
Normal rail tile without signals.
Definition rail_map.h:24
@ Depot
Depot (one entrance).
Definition rail_map.h:26
@ Signals
Normal rail tile with signals.
Definition rail_map.h:25
Track GetRailDepotTrack(Tile t)
Returns the track of a depot, ignoring direction.
Definition rail_map.h:182
void SetTrackReservation(Tile t, TrackBits b)
Sets the reserved track bits of the tile.
Definition rail_map.h:209
bool IsSignalPresent(Tile t, uint8_t signalbit)
Checks whether the given signals is present.
Definition rail_map.h:463
DiagDirection GetRailDepotDirection(Tile t)
Returns the direction the depot is facing to.
Definition rail_map.h:171
void SetTrackBits(Tile t, TrackBits b)
Sets the track bits of the given tile.
Definition rail_map.h:147
RailGroundType
The ground 'under' the rail.
Definition rail_map.h:558
@ FenceSENW
Grass with a fence at the NW and SE edges.
Definition rail_map.h:563
@ FenceVert2
Grass with a fence at the western side.
Definition rail_map.h:568
@ HalfTileSnow
Snow only on higher part of slope (steep or one corner raised).
Definition rail_map.h:573
@ Barren
Nothing (dirt).
Definition rail_map.h:559
@ FenceNESW
Grass with a fence at the NE and SW edges.
Definition rail_map.h:566
@ FenceHoriz2
Grass with a fence at the northern side.
Definition rail_map.h:570
@ SnowOrDesert
Icy or sandy.
Definition rail_map.h:571
@ HalfTileWater
Grass with a fence and shore or water on the free halftile.
Definition rail_map.h:572
@ FenceVert1
Grass with a fence at the eastern side.
Definition rail_map.h:567
@ FenceNW
Grass with a fence at the NW edge.
Definition rail_map.h:561
@ FenceSE
Grass with a fence at the SE edge.
Definition rail_map.h:562
@ FenceNE
Grass with a fence at the NE edge.
Definition rail_map.h:564
@ Grass
Grassy.
Definition rail_map.h:560
@ FenceSW
Grass with a fence at the SW edge.
Definition rail_map.h:565
@ FenceHoriz1
Grass with a fence at the southern side.
Definition rail_map.h:569
uint GetPresentSignals(Tile tile)
Get whether the given signals are present (Along/AgainstTrackDir).
Definition rail_map.h:452
bool HasSignalOnTrack(Tile tile, Track track)
Checks for the presence of signals (either way) on the given track on the given rail tile.
Definition rail_map.h:475
bool IsOnewaySignal(Tile t, Track track)
Is the signal at the given track on a tile a one way signal?
Definition rail_map.h:358
void SetPresentSignals(Tile tile, uint signals)
Set whether the given signals are present (Along/AgainstTrackDir).
Definition rail_map.h:442
void SetRailGroundType(Tile t, RailGroundType rgt)
Set the ground type for rail tiles.
Definition rail_map.h:581
void SetRailDepotExitDirection(Tile tile, DiagDirection dir)
Sets the exit direction of a rail depot.
Definition rail_map.h:633
SignalType GetSignalType(Tile t, Track track)
Get the signal type for a track on a tile.
Definition rail_map.h:304
TrackBits GetRailReservationTrackBits(Tile t)
Returns the reserved track bits of the tile.
Definition rail_map.h:194
void SetSignalType(Tile t, Track track, SignalType s)
Set the signal type for a track on a tile.
Definition rail_map.h:318
bool HasDepotReservation(Tile t)
Get the reservation state of the depot.
Definition rail_map.h:257
void SetHasSignals(Tile tile, bool signals)
Add/remove the 'has signal' bit from the RailTileType.
Definition rail_map.h:83
bool IsSnowOrDesertRailGround(Tile t)
Is the given rail tile snowy or deserty.
Definition rail_map.h:601
bool HasSignals(Tile t)
Checks if a rail tile has signals.
Definition rail_map.h:72
void SetSignalVariant(Tile t, Track track, SignalVariant v)
Set the signal variant for a track on a tile.
Definition rail_map.h:399
static bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
bool HasTrack(Tile tile, Track track)
Returns whether the given track is present on the given tile.
Definition rail_map.h:160
SignalState GetSingleSignalState(Tile t, uint8_t signalbit)
Get the state of a single signal.
Definition rail_map.h:432
void SetSignalStates(Tile tile, uint state)
Set the states of the signals (Along/AgainstTrackDir).
Definition rail_map.h:411
void SetRailType(Tile t, RailType r)
Sets the rail type of the given tile.
Definition rail_map.h:125
EnumBitSet< RailType, uint64_t > RailTypes
Allow incrementing of Track variables.
Definition rail_type.h:38
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ RAILTYPE_END
Used for iterations.
Definition rail_type.h:31
@ INVALID_RAILTYPE
Flag for invalid railtype.
Definition rail_type.h:32
@ RAILTYPE_ELECTRIC
Electric rails.
Definition rail_type.h:28
@ RAILTYPE_RAIL
Standard non-electric rails.
Definition rail_type.h:27
All the railtype-specific information is stored here.
static const RailTypeInfo _original_railtypes[]
Global Railtype definition.
Definition railtypes.h:18
bool RoadNoLevelCrossing(RoadType roadtype)
Test if road disallows level crossings.
Definition road.h:283
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:240
void UpdateAdjacentLevelCrossingTilesOnLevelCrossingRemoval(TileIndex tile, Axis road_axis)
Update adjacent level crossing tiles in this multi-track crossing, due to removal of a level crossing...
void MarkDirtyAdjacentLevelCrossingTiles(TileIndex tile, Axis road_axis)
Find adjacent level crossing tiles in this multi-track crossing and mark them dirty.
void UpdateLevelCrossing(TileIndex tile, bool sound=true, bool force_bar=false)
Update a level crossing to barred or open (crossing may include multiple adjacent tiles).
RoadType GetRoadTypeRoad(Tile t)
Get the road type for RoadTramType being RTT_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 RTT_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
RoadBits
Enumeration for the road parts on a tile.
Definition road_type.h:56
@ ROAD_NONE
No road-part is build.
Definition road_type.h:57
@ ROAD_Y
Full road along the y-axis (north-west + south-east).
Definition road_type.h:63
@ ROAD_X
Full road along the x-axis (south-west + north-east).
Definition road_type.h:62
@ RTT_ROAD
Road road type.
Definition road_type.h:38
@ RTT_TRAM
Tram road type.
Definition road_type.h:39
RoadType
The different roadtypes we support.
Definition road_type.h:23
@ INVALID_ROADTYPE
flag for invalid roadtype
Definition road_type.h:28
@ DRD_NONE
None of the directions are disallowed.
Definition road_type.h:78
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
Add track to signal update buffer.
Definition signal.cpp:596
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:628
uint8_t SignalAgainstTrackdir(Trackdir trackdir)
Maps a trackdir to the bit that stores its status in the map arrays, in the direction against the tra...
Definition signal_func.h:36
uint8_t SignalAlongTrackdir(Trackdir trackdir)
Maps a trackdir to the bit that stores its status in the map arrays, in the direction along with the ...
Definition signal_func.h:24
uint8_t SignalOnTrack(Track track)
Maps a Track to the bits that store the status of the two signals that can be present on the given tr...
Definition signal_func.h:48
SignalType
Type of signal, i.e.
Definition signal_type.h:23
@ SIGTYPE_ENTRY
presignal block entry
Definition signal_type.h:25
@ SIGTYPE_EXIT
presignal block exit
Definition signal_type.h:26
@ SIGTYPE_BLOCK
block signal
Definition signal_type.h:24
SignalState
These are states in which a signal can be.
Definition signal_type.h:42
@ SIGNAL_STATE_RED
The signal is red.
Definition signal_type.h:43
@ SIGNAL_STATE_GREEN
The signal is green.
Definition signal_type.h:44
SignalVariant
Variant of the signal, i.e.
Definition signal_type.h:16
@ SIG_SEMAPHORE
Old-fashioned semaphore signal.
Definition signal_type.h:18
@ SIG_ELECTRIC
Light signal.
Definition signal_type.h:17
bool IsSpecialRailFoundation(Foundation f)
Tests if a foundation is a special rail foundation for single horizontal/vertical track.
Definition slope_func.h:345
Slope SlopeWithThreeCornersRaised(Corner corner)
Returns the slope with all except one corner raised.
Definition slope_func.h:206
Corner OppositeCorner(Corner corner)
Returns the opposite corner.
Definition slope_func.h:184
static constexpr Corner GetHalftileSlopeCorner(Slope s)
Returns the leveled halftile of a halftile slope.
Definition slope_func.h:148
static constexpr Slope RemoveHalftileSlope(Slope s)
Removes a halftile slope from a slope.
Definition slope_func.h:60
bool IsSlopeWithOneCornerRaised(Slope s)
Tests if a specific slope has exactly one corner raised.
Definition slope_func.h:88
uint SlopeToSpriteOffset(Slope s)
Returns the Sprite offset for a given Slope.
Definition slope_func.h:415
bool IsNonContinuousFoundation(Foundation f)
Tests if a foundation is a non-continuous foundation, i.e.
Definition slope_func.h:320
Corner GetHighestSlopeCorner(Slope s)
Returns the highest corner of a slope (one corner raised or a steep slope).
Definition slope_func.h:126
Corner GetHalftileFoundationCorner(Foundation f)
Returns the halftile corner of a halftile-foundation.
Definition slope_func.h:333
static constexpr bool IsValidCorner(Corner corner)
Rangecheck for Corner enumeration.
Definition slope_func.h:24
static constexpr bool IsSteepSlope(Slope s)
Checks if a slope is steep.
Definition slope_func.h:36
bool IsSlopeWithThreeCornersRaised(Slope s)
Tests if a specific slope has exactly three corners raised.
Definition slope_func.h:195
Foundation FlatteningFoundation(Slope s)
Returns the foundation needed to flatten a slope.
Definition slope_func.h:369
static constexpr bool IsHalftileSlope(Slope s)
Checks for non-continuous slope on halftile foundations.
Definition slope_func.h:47
Foundation HalftileFoundation(Corner corner)
Returns the halftile foundation for single horizontal/vertical track.
Definition slope_func.h:391
Foundation SpecialRailFoundation(Corner corner)
Returns the special rail foundation for single horizontal/vertical track.
Definition slope_func.h:403
Slope ComplementSlope(Slope s)
Return the complement of a slope.
Definition slope_func.h:76
Slope SlopeWithOneCornerRaised(Corner corner)
Returns the slope with a specific corner raised.
Definition slope_func.h:99
Corner
Enumeration of tile corners.
Definition slope_type.h:21
Slope
Enumeration for the slope-type.
Definition slope_type.h:47
@ SLOPE_W
the west corner of the tile is raised
Definition slope_type.h:49
@ SLOPE_ELEVATED
bit mask containing all 'simple' slopes
Definition slope_type.h:60
@ SLOPE_NS
north and south corner are raised
Definition slope_type.h:59
@ SLOPE_E
the east corner of the tile is raised
Definition slope_type.h:51
@ SLOPE_S
the south corner of the tile is raised
Definition slope_type.h:50
@ SLOPE_N
the north corner of the tile is raised
Definition slope_type.h:52
@ SLOPE_SW
south and west corner are raised
Definition slope_type.h:55
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:48
@ SLOPE_NE
north and east corner are raised
Definition slope_type.h:57
@ SLOPE_SE
south and east corner are raised
Definition slope_type.h:56
@ SLOPE_NW
north and west corner are raised
Definition slope_type.h:54
@ SLOPE_EW
east and west corner are raised
Definition slope_type.h:58
Foundation
Enumeration for Foundations.
Definition slope_type.h:92
@ FOUNDATION_LEVELED
The tile is leveled up to a flat slope.
Definition slope_type.h:94
@ FOUNDATION_NONE
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:93
@ FOUNDATION_INCLINED_X
The tile has an along X-axis inclined foundation.
Definition slope_type.h:95
@ FOUNDATION_STEEP_BOTH
The tile has a steep slope. The lowest corner is raised by a foundation and the upper halftile is lev...
Definition slope_type.h:100
@ FOUNDATION_INCLINED_Y
The tile has an along Y-axis inclined foundation.
Definition slope_type.h:96
@ FOUNDATION_STEEP_LOWER
The tile has a steep slope. The lowest corner is raised by a foundation to allow building railroad on...
Definition slope_type.h:97
@ FOUNDATION_HALFTILE_N
Level north halftile non-continuously.
Definition slope_type.h:104
@ FOUNDATION_INVALID
Used inside "rail_cmd.cpp" to indicate invalid slope/track combination.
Definition slope_type.h:112
static const uint32_t VALID_LEVEL_CROSSING_SLOPES
Constant bitset with safe slopes for building a level crossing.
Definition slope_type.h:85
Functions related to sound.
void DrawRailTileSeqInGUI(int x, int y, const DrawTileSprites *dts, int32_t total_offset, uint32_t newgrf_offset, PaletteID default_palette)
Draw tile sprite sequence in GUI with railroad specifics.
Definition sprite.h:122
void DrawRailTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, int32_t total_offset, uint32_t newgrf_offset, PaletteID default_palette)
Draw tile sprite sequence on tile with railroad specifics.
Definition sprite.h:108
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:207
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1619
static const PaletteID PALETTE_TO_BARE_LAND
sets colour to bare land stuff for rail, road and crossings
Definition sprites.h:1607
bool IsRailStationTile(Tile t)
Is this tile a station tile and a rail station?
Track GetRailStationTrack(Tile t)
Get the rail track of a rail station tile.
bool IsStationTileBlocked(Tile t)
Is tile t a blocked tile?
bool HasStationRail(Tile t)
Has this station tile a rail?
Definition of base types and functions in a cross-platform compatible way.
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames).
Class to backup a specific variable and restore it later.
void Restore()
Restore the variable.
VehicleType type
Type of vehicle.
std::array< uint32_t, RAILTYPE_END > rail
Count of company owned track bits for each rail type.
CompanyInfrastructure infrastructure
NOSAVE: Counts of company owned infrastructure.
T y
Y coordinate.
T x
X coordinate.
T x
X coordinate.
T y
Y coordinate.
T z
Z coordinate.
Represents a diagonal tile area.
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:55
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:56
Offsets for drawing fences.
Corner height_ref
Corner to use height offset from.
static uint Size()
Get the size of the map.
Definition map_func.h:280
bool IsType(OrderType type) const
Check whether this order is of the given type.
Definition order_base.h:67
Represents the covered area of e.g.
Combination of a palette sprite and a 'real' sprite.
Definition gfx_type.h:22
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed).
Definition gfx_type.h:24
static Company * Get(auto index)
static bool CanAllocateItem(size_t n=1)
static T * Create(Targs &&... args)
static Train * From(Vehicle *v)
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:38
uint16_t rail_speed
Speed limit of rail (bridges and track).
Definition tile_cmd.h:51
StringID str
Description of the tile.
Definition tile_cmd.h:39
TimerGameCalendar::Date build_date
Date of construction of tile contents.
Definition tile_cmd.h:43
std::array< Owner, 4 > owner
Name of the owner(s).
Definition tile_cmd.h:41
StringID railtype
Type of rail on the tile.
Definition tile_cmd.h:50
A pair-construct of a TileIndexDiff.
Definition map_type.h:31
Tile information, used while rendering the tile.
Definition tile_cmd.h:32
Slope tileh
Slope of the tile.
Definition tile_cmd.h:33
TileIndex tile
Tile index.
Definition tile_cmd.h:34
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:212
'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 direction
facing
VehStates vehstatus
Status.
Vehicle * First() const
Get the first vehicle of this vehicle chain.
Order current_order
The current order (+ status, like: loading).
Vehicle * Next() const
Get the next vehicle of this vehicle.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
uint16_t cur_speed
current speed
TileIndex tile
Current tile index.
@ CannotEnter
The vehicle cannot enter the tile.
Definition tile_cmd.h:26
@ EnteredWormhole
The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/...
Definition tile_cmd.h:25
std::tuple< Slope, int > GetTileSlopeZ(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.cpp:55
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
bool IsTileOwner(Tile tile, Owner owner)
Checks if a tile belongs to the given owner.
Definition tile_map.h:214
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
void SetTileOwner(Tile tile, Owner owner)
Sets the owner of a tile.
Definition tile_map.h:198
int GetTileMaxPixelZ(TileIndex tile)
Get top height of the tile.
Definition tile_map.h:312
std::tuple< Slope, int > GetTilePixelSlope(TileIndex tile)
Return the slope of a given tile.
Definition tile_map.h:289
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
TropicZone GetTropicZone(Tile tile)
Get the tropic zone.
Definition tile_map.h:238
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
static TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
@ TROPICZONE_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:287
TownID GetTownIndex(Tile t)
Get the index of which town this house/street is attached to.
Definition town_map.h:23
Trackdir TrackToTrackdir(Track track)
Returns a Trackdir for the given Track.
Definition track_func.h:279
Trackdir RemoveFirstTrackdir(TrackdirBits *trackdirs)
Removes first Trackdir from TrackdirBits and returns it.
Definition track_func.h:156
Track TrackdirToTrack(Trackdir trackdir)
Returns the Track that a given Trackdir represents.
Definition track_func.h:262
TrackBits CornerToTrackBits(Corner corner)
Returns a single horizontal/vertical trackbit that is in a specific tile corner.
Definition track_func.h:99
TrackBits TrackToTrackBits(Track track)
Maps a Track to the corresponding TrackBits value.
Definition track_func.h:77
TrackdirBits TrackBitsToTrackdirBits(TrackBits bits)
Converts TrackBits to TrackdirBits while allowing both directions.
Definition track_func.h:319
TrackdirBits TrackStatusToTrackdirBits(TrackStatus ts)
Returns the present-trackdir-information of a TrackStatus.
Definition track_func.h:354
Track TrackBitsToTrack(TrackBits tracks)
Converts TrackBits to Track.
Definition track_func.h:193
Trackdir ReverseTrackdir(Trackdir trackdir)
Maps a trackdir to the reverse trackdir.
Definition track_func.h:247
TrackStatus CombineTrackStatus(TrackdirBits trackdirbits, TrackdirBits red_signals)
Builds a TrackStatus.
Definition track_func.h:390
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:650
TrackdirBits TrackdirReachesTrackdirs(Trackdir trackdir)
Maps a trackdir to the trackdirs that can be reached from it (ie, when entering the next tile.
Definition track_func.h:589
bool IsValidTrack(Track track)
Checks if a Track is valid.
Definition track_func.h:28
Track FindFirstTrack(TrackBits tracks)
Returns first Track from TrackBits or INVALID_TRACK.
Definition track_func.h:177
bool IsDiagonalTrackdir(Trackdir trackdir)
Checks if a given Trackdir is diagonal.
Definition track_func.h:636
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:529
Track RemoveFirstTrack(TrackBits *tracks)
Removes first Track from TrackBits and returns it.
Definition track_func.h:131
DiagDirection TrackdirToExitdir(Trackdir trackdir)
Maps a trackdir to the (4-way) direction the tile is exited when following that trackdir.
Definition track_func.h:441
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track incidating with that diagdir.
Definition track_func.h:517
TrackBits TrackdirBitsToTrackBits(TrackdirBits bits)
Discards all directional information from a TrackdirBits value.
Definition track_func.h:308
Sprites to use and how to display them for train depot tiles.
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_UPPER
Upper track.
Definition track_type.h:39
@ TRACK_BIT_DEPOT
Bitflag for a depot.
Definition track_type.h:53
@ TRACK_BIT_LEFT
Left track.
Definition track_type.h:41
@ TRACK_BIT_Y
Y-axis track.
Definition track_type.h:38
@ TRACK_BIT_CROSS
X-Y-axis cross.
Definition track_type.h:43
@ TRACK_BIT_HORZ
Upper and lower track.
Definition track_type.h:44
@ TRACK_BIT_NONE
No track.
Definition track_type.h:36
@ TRACK_BIT_3WAY_NW
"Arrow" to the north-west
Definition track_type.h:49
@ TRACK_BIT_X
X-axis track.
Definition track_type.h:37
@ TRACK_BIT_LOWER
Lower track.
Definition track_type.h:40
@ TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:50
@ TRACK_BIT_3WAY_NE
"Arrow" to the north-east
Definition track_type.h:46
@ TRACK_BIT_RIGHT
Right track.
Definition track_type.h:42
@ TRACK_BIT_3WAY_SW
"Arrow" to the south-west
Definition track_type.h:48
@ TRACK_BIT_VERT
Left and right track.
Definition track_type.h:45
@ TRACK_BIT_3WAY_SE
"Arrow" to the south-east
Definition track_type.h:47
Trackdir
Enumeration for tracks and directions.
Definition track_type.h:66
@ INVALID_TRACKDIR
Flag for an invalid trackdir.
Definition track_type.h:85
TrackdirBits
Allow incrementing of Trackdir variables.
Definition track_type.h:97
@ TRACKDIR_BIT_LEFT_S
Track left, direction south.
Definition track_type.h:103
@ TRACKDIR_BIT_Y_NW
Track y-axis, direction north-west.
Definition track_type.h:107
@ TRACKDIR_BIT_UPPER_E
Track upper, direction east.
Definition track_type.h:101
@ TRACKDIR_BIT_X_NE
Track x-axis, direction north-east.
Definition track_type.h:99
@ TRACKDIR_BIT_LOWER_E
Track lower, direction east.
Definition track_type.h:102
@ TRACKDIR_BIT_LEFT_N
Track left, direction north.
Definition track_type.h:110
@ TRACKDIR_BIT_RIGHT_S
Track right, direction south.
Definition track_type.h:104
@ TRACKDIR_BIT_Y_SE
Track y-axis, direction south-east.
Definition track_type.h:100
@ TRACKDIR_BIT_NONE
No track build.
Definition track_type.h:98
@ TRACKDIR_BIT_RIGHT_N
Track right, direction north.
Definition track_type.h:111
@ TRACKDIR_BIT_UPPER_W
Track upper, direction west.
Definition track_type.h:108
@ TRACKDIR_BIT_LOWER_W
Track lower, direction west.
Definition track_type.h:109
@ TRACKDIR_BIT_X_SW
Track x-axis, direction south-west.
Definition track_type.h:106
Track
These are used to specify a single track.
Definition track_type.h:19
@ INVALID_TRACK
Flag for an invalid track.
Definition track_type.h:28
@ TRACK_Y
Track along the y-axis (north-west to south-east).
Definition track_type.h:22
@ TRACK_LOWER
Track in the lower corner of the tile (south).
Definition track_type.h:24
@ TRACK_END
Used for iterations.
Definition track_type.h:27
@ TRACK_LEFT
Track in the left corner of the tile (west).
Definition track_type.h:25
@ TRACK_RIGHT
Track in the right corner of the tile (east).
Definition track_type.h:26
@ TRACK_BEGIN
Used for iterations.
Definition track_type.h:20
@ TRACK_X
Track along the x-axis (north-east to south-west).
Definition track_type.h:21
@ TRACK_UPPER
Track in the upper corner of the tile (north).
Definition track_type.h:23
Base for the train class.
static constexpr ConsistChangeFlags CCF_TRACK
Valid changes while vehicle is driving, and possibly changing tracks.
Definition train.h:53
bool TryPathReserve(Train *v, bool mark_as_stuck=false, bool first_tile_okay=false)
Try to reserve a path to a safe position.
void FreeTrainTrackReservation(const Train *v)
Free the reserved path in front of a vehicle.
uint8_t _display_opt
What do we want to draw/do?
bool IsInvisibilitySet(TransparencyOption to)
Check if the invisibility option bit is set and if we aren't in the game menu (there's never transpar...
@ TO_BUILDINGS
company buildings - depots, stations, HQ, ...
TransportType
Available types of transport.
@ TRANSPORT_RAIL
Transport by train.
@ TRANSPORT_WATER
Transport over water.
Header file for things common for tunnels and bridges.
void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height)
Mark bridge tiles dirty.
uint GetTunnelBridgeLength(TileIndex begin, TileIndex end)
Calculates the length of a tunnel or a bridge (without end tiles).
Functions that have tunnels and bridges in common.
DiagDirection GetTunnelBridgeDirection(Tile t)
Get the direction pointing to the other end.
bool HasTunnelBridgeReservation(Tile t)
Get the reservation state of the rail tunnel/bridge.
TransportType GetTunnelBridgeTransportType(Tile t)
Tunnel: Get the transport type of the tunnel (road or rail) Bridge: Get the transport type of the bri...
TileIndex GetOtherTunnelBridgeEnd(Tile t)
Determines type of the wormhole and returns its other end.
CommandCost EnsureNoVehicleOnGround(TileIndex tile)
Ensure there is no vehicle at the ground at the given position.
Definition vehicle.cpp:556
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1564
CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
Tests if a vehicle interacts with the specified track bits.
Definition vehicle.cpp:604
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:580
const uint TILE_AXIAL_DISTANCE
Logical length of the tile in any DiagDirection used in vehicle movement.
const uint TILE_CORNER_DISTANCE
Logical length of the tile corner crossing in any non-diagonal direction used in vehicle movement.
@ Hidden
Vehicle is not visible.
@ Stopped
Vehicle is stopped by the player.
Functions related to vehicles.
bool HasVehicleOnTile(TileIndex tile, UnaryPred &&predicate)
Loop over vehicles on a tile, and check whether a predicate is true for any of them.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int z, const SpriteBounds &bounds, bool transparent, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:658
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:579
Functions related to (drawing on) viewports.
static constexpr int BB_HEIGHT_UNDER_BRIDGE
Some values for constructing bounding boxes (BB).
Functions related to water management.
void TileLoop_Water(TileIndex tile)
Tile callback function signature for running periodic tile updates.
@ FLOOD_NONE
The tile does not flood neighboured tiles.
Definition water.h:20
FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
Returns the behaviour of a tile during flooding.
void CheckForDockingTile(TileIndex t)
Mark the supplied tile as a docking tile if it is suitable for docking.
bool IsPossibleDockingTile(Tile t)
Check whether it is feasible that the given tile could be a docking tile.
void MakeShore(Tile t)
Helper function to make a coast tile.
Definition water_map.h:385
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:375
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:364
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3321
@ WC_VEHICLE_DEPOT
Depot view; Window numbers:
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers:
Entry point for OpenTTD to YAPF's cache.
void YapfNotifyTrackLayoutChange(TileIndex tile, Track track)
Use this function to notify YAPF that track layout (or signal configuration) has change.