OpenTTD Source 20260206-master-g4d4e37dbf1
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
148RailType AllocateRailType(RailTypeLabel label)
149{
150 auto it = std::ranges::find(_railtypes, 0, &RailTypeInfo::label);
151 if (it == std::end(_railtypes)) return INVALID_RAILTYPE;
152
153 RailTypeInfo &rti = *it;
154 RailType rt = rti.Index();
155
156 /* Set up new rail type based on default rail. */
158 rti.label = label;
159 rti.alternate_labels.clear();
160
161 /* Make us compatible with ourself. */
162 rti.powered_railtypes = rt;
163 rti.compatible_railtypes = rt;
164
165 /* We also introduce ourself. */
166 rti.introduces_railtypes = rt;
167
168 /* Default sort order; order of allocation, but with some
169 * offsets so it's easier for NewGRF to pick a spot without
170 * changing the order of other (original) rail types.
171 * The << is so you can place other railtypes in between the
172 * other railtypes, the 7 is to be able to place something
173 * before the first (default) rail type. */
174 rti.sorting_order = rt << 4 | 7;
175
176 return rt;
177}
178
179static const uint8_t _track_sloped_sprites[14] = {
180 14, 15, 22, 13,
181 0, 21, 17, 12,
182 23, 0, 18, 20,
183 19, 16
184};
185
186
187/* 4
188 * ---------
189 * |\ /|
190 * | \ 1/ |
191 * | \ / |
192 * | \ / |
193 * 16| \ |32
194 * | / \2 |
195 * | / \ |
196 * | / \ |
197 * |/ \|
198 * ---------
199 * 8
200 */
201
202
203
204/* MAP2 byte: abcd???? => Signal On? Same coding as map3lo
205 * MAP3LO byte: abcd???? => Signal Exists?
206 * a and b are for diagonals, upper and left,
207 * one for each direction. (ie a == NE->SW, b ==
208 * SW->NE, or v.v., I don't know. b and c are
209 * similar for lower and right.
210 * MAP2 byte: ????abcd => Type of ground.
211 * MAP3LO byte: ????abcd => Type of rail.
212 * MAP5: 00abcdef => rail
213 * 01abcdef => rail w/ signals
214 * 10uuuuuu => unused
215 * 11uuuudd => rail depot
216 */
217
227{
228 TrackBits rail_bits = TrackToTrackBits(track);
229 return EnsureNoTrainOnTrackBits(tile, rail_bits);
230}
231
239{
240 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
241
242 /* So, we have a tile with tracks on it (and possibly signals). Let's see
243 * what tracks first */
244 TrackBits current = GetTrackBits(tile); // The current track layout.
245 TrackBits future = current | to_build; // The track layout we want to build.
246
247 /* Are we really building something new? */
248 if (current == future) {
249 /* Nothing new is being built */
250 return CommandCost(STR_ERROR_ALREADY_BUILT);
251 }
252
253 /* Normally, we may overlap and any combination is valid */
254 return CommandCost();
255}
256
257
279
301
310{
311 if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE;
312
313 if (IsSteepSlope(tileh)) {
314 /* Test for inclined foundations */
315 if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X;
316 if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y;
317
318 /* Get higher track */
319 Corner highest_corner = GetHighestSlopeCorner(tileh);
320 TrackBits higher_track = CornerToTrackBits(highest_corner);
321
322 /* Only higher track? */
323 if (bits == higher_track) return HalftileFoundation(highest_corner);
324
325 /* Overlap with higher track? */
326 if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID;
327
328 /* either lower track or both higher and lower track */
329 return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER);
330 } else {
331 if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE;
332
333 bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0);
334
335 Corner track_corner;
336 switch (bits) {
337 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
338 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
339 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
340 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
341
342 case TRACK_BIT_HORZ:
343 if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
344 if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
345 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
346
347 case TRACK_BIT_VERT:
348 if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
349 if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
350 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
351
352 case TRACK_BIT_X:
354 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
355
356 case TRACK_BIT_Y:
358 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
359
360 default:
361 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
362 }
363 /* Single diagonal track */
364
365 /* Track must be at least valid on leveled foundation */
366 if (!valid_on_leveled) return FOUNDATION_INVALID;
367
368 /* If slope has three raised corners, build leveled foundation */
370
371 /* If neighboured corners of track_corner are lowered, build halftile foundation */
372 if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
373
374 /* else special anti-zig-zag foundation */
375 return SpecialRailFoundation(track_corner);
376 }
377}
378
379
389static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
390{
391 /* don't allow building on the lower side of a coast */
392 if (GetFloodingBehaviour(tile) != FLOOD_NONE) {
393 if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
394 }
395
396 Foundation f_new = GetRailFoundation(tileh, rail_bits | existing);
397
398 /* check track/slope combination */
399 if ((f_new == FOUNDATION_INVALID) ||
400 ((f_new != FOUNDATION_NONE) && (!_settings_game.construction.build_on_slopes))) {
401 return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
402 }
403
404 Foundation f_old = GetRailFoundation(tileh, existing);
405 return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price[Price::BuildFoundation] : (Money)0);
406}
407
408/* Validate functions for rail building */
409static inline bool ValParamTrackOrientation(Track track)
410{
411 return IsValidTrack(track);
412}
413
423CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
424{
426
427 if (!ValParamRailType(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
428
429 Slope tileh = GetTileSlope(tile);
430 TrackBits trackbit = TrackToTrackBits(track);
431
432 switch (GetTileType(tile)) {
433 case TileType::Railway: {
435 if (ret.Failed()) return ret;
436
437 if (!IsPlainRail(tile)) return Command<Commands::LandscapeClear>::Do(flags, tile); // just get appropriate error message
438
439 if (!IsCompatibleRail(GetRailType(tile), railtype)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
440
441 ret = CheckTrackCombination(tile, trackbit);
442 if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
443 if (ret.Failed()) return ret;
444
445 ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
446 if (ret.Failed()) return ret;
447 cost.AddCost(ret.GetCost());
448
449 if (HasSignals(tile) && TracksOverlap(GetTrackBits(tile) | TrackToTrackBits(track))) {
450 /* If adding the new track causes any overlap, all signals must be removed first */
451 if (!auto_remove_signals) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
452
453 for (Track track_it = TRACK_BEGIN; track_it < TRACK_END; track_it++) {
454 if (HasTrack(tile, track_it) && HasSignalOnTrack(tile, track_it)) {
455 CommandCost ret_remove_signals = Command<Commands::RemoveSignal>::Do(flags, tile, track_it);
456 if (ret_remove_signals.Failed()) return ret_remove_signals;
457 cost.AddCost(ret_remove_signals.GetCost());
458 }
459 }
460 }
461
462 /* If the rail types don't match, try to convert only if engines of
463 * the new rail type are not powered on the present rail type and engines of
464 * the present rail type are powered on the new rail type. */
465 if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
466 if (HasPowerOnRail(GetRailType(tile), railtype)) {
467 ret = Command<Commands::ConvertRail>::Do(flags, tile, tile, railtype, false);
468 if (ret.Failed()) return ret;
469 cost.AddCost(ret.GetCost());
470 } else {
471 return CMD_ERROR;
472 }
473 }
474
475 if (flags.Test(DoCommandFlag::Execute)) {
476 SetRailGroundType(tile, RailGroundType::Barren);
477 TrackBits bits = GetTrackBits(tile);
478 SetTrackBits(tile, bits | trackbit);
479 /* Subtract old infrastructure count. */
480 uint pieces = CountBits(bits);
481 if (TracksOverlap(bits)) pieces *= pieces;
482 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
483 /* Add new infrastructure count. */
484 pieces = CountBits(bits | trackbit);
485 if (TracksOverlap(bits | trackbit)) pieces *= pieces;
486 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
488 }
489 break;
490 }
491
492 case TileType::Road: {
493 /* Level crossings may only be built on these slopes */
494 if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
495
496 if (!_settings_game.construction.crossing_with_competitor && _current_company != OWNER_DEITY) {
498 if (ret.Failed()) return ret;
499 }
500
502 if (ret.Failed()) return ret;
503
504 if (IsNormalRoad(tile)) {
505 if (HasRoadWorks(tile)) return CommandCost(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
506
507 if (GetDisallowedRoadDirections(tile) != DRD_NONE) return CommandCost(STR_ERROR_CROSSING_ON_ONEWAY_ROAD);
508
509 if (RailNoLevelCrossings(railtype)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_RAIL);
510
511 RoadType roadtype_road = GetRoadTypeRoad(tile);
512 RoadType roadtype_tram = GetRoadTypeTram(tile);
513
514 if (roadtype_road != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_road)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
515 if (roadtype_tram != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_tram)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
516
517 RoadBits road = GetRoadBits(tile, RTT_ROAD);
518 RoadBits tram = GetRoadBits(tile, RTT_TRAM);
519 if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) ||
520 (track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) {
521 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
522 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
523 /* Disallow breaking end-of-line of someone else
524 * so trams can still reverse on this tile. */
525 if (Company::IsValidID(tram_owner) && HasExactlyOneBit(tram)) {
526 ret = CheckOwnership(tram_owner);
527 if (ret.Failed()) return ret;
528 }
529
530 uint num_new_road_pieces = (road != ROAD_NONE) ? 2 - CountBits(road) : 0;
531 if (num_new_road_pieces > 0) {
532 cost.AddCost(num_new_road_pieces * RoadBuildCost(roadtype_road));
533 }
534
535 uint num_new_tram_pieces = (tram != ROAD_NONE) ? 2 - CountBits(tram) : 0;
536 if (num_new_tram_pieces > 0) {
537 cost.AddCost(num_new_tram_pieces * RoadBuildCost(roadtype_tram));
538 }
539
540 if (flags.Test(DoCommandFlag::Execute)) {
541 MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile));
542 UpdateLevelCrossing(tile, false);
544 Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR;
546 if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) {
547 Company::Get(road_owner)->infrastructure.road[roadtype_road] += num_new_road_pieces;
549 }
550 if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) {
551 Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces;
553 }
554 }
555 break;
556 }
557 }
558
559 if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) {
560 return CommandCost(STR_ERROR_ALREADY_BUILT);
561 }
562 [[fallthrough]];
563 }
564
565 default: {
566 /* Will there be flat water on the lower halftile? */
567 bool water_ground = IsTileType(tile, TileType::Water) && IsSlopeWithOneCornerRaised(tileh);
568
569 CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile);
570 if (ret.Failed()) return ret;
571 cost.AddCost(ret.GetCost());
572
573 ret = Command<Commands::LandscapeClear>::Do(flags, tile);
574 if (ret.Failed()) return ret;
575 cost.AddCost(ret.GetCost());
576
577 if (water_ground) {
578 cost.AddCost(-_price[Price::ClearWater]);
579 cost.AddCost(_price[Price::ClearRough]);
580 }
581
582 if (flags.Test(DoCommandFlag::Execute)) {
583 MakeRailNormal(tile, _current_company, trackbit, railtype);
584 if (water_ground) {
585 SetRailGroundType(tile, RailGroundType::HalfTileWater);
586 if (IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
587 }
588 Company::Get(_current_company)->infrastructure.rail[railtype]++;
590 }
591 break;
592 }
593 }
594
595 if (flags.Test(DoCommandFlag::Execute)) {
598 YapfNotifyTrackLayoutChange(tile, track);
599 }
600
601 cost.AddCost(RailBuildCost(railtype));
602 return cost;
603}
604
612CommandCost CmdRemoveSingleRail(DoCommandFlags flags, TileIndex tile, Track track)
613{
615 bool crossing = false;
616
617 if (!ValParamTrackOrientation(track)) return CMD_ERROR;
618 TrackBits trackbit = TrackToTrackBits(track);
619
620 /* Need to read tile owner now because it may change when the rail is removed
621 * Also, in case of floods, _current_company != owner
622 * There may be invalid tiletype even in exec run (when removing long track),
623 * so do not call GetTileOwner(tile) in any case here */
624 Owner owner = INVALID_OWNER;
625
626 Train *v = nullptr;
627
628 switch (GetTileType(tile)) {
629 case TileType::Road: {
630 if (!IsLevelCrossing(tile) || GetCrossingRailBits(tile) != trackbit) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
631
634 if (ret.Failed()) return ret;
635 }
636
637 if (!flags.Test(DoCommandFlag::Bankrupt)) {
639 if (ret.Failed()) return ret;
640 }
641
642 cost.AddCost(RailClearCost(GetRailType(tile)));
643
644 if (flags.Test(DoCommandFlag::Execute)) {
646
647 if (HasReservedTracks(tile, trackbit)) {
648 v = GetTrainForReservation(tile, track);
649 if (v != nullptr) FreeTrainTrackReservation(v);
650 }
651
652 owner = GetTileOwner(tile);
653 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR;
655 MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypeRoad(tile), GetRoadTypeTram(tile), GetTownIndex(tile), GetRoadOwner(tile, RTT_ROAD), GetRoadOwner(tile, RTT_TRAM));
656 DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile.base());
657 }
658 break;
659 }
660
661 case TileType::Railway: {
662 TrackBits present;
663 /* There are no rails present at depots. */
664 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
665
668 if (ret.Failed()) return ret;
669 }
670
671 CommandCost ret = EnsureNoTrainOnTrack(tile, track);
672 if (ret.Failed()) return ret;
673
674 present = GetTrackBits(tile);
675 if ((present & trackbit) == 0) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
676 if (present == (TRACK_BIT_X | TRACK_BIT_Y)) crossing = true;
677
678 cost.AddCost(RailClearCost(GetRailType(tile)));
679
680 /* Charge extra to remove signals on the track, if they are there */
681 if (HasSignalOnTrack(tile, track)) {
682 cost.AddCost(Command<Commands::RemoveSignal>::Do(flags, tile, track));
683 }
684
685 if (flags.Test(DoCommandFlag::Execute)) {
686 if (HasReservedTracks(tile, trackbit)) {
687 v = GetTrainForReservation(tile, track);
688 if (v != nullptr) FreeTrainTrackReservation(v);
689 }
690
691 owner = GetTileOwner(tile);
692
693 /* Subtract old infrastructure count. */
694 uint pieces = CountBits(present);
695 if (TracksOverlap(present)) pieces *= pieces;
696 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= pieces;
697 /* Add new infrastructure count. */
698 present ^= trackbit;
699 pieces = CountBits(present);
700 if (TracksOverlap(present)) pieces *= pieces;
701 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] += pieces;
703
704 if (present == 0) {
705 Slope tileh = GetTileSlope(tile);
706 /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */
707 if (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh)) {
708 bool docking = IsDockingTile(tile);
709 MakeShore(tile);
710 SetDockingTile(tile, docking);
711 } else {
712 DoClearSquare(tile);
713 }
714 DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile.base());
715 } else {
716 SetTrackBits(tile, present);
718 }
719 }
720 break;
721 }
722
723 default: return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
724 }
725
726 if (flags.Test(DoCommandFlag::Execute)) {
727 /* if we got that far, 'owner' variable is set correctly */
728 assert(Company::IsValidID(owner));
729
731 if (crossing) {
732 /* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
733 * are removing one of these pieces, we'll need to update signals for
734 * both directions explicitly, as after the track is removed it won't
735 * 'connect' with the other piece. */
736 AddTrackToSignalBuffer(tile, TRACK_X, owner);
737 AddTrackToSignalBuffer(tile, TRACK_Y, owner);
740 } else {
741 AddTrackToSignalBuffer(tile, track, owner);
742 YapfNotifyTrackLayoutChange(tile, track);
743 }
744
745 if (v != nullptr) TryPathReserve(v, true);
746 }
747
748 return cost;
749}
750
751
760{
761 assert(IsPlainRailTile(t));
762
763 bool flooded = false;
764 if (GetRailGroundType(t) == RailGroundType::HalfTileWater) return flooded;
765
766 Slope tileh = GetTileSlope(t);
767 TrackBits rail_bits = GetTrackBits(t);
768
769 if (IsSlopeWithOneCornerRaised(tileh)) {
771
772 TrackBits to_remove = lower_track & rail_bits;
773 if (to_remove != TRACK_BIT_NONE) {
775 flooded = Command<Commands::RemoveRail>::Do(DoCommandFlag::Execute, t, FindFirstTrack(to_remove)).Succeeded();
776 cur_company.Restore();
777 if (!flooded) return flooded; // not yet floodable
778 rail_bits = rail_bits & ~to_remove;
779 if (rail_bits == TRACK_BIT_NONE) {
780 MakeShore(t);
782 return flooded;
783 }
784 }
785
786 if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
787 flooded = true;
788 SetRailGroundType(t, RailGroundType::HalfTileWater);
790 }
791 } else {
792 /* Make shore on steep slopes and 'three-corners-raised'-slopes. */
793 if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), tileh) == 0) {
794 if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) {
795 flooded = true;
796 SetRailGroundType(t, RailGroundType::HalfTileWater);
798 }
799 }
800 }
801 return flooded;
802}
803
804static const TileIndexDiffC _trackdelta[] = {
805 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
806 { 0, 0 },
807 { 0, 0 },
808 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
809 { 0, 0 },
810 { 0, 0 }
811};
812
813
814static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
815{
816 int x = TileX(start);
817 int y = TileY(start);
818 int ex = TileX(end);
819 int ey = TileY(end);
820
821 if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR;
822
823 /* calculate delta x,y from start to end tile */
824 int dx = ex - x;
825 int dy = ey - y;
826
827 /* calculate delta x,y for the first direction */
828 int trdx = _trackdelta[*trackdir].x;
829 int trdy = _trackdelta[*trackdir].y;
830
831 if (!IsDiagonalTrackdir(*trackdir)) {
832 trdx += _trackdelta[*trackdir ^ 1].x;
833 trdy += _trackdelta[*trackdir ^ 1].y;
834 }
835
836 /* validate the direction */
837 while ((trdx <= 0 && dx > 0) ||
838 (trdx >= 0 && dx < 0) ||
839 (trdy <= 0 && dy > 0) ||
840 (trdy >= 0 && dy < 0)) {
841 if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other
842 SetBit(*trackdir, 3); // reverse the direction
843 trdx = -trdx;
844 trdy = -trdy;
845 } else { // other direction is invalid too, invalid drag
846 return CMD_ERROR;
847 }
848 }
849
850 /* (for diagonal tracks, this is already made sure of by above test), but:
851 * for non-diagonal tracks, check if the start and end tile are on 1 line */
852 if (!IsDiagonalTrackdir(*trackdir)) {
853 trdx = _trackdelta[*trackdir].x;
854 trdy = _trackdelta[*trackdir].y;
855 if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) return CMD_ERROR;
856 }
857
858 return CommandCost();
859}
860
873static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
874{
876
877 if ((!remove && !ValParamRailType(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
878 if (end_tile >= Map::Size() || tile >= Map::Size()) return CMD_ERROR;
879
880 Trackdir trackdir = TrackToTrackdir(track);
881
882 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
883 if (ret.Failed()) return ret;
884
885 bool had_success = false;
886 CommandCost last_error = CMD_ERROR;
887 for (;;) {
888 ret = remove ? Command<Commands::RemoveRail>::Do(flags, tile, TrackdirToTrack(trackdir)) : Command<Commands::BuildRail>::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals);
889 if (!remove && !fail_on_obstacle && last_error.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) had_success = true;
890
891 if (ret.Failed()) {
892 last_error = std::move(ret);
893 if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
894 if (fail_on_obstacle) return last_error;
895 if (had_success) break; // Keep going if we haven't constructed any rail yet, skipping the start of the drag
896 }
897
898 /* Ownership errors are more important. */
899 if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
900 } else {
901 had_success = true;
902 total_cost.AddCost(ret.GetCost());
903 }
904
905 if (tile == end_tile) break;
906
907 tile += ToTileIndexDiff(_trackdelta[trackdir]);
908
909 /* toggle railbit for the non-diagonal tracks */
910 if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
911 }
912
913 if (had_success) return total_cost;
914 return last_error;
915}
916
930CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
931{
932 return CmdRailTrackHelper(flags, start_tile, end_tile, railtype, track, false, auto_remove_signals, fail_on_obstacle);
933}
934
945CommandCost CmdRemoveRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, Track track)
946{
947 return CmdRailTrackHelper(flags, start_tile, end_tile, INVALID_RAILTYPE, track, true, false, false);
948}
949
961CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
962{
963 /* check railtype and valid direction for depot (0 through 3), 4 in total */
964 if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
965
966 Slope tileh = GetTileSlope(tile);
967
969
970 /* Prohibit construction if
971 * The tile is non-flat AND
972 * 1) build-on-slopes is disabled
973 * 2) the tile is steep i.e. spans two height levels
974 * 3) the exit points in the wrong direction
975 */
976
977 if (tileh != SLOPE_FLAT) {
978 if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) {
979 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
980 }
981 cost.AddCost(_price[Price::BuildFoundation]);
982 }
983
984 /* Allow the user to rotate the depot instead of having to destroy it and build it again */
985 bool rotate_existing_depot = false;
986 if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
988 if (ret.Failed()) return ret;
989
990 if (dir == GetRailDepotDirection(tile)) return CommandCost();
991
992 ret = EnsureNoVehicleOnGround(tile);
993 if (ret.Failed()) return ret;
994
995 rotate_existing_depot = true;
996 }
997
998 if (!rotate_existing_depot) {
999 cost.AddCost(Command<Commands::LandscapeClear>::Do(flags, tile));
1000 if (cost.Failed()) return cost;
1001
1002 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
1003
1004 if (!Depot::CanAllocateItem()) return CMD_ERROR;
1005 }
1006
1007 if (flags.Test(DoCommandFlag::Execute)) {
1008 if (rotate_existing_depot) {
1009 SetRailDepotExitDirection(tile, dir);
1010 } else {
1011 Depot *d = Depot::Create(tile);
1012
1013 MakeRailDepot(tile, _current_company, d->index, dir, railtype);
1014 MakeDefaultName(d);
1015
1016 Company::Get(_current_company)->infrastructure.rail[railtype]++;
1018 }
1019
1020 MarkTileDirtyByTile(tile);
1023 }
1024
1025 cost.AddCost(_price[Price::BuildDepotTrain]);
1026 cost.AddCost(RailBuildCost(railtype));
1027 return cost;
1028}
1029
1049CommandCost 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)
1050{
1051 if (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE) return CMD_ERROR;
1052 if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
1053
1054 if (ctrl_pressed) sigvar = (SignalVariant)(sigvar ^ SIG_SEMAPHORE);
1055
1056 /* You can only build signals on plain rail tiles, and the selected track must exist */
1057 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
1058 !HasTrack(tile, track)) {
1059 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1060 }
1061 /* Protect against invalid signal copying */
1062 if (signals_copy != 0 && (signals_copy & SignalOnTrack(track)) == 0) return CMD_ERROR;
1063
1064 CommandCost ret = CheckTileOwnership(tile);
1065 if (ret.Failed()) return ret;
1066
1067 /* See if this is a valid track combination for signals (no overlap) */
1068 if (TracksOverlap(GetTrackBits(tile))) return CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
1069
1070 /* In case we don't want to change an existing signal, return without error. */
1071 if (skip_existing_signals && HasSignalOnTrack(tile, track)) return CommandCost();
1072
1073 /* you can not convert a signal if no signal is on track */
1074 if (convert_signal && !HasSignalOnTrack(tile, track)) return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1075
1076 CommandCost cost;
1077 if (!HasSignalOnTrack(tile, track)) {
1078 /* build new signals */
1080 } else {
1081 if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
1082 /* convert signals <-> semaphores */
1084
1085 } else if (convert_signal) {
1086 /* convert button pressed */
1087 if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
1088 /* it costs money to change signal variant (light or semaphore) */
1090 } else {
1091 /* it is free to change signal type (block, exit, entry, combo, path, etc) */
1092 cost = CommandCost();
1093 }
1094
1095 } else {
1096 /* it is free to change orientation or number of signals on the tile (for block/presignals which allow signals in both directions) */
1097 cost = CommandCost();
1098 }
1099 }
1100
1101 if (flags.Test(DoCommandFlag::Execute)) {
1102 Train *v = nullptr;
1103 /* The new/changed signal could block our path. As this can lead to
1104 * stale reservations, we clear the path reservation here and try
1105 * to redo it later on. */
1106 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1107 v = GetTrainForReservation(tile, track);
1108 if (v != nullptr) FreeTrainTrackReservation(v);
1109 }
1110
1111 if (!HasSignals(tile)) {
1112 /* there are no signals at all on this tile yet */
1113 SetHasSignals(tile, true);
1114 SetSignalStates(tile, 0xF); // all signals are on
1115 SetPresentSignals(tile, 0); // no signals built by default
1116 SetSignalType(tile, track, sigtype);
1117 SetSignalVariant(tile, track, sigvar);
1118 }
1119
1120 /* Subtract old signal infrastructure count. */
1121 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1122
1123 if (signals_copy == 0) {
1124 if (!HasSignalOnTrack(tile, track)) {
1125 /* build new signals */
1126 SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
1127 SetSignalType(tile, track, sigtype);
1128 SetSignalVariant(tile, track, sigvar);
1129 while (num_dir_cycle-- > 0) CycleSignalSide(tile, track);
1130 } else {
1131 if (convert_signal) {
1132 /* convert signal button pressed */
1133 if (ctrl_pressed) {
1134 /* toggle the present signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */
1135 SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC);
1136 /* Query current signal type so the check for PBS signals below works. */
1137 sigtype = GetSignalType(tile, track);
1138 } else {
1139 /* convert the present signal to the chosen type and variant */
1140 SetSignalType(tile, track, sigtype);
1141 SetSignalVariant(tile, track, sigvar);
1142 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1144 }
1145 }
1146
1147 } else if (ctrl_pressed) {
1148 /* cycle between cycle_start and cycle_end */
1149 sigtype = (SignalType)(GetSignalType(tile, track) + 1);
1150
1151 if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
1152
1153 SetSignalType(tile, track, sigtype);
1154 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1156 }
1157 } else {
1158 /* cycle the signal side: both -> left -> right -> both -> ... */
1159 CycleSignalSide(tile, track);
1160 /* Query current signal type so the check for PBS signals below works. */
1161 sigtype = GetSignalType(tile, track);
1162 }
1163 }
1164 } else {
1165 /* If CmdBuildManySignals is called with copying signals, just copy the
1166 * direction of the first signal given as parameter by CmdBuildManySignals */
1167 SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (signals_copy & SignalOnTrack(track)));
1168 SetSignalVariant(tile, track, sigvar);
1169 SetSignalType(tile, track, sigtype);
1170 }
1171
1172 /* Add new signal infrastructure count. */
1173 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1175
1176 if (IsPbsSignal(sigtype)) {
1177 /* PBS signals should show red unless they are on reserved tiles without a train. */
1178 uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
1179 SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetRailReservationTrackBits(tile), track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask));
1180 }
1181 MarkTileDirtyByTile(tile);
1183 YapfNotifyTrackLayoutChange(tile, track);
1184 if (v != nullptr && v->track != TRACK_BIT_DEPOT) {
1185 /* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1186 if (!((v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) ||
1187 !IsSafeWaitingPosition(v, v->tile, v->GetVehicleTrackdir(), true, _settings_game.pf.forbid_90_deg)) {
1188 TryPathReserve(v, true);
1189 }
1190 }
1191 }
1192
1193 return cost;
1194}
1195
1196static bool AdvanceSignalAutoFill(TileIndex &tile, Trackdir &trackdir, bool remove)
1197{
1198 /* We only process starting tiles of tunnels or bridges so jump to the other end before moving further. */
1200
1201 tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
1202 if (tile == INVALID_TILE) return false;
1203
1204 /* Check for track bits on the new tile */
1206
1207 if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
1208 trackdirbits &= TrackdirReachesTrackdirs(trackdir);
1209
1210 /* No track bits, must stop */
1211 if (trackdirbits == TRACKDIR_BIT_NONE) return false;
1212
1213 /* Get the first track dir */
1214 trackdir = RemoveFirstTrackdir(&trackdirbits);
1215
1216 /* Any left? It's a junction so we stop */
1217 if (trackdirbits != TRACKDIR_BIT_NONE) return false;
1218
1219 switch (GetTileType(tile)) {
1220 case TileType::Railway:
1221 if (IsRailDepot(tile)) return false;
1222 if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1223 break;
1224
1225 case TileType::Road:
1226 if (!IsLevelCrossing(tile)) return false;
1227 break;
1228
1230 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
1231 if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
1232 break;
1233 }
1234
1235 default: return false;
1236 }
1237 return true;
1238}
1239
1255static 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)
1256{
1258
1259 if (end_tile >= Map::Size() || !ValParamTrackOrientation(track)) return CMD_ERROR;
1260 if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
1261 if (!remove && (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE)) return CMD_ERROR;
1262
1263 if (!IsPlainRailTile(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1264 TileIndex start_tile = tile;
1265
1266 /* Interpret signal_density as the logical length of said amount of tiles in X/Y direction. */
1267 signal_density *= TILE_AXIAL_DISTANCE;
1268
1269 Trackdir trackdir = TrackToTrackdir(track);
1270 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
1271 if (ret.Failed()) return ret;
1272
1273 track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
1274 Trackdir start_trackdir = trackdir;
1275
1276 /* Must start on a valid track to be able to avoid loops */
1277 if (!HasTrack(tile, track)) return CMD_ERROR;
1278
1279 uint8_t signals;
1280 /* copy the signal-style of the first rail-piece if existing */
1281 if (HasSignalOnTrack(tile, track)) {
1282 signals = GetPresentSignals(tile) & SignalOnTrack(track);
1283 assert(signals != 0);
1284
1285 /* copy signal/semaphores style (independent of CTRL) */
1286 sigvar = GetSignalVariant(tile, track);
1287
1288 sigtype = GetSignalType(tile, track);
1289 /* Don't but copy entry or exit-signal type */
1290 if (sigtype == SIGTYPE_ENTRY || sigtype == SIGTYPE_EXIT) sigtype = SIGTYPE_BLOCK;
1291 } else { // no signals exist, drag a two-way signal stretch
1292 signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
1293 }
1294
1295 uint8_t signal_dir = 0;
1296 if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0);
1297 if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
1298
1299 /* signal_ctr - amount of tiles already processed
1300 * last_used_ctr - amount of tiles before previously placed signal
1301 * signals_density - setting to put signal on every Nth tile (double space on |, -- tracks)
1302 * last_suitable_ctr - amount of tiles before last possible signal place
1303 * last_suitable_tile - last tile where it is possible to place a signal
1304 * last_suitable_trackdir - trackdir of the last tile
1305 **********
1306 * trackdir - trackdir to build with autorail
1307 * semaphores - semaphores or signals
1308 * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1309 * and convert all others to semaphore/signal
1310 * remove - 1 remove signals, 0 build signals */
1311 int signal_ctr = 0;
1312 int last_used_ctr = -signal_density; // to force signal at first tile
1313 int last_suitable_ctr = 0;
1314 TileIndex last_suitable_tile = INVALID_TILE;
1315 Trackdir last_suitable_trackdir = INVALID_TRACKDIR;
1316 CommandCost last_error = CMD_ERROR;
1317 bool had_success = false;
1318 auto build_signal = [&](TileIndex tile, Trackdir trackdir, bool test_only) {
1319 /* Pick the correct orientation for the track direction */
1320 uint8_t signals = 0;
1321 if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
1322 if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
1323
1324 DoCommandFlags do_flags = test_only ? DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) : flags;
1325 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);
1326
1327 if (test_only) return ret.Succeeded();
1328
1329 if (ret.Succeeded()) {
1330 had_success = true;
1331 total_cost.AddCost(ret.GetCost());
1332 } else {
1333 /* The "No railway" error is the least important one. */
1334 if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
1335 last_error.GetErrorMessage() == INVALID_STRING_ID) {
1336 last_error = ret;
1337 }
1338 }
1339 return ret.Succeeded();
1340 };
1341
1342 for (;;) {
1343 if (remove) {
1344 /* In remove mode last_* stuff doesn't matter, we simply try to clear every tile. */
1345 build_signal(tile, trackdir, false);
1346 } else if (minimise_gaps) {
1347 /* We're trying to minimize gaps wherever possible, so keep track of last suitable
1348 * position and use it if current gap exceeds required signal density. */
1349
1350 if (signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1351 /* We overshot so build a signal in last good location. */
1352 if (build_signal(last_suitable_tile, last_suitable_trackdir, false)) {
1353 last_suitable_tile = INVALID_TILE;
1354 last_used_ctr = last_suitable_ctr;
1355 }
1356 }
1357
1358 if (signal_ctr == last_used_ctr + signal_density) {
1359 /* Current gap matches the required density, build a signal. */
1360 if (build_signal(tile, trackdir, false)) {
1361 last_used_ctr = signal_ctr;
1362 last_suitable_tile = INVALID_TILE;
1363 }
1364 } else {
1365 /* Test tile for a potential signal spot. */
1366 if (build_signal(tile, trackdir, true)) {
1367 last_suitable_tile = tile;
1368 last_suitable_ctr = signal_ctr;
1369 last_suitable_trackdir = trackdir;
1370 }
1371 }
1372 } else if (signal_ctr >= last_used_ctr + signal_density) {
1373 /* We're always keeping regular interval between signals so doesn't matter whether we succeed or not. */
1374 build_signal(tile, trackdir, false);
1375 last_used_ctr = signal_ctr;
1376 }
1377
1378 if (autofill) {
1379 switch (GetTileType(tile)) {
1380 case TileType::Railway:
1381 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1382 break;
1383
1384 case TileType::Road:
1385 signal_ctr += TILE_AXIAL_DISTANCE;
1386 break;
1387
1389 uint len = (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 2) * TILE_AXIAL_DISTANCE;
1390 if (remove || minimise_gaps) {
1391 signal_ctr += len;
1392 } else {
1393 /* To keep regular interval we need to emulate placing signals on a bridge.
1394 * We start with TILE_AXIAL_DISTANCE as one bridge tile gets processed in the main loop. */
1395 signal_ctr += TILE_AXIAL_DISTANCE;
1396 for (uint i = TILE_AXIAL_DISTANCE; i < len; i += TILE_AXIAL_DISTANCE) {
1397 if (signal_ctr >= last_used_ctr + signal_density) last_used_ctr = signal_ctr;
1398 signal_ctr += TILE_AXIAL_DISTANCE;
1399 }
1400 }
1401 break;
1402 }
1403
1404 default: break;
1405 }
1406
1407 if (!AdvanceSignalAutoFill(tile, trackdir, remove)) break;
1408
1409 /* Prevent possible loops */
1410 if (tile == start_tile && trackdir == start_trackdir) break;
1411 } else {
1412 if (tile == end_tile) break;
1413
1414 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1415 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1416
1417 tile += ToTileIndexDiff(_trackdelta[trackdir]);
1418 if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
1419 }
1420 }
1421
1422 /* We may end up with the current gap exceeding the signal density so fix that if needed. */
1423 if (!remove && minimise_gaps && signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1424 build_signal(last_suitable_tile, last_suitable_trackdir, false);
1425 }
1426
1427 return had_success ? total_cost : last_error;
1428}
1429
1446CommandCost 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)
1447{
1448 return CmdSignalTrackHelper(flags, tile, end_tile, track, sigtype, sigvar, mode, false, autofill, minimise_gaps, signal_density);
1449}
1450
1458CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
1459{
1460 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
1461 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1462 }
1463 if (!HasSignalOnTrack(tile, track)) {
1464 return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1465 }
1466
1467 /* Only water can remove signals from anyone */
1469 CommandCost ret = CheckTileOwnership(tile);
1470 if (ret.Failed()) return ret;
1471 }
1472
1473 /* Do it? */
1474 if (flags.Test(DoCommandFlag::Execute)) {
1475 Train *v = nullptr;
1476 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1477 v = GetTrainForReservation(tile, track);
1478 } else if (IsPbsSignal(GetSignalType(tile, track))) {
1479 /* PBS signal, might be the end of a path reservation. */
1480 Trackdir td = TrackToTrackdir(track);
1481 for (int i = 0; v == nullptr && i < 2; i++, td = ReverseTrackdir(td)) {
1482 /* Only test the active signal side. */
1483 if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
1486 if (HasReservedTracks(next, tracks)) {
1488 }
1489 }
1490 }
1491 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1492 SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
1493 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1495
1496 /* removed last signal from tile? */
1497 if (GetPresentSignals(tile) == 0) {
1498 SetSignalStates(tile, 0);
1499 SetHasSignals(tile, false);
1500 SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores
1501 }
1502
1503 AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
1504 YapfNotifyTrackLayoutChange(tile, track);
1505 if (v != nullptr) TryPathReserve(v, false);
1506
1507 MarkTileDirtyByTile(tile);
1508 }
1509
1511}
1512
1524CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
1525{
1526 return CmdSignalTrackHelper(flags, tile, end_tile, track, SIGTYPE_BLOCK, SIG_ELECTRIC, false, true, autofill, false, 1); // bit 5 is remove bit
1527}
1528
1539CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
1540{
1541 TileIndex area_end = tile;
1542
1543 if (!ValParamRailType(totype)) return CMD_ERROR;
1544 if (area_start >= Map::Size()) return CMD_ERROR;
1545
1546 TrainList affected_trains;
1547
1549 CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
1550 bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
1551
1552 std::unique_ptr<TileIterator> iter = TileIterator::Create(area_start, area_end, diagonal);
1553 for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
1554 TileType tt = GetTileType(tile);
1555
1556 /* Check if there is any track on tile */
1557 switch (tt) {
1558 case TileType::Railway:
1559 break;
1560 case TileType::Station:
1561 if (!HasStationRail(tile)) continue;
1562 break;
1563 case TileType::Road:
1564 if (!IsLevelCrossing(tile)) continue;
1565 if (RailNoLevelCrossings(totype)) {
1566 error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
1567 continue;
1568 }
1569 break;
1571 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
1572 break;
1573 default: continue;
1574 }
1575
1576 /* Original railtype we are converting from */
1577 RailType type = GetRailType(tile);
1578
1579 /* Converting to the same type or converting 'hidden' elrail -> rail */
1580 if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
1581
1582 /* Trying to convert other's rail */
1583 CommandCost ret = CheckTileOwnership(tile);
1584 if (ret.Failed()) {
1585 error = std::move(ret);
1586 continue;
1587 }
1588
1589 std::vector<Train *> vehicles_affected;
1590
1591 /* Vehicle on the tile when not converting Rail <-> ElRail
1592 * Tunnels and bridges have special check later */
1593 if (tt != TileType::TunnelBridge) {
1594 if (!IsCompatibleRail(type, totype)) {
1596 if (ret.Failed()) {
1597 error = std::move(ret);
1598 continue;
1599 }
1600 }
1601 if (flags.Test(DoCommandFlag::Execute)) { // we can safely convert, too
1602 TrackBits reserved = GetReservedTrackbits(tile);
1603 Track track;
1604 while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
1605 Train *v = GetTrainForReservation(tile, track);
1606 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1607 /* No power on new rail type, reroute. */
1609 vehicles_affected.push_back(v);
1610 }
1611 }
1612
1613 /* Update the company infrastructure counters. */
1614 if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
1615 Company *c = Company::Get(GetTileOwner(tile));
1616 uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
1617 if (IsPlainRailTile(tile)) {
1618 TrackBits bits = GetTrackBits(tile);
1619 num_pieces = CountBits(bits);
1620 if (TracksOverlap(bits)) num_pieces *= num_pieces;
1621 }
1622 c->infrastructure.rail[type] -= num_pieces;
1623 c->infrastructure.rail[totype] += num_pieces;
1625 }
1626
1627 SetRailType(tile, totype);
1628 MarkTileDirtyByTile(tile);
1629 /* update power of train on this tile */
1630 for (Vehicle *v : VehiclesOnTile(tile)) {
1631 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1632 }
1633 }
1634 }
1635
1636 switch (tt) {
1637 default: NOT_REACHED();
1638 case TileType::Railway:
1639 switch (GetRailTileType(tile)) {
1641 if (flags.Test(DoCommandFlag::Execute)) {
1642 /* notify YAPF about the track layout change */
1644
1645 /* Update build vehicle window related to this depot */
1648 }
1649 found_convertible_track = true;
1650 cost.AddCost(RailConvertCost(type, totype));
1651 break;
1652
1653 default: // RailTileType::Normal, RailTileType::Signals
1654 if (flags.Test(DoCommandFlag::Execute)) {
1655 /* notify YAPF about the track layout change */
1656 TrackBits tracks = GetTrackBits(tile);
1657 while (tracks != TRACK_BIT_NONE) {
1659 }
1660 }
1661 found_convertible_track = true;
1662 cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
1663 break;
1664 }
1665 break;
1666
1668 TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
1669
1670 /* If both ends of tunnel/bridge are in the range, do not try to convert twice -
1671 * it would cause assert because of different test and exec runs */
1672 if (endtile < tile) {
1673 if (diagonal) {
1674 if (DiagonalTileArea(area_start, area_end).Contains(endtile)) continue;
1675 } else {
1676 if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
1677 }
1678 }
1679
1680 /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
1681 if (!IsCompatibleRail(GetRailType(tile), totype)) {
1682 ret = TunnelBridgeIsFree(tile, endtile);
1683 if (ret.Failed()) {
1684 error = std::move(ret);
1685 continue;
1686 }
1687 }
1688
1689 if (flags.Test(DoCommandFlag::Execute)) {
1691 if (HasTunnelBridgeReservation(tile)) {
1692 Train *v = GetTrainForReservation(tile, track);
1693 if (v != nullptr && !HasPowerOnRail(v->railtypes, totype)) {
1694 /* No power on new rail type, reroute. */
1696 vehicles_affected.push_back(v);
1697 }
1698 }
1699
1700 /* Update the company infrastructure counters. */
1701 uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
1702 Company *c = Company::Get(GetTileOwner(tile));
1703 c->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1704 c->infrastructure.rail[totype] += num_pieces;
1706
1707 SetRailType(tile, totype);
1708 SetRailType(endtile, totype);
1709
1710 for (Vehicle *v : VehiclesOnTile(tile)) {
1711 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1712 }
1713 for (Vehicle *v : VehiclesOnTile(endtile)) {
1714 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1715 }
1716
1717 YapfNotifyTrackLayoutChange(tile, track);
1718 YapfNotifyTrackLayoutChange(endtile, track);
1719
1720 if (IsBridge(tile)) {
1721 MarkBridgeDirty(tile);
1722 } else {
1723 MarkTileDirtyByTile(tile);
1724 MarkTileDirtyByTile(endtile);
1725 }
1726 }
1727
1728 found_convertible_track = true;
1729 cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
1730 break;
1731 }
1732
1733 case TileType::Station:
1734 case TileType::Road:
1735 if (flags.Test(DoCommandFlag::Execute)) {
1736 Track track = ((tt == TileType::Station) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile));
1737 YapfNotifyTrackLayoutChange(tile, track);
1738 }
1739
1740 found_convertible_track = true;
1741 cost.AddCost(RailConvertCost(type, totype));
1742 break;
1743 }
1744
1745 for (uint i = 0; i < vehicles_affected.size(); ++i) {
1746 TryPathReserve(vehicles_affected[i], true);
1747 }
1748 }
1749
1750 if (flags.Test(DoCommandFlag::Execute)) {
1751 /* Railtype changed, update trains as when entering different track */
1752 for (Train *v : affected_trains) {
1753 v->ConsistChanged(CCF_TRACK);
1754 }
1755 }
1756
1757 return found_convertible_track ? cost : error;
1758}
1759
1760static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlags flags)
1761{
1763 CommandCost ret = CheckTileOwnership(tile);
1764 if (ret.Failed()) return ret;
1765 }
1766
1768 if (ret.Failed()) return ret;
1769
1770 if (flags.Test(DoCommandFlag::Execute)) {
1771 /* read variables before the depot is removed */
1773 Owner owner = GetTileOwner(tile);
1774 Train *v = nullptr;
1775
1776 if (HasDepotReservation(tile)) {
1778 if (v != nullptr) FreeTrainTrackReservation(v);
1779 }
1780
1781 Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
1783
1784 delete Depot::GetByTile(tile);
1785 DoClearSquare(tile);
1786 AddSideToSignalBuffer(tile, dir, owner);
1788 if (v != nullptr) TryPathReserve(v, true);
1789 }
1790
1792}
1793
1794static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlags flags)
1795{
1797
1798 if (flags.Test(DoCommandFlag::Auto)) {
1799 if (!IsTileOwner(tile, _current_company)) {
1800 return CommandCost(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
1801 }
1802
1803 if (IsPlainRail(tile)) {
1804 return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
1805 } else {
1806 return CommandCost(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
1807 }
1808 }
1809
1810 switch (GetRailTileType(tile)) {
1812 case RailTileType::Normal: {
1813 Slope tileh = GetTileSlope(tile);
1814 /* Is there flat water on the lower halftile that gets cleared expensively? */
1815 bool water_ground = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh));
1816
1817 TrackBits tracks = GetTrackBits(tile);
1818 while (tracks != TRACK_BIT_NONE) {
1819 Track track = RemoveFirstTrack(&tracks);
1820 CommandCost ret = Command<Commands::RemoveRail>::Do(flags, tile, track);
1821 if (ret.Failed()) return ret;
1822 cost.AddCost(ret.GetCost());
1823 }
1824
1825 /* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
1826 * Same holds for non-companies clearing the tile, e.g. disasters. */
1827 if (water_ground && !flags.Test(DoCommandFlag::Bankrupt) && Company::IsValidID(_current_company)) {
1829 if (ret.Failed()) return ret;
1830
1831 /* The track was removed, and left a coast tile. Now also clear the water. */
1832 if (flags.Test(DoCommandFlag::Execute)) {
1833 DoClearSquare(tile);
1834 }
1835 cost.AddCost(_price[Price::ClearWater]);
1836 }
1837
1838 return cost;
1839 }
1840
1842 return RemoveTrainDepot(tile, flags);
1843
1844 default:
1845 return CMD_ERROR;
1846 }
1847}
1848
1853static uint GetSaveSlopeZ(uint x, uint y, Track track)
1854{
1855 switch (track) {
1856 case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break;
1857 case TRACK_LOWER: x |= 0xF; y |= 0xF; break;
1858 case TRACK_LEFT: x |= 0xF; y &= ~0xF; break;
1859 case TRACK_RIGHT: x &= ~0xF; y |= 0xF; break;
1860 default: break;
1861 }
1862 return GetSlopePixelZ(x, y);
1863}
1864
1865static void DrawSingleSignal(TileIndex tile, const RailTypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos)
1866{
1867 bool side;
1868 switch (_settings_game.construction.train_signal_side) {
1869 case 0: side = false; break; // left
1870 case 2: side = true; break; // right
1871 default: side = _settings_game.vehicle.road_side != 0; break; // driving side
1872 }
1873 static const Point SignalPositions[2][12] = {
1874 { // Signals on the left side
1875 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1876 { 8, 5}, {14, 1}, { 1, 14}, { 9, 11}, { 1, 0}, { 3, 10},
1877 /* LOWER LOWER X X Y Y */
1878 {11, 4}, {14, 14}, {11, 3}, { 4, 13}, { 3, 4}, {11, 13}
1879 }, { // Signals on the right side
1880 /* LEFT LEFT RIGHT RIGHT UPPER UPPER */
1881 {14, 1}, {12, 10}, { 4, 6}, { 1, 14}, {10, 4}, { 0, 1},
1882 /* LOWER LOWER X X Y Y */
1883 {14, 14}, { 5, 12}, {11, 13}, { 4, 3}, {13, 4}, { 3, 11}
1884 }
1885 };
1886
1887 uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x;
1888 uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y;
1889
1890 SignalType type = GetSignalType(tile, track);
1891 SignalVariant variant = GetSignalVariant(tile, track);
1892
1893 SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition);
1894 if (sprite != 0) {
1895 sprite += image;
1896 } else {
1897 /* Normal electric signals are stored in a different sprite block than all other signals. */
1898 sprite = (type == SIGTYPE_BLOCK && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
1899 sprite += type * 16 + variant * 64 + image * 2 + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0);
1900 }
1901
1902 AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, GetSaveSlopeZ(x, y, track), {{}, {1, 1, BB_HEIGHT_UNDER_BRIDGE}, {}});
1903}
1904
1906struct FenceOffset : SpriteBounds {
1908
1909 constexpr FenceOffset(Corner height_ref, int8_t origin_x, int8_t origin_y, uint8_t extent_x, uint8_t extent_y) :
1910 SpriteBounds({origin_x, origin_y, 0}, {extent_x, extent_y, 4}, {}), height_ref(height_ref) {}
1911};
1912
1914static const FenceOffset _fence_offsets[] = {
1915 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_FLAT_X_NW
1916 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_FLAT_Y_NE
1917 { CORNER_W, 8, 8, 1, 1 }, // RFO_FLAT_LEFT
1918 { CORNER_N, 8, 8, 1, 1 }, // RFO_FLAT_UPPER
1919 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_SW_NW
1920 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_SE_NE
1921 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_NE_NW
1922 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_NW_NE
1923 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_FLAT_X_SE
1924 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_FLAT_Y_SW
1925 { CORNER_E, 8, 8, 1, 1 }, // RFO_FLAT_RIGHT
1926 { CORNER_S, 8, 8, 1, 1 }, // RFO_FLAT_LOWER
1927 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_SW_SE
1928 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_SE_SW
1929 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_NE_SE
1930 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_NW_SW
1931};
1932
1940static void DrawTrackFence(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites, RailFenceOffset rfo)
1941{
1942 int z = ti->z;
1943 if (_fence_offsets[rfo].height_ref != CORNER_INVALID) {
1945 }
1946 AddSortableSpriteToDraw(psid.sprite + (rfo % num_sprites), psid.pal, ti->x, ti->y, z, _fence_offsets[rfo]);
1947}
1948
1952static void DrawTrackFence_NW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1953{
1955 if (ti->tileh & SLOPE_NW) rfo = (ti->tileh & SLOPE_W) ? RFO_SLOPE_SW_NW : RFO_SLOPE_NE_NW;
1956 DrawTrackFence(ti, psid, num_sprites, rfo);
1957}
1958
1962static void DrawTrackFence_SE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1963{
1965 if (ti->tileh & SLOPE_SE) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SW_SE : RFO_SLOPE_NE_SE;
1966 DrawTrackFence(ti, psid, num_sprites, rfo);
1967}
1968
1972static void DrawTrackFence_NE(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1973{
1975 if (ti->tileh & SLOPE_NE) rfo = (ti->tileh & SLOPE_E) ? RFO_SLOPE_SE_NE : RFO_SLOPE_NW_NE;
1976 DrawTrackFence(ti, psid, num_sprites, rfo);
1977}
1978
1982static void DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
1983{
1985 if (ti->tileh & SLOPE_SW) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SE_SW : RFO_SLOPE_NW_SW;
1986 DrawTrackFence(ti, psid, num_sprites, rfo);
1987}
1988
1995static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti, PaletteID pal)
1996{
1997 /* Base sprite for track fences.
1998 * Note: Halftile slopes only have fences on the upper part. */
1999 uint num_sprites = 0;
2000 PalSpriteID psid{
2001 .sprite = GetCustomRailSprite(rti, ti->tile, RTSG_FENCES, IsHalftileSlope(ti->tileh) ? TCX_UPPER_HALFTILE : TCX_NORMAL, &num_sprites),
2002 .pal = pal,
2003 };
2004 if (psid.sprite == 0) {
2005 psid.sprite = SPR_TRACK_FENCE_FLAT_X;
2006 num_sprites = 8;
2007 }
2008
2009 assert(num_sprites > 0);
2010
2011 switch (GetRailGroundType(ti->tile)) {
2012 case RailGroundType::FenceNW: DrawTrackFence_NW(ti, psid, num_sprites); break;
2013 case RailGroundType::FenceSE: DrawTrackFence_SE(ti, psid, num_sprites); break;
2014 case RailGroundType::FenceSENW: DrawTrackFence_NW(ti, psid, num_sprites);
2015 DrawTrackFence_SE(ti, psid, num_sprites); break;
2016 case RailGroundType::FenceNE: DrawTrackFence_NE(ti, psid, num_sprites); break;
2017 case RailGroundType::FenceSW: DrawTrackFence_SW(ti, psid, num_sprites); break;
2018 case RailGroundType::FenceNESW: DrawTrackFence_NE(ti, psid, num_sprites);
2019 DrawTrackFence_SW(ti, psid, num_sprites); break;
2020 case RailGroundType::FenceVert1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2021 case RailGroundType::FenceVert2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2022 case RailGroundType::FenceHoriz1: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2023 case RailGroundType::FenceHoriz2: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2025 Corner track_corner;
2026 if (IsHalftileSlope(ti->tileh)) {
2027 /* Steep slope or one-corner-raised slope with halftile foundation */
2028 track_corner = GetHalftileSlopeCorner(ti->tileh);
2029 } else {
2030 /* Three-corner-raised slope */
2032 }
2033 switch (track_corner) {
2034 case CORNER_W: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LEFT); break;
2035 case CORNER_S: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_LOWER); break;
2036 case CORNER_E: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_RIGHT); break;
2037 case CORNER_N: DrawTrackFence(ti, psid, num_sprites, RFO_FLAT_UPPER); break;
2038 default: NOT_REACHED();
2039 }
2040 break;
2041 }
2042 default: break;
2043 }
2044}
2045
2046/* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */
2047static const int INF = 1000; // big number compared to tilesprite size
2048static const SubSprite _halftile_sub_sprite[4] = {
2049 { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right
2050 { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top
2051 { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left
2052 { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2053};
2054
2055static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
2056{
2057 DrawGroundSprite(sprite, pal, nullptr, 0, (ti->tileh & s) ? -8 : 0);
2058}
2059
2060static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti)
2061{
2062 RailGroundType rgt = GetRailGroundType(ti->tile);
2063 Foundation f = GetRailFoundation(ti->tileh, track);
2064 Corner halftile_corner = CORNER_INVALID;
2065
2067 /* Save halftile corner */
2069 /* Draw lower part first */
2070 track &= ~CornerToTrackBits(halftile_corner);
2072 }
2073
2074 DrawFoundation(ti, f);
2075 /* DrawFoundation modifies ti */
2076
2077 /* Draw ground */
2078 if (rgt == RailGroundType::HalfTileWater) {
2079 if (track != TRACK_BIT_NONE || IsSteepSlope(ti->tileh)) {
2080 /* three-corner-raised slope or steep slope with track on upper part */
2081 DrawShoreTile(ti->tileh);
2082 } else {
2083 /* single-corner-raised slope with track on upper part */
2084 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
2085 }
2086 } else {
2087 SpriteID image;
2088
2089 switch (rgt) {
2090 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2091 case RailGroundType::SnowOrDesert: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2092 default: image = SPR_FLAT_GRASS_TILE; break;
2093 }
2094
2095 image += SlopeToSpriteOffset(ti->tileh);
2096
2097 DrawGroundSprite(image, PAL_NONE);
2098 }
2099
2100 bool no_combine = ti->tileh == SLOPE_FLAT && rti->flags.Test(RailTypeFlag::NoSpriteCombine);
2101 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
2102 SpriteID ground = GetCustomRailSprite(rti, ti->tile, no_combine ? RTSG_GROUND_COMPLETE : RTSG_GROUND);
2103 TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE;
2104
2105 if (track == TRACK_BIT_NONE) {
2106 /* Half-tile foundation, no track here? */
2107 } else if (no_combine) {
2108 /* Use trackbits as direct index from ground sprite, subtract 1
2109 * because there is no sprite for no bits. */
2110 DrawGroundSprite(ground + track - 1, PAL_NONE);
2111
2112 /* Draw reserved track bits */
2113 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2114 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2115 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2116 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2117 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2118 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2119 } else if (ti->tileh == SLOPE_NW && track == TRACK_BIT_Y) {
2120 DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
2122 } else if (ti->tileh == SLOPE_NE && track == TRACK_BIT_X) {
2123 DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
2125 } else if (ti->tileh == SLOPE_SE && track == TRACK_BIT_Y) {
2126 DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
2128 } else if (ti->tileh == SLOPE_SW && track == TRACK_BIT_X) {
2129 DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
2131 } else {
2132 switch (track) {
2133 /* Draw single ground sprite when not overlapping. No track overlay
2134 * is necessary for these sprites. */
2135 case TRACK_BIT_X: DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
2136 case TRACK_BIT_Y: DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
2137 case TRACK_BIT_UPPER: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
2138 case TRACK_BIT_LOWER: DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2139 case TRACK_BIT_RIGHT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
2140 case TRACK_BIT_LEFT: DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2141 case TRACK_BIT_CROSS: DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
2142 case TRACK_BIT_HORZ: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
2143 DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2144 case TRACK_BIT_VERT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
2145 DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2146
2147 default:
2148 /* We're drawing a junction tile */
2149 if ((track & TRACK_BIT_3WAY_NE) == 0) {
2150 DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
2151 } else if ((track & TRACK_BIT_3WAY_SW) == 0) {
2152 DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
2153 } else if ((track & TRACK_BIT_3WAY_NW) == 0) {
2154 DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
2155 } else if ((track & TRACK_BIT_3WAY_SE) == 0) {
2156 DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
2157 } else {
2158 DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2159 }
2160
2161 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2162 track &= ~pbs;
2163
2164 /* Draw regular track bits */
2165 if (track & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PAL_NONE);
2166 if (track & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
2167 if (track & TRACK_BIT_UPPER) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
2168 if (track & TRACK_BIT_LOWER) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
2169 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
2170 if (track & TRACK_BIT_LEFT) DrawGroundSprite(overlay + RTO_W, PAL_NONE);
2171 }
2172
2173 /* Draw reserved track bits */
2174 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2175 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2176 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2177 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2178 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2179 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2180 }
2181
2182 if (IsValidCorner(halftile_corner)) {
2183 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2186
2187 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2188 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2189
2190 SpriteID image;
2191 switch (rgt) {
2192 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2194 case RailGroundType::HalfTileSnow: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2195 default: image = SPR_FLAT_GRASS_TILE; break;
2196 }
2197
2198 image += SlopeToSpriteOffset(fake_slope);
2199
2200 DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
2201
2202 track = CornerToTrackBits(halftile_corner);
2203
2204 int offset;
2205 switch (track) {
2206 default: NOT_REACHED();
2207 case TRACK_BIT_UPPER: offset = RTO_N; break;
2208 case TRACK_BIT_LOWER: offset = RTO_S; break;
2209 case TRACK_BIT_RIGHT: offset = RTO_E; break;
2210 case TRACK_BIT_LEFT: offset = RTO_W; break;
2211 }
2212
2213 DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
2214 if (_settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, track)) {
2215 DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
2216 }
2217 }
2218}
2219
2228{
2229 /* If none of the tracks end up in the NE corner, return the ground sprite
2230 * where the NE of the tile is not covered. Repeat for the other directions.
2231 * What remains are junctions where all directions are covered. */
2232 if ((track & TRACK_BIT_3WAY_NE) == 0) return 0;
2233 if ((track & TRACK_BIT_3WAY_SW) == 0) return 1;
2234 if ((track & TRACK_BIT_3WAY_NW) == 0) return 2;
2235 if ((track & TRACK_BIT_3WAY_SE) == 0) return 3;
2236 return 4;
2237}
2238
2244static void DrawTrackBits(TileInfo *ti, TrackBits track)
2245{
2246 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2247
2248 if (rti->UsesOverlay()) {
2249 DrawTrackBitsOverlay(ti, track, rti);
2250 return;
2251 }
2252
2253 RailGroundType rgt = GetRailGroundType(ti->tile);
2254 Foundation f = GetRailFoundation(ti->tileh, track);
2255 Corner halftile_corner = CORNER_INVALID;
2256
2258 /* Save halftile corner */
2260 /* Draw lower part first */
2261 track &= ~CornerToTrackBits(halftile_corner);
2263 }
2264
2265 DrawFoundation(ti, f);
2266 /* DrawFoundation modifies ti */
2267
2268 SpriteID image;
2269 PaletteID pal = PAL_NONE;
2270 const SubSprite *sub = nullptr;
2271 bool junction = false;
2272
2273 /* Select the sprite to use. */
2274 if (track == 0) {
2275 /* Clear ground (only track on halftile foundation) */
2276 if (rgt == RailGroundType::HalfTileWater) {
2277 if (IsSteepSlope(ti->tileh)) {
2278 DrawShoreTile(ti->tileh);
2279 image = 0;
2280 } else {
2281 image = SPR_FLAT_WATER_TILE;
2282 }
2283 } else {
2284 switch (rgt) {
2285 case RailGroundType::Barren: image = SPR_FLAT_BARE_LAND; break;
2286 case RailGroundType::SnowOrDesert: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2287 default: image = SPR_FLAT_GRASS_TILE; break;
2288 }
2289 image += SlopeToSpriteOffset(ti->tileh);
2290 }
2291 } else {
2292 if (ti->tileh != SLOPE_FLAT) {
2293 /* track on non-flat ground */
2294 image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
2295 } else {
2296 /* track on flat ground */
2297 switch (track) {
2298 /* single track, select combined track + ground sprite*/
2299 case TRACK_BIT_Y: image = rti->base_sprites.track_y; break;
2300 case TRACK_BIT_X: image = rti->base_sprites.track_y + 1; break;
2301 case TRACK_BIT_UPPER: image = rti->base_sprites.track_y + 2; break;
2302 case TRACK_BIT_LOWER: image = rti->base_sprites.track_y + 3; break;
2303 case TRACK_BIT_RIGHT: image = rti->base_sprites.track_y + 4; break;
2304 case TRACK_BIT_LEFT: image = rti->base_sprites.track_y + 5; break;
2305 case TRACK_BIT_CROSS: image = rti->base_sprites.track_y + 6; break;
2306
2307 /* double diagonal track, select combined track + ground sprite*/
2308 case TRACK_BIT_HORZ: image = rti->base_sprites.track_ns; break;
2309 case TRACK_BIT_VERT: image = rti->base_sprites.track_ns + 1; break;
2310
2311 /* junction, select only ground sprite, handle track sprite later */
2312 default:
2313 junction = true;
2315 break;
2316 }
2317 }
2318
2319 switch (rgt) {
2321 case RailGroundType::SnowOrDesert: image += rti->snow_offset; break;
2323 /* three-corner-raised slope */
2324 DrawShoreTile(ti->tileh);
2326 sub = &(_halftile_sub_sprite[track_corner]);
2327 break;
2328 }
2329 default: break;
2330 }
2331 }
2332
2333 if (image != 0) DrawGroundSprite(image, pal, sub);
2334
2335 /* Draw track pieces individually for junction tiles */
2336 if (junction) {
2337 if (track & TRACK_BIT_X) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE);
2338 if (track & TRACK_BIT_Y) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE);
2339 if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE);
2340 if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE);
2341 if (track & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
2342 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
2343 }
2344
2345 /* PBS debugging, draw reserved tracks darker */
2346 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) {
2347 /* Get reservation, but mask track on halftile slope */
2348 TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
2349 if (pbs & TRACK_BIT_X) {
2350 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2352 } else {
2353 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2354 }
2355 }
2356 if (pbs & TRACK_BIT_Y) {
2357 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2359 } else {
2360 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2361 }
2362 }
2363 if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_N ? -(int)TILE_HEIGHT : 0);
2364 if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_S ? -(int)TILE_HEIGHT : 0);
2365 if (pbs & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_W ? -(int)TILE_HEIGHT : 0);
2366 if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
2367 }
2368
2369 if (IsValidCorner(halftile_corner)) {
2370 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2371
2372 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2373 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2374 image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y;
2375 pal = PAL_NONE;
2376 switch (rgt) {
2379 case RailGroundType::HalfTileSnow: image += rti->snow_offset; break; // higher part has snow in this case too
2380 default: break;
2381 }
2382 DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
2383
2384 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) {
2385 static const uint8_t _corner_to_track_sprite[] = {3, 1, 2, 0};
2386 DrawGroundSprite(_corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, -(int)TILE_HEIGHT);
2387 }
2388 }
2389}
2390
2391static void DrawSignals(TileIndex tile, TrackBits rails, const RailTypeInfo *rti)
2392{
2393 auto MAYBE_DRAW_SIGNAL = [&](uint8_t signalbit, SignalOffsets image, uint pos, Track track) {
2394 if (IsSignalPresent(tile, signalbit)) DrawSingleSignal(tile, rti, track, GetSingleSignalState(tile, signalbit), image, pos);
2395 };
2396
2397 if (!(rails & TRACK_BIT_Y)) {
2398 if (!(rails & TRACK_BIT_X)) {
2399 if (rails & TRACK_BIT_LEFT) {
2400 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, TRACK_LEFT);
2401 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, TRACK_LEFT);
2402 }
2403 if (rails & TRACK_BIT_RIGHT) {
2404 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, TRACK_RIGHT);
2405 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, TRACK_RIGHT);
2406 }
2407 if (rails & TRACK_BIT_UPPER) {
2408 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, TRACK_UPPER);
2409 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, TRACK_UPPER);
2410 }
2411 if (rails & TRACK_BIT_LOWER) {
2412 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, TRACK_LOWER);
2413 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, TRACK_LOWER);
2414 }
2415 } else {
2416 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, TRACK_X);
2417 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, TRACK_X);
2418 }
2419 } else {
2420 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, TRACK_Y);
2421 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 11, TRACK_Y);
2422 }
2423}
2424
2425static void DrawTile_Track(TileInfo *ti)
2426{
2427 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2428 BridgePillarFlags blocked_pillars{};
2430
2431 if (IsPlainRail(ti->tile)) {
2432 TrackBits rails = GetTrackBits(ti->tile);
2433
2434 DrawTrackBits(ti, rails);
2435
2437
2439
2440 if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
2441
2442 if (IsBridgeAbove(ti->tile)) {
2443 if ((rails & TRACK_BIT_3WAY_NE) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeNE);
2444 if ((rails & TRACK_BIT_3WAY_SE) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeSE);
2445 if ((rails & TRACK_BIT_3WAY_SW) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeSW);
2446 if ((rails & TRACK_BIT_3WAY_NW) != 0) blocked_pillars.Set(BridgePillarFlag::EdgeNW);
2447 }
2448 } else {
2449 /* draw depot */
2450 const DrawTileSprites *dts;
2452
2454
2456 /* Draw rail instead of depot */
2457 dts = &_depot_invisible_gfx_table[dir];
2458 } else {
2459 dts = &_depot_gfx_table[dir];
2460 }
2461
2462 SpriteID image;
2463 if (rti->UsesOverlay()) {
2464 image = SPR_FLAT_GRASS_TILE;
2465 } else {
2466 image = dts->ground.sprite;
2467 if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
2468 }
2469
2470 /* Adjust ground tile for desert and snow. */
2471 if (IsSnowRailGround(ti->tile)) {
2472 if (image != SPR_FLAT_GRASS_TILE) {
2473 image += rti->snow_offset; // tile with tracks
2474 } else {
2475 image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
2476 }
2477 }
2478
2479 DrawGroundSprite(image, GroundSpritePaletteTransform(image, PAL_NONE, pal));
2480
2481 if (rti->UsesOverlay()) {
2482 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
2483
2484 switch (GetRailDepotDirection(ti->tile)) {
2485 case DIAGDIR_NE:
2486 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2487 [[fallthrough]];
2488 case DIAGDIR_SW:
2489 DrawGroundSprite(ground + RTO_X, PAL_NONE);
2490 break;
2491 case DIAGDIR_NW:
2492 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2493 [[fallthrough]];
2494 case DIAGDIR_SE:
2495 DrawGroundSprite(ground + RTO_Y, PAL_NONE);
2496 break;
2497 default:
2498 break;
2499 }
2500
2501 if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2502 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
2503
2504 switch (GetRailDepotDirection(ti->tile)) {
2505 case DIAGDIR_NE:
2506 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2507 [[fallthrough]];
2508 case DIAGDIR_SW:
2510 break;
2511 case DIAGDIR_NW:
2512 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2513 [[fallthrough]];
2514 case DIAGDIR_SE:
2516 break;
2517 default:
2518 break;
2519 }
2520 }
2521 } else {
2522 /* PBS debugging, draw reserved tracks darker */
2523 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2524 switch (GetRailDepotDirection(ti->tile)) {
2525 case DIAGDIR_NE:
2526 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2527 [[fallthrough]];
2528 case DIAGDIR_SW:
2530 break;
2531 case DIAGDIR_NW:
2532 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2533 [[fallthrough]];
2534 case DIAGDIR_SE:
2536 break;
2537 default:
2538 break;
2539 }
2540 }
2541 }
2542 int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT);
2543 int relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
2544
2546
2547 DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, pal);
2548 /* Depots can't have bridges above so no blocked pillars. */
2549 }
2550 DrawBridgeMiddle(ti, blocked_pillars);
2551}
2552
2553void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype)
2554{
2555 const DrawTileSprites *dts = &_depot_gfx_table[dir];
2556 const RailTypeInfo *rti = GetRailTypeInfo(railtype);
2557 SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
2558 uint32_t offset = rti->GetRailtypeSpriteOffset();
2559
2560 if (image != SPR_FLAT_GRASS_TILE) image += offset;
2562
2563 DrawSprite(image, PAL_NONE, x, y);
2564
2565 if (rti->UsesOverlay()) {
2567
2568 switch (dir) {
2569 case DIAGDIR_SW: DrawSprite(ground + RTO_X, PAL_NONE, x, y); break;
2570 case DIAGDIR_SE: DrawSprite(ground + RTO_Y, PAL_NONE, x, y); break;
2571 default: break;
2572 }
2573 }
2574 int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RTSG_DEPOT);
2575 if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
2576
2577 DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette);
2578}
2579
2580static int GetSlopePixelZ_Track(TileIndex tile, uint x, uint y, bool)
2581{
2582 if (IsPlainRail(tile)) {
2583 auto [tileh, z] = GetTilePixelSlope(tile);
2584 if (tileh == SLOPE_FLAT) return z;
2585
2587 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
2588 } else {
2589 return GetTileMaxPixelZ(tile);
2590 }
2591}
2592
2593static Foundation GetFoundation_Track(TileIndex tile, Slope tileh)
2594{
2595 return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
2596}
2597
2598static void TileLoop_Track(TileIndex tile)
2599{
2600 RailGroundType old_ground = GetRailGroundType(tile);
2601 RailGroundType new_ground;
2602
2603 if (old_ground == RailGroundType::HalfTileWater) {
2604 TileLoop_Water(tile);
2605 return;
2606 }
2607
2608 switch (_settings_game.game_creation.landscape) {
2609 case LandscapeType::Arctic: {
2610 auto [slope, z] = GetTileSlopeZ(tile);
2611 bool half = false;
2612
2613 /* for non-flat track, use lower part of track
2614 * in other cases, use the highest part with track */
2615 if (IsPlainRail(tile)) {
2616 TrackBits track = GetTrackBits(tile);
2617 Foundation f = GetRailFoundation(slope, track);
2618
2619 switch (f) {
2620 case FOUNDATION_NONE:
2621 /* no foundation - is the track on the upper side of three corners raised tile? */
2622 if (IsSlopeWithThreeCornersRaised(slope)) z++;
2623 break;
2624
2627 /* sloped track - is it on a steep slope? */
2628 if (IsSteepSlope(slope)) z++;
2629 break;
2630
2632 /* only lower part of steep slope */
2633 z++;
2634 break;
2635
2636 default:
2637 /* if it is a steep slope, then there is a track on higher part */
2638 if (IsSteepSlope(slope)) z++;
2639 z++;
2640 break;
2641 }
2642
2644 } else {
2645 /* is the depot on a non-flat tile? */
2646 if (slope != SLOPE_FLAT) z++;
2647 }
2648
2649 /* 'z' is now the lowest part of the highest track bit -
2650 * for sloped track, it is 'z' of lower part
2651 * for two track bits, it is 'z' of higher track bit
2652 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
2653 if (z > GetSnowLine()) {
2654 if (half && z - GetSnowLine() == 1) {
2655 /* track on non-continuous foundation, lower part is not under snow */
2656 new_ground = RailGroundType::HalfTileSnow;
2657 } else {
2658 new_ground = RailGroundType::SnowOrDesert;
2659 }
2660 goto set_ground;
2661 }
2662 break;
2663 }
2664
2666 if (GetTropicZone(tile) == TROPICZONE_DESERT) {
2667 new_ground = RailGroundType::SnowOrDesert;
2668 goto set_ground;
2669 }
2670 break;
2671
2672 default:
2673 break;
2674 }
2675
2676 new_ground = RailGroundType::Grass;
2677
2678 if (IsPlainRail(tile) && old_ground != RailGroundType::Barren) { // wait until bottom is green
2679 /* determine direction of fence */
2680 TrackBits rail = GetTrackBits(tile);
2681
2682 Owner owner = GetTileOwner(tile);
2683 DiagDirections fences{};
2684
2685 for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
2687
2688 /* Track bit on this edge => no fence. */
2689 if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue;
2690
2691 TileIndex tile2 = tile + TileOffsByDiagDir(d);
2692
2693 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
2694 if (!IsValidTile(tile2) || IsTileType(tile2, TileType::House) || IsTileType(tile2, TileType::Industry) ||
2696 fences.Set(d);
2697 }
2698 }
2699
2700 switch (fences.base()) {
2701 case DiagDirections{}.base(): break;
2702 case DiagDirections{DIAGDIR_NE}.base(): new_ground = RailGroundType::FenceNE; break;
2703 case DiagDirections{DIAGDIR_SE}.base(): new_ground = RailGroundType::FenceSE; break;
2704 case DiagDirections{DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceSW; break;
2705 case DiagDirections{DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceNW; break;
2706 case DiagDirections{DIAGDIR_NE, DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceNESW; break;
2707 case DiagDirections{DIAGDIR_SE, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceSENW; break;
2708 case DiagDirections{DIAGDIR_NE, DIAGDIR_SE}.base(): new_ground = RailGroundType::FenceVert1; break;
2709 case DiagDirections{DIAGDIR_NE, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceHoriz2; break;
2710 case DiagDirections{DIAGDIR_SE, DIAGDIR_SW}.base(): new_ground = RailGroundType::FenceHoriz1; break;
2711 case DiagDirections{DIAGDIR_SW, DIAGDIR_NW}.base(): new_ground = RailGroundType::FenceVert2; break;
2712 default: NOT_REACHED();
2713 }
2714 }
2715
2716set_ground:
2717 if (old_ground != new_ground) {
2718 SetRailGroundType(tile, new_ground);
2719 MarkTileDirtyByTile(tile);
2720 }
2721}
2722
2723
2724static TrackStatus GetTileTrackStatus_Track(TileIndex tile, TransportType mode, uint, DiagDirection side)
2725{
2726 /* Case of half tile slope with water. */
2727 if (mode == TRANSPORT_WATER && IsPlainRail(tile) && GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(GetTileSlope(tile))) {
2728 TrackBits tb = GetTrackBits(tile);
2729 switch (tb) {
2730 default: NOT_REACHED();
2731 case TRACK_BIT_UPPER: tb = TRACK_BIT_LOWER; break;
2732 case TRACK_BIT_LOWER: tb = TRACK_BIT_UPPER; break;
2733 case TRACK_BIT_LEFT: tb = TRACK_BIT_RIGHT; break;
2734 case TRACK_BIT_RIGHT: tb = TRACK_BIT_LEFT; break;
2735 }
2737 }
2738
2739 if (mode != TRANSPORT_RAIL) return 0;
2740
2741 TrackBits trackbits = TRACK_BIT_NONE;
2742 TrackdirBits red_signals = TRACKDIR_BIT_NONE;
2743
2744 switch (GetRailTileType(tile)) {
2745 default: NOT_REACHED();
2747 trackbits = GetTrackBits(tile);
2748 break;
2749
2750 case RailTileType::Signals: {
2751 trackbits = GetTrackBits(tile);
2752 uint8_t a = GetPresentSignals(tile);
2753 uint b = GetSignalStates(tile);
2754
2755 b &= a;
2756
2757 /* When signals are not present (in neither direction),
2758 * we pretend them to be green. Otherwise, it depends on
2759 * the signal type. For signals that are only active from
2760 * one side, we set the missing signals explicitly to
2761 * `green'. Otherwise, they implicitly become `red'. */
2762 if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER);
2763 if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER);
2764
2765 if ((b & 0x8) == 0) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E);
2766 if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W);
2767 if ((b & 0x2) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_E);
2768 if ((b & 0x1) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_W);
2769
2770 break;
2771 }
2772
2773 case RailTileType::Depot: {
2775
2776 if (side != INVALID_DIAGDIR && side != dir) break;
2777
2778 trackbits = DiagDirToDiagTrackBits(dir);
2779 break;
2780 }
2781 }
2782
2783 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), red_signals);
2784}
2785
2786static bool ClickTile_Track(TileIndex tile)
2787{
2788 if (!IsRailDepot(tile)) return false;
2789
2791 return true;
2792}
2793
2794static void GetTileDesc_Track(TileIndex tile, TileDesc &td)
2795{
2796 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
2797 td.rail_speed = rti->max_speed;
2798 td.railtype = rti->strings.name;
2799 td.owner[0] = GetTileOwner(tile);
2800 switch (GetRailTileType(tile)) {
2802 td.str = STR_LAI_RAIL_DESCRIPTION_TRACK;
2803 break;
2804
2805 case RailTileType::Signals: {
2806 static const StringID signal_type[6][6] = {
2807 {
2808 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
2809 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2810 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2811 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2812 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2813 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
2814 },
2815 {
2816 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2817 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS,
2818 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2819 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2820 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2821 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
2822 },
2823 {
2824 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2825 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2826 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
2827 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2828 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2829 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
2830 },
2831 {
2832 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2833 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2834 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2835 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
2836 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2837 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
2838 },
2839 {
2840 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2841 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2842 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2843 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2844 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
2845 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
2846 },
2847 {
2848 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
2849 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
2850 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
2851 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
2852 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
2853 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
2854 }
2855 };
2856
2857 SignalType primary_signal;
2858 SignalType secondary_signal;
2859 if (HasSignalOnTrack(tile, TRACK_UPPER)) {
2860 primary_signal = GetSignalType(tile, TRACK_UPPER);
2861 secondary_signal = HasSignalOnTrack(tile, TRACK_LOWER) ? GetSignalType(tile, TRACK_LOWER) : primary_signal;
2862 } else {
2863 secondary_signal = primary_signal = GetSignalType(tile, TRACK_LOWER);
2864 }
2865
2866 td.str = signal_type[secondary_signal][primary_signal];
2867 break;
2868 }
2869
2871 td.str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
2872 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) {
2873 if (td.rail_speed > 0) {
2874 td.rail_speed = std::min<uint16_t>(td.rail_speed, 61);
2875 } else {
2876 td.rail_speed = 61;
2877 }
2878 }
2879 td.build_date = Depot::GetByTile(tile)->build_date;
2880 break;
2881
2882 default:
2883 NOT_REACHED();
2884 }
2885}
2886
2887static void ChangeTileOwner_Track(TileIndex tile, Owner old_owner, Owner new_owner)
2888{
2889 if (!IsTileOwner(tile, old_owner)) return;
2890
2891 if (new_owner != INVALID_OWNER) {
2892 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
2893 uint num_pieces = 1;
2894 if (IsPlainRail(tile)) {
2895 TrackBits bits = GetTrackBits(tile);
2896 num_pieces = CountBits(bits);
2897 if (TracksOverlap(bits)) num_pieces *= num_pieces;
2898 }
2899 RailType rt = GetRailType(tile);
2900 Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;
2901 Company::Get(new_owner)->infrastructure.rail[rt] += num_pieces;
2902
2903 if (HasSignals(tile)) {
2904 uint num_sigs = CountBits(GetPresentSignals(tile));
2905 Company::Get(old_owner)->infrastructure.signal -= num_sigs;
2906 Company::Get(new_owner)->infrastructure.signal += num_sigs;
2907 }
2908
2909 SetTileOwner(tile, new_owner);
2910 } else {
2911 Command<Commands::LandscapeClear>::Do({DoCommandFlag::Execute, DoCommandFlag::Bankrupt}, tile);
2912 }
2913}
2914
2915static const uint8_t _fractcoords_behind[4] = { 0x8F, 0x8, 0x80, 0xF8 };
2916static const uint8_t _fractcoords_enter[4] = { 0x8A, 0x48, 0x84, 0xA8 };
2917static const int8_t _deltacoord_leaveoffset[8] = {
2918 -1, 0, 1, 0, /* x */
2919 0, 1, 0, -1 /* y */
2920};
2921
2922
2930{
2932 int length = v->CalcNextVehicleOffset();
2933
2934 switch (dir) {
2935 case DIAGDIR_NE: return ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1)));
2936 case DIAGDIR_SE: return -((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) + (length + 1)));
2937 case DIAGDIR_SW: return -((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) + (length + 1)));
2938 case DIAGDIR_NW: return ((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) - (length + 1)));
2939 default: NOT_REACHED();
2940 }
2941}
2942
2947static VehicleEnterTileStates VehicleEnter_Track(Vehicle *u, TileIndex tile, int x, int y)
2948{
2949 /* This routine applies only to trains in depot tiles. */
2950 if (u->type != VEH_TRAIN || !IsRailDepotTile(tile)) return {};
2951
2952 /* Depot direction. */
2954
2955 uint8_t fract_coord = (x & 0xF) + ((y & 0xF) << 4);
2956
2957 /* Make sure a train is not entering the tile from behind. */
2958 if (_fractcoords_behind[dir] == fract_coord) return VehicleEnterTileState::CannotEnter;
2959
2960 Train *v = Train::From(u);
2961
2962 /* Leaving depot? */
2963 if (v->direction == DiagDirToDir(dir)) {
2964 /* Calculate the point where the following wagon should be activated. */
2965 int length = v->CalcNextVehicleOffset();
2966
2967 uint8_t fract_coord_leave =
2968 ((_fractcoords_enter[dir] & 0x0F) + // x
2969 (length + 1) * _deltacoord_leaveoffset[dir]) +
2970 (((_fractcoords_enter[dir] >> 4) + // y
2971 ((length + 1) * _deltacoord_leaveoffset[dir + 4])) << 4);
2972
2973 if (fract_coord_leave == fract_coord) {
2974 /* Leave the depot. */
2975 if ((v = v->Next()) != nullptr) {
2977 v->track = (DiagDirToAxis(dir) == AXIS_X ? TRACK_BIT_X : TRACK_BIT_Y);
2978 }
2979 }
2980 } else if (_fractcoords_enter[dir] == fract_coord) {
2981 /* Entering depot. */
2982 assert(DiagDirToDir(ReverseDiagDir(dir)) == v->direction);
2983 v->track = TRACK_BIT_DEPOT,
2986 if (v->Next() == nullptr) VehicleEnterDepot(v->First());
2987 v->tile = tile;
2988
2991 }
2992
2993 return {};
2994}
2995
3007static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
3008{
3009 if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3010
3011 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3012 if (CheckRailSlope(tileh_new, rail_bits, TRACK_BIT_NONE, tile).Failed()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3013
3014 /* Get the slopes on top of the foundations */
3015 z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), tileh_old);
3016 z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), tileh_new);
3017
3018 Corner track_corner;
3019 switch (rail_bits) {
3020 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
3021 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
3022 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
3023 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
3024
3025 /* Surface slope must not be changed */
3026 default:
3027 if (z_old != z_new || tileh_old != tileh_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3029 }
3030
3031 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3032 z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
3033 z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
3034 if (z_old != z_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3035
3037 /* Make the ground dirty, if surface slope has changed */
3038 if (tileh_old != tileh_new) {
3039 /* If there is flat water on the lower halftile add the cost for clearing it */
3040 if (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh_old)) cost.AddCost(_price[Price::ClearWater]);
3041 if (flags.Test(DoCommandFlag::Execute)) SetRailGroundType(tile, RailGroundType::Barren);
3042 }
3043 return cost;
3044}
3045
3046static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
3047{
3048 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
3049 if (IsPlainRail(tile)) {
3050 TrackBits rail_bits = GetTrackBits(tile);
3051 /* Is there flat water on the lower halftile that must be cleared expensively? */
3052 bool was_water = (GetRailGroundType(tile) == RailGroundType::HalfTileWater && IsSlopeWithOneCornerRaised(tileh_old));
3053
3054 /* Allow clearing the water only if there is no ship */
3055 if (was_water && HasVehicleOnTile(tile, [](const Vehicle *v) {
3056 return v->type == VEH_SHIP;
3057 })) return CommandCost(STR_ERROR_SHIP_IN_THE_WAY);
3058
3059 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3060 CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
3061
3062 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3063 Corner allowed_corner;
3064 switch (rail_bits) {
3065 case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break;
3066 case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break;
3067 case TRACK_BIT_LEFT: allowed_corner = CORNER_E; break;
3068 case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break;
3069 default: return autoslope_result;
3070 }
3071
3072 Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
3073
3074 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3075 if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
3076
3077 /* Everything is valid, which only changes allowed_corner */
3078 for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
3079 if (allowed_corner == corner) continue;
3080 if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
3081 }
3082
3083 /* Make the ground dirty */
3084 if (flags.Test(DoCommandFlag::Execute)) SetRailGroundType(tile, RailGroundType::Barren);
3085
3086 /* allow terraforming */
3087 return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price[Price::ClearWater] : (Money)0);
3088 } else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
3089 AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
3091 }
3092 return Command<Commands::LandscapeClear>::Do(flags, tile);
3093}
3094
3095static CommandCost CheckBuildAbove_Track(TileIndex tile, DoCommandFlags flags, Axis, int)
3096{
3097 if (IsPlainRail(tile)) return CommandCost();
3098 return Command<Commands::LandscapeClear>::Do(flags, tile);
3099}
3100
3101extern const TileTypeProcs _tile_type_rail_procs = {
3102 DrawTile_Track, // draw_tile_proc
3103 GetSlopePixelZ_Track, // get_slope_z_proc
3104 ClearTile_Track, // clear_tile_proc
3105 nullptr, // add_accepted_cargo_proc
3106 GetTileDesc_Track, // get_tile_desc_proc
3107 GetTileTrackStatus_Track, // get_tile_track_status_proc
3108 ClickTile_Track, // click_tile_proc
3109 nullptr, // animate_tile_proc
3110 TileLoop_Track, // tile_loop_proc
3111 ChangeTileOwner_Track, // change_tile_owner_proc
3112 nullptr, // add_produced_cargo_proc
3113 VehicleEnter_Track, // vehicle_enter_tile_proc
3114 GetFoundation_Track, // get_foundation_proc
3115 TerraformTile_Track, // terraform_tile_proc
3116 CheckBuildAbove_Track, // check_build_above_proc
3117};
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:286
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:291
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.
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:550
Header file for electrified rail specific functions.
bool HasRailCatenaryDrawn(RailType rt)
Test if we should draw rail catenary.
Definition elrail_func.h:30
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.
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:517
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:442
TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir)
Adds a DiagDir to a tile.
Definition map_func.h:613
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:427
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:417
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:572
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:331
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:381
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:90
Money RailConvertCost(RailType from, RailType to)
Calculates the cost of rail conversion.
Definition rail.h:456
Money RailClearCost(RailType railtype)
Returns the 'cost' of clearing the specified railtype.
Definition rail.h:439
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:428
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:351
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:376
const RailTypeInfo * GetRailTypeInfo(RailType railtype)
Returns a pointer to the Railtype information for a given railtype.
Definition rail.h:300
@ 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:398
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:238
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:873
CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
Build a train depot.
Definition rail_cmd.cpp:961
static uint GetSaveSlopeZ(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.
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:930
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.
bool FloodHalftile(TileIndex t)
Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
Definition rail_cmd.cpp:759
RailType AllocateRailType(RailTypeLabel label)
Allocate a new rail type label.
Definition rail_cmd.cpp:148
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:389
CommandCost CmdRemoveSingleRail(DoCommandFlags flags, TileIndex tile, Track track)
Remove a single piece of track.
Definition rail_cmd.cpp:612
CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
Remove signals.
static const TrackBits _valid_tracks_without_foundation[15]
Valid TrackBits on a specific (non-steep)-slope without foundation.
Definition rail_cmd.cpp:259
static const TrackBits _valid_tracks_on_leveled_foundation[15]
Valid TrackBits on a specific (non-steep)-slope with leveled foundation.
Definition rail_cmd.cpp:281
static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
Tests if a vehicle interacts with the specified track.
Definition rail_cmd.cpp:226
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 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:945
CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
Build a single piece of rail.
Definition rail_cmd.cpp:423
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 DrawTrackFence_SW(const TileInfo *ti, const PalSpriteID &psid, uint num_sprites)
Draw fence at SW border matching the tile slope.
static VehicleEnterTileStates VehicleEnter_Track(Vehicle *u, TileIndex tile, int x, int y)
Tile callback routine when vehicle enters tile.
void ResetRailTypes()
Reset all rail type information to its default values.
Definition rail_cmd.cpp:65
static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
Tests if autoslope is allowed.
static const FenceOffset _fence_offsets[]
Offsets for drawing fences.
static int GetJunctionGroundSpriteOffset(TrackBits track)
Returns which of the 5 junction-'Rail underlays' to use for the given track bits.
static bool CompareRailTypes(const RailType &first, const RailType &second)
Compare railtypes based on their sorting order.
Definition rail_cmd.cpp:122
static 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:309
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.
std::vector< Train * > TrainList
Helper type for lists/vectors of trains.
Definition rail_cmd.cpp:44
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:361
void MakeRailDepot(Tile tile, Owner owner, DepotID depot_id, DiagDirection dir, RailType rail_type)
Make a rail depot.
Definition rail_map.h:551
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:425
static bool IsRailDepot(Tile t)
Is this rail tile a rail depot?
Definition rail_map.h:95
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
@ 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:403
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:484
@ FenceSENW
Grass with a fence at the NW and SE edges.
Definition rail_map.h:489
@ FenceVert2
Grass with a fence at the western side.
Definition rail_map.h:494
@ HalfTileSnow
Snow only on higher part of slope (steep or one corner raised).
Definition rail_map.h:499
@ Barren
Nothing (dirt).
Definition rail_map.h:485
@ FenceNESW
Grass with a fence at the NE and SW edges.
Definition rail_map.h:492
@ FenceHoriz2
Grass with a fence at the northern side.
Definition rail_map.h:496
@ SnowOrDesert
Icy or sandy.
Definition rail_map.h:497
@ HalfTileWater
Grass with a fence and shore or water on the free halftile.
Definition rail_map.h:498
@ FenceVert1
Grass with a fence at the eastern side.
Definition rail_map.h:493
@ FenceNW
Grass with a fence at the NW edge.
Definition rail_map.h:487
@ FenceSE
Grass with a fence at the SE edge.
Definition rail_map.h:488
@ FenceNE
Grass with a fence at the NE edge.
Definition rail_map.h:490
@ Grass
Grassy.
Definition rail_map.h:486
@ FenceSW
Grass with a fence at the SW edge.
Definition rail_map.h:491
@ FenceHoriz1
Grass with a fence at the southern side.
Definition rail_map.h:495
uint GetPresentSignals(Tile tile)
Get whether the given signals are present (Along/AgainstTrackDir).
Definition rail_map.h:392
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:412
bool IsOnewaySignal(Tile t, Track track)
One-way signals can't be passed the 'wrong' way.
Definition rail_map.h:318
void SetPresentSignals(Tile tile, uint signals)
Set whether the given signals are present (Along/AgainstTrackDir).
Definition rail_map.h:382
void SetRailDepotExitDirection(Tile tile, DiagDirection dir)
Sets the exit direction of a rail depot.
Definition rail_map.h:537
TrackBits GetRailReservationTrackBits(Tile t)
Returns the reserved track bits of the tile.
Definition rail_map.h:194
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 HasSignals(Tile t)
Checks if a rail tile has signals.
Definition rail_map.h:72
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:372
void SetSignalStates(Tile tile, uint state)
Set the states of the signals (Along/AgainstTrackDir).
Definition rail_map.h:351
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).
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:309
TrackBits GetCrossingRailBits(Tile tile)
Get the rail track bits of a level crossing.
Definition road_map.h:352
Track GetCrossingRailTrack(Tile tile)
Get the rail track of a level crossing.
Definition road_map.h:342
DisallowedRoadDirections GetDisallowedRoadDirections(Tile t)
Gets the disallowed directions.
Definition road_map.h:285
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:620
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:646
RoadBits GetCrossingRoadBits(Tile tile)
Get the road bits of a level crossing.
Definition road_map.h:332
Owner GetRoadOwner(Tile t, RoadTramType rtt)
Get the owner of a specific road type.
Definition road_map.h:218
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:493
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:589
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:621
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:32
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:22
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:42
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:113
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:99
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:198
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:52
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:53
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:66
Represents the covered area of e.g.
Combination of a palette sprite and a 'real' sprite.
Definition gfx_type.h:22
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed).
Definition gfx_type.h:24
static Company * Get(auto index)
static bool CanAllocateItem(size_t n=1)
static T * Create(Targs &&... args)
T * Next() const
Get next vehicle in the chain.
static Train * From(Vehicle *v)
T * First() const
Get the first vehicle in the chain.
Used to only draw a part of the sprite.
Definition gfx_type.h:278
Tile description for the 'land area information' tool.
Definition tile_cmd.h: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:154
'Train' is either a loco or a wagon.
Definition train.h:91
Trackdir GetVehicleTrackdir() const override
Get the tracks of the train vehicle.
int CalcNextVehicleOffset() const
Calculate the offset from this vehicle's center to the following center taking the vehicle lengths in...
Definition train.h:173
Vehicle data structure.
Direction direction
facing
VehStates vehstatus
Status.
Order current_order
The current order (+ status, like: loading).
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
uint16_t cur_speed
current speed
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:285
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:352
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:388
bool TracksOverlap(TrackBits bits)
Checks if the given tracks overlap, ie form a crossing.
Definition track_func.h:645
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:584
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:631
TrackBits DiagDirToDiagTrackBits(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track bits incidating with that diagdir.
Definition track_func.h:524
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:439
Track DiagDirToDiagTrack(DiagDirection diagdir)
Maps a (4-way) direction to the diagonal track incidating with that diagdir.
Definition track_func.h:512
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:52
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:528
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1536
CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
Tests if a vehicle interacts with the specified track bits.
Definition vehicle.cpp:576
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:552
@ 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)
Let a water tile floods its diagonal adjoining tiles called from tunnelbridge_cmd,...
@ 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.
void MakeShore(Tile t)
Helper function to make a coast tile.
Definition water_map.h:383
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:373
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:363
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:3310
@ 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.