OpenTTD Source 20250528-master-g3aca5d62a8
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 <http://www.gnu.org/licenses/>.
6 */
7
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 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
133 RailTypeInfo *rti = &_railtypes[rt];
134 ResolveRailTypeGUISprites(rti);
135 if (rti->flags.Test(RailTypeFlag::Hidden)) _railtypes_hidden_mask.Set(rt);
136 }
137
138 _sorted_railtypes.clear();
139 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
140 if (_railtypes[rt].label == 0) continue;
141 _sorted_railtypes.push_back(rt);
142 }
143 std::sort(_sorted_railtypes.begin(), _sorted_railtypes.end(), CompareRailTypes);
144}
145
149RailType AllocateRailType(RailTypeLabel label)
150{
151 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
152 RailTypeInfo *rti = &_railtypes[rt];
153
154 if (rti->label == 0) {
155 /* Set up new rail type */
157 rti->label = label;
158 rti->alternate_labels.clear();
159
160 /* Make us compatible with ourself. */
161 rti->powered_railtypes = rt;
162 rti->compatible_railtypes = rt;
163
164 /* We also introduce ourself. */
165 rti->introduces_railtypes = rt;
166
167 /* Default sort order; order of allocation, but with some
168 * offsets so it's easier for NewGRF to pick a spot without
169 * changing the order of other (original) rail types.
170 * The << is so you can place other railtypes in between the
171 * other railtypes, the 7 is to be able to place something
172 * before the first (default) rail type. */
173 rti->sorting_order = rt << 4 | 7;
174 return rt;
175 }
176 }
177
178 return INVALID_RAILTYPE;
179}
180
181static const uint8_t _track_sloped_sprites[14] = {
182 14, 15, 22, 13,
183 0, 21, 17, 12,
184 23, 0, 18, 20,
185 19, 16
186};
187
188
189/* 4
190 * ---------
191 * |\ /|
192 * | \ 1/ |
193 * | \ / |
194 * | \ / |
195 * 16| \ |32
196 * | / \2 |
197 * | / \ |
198 * | / \ |
199 * |/ \|
200 * ---------
201 * 8
202 */
203
204
205
206/* MAP2 byte: abcd???? => Signal On? Same coding as map3lo
207 * MAP3LO byte: abcd???? => Signal Exists?
208 * a and b are for diagonals, upper and left,
209 * one for each direction. (ie a == NE->SW, b ==
210 * SW->NE, or v.v., I don't know. b and c are
211 * similar for lower and right.
212 * MAP2 byte: ????abcd => Type of ground.
213 * MAP3LO byte: ????abcd => Type of rail.
214 * MAP5: 00abcdef => rail
215 * 01abcdef => rail w/ signals
216 * 10uuuuuu => unused
217 * 11uuuudd => rail depot
218 */
219
229{
230 TrackBits rail_bits = TrackToTrackBits(track);
231 return EnsureNoTrainOnTrackBits(tile, rail_bits);
232}
233
241{
242 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
243
244 /* So, we have a tile with tracks on it (and possibly signals). Let's see
245 * what tracks first */
246 TrackBits current = GetTrackBits(tile); // The current track layout.
247 TrackBits future = current | to_build; // The track layout we want to build.
248
249 /* Are we really building something new? */
250 if (current == future) {
251 /* Nothing new is being built */
252 return CommandCost(STR_ERROR_ALREADY_BUILT);
253 }
254
255 /* Normally, we may overlap and any combination is valid */
256 return CommandCost();
257}
258
259
281
303
312{
313 if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE;
314
315 if (IsSteepSlope(tileh)) {
316 /* Test for inclined foundations */
317 if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X;
318 if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y;
319
320 /* Get higher track */
321 Corner highest_corner = GetHighestSlopeCorner(tileh);
322 TrackBits higher_track = CornerToTrackBits(highest_corner);
323
324 /* Only higher track? */
325 if (bits == higher_track) return HalftileFoundation(highest_corner);
326
327 /* Overlap with higher track? */
328 if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID;
329
330 /* either lower track or both higher and lower track */
331 return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER);
332 } else {
333 if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE;
334
335 bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0);
336
337 Corner track_corner;
338 switch (bits) {
339 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
340 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
341 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
342 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
343
344 case TRACK_BIT_HORZ:
345 if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
346 if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
347 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
348
349 case TRACK_BIT_VERT:
350 if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
351 if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
352 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
353
354 case TRACK_BIT_X:
356 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
357
358 case TRACK_BIT_Y:
360 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
361
362 default:
363 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
364 }
365 /* Single diagonal track */
366
367 /* Track must be at least valid on leveled foundation */
368 if (!valid_on_leveled) return FOUNDATION_INVALID;
369
370 /* If slope has three raised corners, build leveled foundation */
372
373 /* If neighboured corners of track_corner are lowered, build halftile foundation */
374 if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
375
376 /* else special anti-zig-zag foundation */
377 return SpecialRailFoundation(track_corner);
378 }
379}
380
381
391static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
392{
393 /* don't allow building on the lower side of a coast */
394 if (GetFloodingBehaviour(tile) != FLOOD_NONE) {
395 if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return CommandCost(STR_ERROR_CAN_T_BUILD_ON_WATER);
396 }
397
398 Foundation f_new = GetRailFoundation(tileh, rail_bits | existing);
399
400 /* check track/slope combination */
401 if ((f_new == FOUNDATION_INVALID) ||
403 return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
404 }
405
406 Foundation f_old = GetRailFoundation(tileh, existing);
407 return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price[PR_BUILD_FOUNDATION] : (Money)0);
408}
409
410/* Validate functions for rail building */
411static inline bool ValParamTrackOrientation(Track track)
412{
413 return IsValidTrack(track);
414}
415
425CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
426{
428
429 if (!ValParamRailType(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
430
431 Slope tileh = GetTileSlope(tile);
432 TrackBits trackbit = TrackToTrackBits(track);
433
434 switch (GetTileType(tile)) {
435 case MP_RAILWAY: {
437 if (ret.Failed()) return ret;
438
439 if (!IsPlainRail(tile)) return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile); // just get appropriate error message
440
441 if (!IsCompatibleRail(GetRailType(tile), railtype)) return CommandCost(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
442
443 ret = CheckTrackCombination(tile, trackbit);
444 if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
445 if (ret.Failed()) return ret;
446
447 ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
448 if (ret.Failed()) return ret;
449 cost.AddCost(ret.GetCost());
450
451 if (HasSignals(tile) && TracksOverlap(GetTrackBits(tile) | TrackToTrackBits(track))) {
452 /* If adding the new track causes any overlap, all signals must be removed first */
453 if (!auto_remove_signals) return CommandCost(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
454
455 for (Track track_it = TRACK_BEGIN; track_it < TRACK_END; track_it++) {
456 if (HasTrack(tile, track_it) && HasSignalOnTrack(tile, track_it)) {
457 CommandCost ret_remove_signals = Command<CMD_REMOVE_SINGLE_SIGNAL>::Do(flags, tile, track_it);
458 if (ret_remove_signals.Failed()) return ret_remove_signals;
459 cost.AddCost(ret_remove_signals.GetCost());
460 }
461 }
462 }
463
464 /* If the rail types don't match, try to convert only if engines of
465 * the new rail type are not powered on the present rail type and engines of
466 * the present rail type are powered on the new rail type. */
467 if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
468 if (HasPowerOnRail(GetRailType(tile), railtype)) {
469 ret = Command<CMD_CONVERT_RAIL>::Do(flags, tile, tile, railtype, false);
470 if (ret.Failed()) return ret;
471 cost.AddCost(ret.GetCost());
472 } else {
473 return CMD_ERROR;
474 }
475 }
476
477 if (flags.Test(DoCommandFlag::Execute)) {
478 SetRailGroundType(tile, RAIL_GROUND_BARREN);
479 TrackBits bits = GetTrackBits(tile);
480 SetTrackBits(tile, bits | trackbit);
481 /* Subtract old infrastructure count. */
482 uint pieces = CountBits(bits);
483 if (TracksOverlap(bits)) pieces *= pieces;
484 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
485 /* Add new infrastructure count. */
486 pieces = CountBits(bits | trackbit);
487 if (TracksOverlap(bits | trackbit)) pieces *= pieces;
488 Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
490 }
491 break;
492 }
493
494 case MP_ROAD: {
495 /* Level crossings may only be built on these slopes */
496 if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
497
500 if (ret.Failed()) return ret;
501 }
502
504 if (ret.Failed()) return ret;
505
506 if (IsNormalRoad(tile)) {
507 if (HasRoadWorks(tile)) return CommandCost(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
508
509 if (GetDisallowedRoadDirections(tile) != DRD_NONE) return CommandCost(STR_ERROR_CROSSING_ON_ONEWAY_ROAD);
510
511 if (RailNoLevelCrossings(railtype)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_RAIL);
512
513 RoadType roadtype_road = GetRoadTypeRoad(tile);
514 RoadType roadtype_tram = GetRoadTypeTram(tile);
515
516 if (roadtype_road != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_road)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
517 if (roadtype_tram != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_tram)) return CommandCost(STR_ERROR_CROSSING_DISALLOWED_ROAD);
518
519 RoadBits road = GetRoadBits(tile, RTT_ROAD);
520 RoadBits tram = GetRoadBits(tile, RTT_TRAM);
521 if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) ||
522 (track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) {
523 Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
524 Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
525 /* Disallow breaking end-of-line of someone else
526 * so trams can still reverse on this tile. */
527 if (Company::IsValidID(tram_owner) && HasExactlyOneBit(tram)) {
528 ret = CheckOwnership(tram_owner);
529 if (ret.Failed()) return ret;
530 }
531
532 uint num_new_road_pieces = (road != ROAD_NONE) ? 2 - CountBits(road) : 0;
533 if (num_new_road_pieces > 0) {
534 cost.AddCost(num_new_road_pieces * RoadBuildCost(roadtype_road));
535 }
536
537 uint num_new_tram_pieces = (tram != ROAD_NONE) ? 2 - CountBits(tram) : 0;
538 if (num_new_tram_pieces > 0) {
539 cost.AddCost(num_new_tram_pieces * RoadBuildCost(roadtype_tram));
540 }
541
542 if (flags.Test(DoCommandFlag::Execute)) {
543 MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile));
544 UpdateLevelCrossing(tile, false);
546 Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR;
548 if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) {
549 Company::Get(road_owner)->infrastructure.road[roadtype_road] += num_new_road_pieces;
551 }
552 if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) {
553 Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces;
555 }
556 }
557 break;
558 }
559 }
560
561 if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) {
562 return CommandCost(STR_ERROR_ALREADY_BUILT);
563 }
564 [[fallthrough]];
565 }
566
567 default: {
568 /* Will there be flat water on the lower halftile? */
569 bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh);
570
571 CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile);
572 if (ret.Failed()) return ret;
573 cost.AddCost(ret.GetCost());
574
575 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
576 if (ret.Failed()) return ret;
577 cost.AddCost(ret.GetCost());
578
579 if (water_ground) {
580 cost.AddCost(-_price[PR_CLEAR_WATER]);
581 cost.AddCost(_price[PR_CLEAR_ROUGH]);
582 }
583
584 if (flags.Test(DoCommandFlag::Execute)) {
585 MakeRailNormal(tile, _current_company, trackbit, railtype);
586 if (water_ground) {
587 SetRailGroundType(tile, RAIL_GROUND_WATER);
588 if (IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
589 }
590 Company::Get(_current_company)->infrastructure.rail[railtype]++;
592 }
593 break;
594 }
595 }
596
597 if (flags.Test(DoCommandFlag::Execute)) {
600 YapfNotifyTrackLayoutChange(tile, track);
601 }
602
603 cost.AddCost(RailBuildCost(railtype));
604 return cost;
605}
606
615{
617 bool crossing = false;
618
619 if (!ValParamTrackOrientation(track)) return CMD_ERROR;
620 TrackBits trackbit = TrackToTrackBits(track);
621
622 /* Need to read tile owner now because it may change when the rail is removed
623 * Also, in case of floods, _current_company != owner
624 * There may be invalid tiletype even in exec run (when removing long track),
625 * so do not call GetTileOwner(tile) in any case here */
626 Owner owner = INVALID_OWNER;
627
628 Train *v = nullptr;
629
630 switch (GetTileType(tile)) {
631 case MP_ROAD: {
632 if (!IsLevelCrossing(tile) || GetCrossingRailBits(tile) != trackbit) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
633
636 if (ret.Failed()) return ret;
637 }
638
639 if (!flags.Test(DoCommandFlag::Bankrupt)) {
641 if (ret.Failed()) return ret;
642 }
643
644 cost.AddCost(RailClearCost(GetRailType(tile)));
645
646 if (flags.Test(DoCommandFlag::Execute)) {
648
649 if (HasReservedTracks(tile, trackbit)) {
650 v = GetTrainForReservation(tile, track);
651 if (v != nullptr) FreeTrainTrackReservation(v);
652 }
653
654 owner = GetTileOwner(tile);
655 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR;
657 MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypeRoad(tile), GetRoadTypeTram(tile), GetTownIndex(tile), GetRoadOwner(tile, RTT_ROAD), GetRoadOwner(tile, RTT_TRAM));
658 DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile.base());
659 }
660 break;
661 }
662
663 case MP_RAILWAY: {
664 TrackBits present;
665 /* There are no rails present at depots. */
666 if (!IsPlainRail(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
667
670 if (ret.Failed()) return ret;
671 }
672
673 CommandCost ret = EnsureNoTrainOnTrack(tile, track);
674 if (ret.Failed()) return ret;
675
676 present = GetTrackBits(tile);
677 if ((present & trackbit) == 0) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
678 if (present == (TRACK_BIT_X | TRACK_BIT_Y)) crossing = true;
679
680 cost.AddCost(RailClearCost(GetRailType(tile)));
681
682 /* Charge extra to remove signals on the track, if they are there */
683 if (HasSignalOnTrack(tile, track)) {
684 cost.AddCost(Command<CMD_REMOVE_SINGLE_SIGNAL>::Do(flags, tile, track));
685 }
686
687 if (flags.Test(DoCommandFlag::Execute)) {
688 if (HasReservedTracks(tile, trackbit)) {
689 v = GetTrainForReservation(tile, track);
690 if (v != nullptr) FreeTrainTrackReservation(v);
691 }
692
693 owner = GetTileOwner(tile);
694
695 /* Subtract old infrastructure count. */
696 uint pieces = CountBits(present);
697 if (TracksOverlap(present)) pieces *= pieces;
698 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= pieces;
699 /* Add new infrastructure count. */
700 present ^= trackbit;
701 pieces = CountBits(present);
702 if (TracksOverlap(present)) pieces *= pieces;
703 Company::Get(owner)->infrastructure.rail[GetRailType(tile)] += pieces;
705
706 if (present == 0) {
707 Slope tileh = GetTileSlope(tile);
708 /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */
709 if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh)) {
710 bool docking = IsDockingTile(tile);
711 MakeShore(tile);
712 SetDockingTile(tile, docking);
713 } else {
714 DoClearSquare(tile);
715 }
716 DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile.base());
717 } else {
718 SetTrackBits(tile, present);
720 }
721 }
722 break;
723 }
724
725 default: return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
726 }
727
728 if (flags.Test(DoCommandFlag::Execute)) {
729 /* if we got that far, 'owner' variable is set correctly */
730 assert(Company::IsValidID(owner));
731
733 if (crossing) {
734 /* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
735 * are removing one of these pieces, we'll need to update signals for
736 * both directions explicitly, as after the track is removed it won't
737 * 'connect' with the other piece. */
738 AddTrackToSignalBuffer(tile, TRACK_X, owner);
739 AddTrackToSignalBuffer(tile, TRACK_Y, owner);
742 } else {
743 AddTrackToSignalBuffer(tile, track, owner);
744 YapfNotifyTrackLayoutChange(tile, track);
745 }
746
747 if (v != nullptr) TryPathReserve(v, true);
748 }
749
750 return cost;
751}
752
753
762{
763 assert(IsPlainRailTile(t));
764
765 bool flooded = false;
766 if (GetRailGroundType(t) == RAIL_GROUND_WATER) return flooded;
767
768 Slope tileh = GetTileSlope(t);
769 TrackBits rail_bits = GetTrackBits(t);
770
771 if (IsSlopeWithOneCornerRaised(tileh)) {
773
774 TrackBits to_remove = lower_track & rail_bits;
775 if (to_remove != TRACK_BIT_NONE) {
778 cur_company.Restore();
779 if (!flooded) return flooded; // not yet floodable
780 rail_bits = rail_bits & ~to_remove;
781 if (rail_bits == TRACK_BIT_NONE) {
782 MakeShore(t);
784 return flooded;
785 }
786 }
787
788 if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
789 flooded = true;
790 SetRailGroundType(t, RAIL_GROUND_WATER);
792 }
793 } else {
794 /* Make shore on steep slopes and 'three-corners-raised'-slopes. */
795 if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), tileh) == 0) {
796 if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) {
797 flooded = true;
798 SetRailGroundType(t, RAIL_GROUND_WATER);
800 }
801 }
802 }
803 return flooded;
804}
805
806static const TileIndexDiffC _trackdelta[] = {
807 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
808 { 0, 0 },
809 { 0, 0 },
810 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
811 { 0, 0 },
812 { 0, 0 }
813};
814
815
816static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
817{
818 int x = TileX(start);
819 int y = TileY(start);
820 int ex = TileX(end);
821 int ey = TileY(end);
822
823 if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR;
824
825 /* calculate delta x,y from start to end tile */
826 int dx = ex - x;
827 int dy = ey - y;
828
829 /* calculate delta x,y for the first direction */
830 int trdx = _trackdelta[*trackdir].x;
831 int trdy = _trackdelta[*trackdir].y;
832
833 if (!IsDiagonalTrackdir(*trackdir)) {
834 trdx += _trackdelta[*trackdir ^ 1].x;
835 trdy += _trackdelta[*trackdir ^ 1].y;
836 }
837
838 /* validate the direction */
839 while ((trdx <= 0 && dx > 0) ||
840 (trdx >= 0 && dx < 0) ||
841 (trdy <= 0 && dy > 0) ||
842 (trdy >= 0 && dy < 0)) {
843 if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other
844 SetBit(*trackdir, 3); // reverse the direction
845 trdx = -trdx;
846 trdy = -trdy;
847 } else { // other direction is invalid too, invalid drag
848 return CMD_ERROR;
849 }
850 }
851
852 /* (for diagonal tracks, this is already made sure of by above test), but:
853 * for non-diagonal tracks, check if the start and end tile are on 1 line */
854 if (!IsDiagonalTrackdir(*trackdir)) {
855 trdx = _trackdelta[*trackdir].x;
856 trdy = _trackdelta[*trackdir].y;
857 if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) return CMD_ERROR;
858 }
859
860 return CommandCost();
861}
862
875static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
876{
878
879 if ((!remove && !ValParamRailType(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
880 if (end_tile >= Map::Size() || tile >= Map::Size()) return CMD_ERROR;
881
882 Trackdir trackdir = TrackToTrackdir(track);
883
884 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
885 if (ret.Failed()) return ret;
886
887 bool had_success = false;
888 CommandCost last_error = CMD_ERROR;
889 for (;;) {
890 ret = remove ? Command<CMD_REMOVE_SINGLE_RAIL>::Do(flags, tile, TrackdirToTrack(trackdir)) : Command<CMD_BUILD_SINGLE_RAIL>::Do(flags, tile, railtype, TrackdirToTrack(trackdir), auto_remove_signals);
891 if (!remove && !fail_on_obstacle && last_error.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) had_success = true;
892
893 if (ret.Failed()) {
894 last_error = std::move(ret);
895 if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
896 if (fail_on_obstacle) return last_error;
897 if (had_success) break; // Keep going if we haven't constructed any rail yet, skipping the start of the drag
898 }
899
900 /* Ownership errors are more important. */
901 if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
902 } else {
903 had_success = true;
904 total_cost.AddCost(ret.GetCost());
905 }
906
907 if (tile == end_tile) break;
908
909 tile += ToTileIndexDiff(_trackdelta[trackdir]);
910
911 /* toggle railbit for the non-diagonal tracks */
912 if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
913 }
914
915 if (had_success) return total_cost;
916 return last_error;
917}
918
932CommandCost CmdBuildRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, RailType railtype, Track track, bool auto_remove_signals, bool fail_on_obstacle)
933{
934 return CmdRailTrackHelper(flags, start_tile, end_tile, railtype, track, false, auto_remove_signals, fail_on_obstacle);
935}
936
948{
949 return CmdRailTrackHelper(flags, start_tile, end_tile, INVALID_RAILTYPE, track, true, false, false);
950}
951
964{
965 /* check railtype and valid direction for depot (0 through 3), 4 in total */
966 if (!ValParamRailType(railtype) || !IsValidDiagDirection(dir)) return CMD_ERROR;
967
968 Slope tileh = GetTileSlope(tile);
969
971
972 /* Prohibit construction if
973 * The tile is non-flat AND
974 * 1) build-on-slopes is disabled
975 * 2) the tile is steep i.e. spans two height levels
976 * 3) the exit points in the wrong direction
977 */
978
979 if (tileh != SLOPE_FLAT) {
981 return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
982 }
983 cost.AddCost(_price[PR_BUILD_FOUNDATION]);
984 }
985
986 /* Allow the user to rotate the depot instead of having to destroy it and build it again */
987 bool rotate_existing_depot = false;
988 if (IsRailDepotTile(tile) && railtype == GetRailType(tile)) {
990 if (ret.Failed()) return ret;
991
992 if (dir == GetRailDepotDirection(tile)) return CommandCost();
993
994 ret = EnsureNoVehicleOnGround(tile);
995 if (ret.Failed()) return ret;
996
997 rotate_existing_depot = true;
998 }
999
1000 if (!rotate_existing_depot) {
1001 cost.AddCost(Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile));
1002 if (cost.Failed()) return cost;
1003
1004 if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
1005
1006 if (!Depot::CanAllocateItem()) return CMD_ERROR;
1007 }
1008
1009 if (flags.Test(DoCommandFlag::Execute)) {
1010 if (rotate_existing_depot) {
1011 SetRailDepotExitDirection(tile, dir);
1012 } else {
1013 Depot *d = new Depot(tile);
1014
1015 MakeRailDepot(tile, _current_company, d->index, dir, railtype);
1016 MakeDefaultName(d);
1017
1018 Company::Get(_current_company)->infrastructure.rail[railtype]++;
1020 }
1021
1022 MarkTileDirtyByTile(tile);
1025 }
1026
1027 cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN]);
1028 cost.AddCost(RailBuildCost(railtype));
1029 return cost;
1030}
1031
1051CommandCost 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)
1052{
1053 if (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE) return CMD_ERROR;
1054 if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
1055
1056 if (ctrl_pressed) sigvar = (SignalVariant)(sigvar ^ SIG_SEMAPHORE);
1057
1058 /* You can only build signals on plain rail tiles, and the selected track must exist */
1059 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
1060 !HasTrack(tile, track)) {
1061 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1062 }
1063 /* Protect against invalid signal copying */
1064 if (signals_copy != 0 && (signals_copy & SignalOnTrack(track)) == 0) return CMD_ERROR;
1065
1066 CommandCost ret = CheckTileOwnership(tile);
1067 if (ret.Failed()) return ret;
1068
1069 /* See if this is a valid track combination for signals (no overlap) */
1070 if (TracksOverlap(GetTrackBits(tile))) return CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
1071
1072 /* In case we don't want to change an existing signal, return without error. */
1073 if (skip_existing_signals && HasSignalOnTrack(tile, track)) return CommandCost();
1074
1075 /* you can not convert a signal if no signal is on track */
1076 if (convert_signal && !HasSignalOnTrack(tile, track)) return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1077
1078 CommandCost cost;
1079 if (!HasSignalOnTrack(tile, track)) {
1080 /* build new signals */
1081 cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]);
1082 } else {
1083 if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
1084 /* convert signals <-> semaphores */
1085 cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
1086
1087 } else if (convert_signal) {
1088 /* convert button pressed */
1089 if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
1090 /* it costs money to change signal variant (light or semaphore) */
1091 cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
1092 } else {
1093 /* it is free to change signal type (block, exit, entry, combo, path, etc) */
1094 cost = CommandCost();
1095 }
1096
1097 } else {
1098 /* it is free to change orientation or number of signals on the tile (for block/presignals which allow signals in both directions) */
1099 cost = CommandCost();
1100 }
1101 }
1102
1103 if (flags.Test(DoCommandFlag::Execute)) {
1104 Train *v = nullptr;
1105 /* The new/changed signal could block our path. As this can lead to
1106 * stale reservations, we clear the path reservation here and try
1107 * to redo it later on. */
1108 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1109 v = GetTrainForReservation(tile, track);
1110 if (v != nullptr) FreeTrainTrackReservation(v);
1111 }
1112
1113 if (!HasSignals(tile)) {
1114 /* there are no signals at all on this tile yet */
1115 SetHasSignals(tile, true);
1116 SetSignalStates(tile, 0xF); // all signals are on
1117 SetPresentSignals(tile, 0); // no signals built by default
1118 SetSignalType(tile, track, sigtype);
1119 SetSignalVariant(tile, track, sigvar);
1120 }
1121
1122 /* Subtract old signal infrastructure count. */
1123 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1124
1125 if (signals_copy == 0) {
1126 if (!HasSignalOnTrack(tile, track)) {
1127 /* build new signals */
1128 SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
1129 SetSignalType(tile, track, sigtype);
1130 SetSignalVariant(tile, track, sigvar);
1131 while (num_dir_cycle-- > 0) CycleSignalSide(tile, track);
1132 } else {
1133 if (convert_signal) {
1134 /* convert signal button pressed */
1135 if (ctrl_pressed) {
1136 /* toggle the present signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */
1137 SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC);
1138 /* Query current signal type so the check for PBS signals below works. */
1139 sigtype = GetSignalType(tile, track);
1140 } else {
1141 /* convert the present signal to the chosen type and variant */
1142 SetSignalType(tile, track, sigtype);
1143 SetSignalVariant(tile, track, sigvar);
1144 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1146 }
1147 }
1148
1149 } else if (ctrl_pressed) {
1150 /* cycle between cycle_start and cycle_end */
1151 sigtype = (SignalType)(GetSignalType(tile, track) + 1);
1152
1153 if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
1154
1155 SetSignalType(tile, track, sigtype);
1156 if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1158 }
1159 } else {
1160 /* cycle the signal side: both -> left -> right -> both -> ... */
1161 CycleSignalSide(tile, track);
1162 /* Query current signal type so the check for PBS signals below works. */
1163 sigtype = GetSignalType(tile, track);
1164 }
1165 }
1166 } else {
1167 /* If CmdBuildManySignals is called with copying signals, just copy the
1168 * direction of the first signal given as parameter by CmdBuildManySignals */
1169 SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (signals_copy & SignalOnTrack(track)));
1170 SetSignalVariant(tile, track, sigvar);
1171 SetSignalType(tile, track, sigtype);
1172 }
1173
1174 /* Add new signal infrastructure count. */
1175 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1177
1178 if (IsPbsSignal(sigtype)) {
1179 /* PBS signals should show red unless they are on reserved tiles without a train. */
1180 uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
1181 SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetRailReservationTrackBits(tile), track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask));
1182 }
1183 MarkTileDirtyByTile(tile);
1185 YapfNotifyTrackLayoutChange(tile, track);
1186 if (v != nullptr && v->track != TRACK_BIT_DEPOT) {
1187 /* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1188 if (!((v->vehstatus.Test(VehState::Stopped) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) ||
1190 TryPathReserve(v, true);
1191 }
1192 }
1193 }
1194
1195 return cost;
1196}
1197
1198static bool AdvanceSignalAutoFill(TileIndex &tile, Trackdir &trackdir, bool remove)
1199{
1200 /* We only process starting tiles of tunnels or bridges so jump to the other end before moving further. */
1201 if (IsTileType(tile, MP_TUNNELBRIDGE)) tile = GetOtherTunnelBridgeEnd(tile);
1202
1203 tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
1204 if (tile == INVALID_TILE) return false;
1205
1206 /* Check for track bits on the new tile */
1208
1209 if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
1210 trackdirbits &= TrackdirReachesTrackdirs(trackdir);
1211
1212 /* No track bits, must stop */
1213 if (trackdirbits == TRACKDIR_BIT_NONE) return false;
1214
1215 /* Get the first track dir */
1216 trackdir = RemoveFirstTrackdir(&trackdirbits);
1217
1218 /* Any left? It's a junction so we stop */
1219 if (trackdirbits != TRACKDIR_BIT_NONE) return false;
1220
1221 switch (GetTileType(tile)) {
1222 case MP_RAILWAY:
1223 if (IsRailDepot(tile)) return false;
1224 if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1225 break;
1226
1227 case MP_ROAD:
1228 if (!IsLevelCrossing(tile)) return false;
1229 break;
1230
1231 case MP_TUNNELBRIDGE: {
1232 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
1233 if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
1234 break;
1235 }
1236
1237 default: return false;
1238 }
1239 return true;
1240}
1241
1257static 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)
1258{
1260
1261 if (end_tile >= Map::Size() || !ValParamTrackOrientation(track)) return CMD_ERROR;
1262 if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
1263 if (!remove && (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE)) return CMD_ERROR;
1264
1265 if (!IsPlainRailTile(tile)) return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1266 TileIndex start_tile = tile;
1267
1268 /* Interpret signal_density as the logical length of said amount of tiles in X/Y direction. */
1269 signal_density *= TILE_AXIAL_DISTANCE;
1270
1271 Trackdir trackdir = TrackToTrackdir(track);
1272 CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
1273 if (ret.Failed()) return ret;
1274
1275 track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
1276 Trackdir start_trackdir = trackdir;
1277
1278 /* Must start on a valid track to be able to avoid loops */
1279 if (!HasTrack(tile, track)) return CMD_ERROR;
1280
1281 uint8_t signals;
1282 /* copy the signal-style of the first rail-piece if existing */
1283 if (HasSignalOnTrack(tile, track)) {
1284 signals = GetPresentSignals(tile) & SignalOnTrack(track);
1285 assert(signals != 0);
1286
1287 /* copy signal/semaphores style (independent of CTRL) */
1288 sigvar = GetSignalVariant(tile, track);
1289
1290 sigtype = GetSignalType(tile, track);
1291 /* Don't but copy entry or exit-signal type */
1292 if (sigtype == SIGTYPE_ENTRY || sigtype == SIGTYPE_EXIT) sigtype = SIGTYPE_BLOCK;
1293 } else { // no signals exist, drag a two-way signal stretch
1294 signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
1295 }
1296
1297 uint8_t signal_dir = 0;
1298 if (signals & SignalAlongTrackdir(trackdir)) SetBit(signal_dir, 0);
1299 if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
1300
1301 /* signal_ctr - amount of tiles already processed
1302 * last_used_ctr - amount of tiles before previously placed signal
1303 * signals_density - setting to put signal on every Nth tile (double space on |, -- tracks)
1304 * last_suitable_ctr - amount of tiles before last possible signal place
1305 * last_suitable_tile - last tile where it is possible to place a signal
1306 * last_suitable_trackdir - trackdir of the last tile
1307 **********
1308 * trackdir - trackdir to build with autorail
1309 * semaphores - semaphores or signals
1310 * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1311 * and convert all others to semaphore/signal
1312 * remove - 1 remove signals, 0 build signals */
1313 int signal_ctr = 0;
1314 int last_used_ctr = -signal_density; // to force signal at first tile
1315 int last_suitable_ctr = 0;
1316 TileIndex last_suitable_tile = INVALID_TILE;
1317 Trackdir last_suitable_trackdir = INVALID_TRACKDIR;
1318 CommandCost last_error = CMD_ERROR;
1319 bool had_success = false;
1320 auto build_signal = [&](TileIndex tile, Trackdir trackdir, bool test_only) {
1321 /* Pick the correct orientation for the track direction */
1322 uint8_t signals = 0;
1323 if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
1324 if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
1325
1326 DoCommandFlags do_flags = test_only ? DoCommandFlags{flags}.Reset(DoCommandFlag::Execute) : flags;
1327 CommandCost ret = remove ? Command<CMD_REMOVE_SINGLE_SIGNAL>::Do(do_flags, tile, TrackdirToTrack(trackdir)) : Command<CMD_BUILD_SINGLE_SIGNAL>::Do(do_flags, tile, TrackdirToTrack(trackdir), sigtype, sigvar, false, signal_ctr == 0, mode, SIGTYPE_BLOCK, SIGTYPE_BLOCK, 0, signals);
1328
1329 if (test_only) return ret.Succeeded();
1330
1331 if (ret.Succeeded()) {
1332 had_success = true;
1333 total_cost.AddCost(ret.GetCost());
1334 } else {
1335 /* The "No railway" error is the least important one. */
1336 if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
1337 last_error.GetErrorMessage() == INVALID_STRING_ID) {
1338 last_error = ret;
1339 }
1340 }
1341 return ret.Succeeded();
1342 };
1343
1344 for (;;) {
1345 if (remove) {
1346 /* In remove mode last_* stuff doesn't matter, we simply try to clear every tile. */
1347 build_signal(tile, trackdir, false);
1348 } else if (minimise_gaps) {
1349 /* We're trying to minimize gaps wherever possible, so keep track of last suitable
1350 * position and use it if current gap exceeds required signal density. */
1351
1352 if (signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1353 /* We overshot so build a signal in last good location. */
1354 if (build_signal(last_suitable_tile, last_suitable_trackdir, false)) {
1355 last_suitable_tile = INVALID_TILE;
1356 last_used_ctr = last_suitable_ctr;
1357 }
1358 }
1359
1360 if (signal_ctr == last_used_ctr + signal_density) {
1361 /* Current gap matches the required density, build a signal. */
1362 if (build_signal(tile, trackdir, false)) {
1363 last_used_ctr = signal_ctr;
1364 last_suitable_tile = INVALID_TILE;
1365 }
1366 } else {
1367 /* Test tile for a potential signal spot. */
1368 if (build_signal(tile, trackdir, true)) {
1369 last_suitable_tile = tile;
1370 last_suitable_ctr = signal_ctr;
1371 last_suitable_trackdir = trackdir;
1372 }
1373 }
1374 } else if (signal_ctr >= last_used_ctr + signal_density) {
1375 /* We're always keeping regular interval between signals so doesn't matter whether we succeed or not. */
1376 build_signal(tile, trackdir, false);
1377 last_used_ctr = signal_ctr;
1378 }
1379
1380 if (autofill) {
1381 switch (GetTileType(tile)) {
1382 case MP_RAILWAY:
1383 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1384 break;
1385
1386 case MP_ROAD:
1387 signal_ctr += TILE_AXIAL_DISTANCE;
1388 break;
1389
1390 case MP_TUNNELBRIDGE: {
1391 uint len = (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 2) * TILE_AXIAL_DISTANCE;
1392 if (remove || minimise_gaps) {
1393 signal_ctr += len;
1394 } else {
1395 /* To keep regular interval we need to emulate placing signals on a bridge.
1396 * We start with TILE_AXIAL_DISTANCE as one bridge tile gets processed in the main loop. */
1397 signal_ctr += TILE_AXIAL_DISTANCE;
1398 for (uint i = TILE_AXIAL_DISTANCE; i < len; i += TILE_AXIAL_DISTANCE) {
1399 if (signal_ctr >= last_used_ctr + signal_density) last_used_ctr = signal_ctr;
1400 signal_ctr += TILE_AXIAL_DISTANCE;
1401 }
1402 }
1403 break;
1404 }
1405
1406 default: break;
1407 }
1408
1409 if (!AdvanceSignalAutoFill(tile, trackdir, remove)) break;
1410
1411 /* Prevent possible loops */
1412 if (tile == start_tile && trackdir == start_trackdir) break;
1413 } else {
1414 if (tile == end_tile) break;
1415
1416 signal_ctr += (IsDiagonalTrackdir(trackdir) ? TILE_AXIAL_DISTANCE : TILE_CORNER_DISTANCE);
1417 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1418
1419 tile += ToTileIndexDiff(_trackdelta[trackdir]);
1420 if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
1421 }
1422 }
1423
1424 /* We may end up with the current gap exceeding the signal density so fix that if needed. */
1425 if (!remove && minimise_gaps && signal_ctr > last_used_ctr + signal_density && last_suitable_tile != INVALID_TILE) {
1426 build_signal(last_suitable_tile, last_suitable_trackdir, false);
1427 }
1428
1429 return had_success ? total_cost : last_error;
1430}
1431
1448CommandCost 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)
1449{
1450 return CmdSignalTrackHelper(flags, tile, end_tile, track, sigtype, sigvar, mode, false, autofill, minimise_gaps, signal_density);
1451}
1452
1461{
1462 if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
1463 return CommandCost(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1464 }
1465 if (!HasSignalOnTrack(tile, track)) {
1466 return CommandCost(STR_ERROR_THERE_ARE_NO_SIGNALS);
1467 }
1468
1469 /* Only water can remove signals from anyone */
1471 CommandCost ret = CheckTileOwnership(tile);
1472 if (ret.Failed()) return ret;
1473 }
1474
1475 /* Do it? */
1476 if (flags.Test(DoCommandFlag::Execute)) {
1477 Train *v = nullptr;
1478 if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1479 v = GetTrainForReservation(tile, track);
1480 } else if (IsPbsSignal(GetSignalType(tile, track))) {
1481 /* PBS signal, might be the end of a path reservation. */
1482 Trackdir td = TrackToTrackdir(track);
1483 for (int i = 0; v == nullptr && i < 2; i++, td = ReverseTrackdir(td)) {
1484 /* Only test the active signal side. */
1485 if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
1488 if (HasReservedTracks(next, tracks)) {
1490 }
1491 }
1492 }
1493 Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1494 SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
1495 Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1497
1498 /* removed last signal from tile? */
1499 if (GetPresentSignals(tile) == 0) {
1500 SetSignalStates(tile, 0);
1501 SetHasSignals(tile, false);
1502 SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores
1503 }
1504
1505 AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
1506 YapfNotifyTrackLayoutChange(tile, track);
1507 if (v != nullptr) TryPathReserve(v, false);
1508
1509 MarkTileDirtyByTile(tile);
1510 }
1511
1512 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS]);
1513}
1514
1526CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
1527{
1528 return CmdSignalTrackHelper(flags, tile, end_tile, track, SIGTYPE_BLOCK, SIG_ELECTRIC, false, true, autofill, false, 1); // bit 5 is remove bit
1529}
1530
1541CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
1542{
1543 TileIndex area_end = tile;
1544
1545 if (!ValParamRailType(totype)) return CMD_ERROR;
1546 if (area_start >= Map::Size()) return CMD_ERROR;
1547
1548 TrainList affected_trains;
1549
1551 CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
1552 bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
1553
1554 std::unique_ptr<TileIterator> iter = TileIterator::Create(area_start, area_end, diagonal);
1555 for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
1556 TileType tt = GetTileType(tile);
1557
1558 /* Check if there is any track on tile */
1559 switch (tt) {
1560 case MP_RAILWAY:
1561 break;
1562 case MP_STATION:
1563 if (!HasStationRail(tile)) continue;
1564 break;
1565 case MP_ROAD:
1566 if (!IsLevelCrossing(tile)) continue;
1567 if (RailNoLevelCrossings(totype)) {
1568 error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
1569 continue;
1570 }
1571 break;
1572 case MP_TUNNELBRIDGE:
1573 if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
1574 break;
1575 default: continue;
1576 }
1577
1578 /* Original railtype we are converting from */
1579 RailType type = GetRailType(tile);
1580
1581 /* Converting to the same type or converting 'hidden' elrail -> rail */
1582 if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
1583
1584 /* Trying to convert other's rail */
1585 CommandCost ret = CheckTileOwnership(tile);
1586 if (ret.Failed()) {
1587 error = std::move(ret);
1588 continue;
1589 }
1590
1591 std::vector<Train *> vehicles_affected;
1592
1593 /* Vehicle on the tile when not converting Rail <-> ElRail
1594 * Tunnels and bridges have special check later */
1595 if (tt != MP_TUNNELBRIDGE) {
1596 if (!IsCompatibleRail(type, totype)) {
1598 if (ret.Failed()) {
1599 error = std::move(ret);
1600 continue;
1601 }
1602 }
1603 if (flags.Test(DoCommandFlag::Execute)) { // we can safely convert, too
1604 TrackBits reserved = GetReservedTrackbits(tile);
1605 Track track;
1606 while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
1607 Train *v = GetTrainForReservation(tile, track);
1608 if (v != nullptr && !HasPowerOnRail(v->railtype, totype)) {
1609 /* No power on new rail type, reroute. */
1611 vehicles_affected.push_back(v);
1612 }
1613 }
1614
1615 /* Update the company infrastructure counters. */
1616 if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
1617 Company *c = Company::Get(GetTileOwner(tile));
1618 uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
1619 if (IsPlainRailTile(tile)) {
1620 TrackBits bits = GetTrackBits(tile);
1621 num_pieces = CountBits(bits);
1622 if (TracksOverlap(bits)) num_pieces *= num_pieces;
1623 }
1624 c->infrastructure.rail[type] -= num_pieces;
1625 c->infrastructure.rail[totype] += num_pieces;
1627 }
1628
1629 SetRailType(tile, totype);
1630 MarkTileDirtyByTile(tile);
1631 /* update power of train on this tile */
1632 for (Vehicle *v : VehiclesOnTile(tile)) {
1633 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1634 }
1635 }
1636 }
1637
1638 switch (tt) {
1639 case MP_RAILWAY:
1640 switch (GetRailTileType(tile)) {
1641 case RAIL_TILE_DEPOT:
1642 if (flags.Test(DoCommandFlag::Execute)) {
1643 /* notify YAPF about the track layout change */
1645
1646 /* Update build vehicle window related to this depot */
1649 }
1650 found_convertible_track = true;
1651 cost.AddCost(RailConvertCost(type, totype));
1652 break;
1653
1654 default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS
1655 if (flags.Test(DoCommandFlag::Execute)) {
1656 /* notify YAPF about the track layout change */
1657 TrackBits tracks = GetTrackBits(tile);
1658 while (tracks != TRACK_BIT_NONE) {
1660 }
1661 }
1662 found_convertible_track = true;
1663 cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
1664 break;
1665 }
1666 break;
1667
1668 case MP_TUNNELBRIDGE: {
1669 TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
1670
1671 /* If both ends of tunnel/bridge are in the range, do not try to convert twice -
1672 * it would cause assert because of different test and exec runs */
1673 if (endtile < tile) {
1674 if (diagonal) {
1675 if (DiagonalTileArea(area_start, area_end).Contains(endtile)) continue;
1676 } else {
1677 if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
1678 }
1679 }
1680
1681 /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
1682 if (!IsCompatibleRail(GetRailType(tile), totype)) {
1683 ret = TunnelBridgeIsFree(tile, endtile);
1684 if (ret.Failed()) {
1685 error = std::move(ret);
1686 continue;
1687 }
1688 }
1689
1690 if (flags.Test(DoCommandFlag::Execute)) {
1692 if (HasTunnelBridgeReservation(tile)) {
1693 Train *v = GetTrainForReservation(tile, track);
1694 if (v != nullptr && !HasPowerOnRail(v->railtype, totype)) {
1695 /* No power on new rail type, reroute. */
1697 vehicles_affected.push_back(v);
1698 }
1699 }
1700
1701 /* Update the company infrastructure counters. */
1702 uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
1703 Company *c = Company::Get(GetTileOwner(tile));
1704 c->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1705 c->infrastructure.rail[totype] += num_pieces;
1707
1708 SetRailType(tile, totype);
1709 SetRailType(endtile, totype);
1710
1711 for (Vehicle *v : VehiclesOnTile(tile)) {
1712 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1713 }
1714 for (Vehicle *v : VehiclesOnTile(endtile)) {
1715 if (v->type == VEH_TRAIN) include(affected_trains, Train::From(v)->First());
1716 }
1717
1718 YapfNotifyTrackLayoutChange(tile, track);
1719 YapfNotifyTrackLayoutChange(endtile, track);
1720
1721 if (IsBridge(tile)) {
1722 MarkBridgeDirty(tile);
1723 } else {
1724 MarkTileDirtyByTile(tile);
1725 MarkTileDirtyByTile(endtile);
1726 }
1727 }
1728
1729 found_convertible_track = true;
1730 cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
1731 break;
1732 }
1733
1734 default: // MP_STATION, MP_ROAD
1735 if (flags.Test(DoCommandFlag::Execute)) {
1736 Track track = ((tt == MP_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
1791 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]);
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)) {
1811 case RAIL_TILE_SIGNALS:
1812 case RAIL_TILE_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) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh));
1816
1817 TrackBits tracks = GetTrackBits(tile);
1818 while (tracks != TRACK_BIT_NONE) {
1819 Track track = RemoveFirstTrack(&tracks);
1820 CommandCost ret = Command<CMD_REMOVE_SINGLE_RAIL>::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[PR_CLEAR_WATER]);
1836 }
1837
1838 return cost;
1839 }
1840
1841 case RAIL_TILE_DEPOT:
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;
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, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track));
1903}
1904
1905static uint32_t _drawtile_track_palette;
1906
1907
1908
1917
1919static const FenceOffset _fence_offsets[] = {
1920 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_FLAT_X_NW
1921 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_FLAT_Y_NE
1922 { CORNER_W, 8, 8, 1, 1 }, // RFO_FLAT_LEFT
1923 { CORNER_N, 8, 8, 1, 1 }, // RFO_FLAT_UPPER
1924 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_SW_NW
1925 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_SE_NE
1926 { CORNER_INVALID, 0, 1, 16, 1 }, // RFO_SLOPE_NE_NW
1927 { CORNER_INVALID, 1, 0, 1, 16 }, // RFO_SLOPE_NW_NE
1928 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_FLAT_X_SE
1929 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_FLAT_Y_SW
1930 { CORNER_E, 8, 8, 1, 1 }, // RFO_FLAT_RIGHT
1931 { CORNER_S, 8, 8, 1, 1 }, // RFO_FLAT_LOWER
1932 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_SW_SE
1933 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_SE_SW
1934 { CORNER_INVALID, 0, 15, 16, 1 }, // RFO_SLOPE_NE_SE
1935 { CORNER_INVALID, 15, 0, 1, 16 }, // RFO_SLOPE_NW_SW
1936};
1937
1945static void DrawTrackFence(const TileInfo *ti, SpriteID base_image, uint num_sprites, RailFenceOffset rfo)
1946{
1947 int z = ti->z;
1948 if (_fence_offsets[rfo].height_ref != CORNER_INVALID) {
1950 }
1951 AddSortableSpriteToDraw(base_image + (rfo % num_sprites), _drawtile_track_palette,
1952 ti->x + _fence_offsets[rfo].x_offs,
1953 ti->y + _fence_offsets[rfo].y_offs,
1956 4, z);
1957}
1958
1962static void DrawTrackFence_NW(const TileInfo *ti, SpriteID base_image, uint num_sprites)
1963{
1965 if (ti->tileh & SLOPE_NW) rfo = (ti->tileh & SLOPE_W) ? RFO_SLOPE_SW_NW : RFO_SLOPE_NE_NW;
1966 DrawTrackFence(ti, base_image, num_sprites, rfo);
1967}
1968
1972static void DrawTrackFence_SE(const TileInfo *ti, SpriteID base_image, uint num_sprites)
1973{
1975 if (ti->tileh & SLOPE_SE) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SW_SE : RFO_SLOPE_NE_SE;
1976 DrawTrackFence(ti, base_image, num_sprites, rfo);
1977}
1978
1982static void DrawTrackFence_NE(const TileInfo *ti, SpriteID base_image, uint num_sprites)
1983{
1985 if (ti->tileh & SLOPE_NE) rfo = (ti->tileh & SLOPE_E) ? RFO_SLOPE_SE_NE : RFO_SLOPE_NW_NE;
1986 DrawTrackFence(ti, base_image, num_sprites, rfo);
1987}
1988
1992static void DrawTrackFence_SW(const TileInfo *ti, SpriteID base_image, uint num_sprites)
1993{
1995 if (ti->tileh & SLOPE_SW) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SE_SW : RFO_SLOPE_NW_SW;
1996 DrawTrackFence(ti, base_image, num_sprites, rfo);
1997}
1998
2004static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti)
2005{
2006 /* Base sprite for track fences.
2007 * Note: Halftile slopes only have fences on the upper part. */
2008 uint num_sprites = 0;
2009 SpriteID base_image = GetCustomRailSprite(rti, ti->tile, RTSG_FENCES, IsHalftileSlope(ti->tileh) ? TCX_UPPER_HALFTILE : TCX_NORMAL, &num_sprites);
2010 if (base_image == 0) {
2011 base_image = SPR_TRACK_FENCE_FLAT_X;
2012 num_sprites = 8;
2013 }
2014
2015 assert(num_sprites > 0);
2016
2017 switch (GetRailGroundType(ti->tile)) {
2018 case RAIL_GROUND_FENCE_NW: DrawTrackFence_NW(ti, base_image, num_sprites); break;
2019 case RAIL_GROUND_FENCE_SE: DrawTrackFence_SE(ti, base_image, num_sprites); break;
2020 case RAIL_GROUND_FENCE_SENW: DrawTrackFence_NW(ti, base_image, num_sprites);
2021 DrawTrackFence_SE(ti, base_image, num_sprites); break;
2022 case RAIL_GROUND_FENCE_NE: DrawTrackFence_NE(ti, base_image, num_sprites); break;
2023 case RAIL_GROUND_FENCE_SW: DrawTrackFence_SW(ti, base_image, num_sprites); break;
2024 case RAIL_GROUND_FENCE_NESW: DrawTrackFence_NE(ti, base_image, num_sprites);
2025 DrawTrackFence_SW(ti, base_image, num_sprites); break;
2026 case RAIL_GROUND_FENCE_VERT1: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_LEFT); break;
2027 case RAIL_GROUND_FENCE_VERT2: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_RIGHT); break;
2028 case RAIL_GROUND_FENCE_HORIZ1: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_UPPER); break;
2029 case RAIL_GROUND_FENCE_HORIZ2: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_LOWER); break;
2030 case RAIL_GROUND_WATER: {
2031 Corner track_corner;
2032 if (IsHalftileSlope(ti->tileh)) {
2033 /* Steep slope or one-corner-raised slope with halftile foundation */
2034 track_corner = GetHalftileSlopeCorner(ti->tileh);
2035 } else {
2036 /* Three-corner-raised slope */
2038 }
2039 switch (track_corner) {
2040 case CORNER_W: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_LEFT); break;
2041 case CORNER_S: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_LOWER); break;
2042 case CORNER_E: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_RIGHT); break;
2043 case CORNER_N: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_UPPER); break;
2044 default: NOT_REACHED();
2045 }
2046 break;
2047 }
2048 default: break;
2049 }
2050}
2051
2052/* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */
2053static const int INF = 1000; // big number compared to tilesprite size
2054static const SubSprite _halftile_sub_sprite[4] = {
2055 { -INF , -INF , 32 - 33, INF }, // CORNER_W, clip 33 pixels from right
2056 { -INF , 0 + 7, INF , INF }, // CORNER_S, clip 7 pixels from top
2057 { -31 + 33, -INF , INF , INF }, // CORNER_E, clip 33 pixels from left
2058 { -INF , -INF , INF , 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2059};
2060
2061static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
2062{
2063 DrawGroundSprite(sprite, pal, nullptr, 0, (ti->tileh & s) ? -8 : 0);
2064}
2065
2066static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailTypeInfo *rti)
2067{
2068 RailGroundType rgt = GetRailGroundType(ti->tile);
2069 Foundation f = GetRailFoundation(ti->tileh, track);
2070 Corner halftile_corner = CORNER_INVALID;
2071
2073 /* Save halftile corner */
2075 /* Draw lower part first */
2076 track &= ~CornerToTrackBits(halftile_corner);
2078 }
2079
2080 DrawFoundation(ti, f);
2081 /* DrawFoundation modifies ti */
2082
2083 /* Draw ground */
2084 if (rgt == RAIL_GROUND_WATER) {
2085 if (track != TRACK_BIT_NONE || IsSteepSlope(ti->tileh)) {
2086 /* three-corner-raised slope or steep slope with track on upper part */
2087 DrawShoreTile(ti->tileh);
2088 } else {
2089 /* single-corner-raised slope with track on upper part */
2090 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
2091 }
2092 } else {
2093 SpriteID image;
2094
2095 switch (rgt) {
2096 case RAIL_GROUND_BARREN: image = SPR_FLAT_BARE_LAND; break;
2097 case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2098 default: image = SPR_FLAT_GRASS_TILE; break;
2099 }
2100
2101 image += SlopeToSpriteOffset(ti->tileh);
2102
2103 DrawGroundSprite(image, PAL_NONE);
2104 }
2105
2106 bool no_combine = ti->tileh == SLOPE_FLAT && rti->flags.Test(RailTypeFlag::NoSpriteCombine);
2107 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
2108 SpriteID ground = GetCustomRailSprite(rti, ti->tile, no_combine ? RTSG_GROUND_COMPLETE : RTSG_GROUND);
2110
2111 if (track == TRACK_BIT_NONE) {
2112 /* Half-tile foundation, no track here? */
2113 } else if (no_combine) {
2114 /* Use trackbits as direct index from ground sprite, subtract 1
2115 * because there is no sprite for no bits. */
2116 DrawGroundSprite(ground + track - 1, PAL_NONE);
2117
2118 /* Draw reserved track bits */
2119 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2120 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2121 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2122 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2123 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2124 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2125 } else if (ti->tileh == SLOPE_NW && track == TRACK_BIT_Y) {
2126 DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
2128 } else if (ti->tileh == SLOPE_NE && track == TRACK_BIT_X) {
2129 DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
2131 } else if (ti->tileh == SLOPE_SE && track == TRACK_BIT_Y) {
2132 DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
2134 } else if (ti->tileh == SLOPE_SW && track == TRACK_BIT_X) {
2135 DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
2137 } else {
2138 switch (track) {
2139 /* Draw single ground sprite when not overlapping. No track overlay
2140 * is necessary for these sprites. */
2141 case TRACK_BIT_X: DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
2142 case TRACK_BIT_Y: DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
2143 case TRACK_BIT_UPPER: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
2144 case TRACK_BIT_LOWER: DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2145 case TRACK_BIT_RIGHT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
2146 case TRACK_BIT_LEFT: DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2147 case TRACK_BIT_CROSS: DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
2148 case TRACK_BIT_HORZ: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
2149 DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2150 case TRACK_BIT_VERT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
2151 DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2152
2153 default:
2154 /* We're drawing a junction tile */
2155 if ((track & TRACK_BIT_3WAY_NE) == 0) {
2156 DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
2157 } else if ((track & TRACK_BIT_3WAY_SW) == 0) {
2158 DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
2159 } else if ((track & TRACK_BIT_3WAY_NW) == 0) {
2160 DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
2161 } else if ((track & TRACK_BIT_3WAY_SE) == 0) {
2162 DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
2163 } else {
2164 DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2165 }
2166
2167 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2168 track &= ~pbs;
2169
2170 /* Draw regular track bits */
2171 if (track & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PAL_NONE);
2172 if (track & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
2173 if (track & TRACK_BIT_UPPER) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
2174 if (track & TRACK_BIT_LOWER) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
2175 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
2176 if (track & TRACK_BIT_LEFT) DrawGroundSprite(overlay + RTO_W, PAL_NONE);
2177 }
2178
2179 /* Draw reserved track bits */
2180 if (pbs & TRACK_BIT_X) DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2181 if (pbs & TRACK_BIT_Y) DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2182 if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2183 if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2184 if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2185 if (pbs & TRACK_BIT_LEFT) DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2186 }
2187
2188 if (IsValidCorner(halftile_corner)) {
2189 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2192
2193 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2194 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2195
2196 SpriteID image;
2197 switch (rgt) {
2198 case RAIL_GROUND_BARREN: image = SPR_FLAT_BARE_LAND; break;
2200 case RAIL_GROUND_HALF_SNOW: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2201 default: image = SPR_FLAT_GRASS_TILE; break;
2202 }
2203
2204 image += SlopeToSpriteOffset(fake_slope);
2205
2206 DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
2207
2208 track = CornerToTrackBits(halftile_corner);
2209
2210 int offset;
2211 switch (track) {
2212 default: NOT_REACHED();
2213 case TRACK_BIT_UPPER: offset = RTO_N; break;
2214 case TRACK_BIT_LOWER: offset = RTO_S; break;
2215 case TRACK_BIT_RIGHT: offset = RTO_E; break;
2216 case TRACK_BIT_LEFT: offset = RTO_W; break;
2217 }
2218
2219 DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
2221 DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
2222 }
2223 }
2224}
2225
2231static void DrawTrackBits(TileInfo *ti, TrackBits track)
2232{
2233 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2234
2235 if (rti->UsesOverlay()) {
2236 DrawTrackBitsOverlay(ti, track, rti);
2237 return;
2238 }
2239
2240 RailGroundType rgt = GetRailGroundType(ti->tile);
2241 Foundation f = GetRailFoundation(ti->tileh, track);
2242 Corner halftile_corner = CORNER_INVALID;
2243
2245 /* Save halftile corner */
2247 /* Draw lower part first */
2248 track &= ~CornerToTrackBits(halftile_corner);
2250 }
2251
2252 DrawFoundation(ti, f);
2253 /* DrawFoundation modifies ti */
2254
2255 SpriteID image;
2256 PaletteID pal = PAL_NONE;
2257 const SubSprite *sub = nullptr;
2258 bool junction = false;
2259
2260 /* Select the sprite to use. */
2261 if (track == 0) {
2262 /* Clear ground (only track on halftile foundation) */
2263 if (rgt == RAIL_GROUND_WATER) {
2264 if (IsSteepSlope(ti->tileh)) {
2265 DrawShoreTile(ti->tileh);
2266 image = 0;
2267 } else {
2268 image = SPR_FLAT_WATER_TILE;
2269 }
2270 } else {
2271 switch (rgt) {
2272 case RAIL_GROUND_BARREN: image = SPR_FLAT_BARE_LAND; break;
2273 case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2274 default: image = SPR_FLAT_GRASS_TILE; break;
2275 }
2276 image += SlopeToSpriteOffset(ti->tileh);
2277 }
2278 } else {
2279 if (ti->tileh != SLOPE_FLAT) {
2280 /* track on non-flat ground */
2281 image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
2282 } else {
2283 /* track on flat ground */
2284 switch (track) {
2285 /* single track, select combined track + ground sprite*/
2286 case TRACK_BIT_Y: image = rti->base_sprites.track_y; break;
2287 case TRACK_BIT_X: image = rti->base_sprites.track_y + 1; break;
2288 case TRACK_BIT_UPPER: image = rti->base_sprites.track_y + 2; break;
2289 case TRACK_BIT_LOWER: image = rti->base_sprites.track_y + 3; break;
2290 case TRACK_BIT_RIGHT: image = rti->base_sprites.track_y + 4; break;
2291 case TRACK_BIT_LEFT: image = rti->base_sprites.track_y + 5; break;
2292 case TRACK_BIT_CROSS: image = rti->base_sprites.track_y + 6; break;
2293
2294 /* double diagonal track, select combined track + ground sprite*/
2295 case TRACK_BIT_HORZ: image = rti->base_sprites.track_ns; break;
2296 case TRACK_BIT_VERT: image = rti->base_sprites.track_ns + 1; break;
2297
2298 /* junction, select only ground sprite, handle track sprite later */
2299 default:
2300 junction = true;
2301 if ((track & TRACK_BIT_3WAY_NE) == 0) { image = rti->base_sprites.ground; break; }
2302 if ((track & TRACK_BIT_3WAY_SW) == 0) { image = rti->base_sprites.ground + 1; break; }
2303 if ((track & TRACK_BIT_3WAY_NW) == 0) { image = rti->base_sprites.ground + 2; break; }
2304 if ((track & TRACK_BIT_3WAY_SE) == 0) { image = rti->base_sprites.ground + 3; break; }
2305 image = rti->base_sprites.ground + 4;
2306 break;
2307 }
2308 }
2309
2310 switch (rgt) {
2311 case RAIL_GROUND_BARREN: pal = PALETTE_TO_BARE_LAND; break;
2312 case RAIL_GROUND_ICE_DESERT: image += rti->snow_offset; break;
2313 case RAIL_GROUND_WATER: {
2314 /* three-corner-raised slope */
2315 DrawShoreTile(ti->tileh);
2317 sub = &(_halftile_sub_sprite[track_corner]);
2318 break;
2319 }
2320 default: break;
2321 }
2322 }
2323
2324 if (image != 0) DrawGroundSprite(image, pal, sub);
2325
2326 /* Draw track pieces individually for junction tiles */
2327 if (junction) {
2328 if (track & TRACK_BIT_X) DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE);
2329 if (track & TRACK_BIT_Y) DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE);
2330 if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE);
2331 if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE);
2332 if (track & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
2333 if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
2334 }
2335
2336 /* PBS debugging, draw reserved tracks darker */
2337 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) {
2338 /* Get reservation, but mask track on halftile slope */
2339 TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
2340 if (pbs & TRACK_BIT_X) {
2341 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2343 } else {
2344 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2345 }
2346 }
2347 if (pbs & TRACK_BIT_Y) {
2348 if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2350 } else {
2351 DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2352 }
2353 }
2354 if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_N ? -(int)TILE_HEIGHT : 0);
2355 if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_S ? -(int)TILE_HEIGHT : 0);
2356 if (pbs & TRACK_BIT_LEFT) DrawGroundSprite(rti->base_sprites.single_w, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_W ? -(int)TILE_HEIGHT : 0);
2357 if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
2358 }
2359
2360 if (IsValidCorner(halftile_corner)) {
2361 DrawFoundation(ti, HalftileFoundation(halftile_corner));
2362
2363 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2364 Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2365 image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y;
2366 pal = PAL_NONE;
2367 switch (rgt) {
2368 case RAIL_GROUND_BARREN: pal = PALETTE_TO_BARE_LAND; break;
2370 case RAIL_GROUND_HALF_SNOW: image += rti->snow_offset; break; // higher part has snow in this case too
2371 default: break;
2372 }
2373 DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
2374
2375 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) {
2376 static const uint8_t _corner_to_track_sprite[] = {3, 1, 2, 0};
2377 DrawGroundSprite(_corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, -(int)TILE_HEIGHT);
2378 }
2379 }
2380}
2381
2382static void DrawSignals(TileIndex tile, TrackBits rails, const RailTypeInfo *rti)
2383{
2384 auto MAYBE_DRAW_SIGNAL = [&](uint8_t signalbit, SignalOffsets image, uint pos, Track track) {
2385 if (IsSignalPresent(tile, signalbit)) DrawSingleSignal(tile, rti, track, GetSingleSignalState(tile, signalbit), image, pos);
2386 };
2387
2388 if (!(rails & TRACK_BIT_Y)) {
2389 if (!(rails & TRACK_BIT_X)) {
2390 if (rails & TRACK_BIT_LEFT) {
2391 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, TRACK_LEFT);
2392 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, TRACK_LEFT);
2393 }
2394 if (rails & TRACK_BIT_RIGHT) {
2395 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, TRACK_RIGHT);
2396 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, TRACK_RIGHT);
2397 }
2398 if (rails & TRACK_BIT_UPPER) {
2399 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, TRACK_UPPER);
2400 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, TRACK_UPPER);
2401 }
2402 if (rails & TRACK_BIT_LOWER) {
2403 MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, TRACK_LOWER);
2404 MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, TRACK_LOWER);
2405 }
2406 } else {
2407 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, TRACK_X);
2408 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, TRACK_X);
2409 }
2410 } else {
2411 MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, TRACK_Y);
2412 MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 11, TRACK_Y);
2413 }
2414}
2415
2416static void DrawTile_Track(TileInfo *ti)
2417{
2418 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2419
2420 _drawtile_track_palette = GetCompanyPalette(GetTileOwner(ti->tile));
2421
2422 if (IsPlainRail(ti->tile)) {
2423 TrackBits rails = GetTrackBits(ti->tile);
2424
2425 DrawTrackBits(ti, rails);
2426
2428
2430
2431 if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
2432 } else {
2433 /* draw depot */
2434 const DrawTileSprites *dts;
2435 PaletteID pal = PAL_NONE;
2436 SpriteID relocation;
2437
2439
2441 /* Draw rail instead of depot */
2442 dts = &_depot_invisible_gfx_table[GetRailDepotDirection(ti->tile)];
2443 } else {
2444 dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)];
2445 }
2446
2447 SpriteID image;
2448 if (rti->UsesOverlay()) {
2449 image = SPR_FLAT_GRASS_TILE;
2450 } else {
2451 image = dts->ground.sprite;
2452 if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
2453 }
2454
2455 /* Adjust ground tile for desert and snow. */
2456 if (IsSnowRailGround(ti->tile)) {
2457 if (image != SPR_FLAT_GRASS_TILE) {
2458 image += rti->snow_offset; // tile with tracks
2459 } else {
2460 image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
2461 }
2462 }
2463
2464 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, _drawtile_track_palette));
2465
2466 if (rti->UsesOverlay()) {
2467 SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
2468
2469 switch (GetRailDepotDirection(ti->tile)) {
2470 case DIAGDIR_NE:
2471 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2472 [[fallthrough]];
2473 case DIAGDIR_SW:
2474 DrawGroundSprite(ground + RTO_X, PAL_NONE);
2475 break;
2476 case DIAGDIR_NW:
2477 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2478 [[fallthrough]];
2479 case DIAGDIR_SE:
2480 DrawGroundSprite(ground + RTO_Y, PAL_NONE);
2481 break;
2482 default:
2483 break;
2484 }
2485
2487 SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
2488
2489 switch (GetRailDepotDirection(ti->tile)) {
2490 case DIAGDIR_NE:
2491 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2492 [[fallthrough]];
2493 case DIAGDIR_SW:
2495 break;
2496 case DIAGDIR_NW:
2497 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2498 [[fallthrough]];
2499 case DIAGDIR_SE:
2501 break;
2502 default:
2503 break;
2504 }
2505 }
2506 } else {
2507 /* PBS debugging, draw reserved tracks darker */
2508 if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2509 switch (GetRailDepotDirection(ti->tile)) {
2510 case DIAGDIR_NE:
2511 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2512 [[fallthrough]];
2513 case DIAGDIR_SW:
2515 break;
2516 case DIAGDIR_NW:
2517 if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2518 [[fallthrough]];
2519 case DIAGDIR_SE:
2521 break;
2522 default:
2523 break;
2524 }
2525 }
2526 }
2527 int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT);
2528 relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
2529
2531
2532 DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, _drawtile_track_palette);
2533 }
2534 DrawBridgeMiddle(ti);
2535}
2536
2537void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype)
2538{
2539 const DrawTileSprites *dts = &_depot_gfx_table[dir];
2540 const RailTypeInfo *rti = GetRailTypeInfo(railtype);
2541 SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
2542 uint32_t offset = rti->GetRailtypeSpriteOffset();
2543
2544 if (image != SPR_FLAT_GRASS_TILE) image += offset;
2546
2547 DrawSprite(image, PAL_NONE, x, y);
2548
2549 if (rti->UsesOverlay()) {
2551
2552 switch (dir) {
2553 case DIAGDIR_SW: DrawSprite(ground + RTO_X, PAL_NONE, x, y); break;
2554 case DIAGDIR_SE: DrawSprite(ground + RTO_Y, PAL_NONE, x, y); break;
2555 default: break;
2556 }
2557 }
2558 int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RTSG_DEPOT);
2559 if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
2560
2561 DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette);
2562}
2563
2564static int GetSlopePixelZ_Track(TileIndex tile, uint x, uint y, bool)
2565{
2566 if (IsPlainRail(tile)) {
2567 auto [tileh, z] = GetTilePixelSlope(tile);
2568 if (tileh == SLOPE_FLAT) return z;
2569
2571 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
2572 } else {
2573 return GetTileMaxPixelZ(tile);
2574 }
2575}
2576
2577static Foundation GetFoundation_Track(TileIndex tile, Slope tileh)
2578{
2579 return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
2580}
2581
2582static void TileLoop_Track(TileIndex tile)
2583{
2584 RailGroundType old_ground = GetRailGroundType(tile);
2585 RailGroundType new_ground;
2586
2587 if (old_ground == RAIL_GROUND_WATER) {
2588 TileLoop_Water(tile);
2589 return;
2590 }
2591
2593 case LandscapeType::Arctic: {
2594 auto [slope, z] = GetTileSlopeZ(tile);
2595 bool half = false;
2596
2597 /* for non-flat track, use lower part of track
2598 * in other cases, use the highest part with track */
2599 if (IsPlainRail(tile)) {
2600 TrackBits track = GetTrackBits(tile);
2601 Foundation f = GetRailFoundation(slope, track);
2602
2603 switch (f) {
2604 case FOUNDATION_NONE:
2605 /* no foundation - is the track on the upper side of three corners raised tile? */
2606 if (IsSlopeWithThreeCornersRaised(slope)) z++;
2607 break;
2608
2611 /* sloped track - is it on a steep slope? */
2612 if (IsSteepSlope(slope)) z++;
2613 break;
2614
2616 /* only lower part of steep slope */
2617 z++;
2618 break;
2619
2620 default:
2621 /* if it is a steep slope, then there is a track on higher part */
2622 if (IsSteepSlope(slope)) z++;
2623 z++;
2624 break;
2625 }
2626
2628 } else {
2629 /* is the depot on a non-flat tile? */
2630 if (slope != SLOPE_FLAT) z++;
2631 }
2632
2633 /* 'z' is now the lowest part of the highest track bit -
2634 * for sloped track, it is 'z' of lower part
2635 * for two track bits, it is 'z' of higher track bit
2636 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
2637 if (z > GetSnowLine()) {
2638 if (half && z - GetSnowLine() == 1) {
2639 /* track on non-continuous foundation, lower part is not under snow */
2640 new_ground = RAIL_GROUND_HALF_SNOW;
2641 } else {
2642 new_ground = RAIL_GROUND_ICE_DESERT;
2643 }
2644 goto set_ground;
2645 }
2646 break;
2647 }
2648
2649 case LandscapeType::Tropic:
2650 if (GetTropicZone(tile) == TROPICZONE_DESERT) {
2651 new_ground = RAIL_GROUND_ICE_DESERT;
2652 goto set_ground;
2653 }
2654 break;
2655
2656 default:
2657 break;
2658 }
2659
2660 new_ground = RAIL_GROUND_GRASS;
2661
2662 if (IsPlainRail(tile) && old_ground != RAIL_GROUND_BARREN) { // wait until bottom is green
2663 /* determine direction of fence */
2664 TrackBits rail = GetTrackBits(tile);
2665
2666 Owner owner = GetTileOwner(tile);
2667 DiagDirections fences{};
2668
2669 for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
2671
2672 /* Track bit on this edge => no fence. */
2673 if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue;
2674
2675 TileIndex tile2 = tile + TileOffsByDiagDir(d);
2676
2677 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
2678 if (!IsValidTile(tile2) || IsTileType(tile2, MP_HOUSE) || IsTileType(tile2, MP_INDUSTRY) ||
2679 IsTileType(tile2, MP_ROAD) || (IsTileType(tile2, MP_OBJECT) && !IsObjectType(tile2, OBJECT_OWNED_LAND)) || IsTileType(tile2, MP_TUNNELBRIDGE) || !IsTileOwner(tile2, owner)) {
2680 fences.Set(d);
2681 }
2682 }
2683
2684 switch (fences.base()) {
2685 case DiagDirections{}.base(): break;
2686 case DiagDirections{DIAGDIR_NE}.base(): new_ground = RAIL_GROUND_FENCE_NE; break;
2687 case DiagDirections{DIAGDIR_SE}.base(): new_ground = RAIL_GROUND_FENCE_SE; break;
2688 case DiagDirections{DIAGDIR_SW}.base(): new_ground = RAIL_GROUND_FENCE_SW; break;
2689 case DiagDirections{DIAGDIR_NW}.base(): new_ground = RAIL_GROUND_FENCE_NW; break;
2690 case DiagDirections{DIAGDIR_NE, DIAGDIR_SW}.base(): new_ground = RAIL_GROUND_FENCE_NESW; break;
2691 case DiagDirections{DIAGDIR_SE, DIAGDIR_NW}.base(): new_ground = RAIL_GROUND_FENCE_SENW; break;
2692 case DiagDirections{DIAGDIR_NE, DIAGDIR_SE}.base(): new_ground = RAIL_GROUND_FENCE_VERT1; break;
2695 case DiagDirections{DIAGDIR_SW, DIAGDIR_NW}.base(): new_ground = RAIL_GROUND_FENCE_VERT2; break;
2696 default: NOT_REACHED();
2697 }
2698 }
2699
2700set_ground:
2701 if (old_ground != new_ground) {
2702 SetRailGroundType(tile, new_ground);
2703 MarkTileDirtyByTile(tile);
2704 }
2705}
2706
2707
2708static TrackStatus GetTileTrackStatus_Track(TileIndex tile, TransportType mode, uint, DiagDirection side)
2709{
2710 /* Case of half tile slope with water. */
2711 if (mode == TRANSPORT_WATER && IsPlainRail(tile) && GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(GetTileSlope(tile))) {
2712 TrackBits tb = GetTrackBits(tile);
2713 switch (tb) {
2714 default: NOT_REACHED();
2715 case TRACK_BIT_UPPER: tb = TRACK_BIT_LOWER; break;
2716 case TRACK_BIT_LOWER: tb = TRACK_BIT_UPPER; break;
2717 case TRACK_BIT_LEFT: tb = TRACK_BIT_RIGHT; break;
2718 case TRACK_BIT_RIGHT: tb = TRACK_BIT_LEFT; break;
2719 }
2721 }
2722
2723 if (mode != TRANSPORT_RAIL) return 0;
2724
2725 TrackBits trackbits = TRACK_BIT_NONE;
2726 TrackdirBits red_signals = TRACKDIR_BIT_NONE;
2727
2728 switch (GetRailTileType(tile)) {
2729 default: NOT_REACHED();
2730 case RAIL_TILE_NORMAL:
2731 trackbits = GetTrackBits(tile);
2732 break;
2733
2734 case RAIL_TILE_SIGNALS: {
2735 trackbits = GetTrackBits(tile);
2736 uint8_t a = GetPresentSignals(tile);
2737 uint b = GetSignalStates(tile);
2738
2739 b &= a;
2740
2741 /* When signals are not present (in neither direction),
2742 * we pretend them to be green. Otherwise, it depends on
2743 * the signal type. For signals that are only active from
2744 * one side, we set the missing signals explicitly to
2745 * `green'. Otherwise, they implicitly become `red'. */
2746 if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER);
2747 if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER);
2748
2749 if ((b & 0x8) == 0) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E);
2750 if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W);
2751 if ((b & 0x2) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_E);
2752 if ((b & 0x1) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_W);
2753
2754 break;
2755 }
2756
2757 case RAIL_TILE_DEPOT: {
2759
2760 if (side != INVALID_DIAGDIR && side != dir) break;
2761
2762 trackbits = DiagDirToDiagTrackBits(dir);
2763 break;
2764 }
2765 }
2766
2767 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), red_signals);
2768}
2769
2770static bool ClickTile_Track(TileIndex tile)
2771{
2772 if (!IsRailDepot(tile)) return false;
2773
2775 return true;
2776}
2777
2778static void GetTileDesc_Track(TileIndex tile, TileDesc &td)
2779{
2780 const RailTypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
2781 td.rail_speed = rti->max_speed;
2782 td.railtype = rti->strings.name;
2783 td.owner[0] = GetTileOwner(tile);
2784 switch (GetRailTileType(tile)) {
2785 case RAIL_TILE_NORMAL:
2786 td.str = STR_LAI_RAIL_DESCRIPTION_TRACK;
2787 break;
2788
2789 case RAIL_TILE_SIGNALS: {
2790 static const StringID signal_type[6][6] = {
2791 {
2792 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
2793 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2794 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2795 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2796 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2797 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
2798 },
2799 {
2800 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2801 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS,
2802 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2803 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2804 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2805 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
2806 },
2807 {
2808 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2809 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2810 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
2811 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2812 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2813 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
2814 },
2815 {
2816 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2817 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2818 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2819 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
2820 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2821 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
2822 },
2823 {
2824 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2825 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2826 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2827 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2828 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
2829 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
2830 },
2831 {
2832 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
2833 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
2834 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
2835 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
2836 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
2837 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
2838 }
2839 };
2840
2841 SignalType primary_signal;
2842 SignalType secondary_signal;
2843 if (HasSignalOnTrack(tile, TRACK_UPPER)) {
2844 primary_signal = GetSignalType(tile, TRACK_UPPER);
2845 secondary_signal = HasSignalOnTrack(tile, TRACK_LOWER) ? GetSignalType(tile, TRACK_LOWER) : primary_signal;
2846 } else {
2847 secondary_signal = primary_signal = GetSignalType(tile, TRACK_LOWER);
2848 }
2849
2850 td.str = signal_type[secondary_signal][primary_signal];
2851 break;
2852 }
2853
2854 case RAIL_TILE_DEPOT:
2855 td.str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
2856 if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) {
2857 if (td.rail_speed > 0) {
2858 td.rail_speed = std::min<uint16_t>(td.rail_speed, 61);
2859 } else {
2860 td.rail_speed = 61;
2861 }
2862 }
2863 td.build_date = Depot::GetByTile(tile)->build_date;
2864 break;
2865
2866 default:
2867 NOT_REACHED();
2868 }
2869}
2870
2871static void ChangeTileOwner_Track(TileIndex tile, Owner old_owner, Owner new_owner)
2872{
2873 if (!IsTileOwner(tile, old_owner)) return;
2874
2875 if (new_owner != INVALID_OWNER) {
2876 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
2877 uint num_pieces = 1;
2878 if (IsPlainRail(tile)) {
2879 TrackBits bits = GetTrackBits(tile);
2880 num_pieces = CountBits(bits);
2881 if (TracksOverlap(bits)) num_pieces *= num_pieces;
2882 }
2883 RailType rt = GetRailType(tile);
2884 Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;
2885 Company::Get(new_owner)->infrastructure.rail[rt] += num_pieces;
2886
2887 if (HasSignals(tile)) {
2888 uint num_sigs = CountBits(GetPresentSignals(tile));
2889 Company::Get(old_owner)->infrastructure.signal -= num_sigs;
2890 Company::Get(new_owner)->infrastructure.signal += num_sigs;
2891 }
2892
2893 SetTileOwner(tile, new_owner);
2894 } else {
2896 }
2897}
2898
2899static const uint8_t _fractcoords_behind[4] = { 0x8F, 0x8, 0x80, 0xF8 };
2900static const uint8_t _fractcoords_enter[4] = { 0x8A, 0x48, 0x84, 0xA8 };
2901static const int8_t _deltacoord_leaveoffset[8] = {
2902 -1, 0, 1, 0, /* x */
2903 0, 1, 0, -1 /* y */
2904};
2905
2906
2914{
2916 int length = v->CalcNextVehicleOffset();
2917
2918 switch (dir) {
2919 case DIAGDIR_NE: return ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1)));
2920 case DIAGDIR_SE: return -((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) + (length + 1)));
2921 case DIAGDIR_SW: return -((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) + (length + 1)));
2922 case DIAGDIR_NW: return ((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) - (length + 1)));
2923 default: NOT_REACHED();
2924 }
2925}
2926
2932{
2933 /* This routine applies only to trains in depot tiles. */
2934 if (u->type != VEH_TRAIN || !IsRailDepotTile(tile)) return {};
2935
2936 /* Depot direction. */
2938
2939 uint8_t fract_coord = (x & 0xF) + ((y & 0xF) << 4);
2940
2941 /* Make sure a train is not entering the tile from behind. */
2942 if (_fractcoords_behind[dir] == fract_coord) return VehicleEnterTileState::CannotEnter;
2943
2944 Train *v = Train::From(u);
2945
2946 /* Leaving depot? */
2947 if (v->direction == DiagDirToDir(dir)) {
2948 /* Calculate the point where the following wagon should be activated. */
2949 int length = v->CalcNextVehicleOffset();
2950
2951 uint8_t fract_coord_leave =
2952 ((_fractcoords_enter[dir] & 0x0F) + // x
2953 (length + 1) * _deltacoord_leaveoffset[dir]) +
2954 (((_fractcoords_enter[dir] >> 4) + // y
2955 ((length + 1) * _deltacoord_leaveoffset[dir + 4])) << 4);
2956
2957 if (fract_coord_leave == fract_coord) {
2958 /* Leave the depot. */
2959 if ((v = v->Next()) != nullptr) {
2961 v->track = (DiagDirToAxis(dir) == AXIS_X ? TRACK_BIT_X : TRACK_BIT_Y);
2962 }
2963 }
2964 } else if (_fractcoords_enter[dir] == fract_coord) {
2965 /* Entering depot. */
2966 assert(DiagDirToDir(ReverseDiagDir(dir)) == v->direction);
2967 v->track = TRACK_BIT_DEPOT,
2970 if (v->Next() == nullptr) VehicleEnterDepot(v->First());
2971 v->tile = tile;
2972
2975 }
2976
2977 return {};
2978}
2979
2991static CommandCost TestAutoslopeOnRailTile(TileIndex tile, DoCommandFlags flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
2992{
2993 if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
2994
2995 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
2996 if (CheckRailSlope(tileh_new, rail_bits, TRACK_BIT_NONE, tile).Failed()) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
2997
2998 /* Get the slopes on top of the foundations */
2999 z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), tileh_old);
3000 z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), tileh_new);
3001
3002 Corner track_corner;
3003 switch (rail_bits) {
3004 case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
3005 case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
3006 case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
3007 case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
3008
3009 /* Surface slope must not be changed */
3010 default:
3011 if (z_old != z_new || tileh_old != tileh_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3012 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
3013 }
3014
3015 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3016 z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
3017 z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
3018 if (z_old != z_new) return CommandCost(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3019
3020 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
3021 /* Make the ground dirty, if surface slope has changed */
3022 if (tileh_old != tileh_new) {
3023 /* If there is flat water on the lower halftile add the cost for clearing it */
3024 if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old)) cost.AddCost(_price[PR_CLEAR_WATER]);
3025 if (flags.Test(DoCommandFlag::Execute)) SetRailGroundType(tile, RAIL_GROUND_BARREN);
3026 }
3027 return cost;
3028}
3029
3030static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlags flags, int z_new, Slope tileh_new)
3031{
3032 auto [tileh_old, z_old] = GetTileSlopeZ(tile);
3033 if (IsPlainRail(tile)) {
3034 TrackBits rail_bits = GetTrackBits(tile);
3035 /* Is there flat water on the lower halftile that must be cleared expensively? */
3036 bool was_water = (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old));
3037
3038 /* Allow clearing the water only if there is no ship */
3039 if (was_water && HasVehicleOnTile(tile, [](const Vehicle *v) {
3040 return v->type == VEH_SHIP;
3041 })) return CommandCost(STR_ERROR_SHIP_IN_THE_WAY);
3042
3043 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3044 CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
3045
3046 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3047 Corner allowed_corner;
3048 switch (rail_bits) {
3049 case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break;
3050 case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break;
3051 case TRACK_BIT_LEFT: allowed_corner = CORNER_E; break;
3052 case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break;
3053 default: return autoslope_result;
3054 }
3055
3056 Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
3057
3058 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3059 if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
3060
3061 /* Everything is valid, which only changes allowed_corner */
3062 for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
3063 if (allowed_corner == corner) continue;
3064 if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
3065 }
3066
3067 /* Make the ground dirty */
3068 if (flags.Test(DoCommandFlag::Execute)) SetRailGroundType(tile, RAIL_GROUND_BARREN);
3069
3070 /* allow terraforming */
3071 return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price[PR_CLEAR_WATER] : (Money)0);
3073 AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
3074 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
3075 }
3076 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
3077}
3078
3079
3080extern const TileTypeProcs _tile_type_rail_procs = {
3081 DrawTile_Track, // draw_tile_proc
3082 GetSlopePixelZ_Track, // get_slope_z_proc
3083 ClearTile_Track, // clear_tile_proc
3084 nullptr, // add_accepted_cargo_proc
3085 GetTileDesc_Track, // get_tile_desc_proc
3086 GetTileTrackStatus_Track, // get_tile_track_status_proc
3087 ClickTile_Track, // click_tile_proc
3088 nullptr, // animate_tile_proc
3089 TileLoop_Track, // tile_loop_proc
3090 ChangeTileOwner_Track, // change_tile_owner_proc
3091 nullptr, // add_produced_cargo_proc
3092 VehicleEnter_Track, // vehicle_enter_tile_proc
3093 GetFoundation_Track, // get_foundation_proc
3094 TerraformTile_Track, // terraform_tile_proc
3095};
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.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
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 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)
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
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:118
SpriteID single_x
single piece of rail in X direction, without ground
Definition rail.h:128
uint16_t max_speed
Maximum speed for vehicles travelling on this rail type.
Definition rail.h:222
SpriteID build_tunnel
button for building a tunnel
Definition rail.h:150
CursorID rail_swne
Cursor for building rail in X direction.
Definition rail.h:157
SpriteID convert_rail
button for converting rail
Definition rail.h:151
CursorID convert
Cursor for converting track.
Definition rail.h:163
CursorID depot
Cursor for building a depot.
Definition rail.h:161
RailTypes powered_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype generates power
Definition rail.h:179
RailTypes introduces_railtypes
Bitmask of which other railtypes are introduced when this railtype is introduced.
Definition rail.h:257
SpriteID ground
ground sprite for a 3-way switch
Definition rail.h:127
CursorID rail_nwse
Cursor for building rail in Y direction.
Definition rail.h:159
SpriteID build_x_rail
button for building single rail in X direction
Definition rail.h:145
uint8_t sorting_order
The sorting order of this railtype for the toolbar dropdown.
Definition rail.h:262
RailTypeLabel label
Unique 32 bit rail type identifier.
Definition rail.h:227
SpriteID single_n
single piece of rail in the northern corner
Definition rail.h:130
struct RailTypeInfo::@20 base_sprites
Struct containing the main sprites.
CursorID rail_ew
Cursor for building rail in E-W direction.
Definition rail.h:158
SpriteID auto_rail
button for the autorail construction
Definition rail.h:148
CursorID autorail
Cursor for autorail tool.
Definition rail.h:160
SpriteID single_y
single piece of rail in Y direction, without ground
Definition rail.h:129
struct RailTypeInfo::@21 gui_sprites
struct containing the sprites for the rail GUI.
StringID name
Name of this rail type.
Definition rail.h:167
RailTypes compatible_railtypes
bitmask to the OTHER railtypes on which an engine of THIS railtype can physically travel
Definition rail.h:182
uint GetRailtypeSpriteOffset() const
Offset between the current railtype and normal rail.
Definition rail.h:288
SpriteID single_s
single piece of rail in the southern corner
Definition rail.h:131
RailTypeFlags flags
Bit mask of rail type flags.
Definition rail.h:202
SpriteID signals[SIGTYPE_END][2][2]
signal GUI sprites (type, variant, state)
Definition rail.h:152
SpriteID build_ew_rail
button for building single rail in E-W direction
Definition rail.h:146
SpriteID build_y_rail
button for building single rail in Y direction
Definition rail.h:147
struct RailTypeInfo::@22 cursor
Cursors associated with the rail type.
SpriteID track_ns
two pieces of rail in North and South corner (East-West direction)
Definition rail.h:126
RailTypeLabelList alternate_labels
Rail type labels this type provides in addition to the main label.
Definition rail.h:232
SpriteID snow_offset
sprite number difference between a piece of track on a snowy ground and the corresponding one on norm...
Definition rail.h:176
SpriteID track_y
single piece of rail in Y direction, with ground
Definition rail.h:125
SpriteID build_depot
button for building depots
Definition rail.h:149
SpriteID single_w
single piece of rail in the western corner
Definition rail.h:133
SpriteID single_e
single piece of rail in the eastern corner
Definition rail.h:132
SpriteID build_ns_rail
button for building single rail in N-S direction
Definition rail.h:144
CursorID rail_ns
Cursor for building rail in N-S direction.
Definition rail.h:156
struct RailTypeInfo::@23 strings
Strings associated with the rail type.
SpriteID tunnel
tunnel sprites base
Definition rail.h:136
SpriteID single_sloped
single piece of rail for slopes
Definition rail.h:134
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.
@ Execute
execute the given command
@ Bankrupt
company bankrupts, skip money check, skip vehicle on tile check in some cases
@ Auto
don't allow building on structures
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_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.
void DrawRailCatenary(const TileInfo *ti)
Draws overhead wires and pylons for electric railways.
Definition elrail.cpp:567
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:1024
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:126
int GetSlopePixelZInCorner(Slope tileh, Corner corner)
Determine the Z height of a corner relative to TileZ.
Definition landscape.h:53
Command definitions related to landscape (slopes etc.).
TileIndex AddTileIndexDiffCWrap(TileIndex tile, TileIndexDiffC diff)
Add a TileIndexDiffC to a TileIndex and returns the new one.
Definition map_func.h:514
TileIndexDiff ToTileIndexDiff(TileIndexDiffC tidc)
Return the offset between two tiles from a TileIndexDiffC struct.
Definition map_func.h:439
TileIndex TileAddByDiagDir(TileIndex tile, DiagDirection dir)
Adds a DiagDir to a tile.
Definition map_func.h:610
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:424
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:414
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:569
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:208
Money RailConvertCost(RailType from, RailType to)
Calculates the cost of rail conversion.
Definition rail.h:409
Money RailClearCost(RailType railtype)
Returns the 'cost' of clearing the specified railtype.
Definition rail.h:392
Money RailBuildCost(RailType railtype)
Returns the cost of building the specified railtype.
Definition rail.h:381
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:328
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:341
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.
@ NoSpriteCombine
Bit number for using non-combined junctions.
@ 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:351
CommandCost CmdRemoveSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, bool autofill)
Remove signals on a stretch of track.
int TicksToLeaveDepot(const Train *v)
Compute number of ticks when next wagon will leave a depot.
static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build)
Check that the new track bits may be built.
Definition rail_cmd.cpp:240
static CommandCost CmdRailTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
Build or remove a stretch of railroad tracks.
Definition rail_cmd.cpp:875
CommandCost CmdBuildTrainDepot(DoCommandFlags flags, TileIndex tile, RailType railtype, DiagDirection dir)
Build a train depot.
Definition rail_cmd.cpp:963
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:932
CommandCost CmdBuildSignalTrack(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool autofill, bool minimise_gaps, uint8_t signal_density)
Build signals on a stretch of track.
bool FloodHalftile(TileIndex t)
Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
Definition rail_cmd.cpp:761
RailType AllocateRailType(RailTypeLabel label)
Allocate a new rail type label.
Definition rail_cmd.cpp:149
static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
Tests if a track can be build on a tile.
Definition rail_cmd.cpp:391
CommandCost CmdRemoveSingleRail(DoCommandFlags flags, TileIndex tile, Track track)
Remove a single piece of track.
Definition rail_cmd.cpp:614
std::vector< RailType > _sorted_railtypes
Sorted list of rail types.
Definition rail_cmd.cpp:47
CommandCost CmdRemoveSingleSignal(DoCommandFlags flags, TileIndex tile, Track track)
Remove signals.
static void DrawTrackFence_NW(const TileInfo *ti, SpriteID base_image, uint num_sprites)
Draw fence at NW border matching the tile slope.
static const TrackBits _valid_tracks_without_foundation[15]
Valid TrackBits on a specific (non-steep)-slope without foundation.
Definition rail_cmd.cpp:261
static const TrackBits _valid_tracks_on_leveled_foundation[15]
Valid TrackBits on a specific (non-steep)-slope with leveled foundation.
Definition rail_cmd.cpp:283
static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
Tests if a vehicle interacts with the specified track.
Definition rail_cmd.cpp:228
static CommandCost CmdSignalTrackHelper(DoCommandFlags flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool remove, bool autofill, bool minimise_gaps, int signal_density)
Build many signals by dragging; AutoSignals.
static void DrawTrackBits(TileInfo *ti, TrackBits track)
Draw ground sprite and track bits.
static void DrawTrackFence_NE(const TileInfo *ti, SpriteID base_image, uint num_sprites)
Draw fence at NE border matching the tile slope.
SignalOffsets
Enum holding the signal offset in the sprite sheet according to the side it is representing.
Definition rail_cmd.cpp:51
CommandCost CmdRemoveRailroadTrack(DoCommandFlags flags, TileIndex end_tile, TileIndex start_tile, Track track)
Build rail on a stretch of track.
Definition rail_cmd.cpp:947
CommandCost CmdBuildSingleRail(DoCommandFlags flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
Build a single piece of rail.
Definition rail_cmd.cpp:425
CommandCost CmdConvertRail(DoCommandFlags flags, TileIndex tile, TileIndex area_start, RailType totype, bool diagonal)
Convert one rail type to the other.
void InitRailTypes()
Resolve sprites of custom rail types.
Definition rail_cmd.cpp:130
static void DrawTrackFence(const TileInfo *ti, SpriteID base_image, uint num_sprites, RailFenceOffset rfo)
Draw a track fence.
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 void DrawTrackFence_SW(const TileInfo *ti, SpriteID base_image, uint num_sprites)
Draw fence at SW border matching the tile slope.
static void DrawTrackFence_SE(const TileInfo *ti, SpriteID base_image, uint num_sprites)
Draw fence at SE border matching the tile slope.
static bool CompareRailTypes(const RailType &first, const RailType &second)
Compare railtypes based on their sorting order.
Definition rail_cmd.cpp:122
Foundation GetRailFoundation(Slope tileh, TrackBits bits)
Checks if a track combination is valid on a specific slope and returns the needed foundation.
Definition rail_cmd.cpp:311
std::vector< Train * > TrainList
Helper type for lists/vectors of trains.
Definition rail_cmd.cpp:44
static void DrawTrackDetails(const TileInfo *ti, const RailTypeInfo *rti)
Draw track fences.
Command definitions for rail.
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 debug_inline RailTileType GetRailTileType(Tile t)
Returns the RailTileType (normal with or without signals, waypoint or depot).
Definition rail_map.h:36
TrackBits GetTrackBits(Tile tile)
Gets the track bits of the given tile.
Definition rail_map.h:136
@ RAIL_TILE_DEPOT
Depot (one entrance)
Definition rail_map.h:26
@ RAIL_TILE_NORMAL
Normal rail tile without signals.
Definition rail_map.h:24
@ RAIL_TILE_SIGNALS
Normal rail tile with signals.
Definition rail_map.h:25
static debug_inline bool IsRailDepotTile(Tile t)
Is this tile rail tile and a rail depot?
Definition rail_map.h:105
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
@ RAIL_GROUND_FENCE_HORIZ1
Grass with a fence at the southern side.
Definition rail_map.h:495
@ RAIL_GROUND_FENCE_VERT1
Grass with a fence at the eastern side.
Definition rail_map.h:493
@ RAIL_GROUND_ICE_DESERT
Icy or sandy.
Definition rail_map.h:497
@ RAIL_GROUND_FENCE_NE
Grass with a fence at the NE edge.
Definition rail_map.h:490
@ RAIL_GROUND_FENCE_NESW
Grass with a fence at the NE and SW edges.
Definition rail_map.h:492
@ RAIL_GROUND_FENCE_HORIZ2
Grass with a fence at the northern side.
Definition rail_map.h:496
@ RAIL_GROUND_FENCE_SW
Grass with a fence at the SW edge.
Definition rail_map.h:491
@ RAIL_GROUND_FENCE_NW
Grass with a fence at the NW edge.
Definition rail_map.h:487
@ RAIL_GROUND_WATER
Grass with a fence and shore or water on the free halftile.
Definition rail_map.h:498
@ RAIL_GROUND_BARREN
Nothing (dirt)
Definition rail_map.h:485
@ RAIL_GROUND_FENCE_VERT2
Grass with a fence at the western side.
Definition rail_map.h:494
@ RAIL_GROUND_GRASS
Grassy.
Definition rail_map.h:486
@ RAIL_GROUND_HALF_SNOW
Snow only on higher part of slope (steep or one corner raised)
Definition rail_map.h:499
@ RAIL_GROUND_FENCE_SENW
Grass with a fence at the NW and SE edges.
Definition rail_map.h:489
@ RAIL_GROUND_FENCE_SE
Grass with a fence at the SE edge.
Definition rail_map.h:488
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
static debug_inline bool IsRailDepot(Tile t)
Is this rail tile a rail depot?
Definition rail_map.h:95
void SetRailDepotExitDirection(Tile tile, DiagDirection dir)
Sets the exit direction of a rail depot.
Definition rail_map.h:537
static debug_inline bool IsPlainRail(Tile t)
Returns whether this is plain rails, with or without signals.
Definition rail_map.h:49
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 debug_inline bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
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
RailType
Enumeration for all possible railtypes.
Definition rail_type.h:25
@ RAILTYPE_BEGIN
Used for iterations.
Definition rail_type.h:26
@ 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:21
bool RoadNoLevelCrossing(RoadType roadtype)
Test if road disallows level crossings.
Definition road.h:311
Money RoadBuildCost(RoadType roadtype)
Returns the cost of building the specified roadtype.
Definition road.h:268
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:615
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:640
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
bool HasRoadWorks(Tile t)
Check if a tile has road works.
Definition road_map.h:493
static debug_inline bool IsNormalRoad(Tile t)
Return whether a tile is a normal road.
Definition road_map.h:48
RoadBits
Enumeration for the road parts on a tile.
Definition road_type.h:40
@ ROAD_NONE
No road-part is build.
Definition road_type.h:41
@ ROAD_Y
Full road along the y-axis (north-west + south-east)
Definition road_type.h:47
@ ROAD_X
Full road along the x-axis (south-west + north-east)
Definition road_type.h:46
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:62
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:587
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
Add side of tile to signal update buffer.
Definition signal.cpp:619
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:22
Slope
Enumeration for the slope-type.
Definition slope_type.h:48
@ SLOPE_W
the west corner of the tile is raised
Definition slope_type.h:50
@ SLOPE_ELEVATED
bit mask containing all 'simple' slopes
Definition slope_type.h:61
@ SLOPE_NS
north and south corner are raised
Definition slope_type.h:60
@ SLOPE_E
the east corner of the tile is raised
Definition slope_type.h:52
@ SLOPE_S
the south corner of the tile is raised
Definition slope_type.h:51
@ SLOPE_N
the north corner of the tile is raised
Definition slope_type.h:53
@ SLOPE_SW
south and west corner are raised
Definition slope_type.h:56
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:49
@ SLOPE_NE
north and east corner are raised
Definition slope_type.h:58
@ SLOPE_SE
south and east corner are raised
Definition slope_type.h:57
@ SLOPE_NW
north and west corner are raised
Definition slope_type.h:55
@ SLOPE_EW
east and west corner are raised
Definition slope_type.h:59
Foundation
Enumeration for Foundations.
Definition slope_type.h:93
@ FOUNDATION_LEVELED
The tile is leveled up to a flat slope.
Definition slope_type.h:95
@ FOUNDATION_NONE
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:94
@ FOUNDATION_INCLINED_X
The tile has an along X-axis inclined foundation.
Definition slope_type.h:96
@ 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:101
@ FOUNDATION_INCLINED_Y
The tile has an along Y-axis inclined foundation.
Definition slope_type.h:97
@ 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:98
@ FOUNDATION_HALFTILE_N
Level north halftile non-continuously.
Definition slope_type.h:105
@ FOUNDATION_INVALID
Used inside "rail_cmd.cpp" to indicate invalid slope/track combination.
Definition slope_type.h:113
static const uint32_t VALID_LEVEL_CROSSING_SLOPES
Constant bitset with safe slopes for building a level crossing.
Definition slope_type.h:86
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:101
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:91
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:170
static const PaletteID PALETTE_CRASH
Recolour sprite greying of crashed vehicles.
Definition sprites.h:1611
static const PaletteID PALETTE_TO_BARE_LAND
sets colour to bare land stuff for rail, road and crossings
Definition sprites.h:1599
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? In other words, is this station tile a rail station or rail waypoint?
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.
GUISettings gui
settings related to the GUI
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.
bool build_on_slopes
allow building on slopes
bool crossing_with_competitor
allow building of level crossings with competitor roads or rails
uint8_t train_signal_side
show signals on left / driving / right side
TimerGameCalendar::Date build_date
Date of construction.
Definition depot_base.h:26
Represents a diagonal tile area.
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:43
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:44
Offsets for drawing fences.
int y_offs
Bounding box Y offset.
Corner height_ref
Corner to use height offset from.
int x_offs
Bounding box X offset.
int y_size
Bounding box Y size.
int x_size
Bounding box X size.
bool show_track_reservation
highlight reserved tracks.
LandscapeType landscape
the landscape we're currently in
PathfinderSettings pf
settings for all pathfinders
ConstructionSettings construction
construction of things in-game
GameCreationSettings game_creation
settings used during the creation of a game (map)
VehicleSettings vehicle
options for vehicles
static debug_inline uint Size()
Get the size of the map.
Definition map_func.h:287
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.
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
bool forbid_90_deg
forbid trains to make 90 deg turns
Coordinates of a point in 2D.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
T * Next() const
Get next vehicle in the chain.
static T * From(Vehicle *v)
Converts a Vehicle to SpecializedVehicle with type checking.
T * First() const
Get the first vehicle in the chain.
Used to only draw a part of the sprite.
Definition gfx_type.h:273
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
int16_t x
The x value of the coordinate.
Definition map_type.h:32
int16_t y
The y value of the coordinate.
Definition map_type.h:33
Tile information, used while rendering the tile.
Definition tile_cmd.h:29
int z
Height.
Definition tile_cmd.h:34
int x
X position of the tile in unit coordinates.
Definition tile_cmd.h:30
Slope tileh
Slope of the tile.
Definition tile_cmd.h:32
TileIndex tile
Tile index.
Definition tile_cmd.h:33
int y
Y position of the tile in unit coordinates.
Definition tile_cmd.h:31
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:144
'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:174
uint8_t train_acceleration_model
realistic acceleration for trains
uint8_t road_side
the side of the road vehicles drive on
bool disable_elrails
when true, the elrails are disabled
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.
@ EnteredWormhole
The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/...
std::tuple< Slope, int > GetTileSlopeZ(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.cpp:55
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
static debug_inline TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
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
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
static const uint TILE_HEIGHT
Height of a height level in world coordinate AND in pixels in ZOOM_BASE.
Definition tile_type.h:18
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:78
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
TileType
The different types of tiles.
Definition tile_type.h:47
@ MP_ROAD
A tile with road (or tram tracks)
Definition tile_type.h:50
@ MP_STATION
A tile of a station.
Definition tile_type.h:53
@ MP_TUNNELBRIDGE
Tunnel entry/exit and bridge heads.
Definition tile_type.h:57
@ MP_HOUSE
A house by a town.
Definition tile_type.h:51
@ MP_WATER
Water tile.
Definition tile_type.h:54
@ MP_RAILWAY
A railway.
Definition tile_type.h:49
@ MP_INDUSTRY
Part of an industry.
Definition tile_type.h:56
@ MP_OBJECT
Contains objects such as transmitters and owned land.
Definition tile_type.h:58
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:247
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:527
void VehicleEnterDepot(Vehicle *v)
Vehicle entirely entered the depot, update its status, orders, vehicle windows, service it,...
Definition vehicle.cpp:1521
CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits)
Tests if a vehicle interacts with the specified track bits.
Definition vehicle.cpp:575
CommandCost TunnelBridgeIsFree(TileIndex tile, TileIndex endtile, const Vehicle *ignore)
Finds vehicle in tunnel / bridge.
Definition vehicle.cpp:551
@ 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 w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:663
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:579
Functions related to (drawing on) viewports.
static const uint 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:381
bool IsDockingTile(Tile t)
Checks whether the tile is marked as a dockling tile.
Definition water_map.h:371
void SetDockingTile(Tile t, bool b)
Set the docking tile state of a tile.
Definition water_map.h:361
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:3265
@ 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.