4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file rail_cmd.cpp Handling of rail tiles. */
13 #include "map/zoneheight.h"
15 #include "cmd_helper.h"
16 #include "viewport_func.h"
17 #include "command_func.h"
18 #include "depot_base.h"
19 #include "pathfinder/yapf/yapf_cache.h"
20 #include "newgrf_debug.h"
21 #include "newgrf_railtype.h"
23 #include "autoslope.h"
25 #include "vehicle_func.h"
26 #include "sound_func.h"
27 #include "tunnelbridge.h"
28 #include "elrail_func.h"
31 #include "company_base.h"
32 #include "core/backup_type.hpp"
33 #include "date_func.h"
34 #include "strings_func.h"
35 #include "company_gui.h"
36 #include "map/object.h"
38 #include "signal_func.h"
41 #include "table/strings.h"
42 #include "table/railtypes.h"
44 /** Helper type for lists/vectors of trains */
45 typedef SmallVector
<Train
*, 16> TrainList
;
47 RailtypeInfo _railtypes
[RAILTYPE_END
];
49 assert_compile(sizeof(_original_railtypes
) <= sizeof(_railtypes
));
51 /** Enum holding the signal offset in the sprite sheet according to the side it is representing. */
64 * Reset all rail type information to its default values.
68 memset(_railtypes
, 0, sizeof(_railtypes
));
69 memcpy(_railtypes
, _original_railtypes
, sizeof(_original_railtypes
));
72 void ResolveRailTypeGUISprites(RailtypeInfo
*rti
)
74 SpriteID cursors_base
= GetCustomRailSprite(rti
, INVALID_TILE
, RTSG_CURSORS
);
75 if (cursors_base
!= 0) {
76 rti
->gui_sprites
.build_ns_rail
= cursors_base
+ 0;
77 rti
->gui_sprites
.build_x_rail
= cursors_base
+ 1;
78 rti
->gui_sprites
.build_ew_rail
= cursors_base
+ 2;
79 rti
->gui_sprites
.build_y_rail
= cursors_base
+ 3;
80 rti
->gui_sprites
.auto_rail
= cursors_base
+ 4;
81 rti
->gui_sprites
.build_depot
= cursors_base
+ 5;
82 rti
->gui_sprites
.build_tunnel
= cursors_base
+ 6;
83 rti
->gui_sprites
.convert_rail
= cursors_base
+ 7;
84 rti
->cursor
.rail_ns
= cursors_base
+ 8;
85 rti
->cursor
.rail_swne
= cursors_base
+ 9;
86 rti
->cursor
.rail_ew
= cursors_base
+ 10;
87 rti
->cursor
.rail_nwse
= cursors_base
+ 11;
88 rti
->cursor
.autorail
= cursors_base
+ 12;
89 rti
->cursor
.depot
= cursors_base
+ 13;
90 rti
->cursor
.tunnel
= cursors_base
+ 14;
91 rti
->cursor
.convert
= cursors_base
+ 15;
94 /* Array of default GUI signal sprite numbers. */
95 const SpriteID _signal_lookup
[2][SIGTYPE_END
] = {
96 {SPR_IMG_SIGNAL_ELECTRIC_NORM
, SPR_IMG_SIGNAL_ELECTRIC_ENTRY
, SPR_IMG_SIGNAL_ELECTRIC_EXIT
,
97 SPR_IMG_SIGNAL_ELECTRIC_COMBO
, SPR_IMG_SIGNAL_ELECTRIC_PBS
, SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY
},
99 {SPR_IMG_SIGNAL_SEMAPHORE_NORM
, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY
, SPR_IMG_SIGNAL_SEMAPHORE_EXIT
,
100 SPR_IMG_SIGNAL_SEMAPHORE_COMBO
, SPR_IMG_SIGNAL_SEMAPHORE_PBS
, SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY
},
103 for (SignalType type
= SIGTYPE_NORMAL
; type
< SIGTYPE_END
; type
= (SignalType
)(type
+ 1)) {
104 for (SignalVariant var
= SIG_ELECTRIC
; var
<= SIG_SEMAPHORE
; var
= (SignalVariant
)(var
+ 1)) {
105 SpriteID red
= GetCustomSignalSprite(rti
, INVALID_TILE
, type
, var
, SIGNAL_STATE_RED
, true);
106 SpriteID green
= GetCustomSignalSprite(rti
, INVALID_TILE
, type
, var
, SIGNAL_STATE_GREEN
, true);
107 rti
->gui_sprites
.signals
[type
][var
][0] = (red
!= 0) ? red
+ SIGNAL_TO_NORTH
: _signal_lookup
[var
][type
];
108 rti
->gui_sprites
.signals
[type
][var
][1] = (green
!= 0) ? green
+ SIGNAL_TO_NORTH
: _signal_lookup
[var
][type
] + 1;
114 * Resolve sprites of custom rail types
118 for (RailType rt
= RAILTYPE_BEGIN
; rt
!= RAILTYPE_END
; rt
++) {
119 RailtypeInfo
*rti
= &_railtypes
[rt
];
120 ResolveRailTypeGUISprites(rti
);
125 * Allocate a new rail type label
127 RailType
AllocateRailType(RailTypeLabel label
)
129 for (RailType rt
= RAILTYPE_BEGIN
; rt
!= RAILTYPE_END
; rt
++) {
130 RailtypeInfo
*rti
= &_railtypes
[rt
];
132 if (rti
->label
== 0) {
133 /* Set up new rail type */
134 memcpy(rti
, &_railtypes
[RAILTYPE_RAIL
], sizeof(*rti
));
136 /* Clear alternate label list. Can't use Reset() here as that would free
137 * the data pointer of RAILTYPE_RAIL and not our new rail type. */
138 new (&rti
->alternate_labels
) RailTypeLabelList
;
140 /* Make us compatible with ourself. */
141 rti
->powered_railtypes
= (RailTypes
)(1 << rt
);
142 rti
->compatible_railtypes
= (RailTypes
)(1 << rt
);
144 /* We also introduce ourself. */
145 rti
->introduces_railtypes
= (RailTypes
)(1 << rt
);
147 /* Default sort order; order of allocation, but with some
148 * offsets so it's easier for NewGRF to pick a spot without
149 * changing the order of other (original) rail types.
150 * The << is so you can place other railtypes in between the
151 * other railtypes, the 7 is to be able to place something
152 * before the first (default) rail type. */
153 rti
->sorting_order
= rt
<< 4 | 7;
158 return INVALID_RAILTYPE
;
161 static const byte _track_sloped_sprites
[14] = {
186 * Tests if a vehicle interacts with the specified track.
187 * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
189 * @param tile The tile.
190 * @param track The track.
191 * @return Succeeded command (no train found), or a failed command (a train was found).
193 static CommandCost
EnsureNoTrainOnTrack(TileIndex tile
, Track track
)
195 TrackBits rail_bits
= TrackToTrackBits(track
);
196 return EnsureNoTrainOnTrackBits(tile
, rail_bits
);
200 * Check that the new track bits may be built.
201 * @param tile %Tile to build on.
202 * @param to_build New track bits.
203 * @param flags Flags of the operation.
204 * @return Succeeded or failed command.
206 static CommandCost
CheckTrackCombination(TileIndex tile
, Track to_build
, RailType railtype
, DoCommandFlag flags
)
208 assert(IsRailwayTile(tile
));
210 TrackBits current
= GetTrackBits(tile
); // The current track layout.
211 assert(current
!= TRACK_BIT_NONE
);
213 TrackBits future
= current
| TrackToTrackBits(to_build
); // The track layout we want to build.
215 /* Are we really building something new? */
216 if (current
== future
) {
217 /* Nothing new is being built */
218 if (IsCompatibleRail(GetRailType(tile
, to_build
), railtype
)) {
219 return_cmd_error(STR_ERROR_ALREADY_BUILT
);
221 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION
);
225 /* These combinations are always allowed */
226 if (future
== TRACK_BIT_HORZ
|| future
== TRACK_BIT_VERT
) {
227 if (flags
& DC_EXEC
) {
228 SetRailType(tile
, railtype
, to_build
);
230 return CommandCost();
233 if (flags
& DC_NO_RAIL_OVERLAP
) {
234 /* If we are not allowed to overlap (flag is on for ai companies), check that */
235 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION
);
238 RailType rt
; // RailType to convert to, or INVALID_RAILTYPE if no conversion is necessary
240 if (current
== TRACK_BIT_HORZ
|| current
== TRACK_BIT_VERT
) {
241 RailType rt1
= GetRailType(tile
, TRACK_UPPER
);
242 if (!IsCompatibleRail(rt1
, railtype
)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION
);
244 RailType rt2
= GetRailType(tile
, TRACK_LOWER
);
245 if (!IsCompatibleRail(rt2
, railtype
)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION
);
248 /* Two different railtypes present */
249 if ((railtype
== rt1
|| HasPowerOnRail(rt1
, railtype
)) && (railtype
== rt2
|| HasPowerOnRail(rt2
, railtype
))) {
251 } else if ((railtype
== rt1
|| HasPowerOnRail(railtype
, rt1
)) && HasPowerOnRail(rt2
, rt1
)) {
253 } else if ((railtype
== rt2
|| HasPowerOnRail(railtype
, rt2
)) && HasPowerOnRail(rt1
, rt2
)) {
256 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION
);
258 } else if (railtype
== rt1
) {
260 rt
= INVALID_RAILTYPE
;
261 } else if (HasPowerOnRail(railtype
, rt1
)) {
262 /* Try to keep existing railtype */
264 rt
= INVALID_RAILTYPE
;
265 } else if (HasPowerOnRail(rt1
, railtype
)) {
268 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION
);
271 rt
= GetRailType(tile
, FindFirstTrack(current
));
273 if (railtype
== rt
) {
275 rt
= INVALID_RAILTYPE
;
276 } else if (!IsCompatibleRail(rt
, railtype
)) {
277 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION
);
278 } else if (HasPowerOnRail(railtype
, rt
)) {
279 /* Try to keep existing railtype */
281 rt
= INVALID_RAILTYPE
;
282 } else if (HasPowerOnRail(rt
, railtype
)) {
285 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION
);
290 if (rt
!= INVALID_RAILTYPE
) {
291 ret
= DoCommand(tile
, tile
, rt
, flags
, CMD_CONVERT_RAIL
);
292 if (ret
.Failed()) return ret
;
295 if (HasSignalOnTrack(tile
, TRACK_UPPER
) || HasSignalOnTrack(tile
, TRACK_LOWER
)) {
296 return_cmd_error(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST
);
299 if (flags
& DC_EXEC
) {
300 SetRailType(tile
, railtype
, to_build
);
307 /** Valid TrackBits on a specific (non-steep)-slope without foundation */
308 static const TrackBits _valid_tracks_without_foundation
[15] = {
329 /** Valid TrackBits on a specific (non-steep)-slope with leveled foundation */
330 static const TrackBits _valid_tracks_on_leveled_foundation
[15] = {
334 TRACK_BIT_Y
| TRACK_BIT_LOWER
| TRACK_BIT_LEFT
,
338 TRACK_BIT_X
| TRACK_BIT_LOWER
| TRACK_BIT_RIGHT
,
342 TRACK_BIT_X
| TRACK_BIT_UPPER
| TRACK_BIT_LEFT
,
346 TRACK_BIT_Y
| TRACK_BIT_UPPER
| TRACK_BIT_RIGHT
,
352 * Checks if a track combination is valid on a specific slope and returns the needed foundation.
354 * @param tileh Tile slope.
355 * @param bits Trackbits.
356 * @return Needed foundation or FOUNDATION_INVALID if track/slope combination is not allowed.
358 Foundation
GetRailFoundation(Slope tileh
, TrackBits bits
)
360 if (bits
== TRACK_BIT_NONE
) return FOUNDATION_NONE
;
362 if (IsSteepSlope(tileh
)) {
363 /* Test for inclined foundations */
364 if (bits
== TRACK_BIT_X
) return FOUNDATION_INCLINED_X
;
365 if (bits
== TRACK_BIT_Y
) return FOUNDATION_INCLINED_Y
;
367 /* Get higher track */
368 Corner highest_corner
= GetHighestSlopeCorner(tileh
);
369 TrackBits higher_track
= CornerToTrackBits(highest_corner
);
371 /* Only higher track? */
372 if (bits
== higher_track
) return HalftileFoundation(highest_corner
);
374 /* Overlap with higher track? */
375 if (TracksOverlap(bits
| higher_track
)) return FOUNDATION_INVALID
;
377 /* either lower track or both higher and lower track */
378 return ((bits
& higher_track
) != 0 ? FOUNDATION_STEEP_BOTH
: FOUNDATION_STEEP_LOWER
);
380 if ((~_valid_tracks_without_foundation
[tileh
] & bits
) == 0) return FOUNDATION_NONE
;
382 bool valid_on_leveled
= ((~_valid_tracks_on_leveled_foundation
[tileh
] & bits
) == 0);
386 case TRACK_BIT_LEFT
: track_corner
= CORNER_W
; break;
387 case TRACK_BIT_LOWER
: track_corner
= CORNER_S
; break;
388 case TRACK_BIT_RIGHT
: track_corner
= CORNER_E
; break;
389 case TRACK_BIT_UPPER
: track_corner
= CORNER_N
; break;
392 if (tileh
== SLOPE_N
) return HalftileFoundation(CORNER_N
);
393 if (tileh
== SLOPE_S
) return HalftileFoundation(CORNER_S
);
394 return (valid_on_leveled
? FOUNDATION_LEVELED
: FOUNDATION_INVALID
);
397 if (tileh
== SLOPE_W
) return HalftileFoundation(CORNER_W
);
398 if (tileh
== SLOPE_E
) return HalftileFoundation(CORNER_E
);
399 return (valid_on_leveled
? FOUNDATION_LEVELED
: FOUNDATION_INVALID
);
402 if (IsSlopeWithOneCornerRaised(tileh
)) return FOUNDATION_INCLINED_X
;
403 return (valid_on_leveled
? FOUNDATION_LEVELED
: FOUNDATION_INVALID
);
406 if (IsSlopeWithOneCornerRaised(tileh
)) return FOUNDATION_INCLINED_Y
;
407 return (valid_on_leveled
? FOUNDATION_LEVELED
: FOUNDATION_INVALID
);
410 return (valid_on_leveled
? FOUNDATION_LEVELED
: FOUNDATION_INVALID
);
412 /* Single diagonal track */
414 /* Track must be at least valid on leveled foundation */
415 if (!valid_on_leveled
) return FOUNDATION_INVALID
;
417 /* If slope has three raised corners, build leveled foundation */
418 if (IsSlopeWithThreeCornersRaised(tileh
)) return FOUNDATION_LEVELED
;
420 /* If neighboured corners of track_corner are lowered, build halftile foundation */
421 if ((tileh
& SlopeWithThreeCornersRaised(OppositeCorner(track_corner
))) == SlopeWithOneCornerRaised(track_corner
)) return HalftileFoundation(track_corner
);
423 /* else special anti-zig-zag foundation */
424 return SpecialRailFoundation(track_corner
);
430 * Tests if a track can be build on a tile.
432 * @param tileh Tile slope.
433 * @param rail_bits Tracks to build.
434 * @param existing Tracks already built.
435 * @param tile Tile (used for water test)
436 * @return Error message or cost for foundation building.
438 static CommandCost
CheckRailSlope(Slope tileh
, TrackBits rail_bits
, TrackBits existing
, TileIndex tile
)
440 /* don't allow building on the lower side of a coast */
441 if (GetFloodingBehaviour(tile
) != FLOOD_NONE
) {
442 if (!IsSteepSlope(tileh
) && ((~_valid_tracks_on_leveled_foundation
[tileh
] & (rail_bits
| existing
)) != 0)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER
);
445 Foundation f_new
= GetRailFoundation(tileh
, rail_bits
| existing
);
447 /* check track/slope combination */
448 if ((f_new
== FOUNDATION_INVALID
) ||
449 ((f_new
!= FOUNDATION_NONE
) && (!_settings_game
.construction
.build_on_slopes
))) {
450 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
453 Foundation f_old
= GetRailFoundation(tileh
, existing
);
454 return CommandCost(EXPENSES_CONSTRUCTION
, f_new
!= f_old
? _price
[PR_BUILD_FOUNDATION
] : (Money
)0);
457 /* Validate functions for rail building */
458 static inline bool ValParamTrackOrientation(Track track
)
460 return IsValidTrack(track
);
465 * Check if a given trackbits set is valid for a rail bridge head
466 * @param tileh The slope
467 * @param dir The bridge direction
468 * @param bits The trackbits
469 * @return Whether the given combination is valid
471 bool IsValidRailBridgeBits(Slope tileh
, DiagDirection dir
, TrackBits bits
)
473 DiagDirDiff diff
= CheckExtendedBridgeHead(tileh
, dir
);
476 case DIAGDIRDIFF_SAME
: return true;
477 case DIAGDIRDIFF_REVERSE
: return false;
478 default: return (bits
& DiagdirReachesTracks(ReverseDiagDir(ChangeDiagDir(dir
, diff
)))) == 0;
484 * Build a single piece of rail
485 * @param tile tile to build on
486 * @param flags operation to perform
487 * @param p1 railtype of being built piece (normal, mono, maglev)
488 * @param p2 rail track to build
490 * @return the cost of this operation or an error
492 CommandCost
CmdBuildSingleRail(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
494 RailType railtype
= Extract
<RailType
, 0, 4>(p1
);
495 Track track
= Extract
<Track
, 0, 3>(p2
);
496 CommandCost
cost(EXPENSES_CONSTRUCTION
);
498 if (!ValParamRailtype(railtype
) || !ValParamTrackOrientation(track
)) return CMD_ERROR
;
500 Slope tileh
= GetTileSlope(tile
);
501 TrackBits trackbit
= TrackToTrackBits(track
);
503 switch (GetTileType(tile
)) {
505 CommandCost ret
= CheckTileOwnership(tile
);
506 if (ret
.Failed()) return ret
;
508 ret
= CheckTrackCombination(tile
, track
, railtype
, flags
);
509 if (ret
.Failed()) return ret
;
512 if (IsTileSubtype(tile
, TT_TRACK
)) {
513 ret
= CheckRailSlope(tileh
, trackbit
, GetTrackBits(tile
), tile
);
514 if (ret
.Failed()) return ret
;
517 if (!IsValidRailBridgeBits(tileh
, GetTunnelBridgeDirection(tile
), GetTrackBits(tile
) | trackbit
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
520 ret
= EnsureNoTrainOnTrack(tile
, track
);
521 if (ret
.Failed()) return ret
;
523 if (flags
& DC_EXEC
) {
524 if (IsTileSubtype(tile
, TT_TRACK
)) SetRailGroundType(tile
, RAIL_GROUND_BARREN
);
525 TrackBits bits
= GetTrackBits(tile
);
526 TrackBits newbits
= bits
| trackbit
;
527 SetTrackBits(tile
, newbits
);
529 /* Update infrastructure count. */
530 Owner owner
= GetTileOwner(tile
);
531 if (newbits
== TRACK_BIT_HORZ
|| newbits
== TRACK_BIT_VERT
) {
532 Company::Get(owner
)->infrastructure
.rail
[railtype
]++;
534 RailType rt
= GetRailType(tile
, track
);
535 if (bits
== TRACK_BIT_HORZ
|| bits
== TRACK_BIT_VERT
) {
536 Company::Get(owner
)->infrastructure
.rail
[rt
] -= IsTileSubtype(tile
, TT_BRIDGE
) ? TUNNELBRIDGE_TRACKBIT_FACTOR
+ 1 : 2;
538 uint pieces
= CountBits(bits
);
540 if (IsTileSubtype(tile
, TT_BRIDGE
)) pieces
*= TUNNELBRIDGE_TRACKBIT_FACTOR
;
541 Company::Get(owner
)->infrastructure
.rail
[rt
] -= pieces
;
543 uint pieces
= CountBits(newbits
);
544 assert(TracksOverlap(newbits
));
546 if (IsTileSubtype(tile
, TT_BRIDGE
)) pieces
*= TUNNELBRIDGE_TRACKBIT_FACTOR
;
547 Company::Get(owner
)->infrastructure
.rail
[rt
] += pieces
;
549 DirtyCompanyInfrastructureWindows(owner
);
555 if (!IsTileSubtype(tile
, TT_TRACK
)) goto try_clear
;
557 /* Level crossings may only be built on these slopes */
558 if (!HasBit(VALID_LEVEL_CROSSING_SLOPES
, tileh
)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
560 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
561 if (ret
.Failed()) return ret
;
563 if (HasRoadWorks(tile
)) return_cmd_error(STR_ERROR_ROAD_WORKS_IN_PROGRESS
);
565 if (GetDisallowedRoadDirections(tile
) != DRD_NONE
) return_cmd_error(STR_ERROR_CROSSING_ON_ONEWAY_ROAD
);
567 if (RailNoLevelCrossings(railtype
)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED
);
569 RoadTypes roadtypes
= GetRoadTypes(tile
);
570 RoadBits road
= GetRoadBits(tile
, ROADTYPE_ROAD
);
571 RoadBits tram
= GetRoadBits(tile
, ROADTYPE_TRAM
);
575 /* Tram crossings must always have road. */
576 if (flags
& DC_EXEC
) {
577 SetRoadOwner(tile
, ROADTYPE_ROAD
, _current_company
);
578 Company
*c
= Company::GetIfValid(_current_company
);
580 /* A full diagonal tile has two road bits. */
581 c
->infrastructure
.road
[ROADTYPE_ROAD
] += 2;
582 DirtyCompanyInfrastructureWindows(c
->index
);
585 roadtypes
|= ROADTYPES_ROAD
;
589 if (road
!= tram
) return CMD_ERROR
;
595 if ((track
== TRACK_X
&& road
== ROAD_Y
) ||
596 (track
== TRACK_Y
&& road
== ROAD_X
)) {
597 if (flags
& DC_EXEC
) {
598 MakeRoadCrossing(tile
, GetRoadOwner(tile
, ROADTYPE_ROAD
), GetRoadOwner(tile
, ROADTYPE_TRAM
), _current_company
, (track
== TRACK_X
? AXIS_Y
: AXIS_X
), railtype
, roadtypes
, GetTownIndex(tile
));
599 UpdateLevelCrossing(tile
, false);
600 Company::Get(_current_company
)->infrastructure
.rail
[railtype
] += LEVELCROSSING_TRACKBIT_FACTOR
;
601 DirtyCompanyInfrastructureWindows(_current_company
);
610 if (IsLevelCrossingTile(tile
) && GetCrossingRailBits(tile
) == trackbit
) {
611 return_cmd_error(STR_ERROR_ALREADY_BUILT
);
617 /* Will there be flat water on the lower halftile? */
618 bool water_ground
= IsWaterTile(tile
) && IsSlopeWithOneCornerRaised(tileh
);
620 CommandCost ret
= CheckRailSlope(tileh
, trackbit
, TRACK_BIT_NONE
, tile
);
621 if (ret
.Failed()) return ret
;
624 ret
= DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
625 if (ret
.Failed()) return ret
;
629 cost
.AddCost(-_price
[PR_CLEAR_WATER
]);
630 cost
.AddCost(_price
[PR_CLEAR_ROUGH
]);
633 if (flags
& DC_EXEC
) {
634 MakeRailNormal(tile
, _current_company
, trackbit
, railtype
);
635 if (water_ground
) SetRailGroundType(tile
, RAIL_GROUND_WATER
);
636 Company::Get(_current_company
)->infrastructure
.rail
[railtype
]++;
637 DirtyCompanyInfrastructureWindows(_current_company
);
643 if (flags
& DC_EXEC
) {
644 MarkTileDirtyByTile(tile
);
645 AddTrackToSignalBuffer(tile
, track
, _current_company
);
646 YapfNotifyTrackLayoutChange(tile
, track
);
649 cost
.AddCost(RailBuildCost(railtype
));
653 static void NotifyTrackRemoval(TileIndex tile
, Track track
, bool was_crossing
, Owner owner
)
656 /* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
657 * are removing one of these pieces, we'll need to update signals for
658 * both directions explicitly, as after the track is removed it won't
659 * 'connect' with the other piece. */
660 AddTrackToSignalBuffer(tile
, TRACK_X
, owner
);
661 AddTrackToSignalBuffer(tile
, TRACK_Y
, owner
);
662 YapfNotifyTrackLayoutChange(tile
, TRACK_X
);
663 YapfNotifyTrackLayoutChange(tile
, TRACK_Y
);
665 AddTrackToSignalBuffer(tile
, track
, owner
);
666 YapfNotifyTrackLayoutChange(tile
, track
);
671 * Remove a single piece of track from a railway tile
672 * @param tile tile to remove track from
673 * @param track the track to remove
674 * @param flags operation to perform
675 * @return the cost of this operation or an error
677 static CommandCost
RemoveRailTrack(TileIndex tile
, Track track
, DoCommandFlag flags
)
679 if (_current_company
!= OWNER_WATER
) {
680 CommandCost ret
= CheckTileOwnership(tile
);
681 if (ret
.Failed()) return ret
;
684 CommandCost ret
= EnsureNoTrainOnTrack(tile
, track
);
685 if (ret
.Failed()) return ret
;
687 TrackBits present
= GetTrackBits(tile
);
688 TrackBits trackbit
= TrackToTrackBits(track
);
689 bool crossing
= false;
691 if ((present
& trackbit
) == 0) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
692 if (present
== (TRACK_BIT_X
| TRACK_BIT_Y
)) crossing
= true;
694 RailType rt
= GetRailType(tile
, track
);
695 CommandCost
cost(EXPENSES_CONSTRUCTION
, RailClearCost(rt
));
697 /* Charge extra to remove signals on the track, if they are there */
698 if (HasSignalOnTrack(tile
, track
)) {
699 cost
.AddCost(DoCommand(tile
, track
, 0, flags
, CMD_REMOVE_SIGNALS
));
702 if (flags
& DC_EXEC
) {
705 if (HasReservedTrack(tile
, track
)) {
706 v
= GetTrainForReservation(tile
, track
);
707 if (v
!= NULL
) FreeTrainTrackReservation(v
);
710 Owner owner
= GetTileOwner(tile
);
712 if (TracksOverlap(present
)) {
713 /* Subtract old infrastructure count. */
714 uint pieces
= CountBits(present
);
716 if (IsTileSubtype(tile
, TT_BRIDGE
)) pieces
*= TUNNELBRIDGE_TRACKBIT_FACTOR
;
717 Company::Get(owner
)->infrastructure
.rail
[rt
] -= pieces
;
718 /* Add new infrastructure count. */
720 if (present
== TRACK_BIT_HORZ
|| present
== TRACK_BIT_VERT
) {
721 pieces
= IsTileSubtype(tile
, TT_BRIDGE
) ? TUNNELBRIDGE_TRACKBIT_FACTOR
+ 1 : 2;
723 pieces
= CountBits(present
);
725 if (IsTileSubtype(tile
, TT_BRIDGE
)) pieces
*= TUNNELBRIDGE_TRACKBIT_FACTOR
;
727 Company::Get(owner
)->infrastructure
.rail
[rt
] += pieces
;
729 Company::Get(owner
)->infrastructure
.rail
[rt
]--;
732 DirtyCompanyInfrastructureWindows(owner
);
735 Slope tileh
= GetTileSlope(tile
);
736 /* If there is flat water on the lower halftile, convert the tile to shore so the water remains */
737 if (GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(tileh
)) {
742 DeleteNewGRFInspectWindow(GSF_RAILTYPES
, tile
);
744 SetTrackBits(tile
, present
);
745 SetTrackReservation(tile
, GetRailReservationTrackBits(tile
) & present
);
748 MarkTileDirtyByTile(tile
);
749 NotifyTrackRemoval(tile
, track
, crossing
, owner
);
751 if (v
!= NULL
) TryPathReserve(v
, true);
757 static void RemoveRailBridgeHead(TileIndex tile
, TrackBits remove
, RailType rt
)
759 Owner owner
= GetTileOwner(tile
);
761 TrackBits bits
= GetTrackBits(tile
);
762 bool crossing
= (bits
== (TRACK_BIT_X
| TRACK_BIT_Y
));
764 /* Update infrastructure count. */
765 if (HasExactlyOneBit(bits
)) {
766 assert((bits
& ~remove
) == TRACK_BIT_NONE
);
767 bits
= TRACK_BIT_NONE
;
768 Company::Get(owner
)->infrastructure
.rail
[rt
] -= TUNNELBRIDGE_TRACKBIT_FACTOR
;
769 } else if (bits
!= TRACK_BIT_HORZ
&& bits
!= TRACK_BIT_VERT
) {
770 assert(TracksOverlap(bits
));
771 uint pieces
= CountBits(bits
);
772 Company::Get(owner
)->infrastructure
.rail
[rt
] -= pieces
* pieces
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
774 pieces
= CountBits(bits
);
775 Company::Get(owner
)->infrastructure
.rail
[rt
] += pieces
* pieces
;
776 } else if (remove
== bits
) {
777 bits
= TRACK_BIT_NONE
;
778 Company::Get(owner
)->infrastructure
.rail
[rt
] -= TUNNELBRIDGE_TRACKBIT_FACTOR
;
779 Company::Get(owner
)->infrastructure
.rail
[GetSideRailType(tile
, ReverseDiagDir(GetTunnelBridgeDirection(tile
)))]--;
782 Company::Get(owner
)->infrastructure
.rail
[rt
] -= TUNNELBRIDGE_TRACKBIT_FACTOR
;
785 if (bits
== TRACK_BIT_NONE
) {
787 DeleteNewGRFInspectWindow(GSF_RAILTYPES
, tile
);
789 assert((DiagdirReachesTracks(ReverseDiagDir(GetTunnelBridgeDirection(tile
))) & bits
) == TRACK_BIT_NONE
);
790 MakeNormalRailFromBridge(tile
);
791 SetTrackBits(tile
, bits
);
792 SetTrackReservation(tile
, GetRailReservationTrackBits(tile
) & bits
);
795 MarkTileDirtyByTile(tile
);
797 while (remove
!= TRACK_BIT_NONE
) {
798 Track track
= RemoveFirstTrack(&remove
);
799 NotifyTrackRemoval(tile
, track
, crossing
, owner
);
803 static void RemoveRailBridge(TileIndex tile
, TrackBits remove
, TileIndex other_tile
, TrackBits other_remove
)
805 SmallVector
<Train
*, 4> affected
;
807 TrackBits bits
= GetReservedTrackbits(tile
);
808 while (bits
!= TRACK_BIT_NONE
) {
809 Track track
= RemoveFirstTrack(&bits
);
810 if ((TrackToTrackBits(track
) & remove
) != TRACK_BIT_NONE
) {
811 Train
*v
= GetTrainForReservation(tile
, track
);
812 FreeTrainTrackReservation(v
);
813 *affected
.Append() = v
;
817 bits
= GetReservedTrackbits(other_tile
);
818 while (bits
!= TRACK_BIT_NONE
) {
819 Track track
= RemoveFirstTrack(&bits
);
820 if ((TrackToTrackBits(track
) & other_remove
) != TRACK_BIT_NONE
) {
821 Train
*v
= GetTrainForReservation(other_tile
, track
);
822 FreeTrainTrackReservation(v
);
823 *affected
.Append() = v
;
827 RailType rt
= GetBridgeRailType(tile
);
828 Owner owner
= GetTileOwner(tile
);
829 assert(GetTileOwner(other_tile
) == owner
);
831 RemoveBridgeMiddleTiles(tile
, other_tile
);
832 Company::Get(owner
)->infrastructure
.rail
[rt
] -= GetTunnelBridgeLength(tile
, other_tile
) * TUNNELBRIDGE_TRACKBIT_FACTOR
;
834 RemoveRailBridgeHead(tile
, remove
, rt
);
835 RemoveRailBridgeHead(other_tile
, other_remove
, rt
);
837 DirtyCompanyInfrastructureWindows(owner
);
839 for (uint i
= 0; i
< affected
.Length(); ++i
) {
840 TryPathReserve(affected
[i
], true);
845 * Remove a single piece of track from a rail bridge tile
846 * @param tile tile to remove track from
847 * @param track the track to remove
848 * @param flags operation to perform
849 * @return the cost of this operation or an error
851 static CommandCost
RemoveBridgeTrack(TileIndex tile
, Track track
, DoCommandFlag flags
)
853 if (_current_company
!= OWNER_WATER
) {
854 CommandCost ret
= CheckTileOwnership(tile
);
855 if (ret
.Failed()) return ret
;
858 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
859 TrackBits present
= GetTrackBits(tile
);
860 TrackBits trackbit
= TrackToTrackBits(track
);
862 if ((present
& trackbit
) == TRACK_BIT_NONE
) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
864 if ((present
& DiagdirReachesTracks(ReverseDiagDir(dir
)) & ~trackbit
) != TRACK_BIT_NONE
) {
865 return RemoveRailTrack(tile
, track
, flags
);
868 /* bridge must be torn down */
870 TileIndex other_tile
= GetOtherBridgeEnd(tile
);
871 TrackBits other_present
= GetTrackBits(other_tile
);
872 TrackBits other_remove
= other_present
& DiagdirReachesTracks(dir
);
874 assert(other_remove
!= TRACK_BIT_NONE
);
876 CommandCost ret
= EnsureNoTrainOnBridgeTrackBits(tile
, trackbit
, other_tile
, other_remove
);
877 if (ret
.Failed()) return ret
;
879 CommandCost
cost(EXPENSES_CONSTRUCTION
, (GetTunnelBridgeLength(tile
, other_tile
) + 2) * _price
[PR_CLEAR_BRIDGE
]);
881 /* Charge extra to remove signals on the track, if they are there */
882 if (HasSignalOnTrack(tile
, track
)) {
883 cost
.AddCost(DoCommand(tile
, track
, 0, flags
, CMD_REMOVE_SIGNALS
));
886 int n
= CountBits(other_remove
);
888 Track other_track
= FindFirstTrack(other_remove
);
889 if (HasSignalOnTrack(other_tile
, other_track
)) {
890 cost
.AddCost(DoCommand(other_tile
, other_track
, 0, flags
, CMD_REMOVE_SIGNALS
));
893 assert(GetRailType(tile
, track
) == GetBridgeRailType(other_tile
));
894 cost
.AddCost((n
- 1) * RailClearCost(GetRailType(tile
, track
)));
897 if (flags
& DC_EXEC
) {
898 RemoveRailBridge(tile
, trackbit
, other_tile
, other_remove
);
905 * Remove the rail track from a crossing
906 * @param tile tile to remove track from
907 * @param flags operation to perform
908 * @return the cost of this operation or an error
910 static CommandCost
RemoveCrossingTrack(TileIndex tile
, DoCommandFlag flags
)
912 if (_current_company
!= OWNER_WATER
) {
913 CommandCost ret
= CheckTileOwnership(tile
);
914 if (ret
.Failed()) return ret
;
917 if (!(flags
& DC_BANKRUPT
)) {
918 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
919 if (ret
.Failed()) return ret
;
922 CommandCost
cost(EXPENSES_CONSTRUCTION
, RailClearCost(GetRailType(tile
)));
924 if (flags
& DC_EXEC
) {
925 Track track
= GetCrossingRailTrack(tile
);
928 if (HasCrossingReservation(tile
)) {
929 v
= GetTrainForReservation(tile
, track
);
930 if (v
!= NULL
) FreeTrainTrackReservation(v
);
933 Owner owner
= GetTileOwner(tile
);
934 Company::Get(owner
)->infrastructure
.rail
[GetRailType(tile
)] -= LEVELCROSSING_TRACKBIT_FACTOR
;
935 DirtyCompanyInfrastructureWindows(owner
);
936 MakeRoadNormal(tile
, GetCrossingRoadBits(tile
), GetRoadTypes(tile
), GetTownIndex(tile
), GetRoadOwner(tile
, ROADTYPE_ROAD
), GetRoadOwner(tile
, ROADTYPE_TRAM
));
937 DeleteNewGRFInspectWindow(GSF_RAILTYPES
, tile
);
939 MarkTileDirtyByTile(tile
);
941 AddTrackToSignalBuffer(tile
, track
, owner
);
942 YapfNotifyTrackLayoutChange(tile
, track
);
944 if (v
!= NULL
) TryPathReserve(v
, true);
951 * Remove a single piece of track
952 * @param tile tile to remove track from
953 * @param flags operation to perform
955 * @param p2 rail orientation
957 * @return the cost of this operation or an error
959 CommandCost
CmdRemoveSingleRail(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
961 Track track
= Extract
<Track
, 0, 3>(p2
);
963 if (!ValParamTrackOrientation(track
)) return CMD_ERROR
;
965 switch (GetTileType(tile
)) {
967 if (!IsLevelCrossingTile(tile
) || GetCrossingRailTrack(tile
) != track
) break;
968 return RemoveCrossingTrack(tile
, flags
);
971 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
972 return RemoveBridgeTrack(tile
, track
, flags
);
974 return RemoveRailTrack(tile
, track
, flags
);
980 return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
985 * Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
986 * The function floods the lower halftile, if the tile has a halftile foundation.
988 * @param t The tile to flood.
989 * @return true if something was flooded.
991 bool FloodHalftile(TileIndex t
)
993 assert(IsNormalRailTile(t
));
995 bool flooded
= false;
996 if (GetRailGroundType(t
) == RAIL_GROUND_WATER
) return flooded
;
998 Slope tileh
= GetTileSlope(t
);
999 TrackBits rail_bits
= GetTrackBits(t
);
1001 if (IsSlopeWithOneCornerRaised(tileh
)) {
1002 TrackBits lower_track
= CornerToTrackBits(OppositeCorner(GetHighestSlopeCorner(tileh
)));
1004 TrackBits to_remove
= lower_track
& rail_bits
;
1005 if (to_remove
!= 0) {
1006 Backup
<CompanyByte
> cur_company(_current_company
, OWNER_WATER
, FILE_LINE
);
1007 flooded
= DoCommand(t
, 0, FIND_FIRST_BIT(to_remove
), DC_EXEC
, CMD_REMOVE_SINGLE_RAIL
).Succeeded();
1008 cur_company
.Restore();
1009 if (!flooded
) return flooded
; // not yet floodable
1010 rail_bits
= rail_bits
& ~to_remove
;
1011 if (rail_bits
== 0) {
1013 MarkTileDirtyByTile(t
);
1018 if (IsNonContinuousFoundation(GetRailFoundation(tileh
, rail_bits
))) {
1020 SetRailGroundType(t
, RAIL_GROUND_WATER
);
1021 MarkTileDirtyByTile(t
);
1024 /* Make shore on steep slopes and 'three-corners-raised'-slopes. */
1025 if (ApplyFoundationToSlope(GetRailFoundation(tileh
, rail_bits
), &tileh
) == 0) {
1026 if (IsSteepSlope(tileh
) || IsSlopeWithThreeCornersRaised(tileh
)) {
1028 SetRailGroundType(t
, RAIL_GROUND_WATER
);
1029 MarkTileDirtyByTile(t
);
1036 static const CoordDiff _trackdelta
[] = {
1037 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
1040 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
1046 static CommandCost
ValidateAutoDrag(Trackdir
*trackdir
, TileIndex start
, TileIndex end
)
1048 int x
= TileX(start
);
1049 int y
= TileY(start
);
1050 int ex
= TileX(end
);
1051 int ey
= TileY(end
);
1053 if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir
))) return CMD_ERROR
;
1055 /* calculate delta x,y from start to end tile */
1059 /* calculate delta x,y for the first direction */
1060 int trdx
= _trackdelta
[*trackdir
].x
;
1061 int trdy
= _trackdelta
[*trackdir
].y
;
1063 if (!IsDiagonalTrackdir(*trackdir
)) {
1064 trdx
+= _trackdelta
[*trackdir
^ 1].x
;
1065 trdy
+= _trackdelta
[*trackdir
^ 1].y
;
1068 /* validate the direction */
1069 while ((trdx
<= 0 && dx
> 0) ||
1070 (trdx
>= 0 && dx
< 0) ||
1071 (trdy
<= 0 && dy
> 0) ||
1072 (trdy
>= 0 && dy
< 0)) {
1073 if (!HasBit(*trackdir
, 3)) { // first direction is invalid, try the other
1074 SetBit(*trackdir
, 3); // reverse the direction
1077 } else { // other direction is invalid too, invalid drag
1082 /* (for diagonal tracks, this is already made sure of by above test), but:
1083 * for non-diagonal tracks, check if the start and end tile are on 1 line */
1084 if (!IsDiagonalTrackdir(*trackdir
)) {
1085 trdx
= _trackdelta
[*trackdir
].x
;
1086 trdy
= _trackdelta
[*trackdir
].y
;
1087 if (abs(dx
) != abs(dy
) && abs(dx
) + abs(trdy
) != abs(dy
) + abs(trdx
)) return CMD_ERROR
;
1090 return CommandCost();
1094 * Build or remove a stretch of railroad tracks.
1095 * @param tile start tile of drag
1096 * @param flags operation to perform
1097 * @param p1 end tile of drag
1098 * @param p2 various bitstuffed elements
1099 * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev)
1100 * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum)
1101 * - p2 = (bit 7) - 0 = build, 1 = remove tracks
1102 * - p2 = (bit 8) - 0 = build up to an obstacle, 1 = fail if an obstacle is found (used for AIs).
1103 * @param text unused
1104 * @return the cost of this operation or an error
1106 static CommandCost
CmdRailTrackHelper(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1108 CommandCost
total_cost(EXPENSES_CONSTRUCTION
);
1109 Track track
= Extract
<Track
, 4, 3>(p2
);
1110 bool remove
= HasBit(p2
, 7);
1111 RailType railtype
= Extract
<RailType
, 0, 4>(p2
);
1113 if (!ValParamRailtype(railtype
) || !ValParamTrackOrientation(track
)) return CMD_ERROR
;
1114 if (p1
>= MapSize()) return CMD_ERROR
;
1115 TileIndex end_tile
= p1
;
1116 Trackdir trackdir
= TrackToTrackdir(track
);
1118 CommandCost ret
= ValidateAutoDrag(&trackdir
, tile
, end_tile
);
1119 if (ret
.Failed()) return ret
;
1121 bool had_success
= false;
1122 CommandCost last_error
= CMD_ERROR
;
1123 bool seen_bridgehead
= false;
1125 if (seen_bridgehead
&& IsRailBridgeTile(tile
) && DiagDirToDiagTrackdir(ReverseDiagDir(GetTunnelBridgeDirection(tile
))) == trackdir
) {
1126 seen_bridgehead
= false;
1128 CommandCost ret
= DoCommand(tile
, railtype
, TrackdirToTrack(trackdir
), flags
, remove
? CMD_REMOVE_SINGLE_RAIL
: CMD_BUILD_SINGLE_RAIL
);
1132 if (last_error
.GetErrorMessage() != STR_ERROR_ALREADY_BUILT
&& !remove
) {
1133 if (HasBit(p2
, 8)) return last_error
;
1137 /* Ownership errors are more important. */
1138 if (last_error
.GetErrorMessage() == STR_ERROR_OWNED_BY
&& remove
) break;
1141 total_cost
.AddCost(ret
);
1145 if (IsRailBridgeTile(tile
) && DiagDirToDiagTrackdir(GetTunnelBridgeDirection(tile
)) == trackdir
) {
1146 seen_bridgehead
= true;
1149 if (tile
== end_tile
) break;
1151 tile
+= ToTileIndexDiff(_trackdelta
[trackdir
]);
1153 /* toggle railbit for the non-diagonal tracks */
1154 if (!IsDiagonalTrackdir(trackdir
)) ToggleBit(trackdir
, 0);
1157 if (had_success
) return total_cost
;
1162 * Build rail on a stretch of track.
1163 * Stub for the unified rail builder/remover
1164 * @param tile start tile of drag
1165 * @param flags operation to perform
1166 * @param p1 end tile of drag
1167 * @param p2 various bitstuffed elements
1168 * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev)
1169 * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum)
1170 * - p2 = (bit 7) - 0 = build, 1 = remove tracks
1171 * @param text unused
1172 * @return the cost of this operation or an error
1173 * @see CmdRailTrackHelper
1175 CommandCost
CmdBuildRailroadTrack(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1177 return CmdRailTrackHelper(tile
, flags
, p1
, ClrBit(p2
, 7), text
);
1181 * Build rail on a stretch of track.
1182 * Stub for the unified rail builder/remover
1183 * @param tile start tile of drag
1184 * @param flags operation to perform
1185 * @param p1 end tile of drag
1186 * @param p2 various bitstuffed elements
1187 * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev)
1188 * - p2 = (bit 4-6) - track-orientation, valid values: 0-5 (Track enum)
1189 * - p2 = (bit 7) - 0 = build, 1 = remove tracks
1190 * @param text unused
1191 * @return the cost of this operation or an error
1192 * @see CmdRailTrackHelper
1194 CommandCost
CmdRemoveRailroadTrack(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1196 return CmdRailTrackHelper(tile
, flags
, p1
, SetBit(p2
, 7), text
);
1200 * Build a train depot
1201 * @param tile position of the train depot
1202 * @param flags operation to perform
1203 * @param p1 rail type
1204 * @param p2 bit 0..1 entrance direction (DiagDirection)
1205 * @param text unused
1206 * @return the cost of this operation or an error
1208 * @todo When checking for the tile slope,
1209 * distinguish between "Flat land required" and "land sloped in wrong direction"
1211 CommandCost
CmdBuildTrainDepot(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1213 /* check railtype and valid direction for depot (0 through 3), 4 in total */
1214 RailType railtype
= Extract
<RailType
, 0, 4>(p1
);
1215 if (!ValParamRailtype(railtype
)) return CMD_ERROR
;
1217 Slope tileh
= GetTileSlope(tile
);
1219 DiagDirection dir
= Extract
<DiagDirection
, 0, 2>(p2
);
1221 /* Prohibit construction if
1222 * The tile is non-flat AND
1223 * 1) build-on-slopes is disabled
1224 * 2) the tile is steep i.e. spans two height levels
1225 * 3) the exit points in the wrong direction
1228 if (tileh
!= SLOPE_FLAT
&& (
1229 !_settings_game
.construction
.build_on_slopes
||
1230 !CanBuildDepotByTileh(dir
, tileh
)
1232 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED
);
1235 CommandCost cost
= DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
1236 if (cost
.Failed()) return cost
;
1238 if (HasBridgeAbove(tile
)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
1240 if (!Depot::CanAllocateItem()) return CMD_ERROR
;
1242 if (flags
& DC_EXEC
) {
1243 Depot
*d
= new Depot(tile
);
1244 d
->build_date
= _date
;
1246 MakeRailDepot(tile
, _current_company
, d
->index
, dir
, railtype
);
1247 MarkTileDirtyByTile(tile
);
1250 Company::Get(_current_company
)->infrastructure
.rail
[railtype
]++;
1251 DirtyCompanyInfrastructureWindows(_current_company
);
1253 AddDepotToSignalBuffer(tile
, _current_company
);
1254 YapfNotifyTrackLayoutChange(tile
, DiagDirToDiagTrack(dir
));
1257 cost
.AddCost(_price
[PR_BUILD_DEPOT_TRAIN
]);
1258 cost
.AddCost(RailBuildCost(railtype
));
1263 * Build signals, alternate between double/single, signal/semaphore,
1264 * pre/exit/combo-signals, and what-else not. If the rail piece does not
1265 * have any signals, bit 4 (cycle signal-type) is ignored
1266 * @param tile tile where to build the signals
1267 * @param flags operation to perform
1268 * @param p1 various bitstuffed elements
1269 * - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum)
1270 * - p1 = (bit 4) - 0 = signals, 1 = semaphores
1271 * - p1 = (bit 5-7) - type of the signal, for valid values see enum SignalType in signal_type.h
1272 * - p1 = (bit 17-19)-operation mode (BuildSignalMode)
1273 * @param p2 extra data depending on the operation mode
1274 * - for SIGNALS_COPY and SIGNALS_COPY_SOFT, signals to build
1275 * - for SIGNALS_CYCLE_TYPE, bitmask of signal types to cycle through
1276 * @param text unused
1277 * @return the cost of this operation or an error
1278 * @todo p2 should be replaced by two bits for "along" and "against" the track.
1280 CommandCost
CmdBuildSingleSignal(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1282 Track track
= Extract
<Track
, 0, 3>(p1
);
1283 SignalVariant sigvar
= HasBit(p1
, 4) ? SIG_SEMAPHORE
: SIG_ELECTRIC
; // the signal variant of the new signal
1284 SignalType sigtype
= Extract
<SignalType
, 5, 3>(p1
); // the signal type of the new signal
1285 BuildSignalMode mode
= (BuildSignalMode
) GB(p1
, 17, 3);
1287 /* You can only build signals on rail tiles, and the selected track must exist */
1289 TileIndex other_end
;
1290 if (IsRailwayTile(tile
)) {
1291 if (sigtype
> SIGTYPE_LAST
) return CMD_ERROR
;
1293 if (!ValParamTrackOrientation(track
) || !HasTrack(tile
, track
)) {
1294 return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
1297 other_end
= INVALID_TILE
;
1299 if (mode
== SIGNALS_CYCLE_TYPE
&& (p2
== 0 || p2
> (1 << SIGTYPE_END
) - 1)) return CMD_ERROR
;
1300 /* Protect against invalid signal copying */
1301 if ((mode
== SIGNALS_COPY
|| mode
== SIGNALS_COPY_SOFT
) && (p2
== 0 || p2
> 3)) return CMD_ERROR
;
1303 CommandCost ret
= CheckTileOwnership(tile
);
1304 if (ret
.Failed()) return ret
;
1306 /* See if this is a valid track combination for signals (no overlap) */
1307 if (TracksOverlap(GetTrackBits(tile
))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK
);
1309 signals
= *maptile_signalpair(tile
, track
);
1310 } else if (maptile_is_rail_tunnel(tile
)) {
1311 if (track
!= DiagDirToDiagTrack(GetTunnelBridgeDirection(tile
))) {
1312 return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
1315 /* Protect against invalid signal copying */
1316 if (mode
== SIGNALS_COPY
|| mode
== SIGNALS_COPY_SOFT
) {
1317 if (sigtype
== SIGTYPE_PBS_ONEWAY
) {
1318 if (p2
!= 1) return CMD_ERROR
;
1320 if (sigtype
!= SIGTYPE_NORMAL
|| p2
== 0 || p2
> 2) return CMD_ERROR
;
1323 if (sigtype
!= SIGTYPE_NORMAL
&& sigtype
!= SIGTYPE_PBS_ONEWAY
) return CMD_ERROR
;
1326 CommandCost ret
= CheckTileOwnership(tile
);
1327 if (ret
.Failed()) return ret
;
1329 /* prevent updating signals in a busy tunnel */
1330 ret
= EnsureNoVehicleOnGround(tile
);
1331 if (ret
.Failed()) return ret
;
1332 other_end
= GetOtherTunnelEnd(tile
);
1333 ret
= EnsureNoVehicleOnGround(other_end
);
1334 if (ret
.Failed()) return ret
;
1336 signals
= *maptile_tunnel_signalpair(tile
);
1338 return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
1341 CommandCost
cost (EXPENSES_CONSTRUCTION
);
1343 default: return CMD_ERROR
;
1345 case SIGNALS_CYCLE_TYPE
:
1346 if (signalpair_has_signals(&signals
)) {
1347 /* it is free to change signal type: normal-pre-exit-combo */
1348 sigtype
= signalpair_get_type(&signals
);
1349 if (other_end
== INVALID_TILE
) {
1350 assert_compile(SIGTYPE_END
<= 8);
1352 /* cycle through allowed signals */
1353 sigtype
= (SignalType
) (FindFirstBit((p2
| (p2
<< 8)) & ~((1 << (sigtype
+ 1)) - 1)) & 0x7);
1355 signalpair_set_type(&signals
, sigtype
);
1356 if (IsPbsSignal(sigtype
) && signalpair_get_present(&signals
) == 3) {
1357 signalpair_set_present(&signals
, 1);
1359 } else if (signalpair_has_signal(&signals
, false)) {
1360 /* toggle normal/path signal */
1361 assert(!signalpair_has_signal(&signals
, true));
1362 assert(sigtype
== SIGTYPE_NORMAL
|| sigtype
== SIGTYPE_PBS_ONEWAY
);
1363 sigtype
= (sigtype
== SIGTYPE_NORMAL
) ? SIGTYPE_PBS_ONEWAY
: SIGTYPE_NORMAL
;
1364 signalpair_set_type(&signals
, sigtype
);
1368 /* build new signals--fall through */
1370 if (signalpair_has_signals(&signals
)) {
1371 /* it is free to change signal type: normal-pre-exit-combo */
1372 if (other_end
== INVALID_TILE
) {
1373 /* cycle the signal side: both -> left -> right -> both -> ... */
1374 uint sig
= signalpair_get_present(&signals
);
1375 if (--sig
== 0) sig
= IsPbsSignal(signalpair_get_type(&signals
)) ? 2 : 3;
1376 signalpair_set_present(&signals
, sig
);
1377 } else if (signalpair_has_signal(&signals
, true)) {
1378 assert(signalpair_get_type(&signals
) == SIGTYPE_NORMAL
);
1379 sigtype
= SIGTYPE_NORMAL
;
1380 signalpair_set_present(&signals
, 1);
1381 assert(maptile_get_tunnel_present_signals(other_end
) == 1);
1383 assert(signalpair_has_signal(&signals
, false));
1384 sigtype
= SIGTYPE_NORMAL
;
1385 signalpair_set_present(&signals
, 2);
1386 signalpair_set_type(&signals
, SIGTYPE_NORMAL
);
1389 /* build new signals */
1390 cost
.AddCost(_price
[PR_BUILD_SIGNALS
]);
1393 if (other_end
== INVALID_TILE
) {
1394 present
= IsPbsSignal(sigtype
) ? 1 : 3;
1395 } else if (maptile_has_tunnel_signals(other_end
)) {
1396 assert(maptile_get_tunnel_present_signals(other_end
) == 1);
1397 sigtype
= SIGTYPE_NORMAL
;
1400 assert(sigtype
== SIGTYPE_NORMAL
|| sigtype
== SIGTYPE_PBS_ONEWAY
);
1403 signalpair_set_present(&signals
, present
);
1404 signalpair_set_type_variant(&signals
, sigtype
, sigvar
);
1405 signalpair_set_states(&signals
, 3);
1409 case SIGNALS_COPY_SOFT
:
1410 /* In case we don't want to change an existing signal, return without error. */
1411 if (signalpair_has_signals(&signals
)) return CommandCost();
1414 if (!signalpair_has_signals(&signals
)) {
1415 /* build new signals */
1416 cost
.AddCost(_price
[PR_BUILD_SIGNALS
]);
1417 signalpair_set_states(&signals
, 3);
1418 } else if (sigvar
!= signalpair_get_variant(&signals
)) {
1419 /* convert signals <-> semaphores */
1420 cost
.AddCost(_price
[PR_BUILD_SIGNALS
] + _price
[PR_CLEAR_SIGNALS
]);
1422 /* it is free to change signal type: normal-pre-exit-combo */
1425 /* If CmdBuildManySignals is called with copying signals, just copy the
1426 * direction of the first signal given as parameter by CmdBuildManySignals */
1427 signalpair_set_present(&signals
, p2
);
1428 signalpair_set_type_variant(&signals
, sigtype
, sigvar
);
1431 case SIGNALS_CONVERT
:
1432 if (!signalpair_has_signals(&signals
)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS
);
1434 /* convert the present signal to the chosen type and variant */
1435 if (other_end
!= INVALID_TILE
&& signalpair_get_present(&signals
) != 1 && sigtype
!= SIGTYPE_NORMAL
) return CMD_ERROR
;
1436 if (sigvar
!= signalpair_get_variant(&signals
)) {
1437 /* convert signals <-> semaphores */
1438 cost
.AddCost(_price
[PR_BUILD_SIGNALS
] + _price
[PR_CLEAR_SIGNALS
]);
1440 /* it is free to change signal type: normal-pre-exit-combo */
1443 signalpair_set_type_variant(&signals
, sigtype
, sigvar
);
1444 if (IsPbsSignal(sigtype
) && (signalpair_get_present(&signals
) == 3)) {
1445 signalpair_set_present(&signals
, 1);
1449 case SIGNALS_TOGGLE_VARIANT
:
1450 if (!signalpair_has_signals(&signals
)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS
);
1451 /* convert electric <-> semaphore */
1452 cost
.AddCost(_price
[PR_BUILD_SIGNALS
] + _price
[PR_CLEAR_SIGNALS
]);
1453 signalpair_toggle_variant(&signals
);
1457 SignalPair other_signals
;
1458 if (other_end
!= INVALID_TILE
) {
1459 /* make any necessary adjustments to the other end of the tunnel */
1460 other_signals
= *maptile_tunnel_signalpair(other_end
);
1461 if (signalpair_has_signal(&signals
, true)) {
1462 if (!signalpair_has_signals(&other_signals
)) {
1463 cost
.AddCost(_price
[PR_BUILD_SIGNALS
]);
1464 signalpair_set_present(&other_signals
, 1);
1465 signalpair_set_type_variant(&other_signals
, SIGTYPE_NORMAL
, signalpair_get_variant(&signals
));
1466 signalpair_set_states(&other_signals
, 3);
1467 } else if (signalpair_has_signal(&other_signals
, true)) {
1468 signalpair_set_present(&other_signals
, 1);
1469 assert(signalpair_get_type(&other_signals
) == SIGTYPE_NORMAL
);
1471 other_signals
= 0; // 0 means no changes
1474 if (signalpair_has_signal(&other_signals
, false)) {
1475 signalpair_set_present(&other_signals
, 2);
1476 signalpair_set_type(&other_signals
, SIGTYPE_NORMAL
);
1478 other_signals
= 0; // 0 means no changes
1485 if (flags
& DC_EXEC
) {
1486 Train
*v
[2] = { NULL
, NULL
};
1488 if (mode
!= SIGNALS_TOGGLE_VARIANT
) {
1489 /* The new/changed signal could block our path. As this can lead to
1490 * stale reservations, we clear the path reservation here and try
1491 * to redo it later on. */
1492 if (HasReservedTrack(tile
, track
)) {
1493 v
[0] = GetTrainForReservation(tile
, track
);
1494 if (v
[0] != NULL
) FreeTrainTrackReservation(v
[0]);
1497 if (other_end
!= INVALID_TILE
&& HasReservedTrack(other_end
, track
)) {
1498 v
[1] = GetTrainForReservation(other_end
, track
);
1499 if (v
[1] != NULL
) FreeTrainTrackReservation(v
[1]);
1502 /* Update signal infrastructure count. */
1503 int infra_diff
= CountBits(signalpair_get_present(&signals
));
1504 if (other_end
== INVALID_TILE
) {
1505 infra_diff
-= CountBits(GetPresentSignals(tile
, track
));
1507 infra_diff
-= CountBits(maptile_get_tunnel_present_signals(tile
));
1508 if (other_signals
!= 0) {
1509 infra_diff
+= CountBits(signalpair_get_present(&other_signals
)) - CountBits(maptile_get_tunnel_present_signals(other_end
));
1512 if (infra_diff
!= 0) {
1513 Owner owner
= GetTileOwner(tile
);
1514 Company::Get(owner
)->infrastructure
.signal
+= infra_diff
;
1515 DirtyCompanyInfrastructureWindows(owner
);
1518 if (IsPbsSignal(signalpair_get_type(&signals
))) {
1519 /* PBS signals should show red unless they are on reserved tiles without a train. */
1520 uint mask
= signalpair_get_present(&signals
);
1521 uint state
= signalpair_get_states(&signals
);
1522 signalpair_set_states(&signals
, HasReservedTrack(tile
, track
) && EnsureNoTrainOnTrack(tile
, track
).Succeeded() ? (state
| mask
) : (state
& ~mask
));
1526 if (other_end
== INVALID_TILE
) {
1527 *maptile_signalpair(tile
, track
) = signals
;
1529 *maptile_tunnel_signalpair(tile
) = signals
;
1530 if (other_signals
!= 0) *maptile_tunnel_signalpair(other_end
) = other_signals
;
1533 MarkTileDirtyByTile(tile
);
1534 AddTrackToSignalBuffer(tile
, track
, _current_company
);
1535 YapfNotifyTrackLayoutChange(tile
, track
);
1537 if (other_signals
!= 0) {
1538 MarkTileDirtyByTile(other_end
);
1539 AddTrackToSignalBuffer(other_end
, track
, _current_company
);
1540 YapfNotifyTrackLayoutChange(other_end
, track
);
1543 for (int i
= 0; i
< 2; i
++) {
1545 /* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1546 if (!(((v
[i
]->vehstatus
& VS_STOPPED
) && v
[i
]->cur_speed
== 0) || v
[i
]->current_order
.IsType(OT_LOADING
)) ||
1547 !IsSafeWaitingPosition(v
[i
], v
[i
]->GetPos(), _settings_game
.pf
.forbid_90_deg
)) {
1548 TryPathReserve(v
[i
], true);
1557 static bool CheckSignalAutoFill(TileIndex
&tile
, Trackdir
&trackdir
, int &signal_ctr
, bool remove
)
1559 tile
= AddCoordDiffWrap(tile
, _trackdelta
[trackdir
]);
1560 if (tile
== INVALID_TILE
) return false;
1562 /* Check for track bits on the new tile */
1563 TrackdirBits trackdirbits
= TrackStatusToTrackdirBits(GetTileRailwayStatus(tile
));
1565 if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits
))) return false;
1566 trackdirbits
&= TrackdirReachesTrackdirs(trackdir
);
1568 /* No track bits, must stop */
1569 if (trackdirbits
== TRACKDIR_BIT_NONE
) return false;
1571 /* Get the first track dir */
1572 trackdir
= RemoveFirstTrackdir(&trackdirbits
);
1574 /* Any left? It's a junction so we stop */
1575 if (trackdirbits
!= TRACKDIR_BIT_NONE
) return false;
1577 switch (GetTileType(tile
)) {
1579 if (!IsTileSubtype(tile
, TT_TRACK
)) goto bridge
;
1580 if (!remove
&& HasSignalOnTrack(tile
, TrackdirToTrack(trackdir
))) return false;
1582 if (IsDiagonalTrackdir(trackdir
)) {
1584 /* Ensure signal_ctr even so X and Y pieces get signals */
1585 ClrBit(signal_ctr
, 0);
1590 if (IsLevelCrossingTile(tile
)) {
1593 } else if (!IsTunnelTile(tile
)) return false;
1595 if (GetTunnelTransportType(tile
) != TRANSPORT_RAIL
) return false;
1597 TileIndex orig_tile
= tile
; // backup old value
1599 if (GetTunnelBridgeDirection(tile
) != TrackdirToExitdir(trackdir
)) return false;
1601 /* Skip to end of tunnel or bridge
1602 * note that tile is a parameter by reference, so it must be updated */
1603 tile
= GetOtherTunnelBridgeEnd(tile
);
1605 signal_ctr
+= (GetTunnelBridgeLength(orig_tile
, tile
) + 2) * 2;
1609 default: return false;
1614 * Build many signals by dragging; AutoSignals
1615 * @param tile start tile of drag
1616 * @param flags operation to perform
1617 * @param p1 end tile of drag
1618 * @param p2 various bitstuffed elements
1619 * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum)
1620 * - p2 = (bit 4) - 0 = signals, 1 = semaphores
1621 * - p2 = (bit 5) - 0 = build, 1 = remove signals
1622 * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill
1623 * - p2 = (bit 7- 9) - default signal type
1624 * - p2 = (bit 10) - 0 = keep fixed distance, 1 = minimise gaps between signals
1625 * - p2 = (bit 24-31) - user defined signals_density
1626 * @param text unused
1627 * @return the cost of this operation or an error
1629 static CommandCost
CmdSignalTrackHelper(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1631 CommandCost
total_cost(EXPENSES_CONSTRUCTION
);
1632 TileIndex start_tile
= tile
;
1634 Track track
= Extract
<Track
, 0, 3>(p2
);
1635 bool semaphores
= HasBit(p2
, 4);
1636 bool remove
= HasBit(p2
, 5);
1637 bool autofill
= HasBit(p2
, 6);
1638 bool minimise_gaps
= HasBit(p2
, 10);
1639 byte signal_density
= GB(p2
, 24, 8);
1641 if (p1
>= MapSize() || !ValParamTrackOrientation(track
)) return CMD_ERROR
;
1642 TileIndex end_tile
= p1
;
1643 if (signal_density
== 0 || signal_density
> 20) return CMD_ERROR
;
1645 if (!IsRailwayTile(tile
)) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
1647 /* for vertical/horizontal tracks, double the given signals density
1648 * since the original amount will be too dense (shorter tracks) */
1649 signal_density
*= 2;
1651 Trackdir trackdir
= TrackToTrackdir(track
);
1652 CommandCost ret
= ValidateAutoDrag(&trackdir
, tile
, end_tile
);
1653 if (ret
.Failed()) return ret
;
1655 track
= TrackdirToTrack(trackdir
); // trackdir might have changed, keep track in sync
1656 Trackdir start_trackdir
= trackdir
;
1658 /* Must start on a valid track to be able to avoid loops */
1659 if (!HasTrack(tile
, track
)) return CMD_ERROR
;
1661 SignalType sigtype
= (SignalType
)GB(p2
, 7, 3);
1662 if (sigtype
> SIGTYPE_LAST
) return CMD_ERROR
;
1665 /* copy the signal-style of the first rail-piece if existing */
1666 if (HasSignalOnTrack(tile
, track
)) {
1667 signals_ref
= GetPresentSignals(tile
, track
);
1668 assert(signals_ref
!= 0);
1669 if (!trackdir_is_signal_along(trackdir
) && signals_ref
< 3) {
1673 /* copy signal/semaphores style (independent of CTRL) */
1674 semaphores
= GetSignalVariant(tile
, track
) != SIG_ELECTRIC
;
1676 sigtype
= GetSignalType(tile
, track
);
1677 /* Don't but copy entry or exit-signal type */
1678 if (sigtype
== SIGTYPE_ENTRY
|| sigtype
== SIGTYPE_EXIT
) sigtype
= SIGTYPE_NORMAL
;
1679 } else { // no signals exist, drag a two-way signal stretch
1680 signals_ref
= IsPbsSignal(sigtype
) ? 2 : 3;
1683 /* signal_ctr - amount of tiles already processed
1684 * last_used_ctr - amount of tiles before previously placed signal
1685 * signals_density - setting to put signal on every Nth tile (double space on |, -- tracks)
1686 * last_suitable_ctr - amount of tiles before last possible signal place
1687 * last_suitable_tile - last tile where it is possible to place a signal
1688 * last_suitable_trackdir - trackdir of the last tile
1690 * trackdir - trackdir to build with autorail
1691 * semaphores - semaphores or signals
1692 * signals - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1693 * and convert all others to semaphore/signal
1694 * remove - 1 remove signals, 0 build signals */
1696 int last_used_ctr
= INT_MIN
; // initially INT_MIN to force building/removing at the first tile
1697 int last_suitable_ctr
= 0;
1698 TileIndex last_suitable_tile
= INVALID_TILE
;
1699 Trackdir last_suitable_trackdir
= INVALID_TRACKDIR
;
1700 CommandCost last_error
= CMD_ERROR
;
1701 bool had_success
= false;
1703 /* only build/remove signals with the specified density */
1704 if (remove
|| minimise_gaps
|| signal_ctr
% signal_density
== 0) {
1705 uint32 p1
= GB(TrackdirToTrack(trackdir
), 0, 3);
1706 SB(p1
, 4, 1, semaphores
);
1707 SB(p1
, 5, 3, sigtype
);
1708 SB(p1
, 17, 3, !remove
&& signal_ctr
== 0 ? SIGNALS_COPY_SOFT
: SIGNALS_COPY
);
1710 /* Pick the correct orientation for the track direction */
1711 byte signals
= signals_ref
;
1712 if (!trackdir_is_signal_along(trackdir
) && signals
< 3) {
1716 /* Test tiles in between for suitability as well if minimising gaps. */
1717 bool test_only
= !remove
&& minimise_gaps
&& signal_ctr
< (last_used_ctr
+ signal_density
);
1718 CommandCost ret
= DoCommand(tile
, p1
, signals
, test_only
? flags
& ~DC_EXEC
: flags
, remove
? CMD_REMOVE_SIGNALS
: CMD_BUILD_SIGNALS
);
1720 if (ret
.Succeeded()) {
1721 /* Remember last track piece where we can place a signal. */
1722 last_suitable_ctr
= signal_ctr
;
1723 last_suitable_tile
= tile
;
1724 last_suitable_trackdir
= trackdir
;
1725 } else if (!test_only
&& last_suitable_tile
!= INVALID_TILE
) {
1726 /* If a signal can't be placed, place it at the last possible position. */
1727 SB(p1
, 0, 3, TrackdirToTrack(last_suitable_trackdir
));
1728 SB(p1
, 17, 3, SIGNALS_COPY
);
1730 /* Pick the correct orientation for the track direction. */
1731 signals
= signals_ref
;
1732 if (!trackdir_is_signal_along(last_suitable_trackdir
) && signals
< 3) {
1736 ret
= DoCommand(last_suitable_tile
, p1
, signals
, flags
, remove
? CMD_REMOVE_SIGNALS
: CMD_BUILD_SIGNALS
);
1741 /* Be user-friendly and try placing signals as much as possible */
1742 if (ret
.Succeeded()) {
1744 total_cost
.AddCost(ret
);
1745 last_used_ctr
= last_suitable_ctr
;
1746 last_suitable_tile
= INVALID_TILE
;
1748 /* The "No railway" error is the least important one. */
1749 if (ret
.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
||
1750 last_error
.GetErrorMessage() == INVALID_STRING_ID
) {
1758 if (!CheckSignalAutoFill(tile
, trackdir
, signal_ctr
, remove
)) break;
1760 /* Prevent possible loops */
1761 if (tile
== start_tile
&& trackdir
== start_trackdir
) break;
1763 if (tile
== end_tile
) break;
1765 tile
+= ToTileIndexDiff(_trackdelta
[trackdir
]);
1768 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1769 if (IsDiagonalTrackdir(trackdir
)) {
1772 ToggleBit(trackdir
, 0);
1777 return had_success
? total_cost
: last_error
;
1781 * Build signals on a stretch of track.
1782 * Stub for the unified signal builder/remover
1783 * @param tile start tile of drag
1784 * @param flags operation to perform
1785 * @param p1 end tile of drag
1786 * @param p2 various bitstuffed elements
1787 * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum)
1788 * - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
1789 * - p2 = (bit 4) - 0 = signals, 1 = semaphores
1790 * - p2 = (bit 5) - 0 = build, 1 = remove signals
1791 * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill
1792 * - p2 = (bit 7- 9) - default signal type
1793 * - p2 = (bit 24-31) - user defined signals_density
1794 * @param text unused
1795 * @return the cost of this operation or an error
1796 * @see CmdSignalTrackHelper
1798 CommandCost
CmdBuildSignalTrack(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1800 return CmdSignalTrackHelper(tile
, flags
, p1
, p2
, text
);
1805 * @param tile coordinates where signal is being deleted from
1806 * @param flags operation to perform
1807 * @param p1 various bitstuffed elements, only track information is used
1808 * - (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum)
1809 * - (bit 3) - pre/exit/combo signal (CTRL-toggle)
1810 * - (bit 4) - 0 = signals, 1 = semaphores
1812 * @param text unused
1813 * @return the cost of this operation or an error
1815 CommandCost
CmdRemoveSingleSignal(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1817 Track track
= Extract
<Track
, 0, 3>(p1
);
1819 SignalPair
*signals
;
1820 TileIndex other_end
;
1821 if (IsRailwayTile(tile
)) {
1822 if (!ValParamTrackOrientation(track
) || !HasTrack(tile
, track
)) {
1823 return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
1826 signals
= maptile_signalpair(tile
, track
);
1827 other_end
= INVALID_TILE
;
1828 } else if (maptile_is_rail_tunnel(tile
)) {
1829 if (track
!= DiagDirToDiagTrack(GetTunnelBridgeDirection(tile
))) {
1830 return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
1833 signals
= maptile_tunnel_signalpair(tile
);
1834 other_end
= GetOtherTunnelEnd(tile
);
1836 return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK
);
1839 if (!signalpair_has_signals(signals
)) {
1840 return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS
);
1843 /* Only water can remove signals from anyone */
1844 if (_current_company
!= OWNER_WATER
) {
1845 CommandCost ret
= CheckTileOwnership(tile
);
1846 if (ret
.Failed()) return ret
;
1849 if (other_end
!= INVALID_TILE
) {
1850 /* prevent updating signals in a busy tunnel */
1851 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
1852 if (ret
.Failed()) return ret
;
1853 ret
= EnsureNoVehicleOnGround(other_end
);
1854 if (ret
.Failed()) return ret
;
1856 if (signalpair_has_signal(signals
, true)) {
1857 /* We can remove a signal into a tunnel without
1858 * also removing the other signal. */
1859 assert(!signalpair_has_signal(signals
, false));
1860 assert(maptile_get_tunnel_present_signals(other_end
) == 1);
1861 other_end
= INVALID_TILE
;
1866 if (flags
& DC_EXEC
) {
1868 if (HasReservedTrack(tile
, track
)) {
1869 v
= GetTrainForReservation(tile
, track
);
1870 } else if (other_end
!= INVALID_TILE
&& HasTunnelHeadReservation(other_end
)) {
1871 v
= GetTrainForReservation(other_end
, track
);
1872 } else if (other_end
== INVALID_TILE
&& IsPbsSignal(signalpair_get_type(signals
))) {
1873 /* PBS signal, might be the end of a path reservation.
1875 * We do not allow removal of signals in busy tunnels,
1877 * - If this tile has a path signal and the other end
1878 * has a (normal) signal, then this tile is not the
1879 * end of a reservation, because there is no train in
1881 * - If this tile has a path signal and the other end
1882 * does not have any signals, a train with this signal
1883 * as reservation end would have been caught by the
1884 * previous check (would own the reservation for the
1886 * - If this tile has a non-path signal and the other
1887 * end has a path signal, the other end is not the end
1888 * of a reservation, because there is no train in the
1891 Trackdir td
= TrackToTrackdir(track
);
1892 for (int i
= 0; v
== NULL
&& i
< 2; i
++, td
= ReverseTrackdir(td
)) {
1893 /* Only test the active signal side. */
1894 if (!HasSignalOnTrackdir(tile
, ReverseTrackdir(td
))) continue;
1895 TileIndex next
= TileAddByDiagDir(tile
, TrackdirToExitdir(td
));
1896 TrackBits tracks
= TrackdirBitsToTrackBits(TrackdirReachesTrackdirs(td
));
1897 if (HasReservedTracks(next
, tracks
)) {
1898 v
= GetTrainForReservation(next
, TrackBitsToTrack(GetReservedTrackbits(next
) & tracks
));
1903 Owner owner
= GetTileOwner(tile
);
1904 Company::Get(owner
)->infrastructure
.signal
-= CountBits(signalpair_get_present(signals
)) + (other_end
!= INVALID_TILE
? 1 : 0);
1905 DirtyCompanyInfrastructureWindows(owner
);
1907 signalpair_clear(signals
);
1908 AddTrackToSignalBuffer(tile
, track
, owner
);
1909 YapfNotifyTrackLayoutChange(tile
, track
);
1911 if (other_end
!= INVALID_TILE
) {
1912 maptile_clear_tunnel_signals(other_end
);
1913 AddTrackToSignalBuffer(other_end
, track
, owner
);
1914 YapfNotifyTrackLayoutChange(other_end
, track
);
1917 if (v
!= NULL
) TryPathReserve(v
, false);
1919 MarkTileDirtyByTile(tile
);
1920 if (other_end
!= INVALID_TILE
) MarkTileDirtyByTile(other_end
);
1923 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_SIGNALS
] * (other_end
!= INVALID_TILE
? 2 : 1));
1927 * Remove signals on a stretch of track.
1928 * Stub for the unified signal builder/remover
1929 * @param tile start tile of drag
1930 * @param flags operation to perform
1931 * @param p1 end tile of drag
1932 * @param p2 various bitstuffed elements
1933 * - p2 = (bit 0- 2) - track-orientation, valid values: 0-5 (Track enum)
1934 * - p2 = (bit 3) - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
1935 * - p2 = (bit 4) - 0 = signals, 1 = semaphores
1936 * - p2 = (bit 5) - 0 = build, 1 = remove signals
1937 * - p2 = (bit 6) - 0 = selected stretch, 1 = auto fill
1938 * - p2 = (bit 7- 9) - default signal type
1939 * - p2 = (bit 24-31) - user defined signals_density
1940 * @param text unused
1941 * @return the cost of this operation or an error
1942 * @see CmdSignalTrackHelper
1944 CommandCost
CmdRemoveSignalTrack(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
1946 return CmdSignalTrackHelper(tile
, flags
, p1
, SetBit(p2
, 5), text
); // bit 5 is remove bit
1949 /** Update power of all trains on a tile under which railtype is converted. */
1950 static void UpdateTrainPower (TileIndex tile
, TrainList
*affected
)
1952 VehicleTileIterator
iter (tile
);
1953 while (!iter
.finished()) {
1954 Vehicle
*v
= iter
.next();
1956 if (v
->type
!= VEH_TRAIN
) continue;
1958 affected
->Include(Train::From(v
)->First());
1962 /** Check if the given tile track is reserved by a train which will be unpowered on the given railtype.
1963 * If it is, remove its reservation and return it. Otherwise, return NULL. */
1964 static inline Train
*FindUnpoweredReservationTrain(TileIndex tile
, Track track
, RailType rt
)
1966 Train
*v
= GetTrainForReservation(tile
, track
);
1967 if (v
== NULL
|| HasPowerOnRail(v
->railtype
, rt
)) return NULL
;
1968 /* No power on new rail type, reroute. */
1969 FreeTrainTrackReservation(v
);
1973 template <typename T
>
1974 static inline void FindUnpoweredReservationTrains(T
*vector
, TileIndex tile
, RailType rt
)
1976 TrackBits reserved
= GetReservedTrackbits(tile
);
1978 while ((track
= RemoveFirstTrack(&reserved
)) != INVALID_TRACK
) {
1979 Train
*v
= FindUnpoweredReservationTrain(tile
, track
, rt
);
1980 if (v
!= NULL
) *vector
->Append() = v
;
1984 /** Check rail tile conversion */
1985 static CommandCost
CheckRailConversion(TileIndex tile
, RailType totype
)
1987 assert(IsRailwayTile(tile
));
1989 /* Trying to convert other's rail */
1990 CommandCost ret
= CheckTileOwnership(tile
);
1991 if (ret
.Failed()) return ret
;
1993 bool ignore_electric
= _settings_game
.vehicle
.disable_elrails
&& totype
== RAILTYPE_RAIL
;
1995 TrackBits trackbits
= GetTrackBits(tile
);
1996 CommandCost
cost(EXPENSES_CONSTRUCTION
);
1998 RailType type
= GetRailType(tile
, TRACK_UPPER
);
2000 switch (trackbits
) {
2001 case TRACK_BIT_HORZ
:
2002 case TRACK_BIT_VERT
: {
2003 RailType type2
= GetRailType(tile
, TRACK_LOWER
);
2004 if (type
!= type2
) {
2005 bool ignore1
= type
== totype
|| (ignore_electric
&& type
== RAILTYPE_ELECTRIC
);
2006 bool ignore2
= type2
== totype
|| (ignore_electric
&& type2
== RAILTYPE_ELECTRIC
);
2007 if (ignore1
&& ignore2
) return CommandCost();
2009 Track track
= (trackbits
== TRACK_BIT_HORZ
) ? TRACK_UPPER
: TRACK_LEFT
;
2010 if (!ignore1
&& !IsCompatibleRail(type
, totype
)) {
2011 CommandCost ret
= EnsureNoTrainOnTrack(tile
, track
);
2012 if (ret
.Failed()) return ret
;
2015 if (!ignore2
&& !IsCompatibleRail(type2
, totype
)) {
2016 CommandCost ret
= EnsureNoTrainOnTrack(tile
, TrackToOppositeTrack(track
));
2017 if (ret
.Failed()) return ret
;
2020 cost
.AddCost(RailConvertCost(type
, totype
));
2021 cost
.AddCost(RailConvertCost(type2
, totype
));
2026 case TRACK_BIT_RIGHT
:
2027 case TRACK_BIT_LOWER
:
2028 case TRACK_BIT_LOWER_RIGHT
:
2029 type
= GetRailType(tile
, TRACK_LOWER
);
2032 /* Converting to the same type or converting 'hidden' elrail -> rail */
2033 if (type
== totype
|| (ignore_electric
&& type
== RAILTYPE_ELECTRIC
)) return CommandCost();
2035 if (!IsCompatibleRail(type
, totype
)) {
2036 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
2037 if (ret
.Failed()) return ret
;
2040 cost
.AddCost(RailConvertCost(type
, totype
) * CountBits(trackbits
));
2048 * Convert one rail type to another, for normal rail tiles
2049 * @param tile tile to convert
2050 * @param totype new railtype to convert to
2051 * @param affected list of affected trains
2052 * @param flags operation to perform
2053 * @return the cost of this operation or an error
2055 static CommandCost
ConvertTrack(TileIndex tile
, RailType totype
, TrainList
*affected
, DoCommandFlag flags
)
2057 CommandCost ret
= CheckRailConversion(tile
, totype
);
2058 if (ret
.Failed()) return ret
;
2060 if (flags
& DC_EXEC
) { // we can safely convert, too
2061 SmallVector
<Train
*, 2> vehicles_affected
;
2062 FindUnpoweredReservationTrains(&vehicles_affected
, tile
, totype
);
2064 /* Update the company infrastructure counters. */
2065 Company
*c
= Company::Get(GetTileOwner(tile
));
2068 TrackBits bits
= GetTrackBits(tile
);
2070 case TRACK_BIT_HORZ
:
2071 case TRACK_BIT_VERT
:
2073 c
->infrastructure
.rail
[GetRailType(tile
, TRACK_UPPER
)]--;
2074 c
->infrastructure
.rail
[GetRailType(tile
, TRACK_LOWER
)]--;
2077 case TRACK_BIT_RIGHT
:
2078 case TRACK_BIT_LOWER
:
2080 c
->infrastructure
.rail
[GetRailType(tile
, TRACK_LOWER
)]--;
2083 case TRACK_BIT_LOWER_RIGHT
:
2085 c
->infrastructure
.rail
[GetRailType(tile
, TRACK_LOWER
)] -= 2 * 2;
2089 num_pieces
= CountBits(bits
);
2090 if (TracksOverlap(bits
)) num_pieces
*= num_pieces
;
2091 c
->infrastructure
.rail
[GetRailType(tile
, TRACK_UPPER
)] -= num_pieces
;
2095 c
->infrastructure
.rail
[totype
] += num_pieces
;
2096 DirtyCompanyInfrastructureWindows(c
->index
);
2098 SetRailType(tile
, totype
);
2099 MarkTileDirtyByTile(tile
);
2100 /* update power of train on this tile */
2101 UpdateTrainPower (tile
, affected
);
2103 /* notify YAPF about the track layout change */
2104 TrackBits trackbits
= GetTrackBits(tile
);
2105 while (trackbits
!= TRACK_BIT_NONE
) {
2106 YapfNotifyTrackLayoutChange(tile
, RemoveFirstTrack(&trackbits
));
2109 for (uint i
= 0; i
< vehicles_affected
.Length(); ++i
) {
2110 TryPathReserve(vehicles_affected
[i
], true);
2118 * Convert one rail type to another, for bridge tiles
2119 * @param tile tile to convert
2120 * @param endtile bridge end
2121 * @param totype new railtype to convert to
2122 * @param affected list of affected trains
2123 * @param flags operation to perform
2124 * @return the cost of this operation or an error
2126 static CommandCost
ConvertBridge(TileIndex tile
, TileIndex endtile
, RailType totype
, TrainList
*affected
, DoCommandFlag flags
)
2128 CommandCost cost
= CheckRailConversion(tile
, totype
);
2129 if (cost
.Failed()) return cost
;
2131 CommandCost ret
= CheckRailConversion(endtile
, totype
);
2132 if (ret
.Failed()) return ret
;
2135 /* Original railtype we are converting from */
2136 RailType type
= GetBridgeRailType(tile
);
2138 /* Converting to the same type or converting 'hidden' elrail -> rail */
2139 if (type
== totype
) return cost
;
2140 if (_settings_game
.vehicle
.disable_elrails
&& totype
== RAILTYPE_RAIL
&& type
== RAILTYPE_ELECTRIC
) return cost
;
2142 /* When not converting rail <-> el. rail, no vehicle can be in the bridge */
2143 if (!IsCompatibleRail(type
, totype
)) {
2144 CommandCost ret
= EnsureNoTrainOnTunnelBridgeMiddle(tile
, endtile
);
2145 if (ret
.Failed()) return ret
;
2148 uint len
= GetTunnelBridgeLength(tile
, endtile
);
2149 cost
.AddCost(len
* RailConvertCost(type
, totype
));
2151 if (flags
& DC_EXEC
) {
2152 SmallVector
<Train
*, 4> vehicles_affected
;
2153 FindUnpoweredReservationTrains(&vehicles_affected
, tile
, totype
);
2154 FindUnpoweredReservationTrains(&vehicles_affected
, endtile
, totype
);
2156 /* Update the company infrastructure counters. */
2157 Company
*c
= Company::Get(GetTileOwner(tile
));
2158 uint num_pieces
= len
;
2159 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
2161 TrackBits bits
= GetTrackBits(tile
);
2162 if (bits
== TRACK_BIT_HORZ
|| bits
== TRACK_BIT_VERT
) {
2163 c
->infrastructure
.rail
[GetSideRailType(tile
, ReverseDiagDir(dir
))]--;
2164 c
->infrastructure
.rail
[totype
]++;
2167 uint n
= CountBits(bits
);
2168 num_pieces
+= n
* n
;
2171 bits
= GetTrackBits(endtile
);
2172 if (bits
== TRACK_BIT_HORZ
|| bits
== TRACK_BIT_VERT
) {
2173 c
->infrastructure
.rail
[GetSideRailType(tile
, dir
)]--;
2174 c
->infrastructure
.rail
[totype
]++;
2177 uint n
= CountBits(bits
);
2178 num_pieces
+= n
* n
;
2181 num_pieces
*= TUNNELBRIDGE_TRACKBIT_FACTOR
;
2182 c
->infrastructure
.rail
[type
] -= num_pieces
;
2183 c
->infrastructure
.rail
[totype
] += num_pieces
;
2184 DirtyCompanyInfrastructureWindows(c
->index
);
2186 SetRailType(tile
, totype
);
2187 SetRailType(endtile
, totype
);
2189 UpdateTrainPower (tile
, affected
);
2190 UpdateTrainPower (endtile
, affected
);
2192 /* notify YAPF about the track layout change */
2193 TrackBits trackbits
= GetTrackBits(tile
);
2194 while (trackbits
!= TRACK_BIT_NONE
) {
2195 YapfNotifyTrackLayoutChange(tile
, RemoveFirstTrack(&trackbits
));
2197 trackbits
= GetTrackBits(endtile
);
2198 while (trackbits
!= TRACK_BIT_NONE
) {
2199 YapfNotifyTrackLayoutChange(tile
, RemoveFirstTrack(&trackbits
));
2202 MarkBridgeTilesDirty(tile
, endtile
, dir
);
2204 for (uint i
= 0; i
< vehicles_affected
.Length(); ++i
) {
2205 TryPathReserve(vehicles_affected
[i
], true);
2213 * Convert one rail type to another, for tunnel tiles
2214 * @param tile tile to convert
2215 * @param endtile tunnel end
2216 * @param totype new railtype to convert to
2217 * @param affected list of affected trains
2218 * @param flags operation to perform
2219 * @return the cost of this operation or an error
2221 static CommandCost
ConvertTunnel(TileIndex tile
, TileIndex endtile
, RailType totype
, TrainList
*affected
, DoCommandFlag flags
)
2223 /* Trying to convert other's rail */
2224 CommandCost ret
= CheckTileOwnership(tile
);
2225 if (ret
.Failed()) return ret
;
2227 /* Original railtype we are converting from */
2228 RailType type
= GetRailType(tile
);
2230 /* Converting to the same type or converting 'hidden' elrail -> rail */
2231 if (type
== totype
) return CommandCost();
2232 if (_settings_game
.vehicle
.disable_elrails
&& totype
== RAILTYPE_RAIL
&& type
== RAILTYPE_ELECTRIC
) return CommandCost();
2234 /* When not converting rail <-> el. rail, no vehicle can be in the tunnel */
2235 if (!IsCompatibleRail(type
, totype
)) {
2236 CommandCost ret
= TunnelBridgeIsFree(tile
, endtile
);
2237 if (ret
.Failed()) return ret
;
2240 uint len
= GetTunnelBridgeLength(tile
, endtile
) + 2;
2242 if (flags
& DC_EXEC
) {
2243 Track track
= DiagDirToDiagTrack(GetTunnelBridgeDirection(tile
));
2246 if (HasTunnelHeadReservation(tile
)) {
2247 v
= FindUnpoweredReservationTrain(tile
, track
, totype
);
2251 if (HasTunnelHeadReservation(endtile
)) {
2252 w
= FindUnpoweredReservationTrain(endtile
, track
, totype
);
2255 /* Update the company infrastructure counters. */
2256 uint num_pieces
= len
* TUNNELBRIDGE_TRACKBIT_FACTOR
;
2257 Company
*c
= Company::Get(GetTileOwner(tile
));
2258 c
->infrastructure
.rail
[type
] -= num_pieces
;
2259 c
->infrastructure
.rail
[totype
] += num_pieces
;
2260 DirtyCompanyInfrastructureWindows(c
->index
);
2262 SetRailType(tile
, totype
);
2263 SetRailType(endtile
, totype
);
2265 UpdateTrainPower (tile
, affected
);
2266 UpdateTrainPower (endtile
, affected
);
2268 YapfNotifyTrackLayoutChange(tile
, track
);
2269 YapfNotifyTrackLayoutChange(endtile
, track
);
2271 MarkTileDirtyByTile(tile
);
2272 MarkTileDirtyByTile(endtile
);
2274 if (v
!= NULL
) TryPathReserve(v
, true);
2275 if (w
!= NULL
) TryPathReserve(w
, true);
2278 return CommandCost(EXPENSES_CONSTRUCTION
, len
* RailConvertCost(type
, totype
));
2282 * Convert one rail type to another, generic version
2283 * @param tile tile to convert
2284 * @param totype new railtype to convert to
2285 * @param track present track in the tile
2286 * @param reserved whether the track is reserved
2287 * @param affected list of affected trains
2288 * @param flags operation to perform
2289 * @return the cost of this operation or an error
2291 static CommandCost
ConvertGeneric(TileIndex tile
, RailType totype
, Track track
, bool reserved
, TrainList
*affected
, DoCommandFlag flags
)
2293 /* Trying to convert other's rail */
2294 CommandCost ret
= CheckTileOwnership(tile
);
2295 if (ret
.Failed()) return ret
;
2297 /* Original railtype we are converting from */
2298 RailType type
= GetRailType(tile
);
2300 /* Converting to the same type or converting 'hidden' elrail -> rail */
2301 if (type
== totype
) return CommandCost();
2302 if (_settings_game
.vehicle
.disable_elrails
&& totype
== RAILTYPE_RAIL
&& type
== RAILTYPE_ELECTRIC
) return CommandCost();
2304 if (!IsCompatibleRail(type
, totype
)) {
2305 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
2306 if (ret
.Failed()) return ret
;
2309 if (flags
& DC_EXEC
) { // we can safely convert, too
2311 if (reserved
) v
= FindUnpoweredReservationTrain(tile
, track
, totype
);
2313 /* Update the company infrastructure counters. */
2314 if (!IsRailStationTile(tile
) || !IsStationTileBlocked(tile
)) {
2315 Company
*c
= Company::Get(GetTileOwner(tile
));
2316 uint num_pieces
= IsLevelCrossingTile(tile
) ? LEVELCROSSING_TRACKBIT_FACTOR
: 1;
2317 c
->infrastructure
.rail
[type
] -= num_pieces
;
2318 c
->infrastructure
.rail
[totype
] += num_pieces
;
2319 DirtyCompanyInfrastructureWindows(c
->index
);
2322 SetRailType(tile
, totype
);
2323 MarkTileDirtyByTile(tile
);
2324 /* update power of train on this tile */
2325 UpdateTrainPower (tile
, affected
);
2327 /* notify YAPF about the track layout change */
2328 YapfNotifyTrackLayoutChange(tile
, track
);
2330 if (v
!= NULL
) TryPathReserve(v
, true);
2333 return CommandCost(EXPENSES_CONSTRUCTION
, RailConvertCost(type
, totype
));
2337 * Convert one rail type to the other. You can convert normal rail to
2338 * monorail/maglev easily or vice-versa.
2339 * @param tile end tile of rail conversion drag
2340 * @param flags operation to perform
2341 * @param p1 start tile of drag
2342 * @param p2 various bitstuffed elements:
2343 * - p2 = (bit 0- 3) new railtype to convert to.
2344 * - p2 = (bit 4) build diagonally or not.
2345 * @param text unused
2346 * @return the cost of this operation or an error
2348 CommandCost
CmdConvertRail(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
2350 RailType totype
= Extract
<RailType
, 0, 4>(p2
);
2352 if (!ValParamRailtype(totype
)) return CMD_ERROR
;
2353 if (p1
>= MapSize()) return CMD_ERROR
;
2355 TrainList affected_trains
;
2357 CommandCost
cost(EXPENSES_CONSTRUCTION
);
2358 CommandCost err
= CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK
); // by default, there is no track to convert.
2359 TileArea
ta(tile
, p1
);
2360 TileIterator
*iter
= HasBit(p2
, 4) ? (TileIterator
*)new DiagonalTileIterator(tile
, p1
) : new OrthogonalTileIterator(ta
);
2361 for (; (tile
= *iter
) != INVALID_TILE
; ++(*iter
)) {
2364 Track track
= INVALID_TRACK
;
2367 /* Check if there is any track on tile */
2368 switch (GetTileType(tile
)) {
2370 if (IsTileSubtype(tile
, TT_TRACK
)) {
2371 ret
= ConvertTrack(tile
, totype
, &affected_trains
, flags
);
2373 /* If both ends of bridge are in the range, do not try to convert twice -
2374 * it would cause assert because of different test and exec runs */
2375 TileIndex endtile
= GetOtherBridgeEnd(tile
);
2376 if (endtile
< tile
&& TileX(endtile
) >= TileX(ta
.tile
) && TileX(endtile
) < TileX(ta
.tile
) + ta
.w
&&
2377 TileY(endtile
) >= TileY(ta
.tile
) && TileY(endtile
) < TileY(ta
.tile
) + ta
.h
) continue;
2379 ret
= ConvertBridge(tile
, endtile
, totype
, &affected_trains
, flags
);
2384 switch (GetTileSubtype(tile
)) {
2387 case TT_MISC_CROSSING
:
2388 if (RailNoLevelCrossings(totype
)) {
2389 err
.MakeError(STR_ERROR_CROSSING_DISALLOWED
);
2392 track
= GetCrossingRailTrack(tile
);
2393 reserved
= HasCrossingReservation(tile
);
2396 case TT_MISC_TUNNEL
: {
2397 if (GetTunnelTransportType(tile
) != TRANSPORT_RAIL
) continue;
2399 /* If both ends of tunnel are in the range, do not try to convert twice -
2400 * it would cause assert because of different test and exec runs */
2401 TileIndex endtile
= GetOtherTunnelEnd(tile
);
2402 if (endtile
< tile
&& TileX(endtile
) >= TileX(ta
.tile
) && TileX(endtile
) < TileX(ta
.tile
) + ta
.w
&&
2403 TileY(endtile
) >= TileY(ta
.tile
) && TileY(endtile
) < TileY(ta
.tile
) + ta
.h
) continue;
2405 ret
= ConvertTunnel(tile
, endtile
, totype
, &affected_trains
, flags
);
2410 if (!IsRailDepot(tile
)) continue;
2411 track
= GetRailDepotTrack(tile
);
2412 reserved
= HasDepotReservation(tile
);
2418 if (!HasStationRail(tile
)) continue;
2419 track
= GetRailStationTrack(tile
);
2420 reserved
= HasStationReservation(tile
);
2426 if (track
!= INVALID_TRACK
) {
2427 ret
= ConvertGeneric(tile
, totype
, track
, reserved
, &affected_trains
, flags
);
2435 if (IsRailDepotTile(tile
) && (flags
& DC_EXEC
)) {
2436 /* Update build vehicle window related to this depot */
2437 InvalidateWindowData(WC_VEHICLE_DEPOT
, tile
);
2438 InvalidateWindowData(WC_BUILD_VEHICLE
, tile
);
2443 if (flags
& DC_EXEC
) {
2444 /* Railtype changed, update trains as when entering different track */
2445 for (Train
**v
= affected_trains
.Begin(); v
!= affected_trains
.End(); v
++) {
2446 (*v
)->ConsistChanged(true);
2451 return (cost
.GetCost() == 0) ? err
: cost
;
2454 static CommandCost
ClearTile_Track(TileIndex tile
, DoCommandFlag flags
)
2456 if (flags
& DC_AUTO
) {
2457 if (!IsTileOwner(tile
, _current_company
)) {
2458 return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER
);
2459 } else if (IsTileSubtype(tile
, TT_BRIDGE
)) {
2460 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
2462 return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
2466 if (IsTileSubtype(tile
, TT_TRACK
)) {
2467 CommandCost
cost(EXPENSES_CONSTRUCTION
);
2469 Slope tileh
= GetTileSlope(tile
);
2470 /* Is there flat water on the lower halftile that gets cleared expensively? */
2471 bool water_ground
= (GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(tileh
));
2473 TrackBits tracks
= GetTrackBits(tile
);
2474 while (tracks
!= TRACK_BIT_NONE
) {
2475 Track track
= RemoveFirstTrack(&tracks
);
2476 CommandCost ret
= DoCommand(tile
, 0, track
, flags
, CMD_REMOVE_SINGLE_RAIL
);
2477 if (ret
.Failed()) return ret
;
2481 /* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
2482 * Same holds for non-companies clearing the tile, e.g. disasters. */
2483 if (water_ground
&& !(flags
& DC_BANKRUPT
) && Company::IsValidID(_current_company
)) {
2484 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
2485 if (ret
.Failed()) return ret
;
2487 /* The track was removed, and left a coast tile. Now also clear the water. */
2488 if (flags
& DC_EXEC
) DoClearSquare(tile
);
2489 cost
.AddCost(_price
[PR_CLEAR_WATER
]);
2494 if (_current_company
!= OWNER_WATER
&& _game_mode
!= GM_EDITOR
) {
2495 CommandCost ret
= CheckOwnership(GetTileOwner(tile
));
2496 if (ret
.Failed()) return ret
;
2499 TrackBits present
= GetTrackBits(tile
);
2501 if ((present
== TRACK_BIT_HORZ
) || (present
== TRACK_BIT_VERT
)) {
2502 Track track
= FindFirstTrack(DiagdirReachesTracks(GetTunnelBridgeDirection(tile
)) & present
);
2504 CommandCost cost
= DoCommand(tile
, 0, track
, flags
, CMD_REMOVE_SINGLE_RAIL
);
2505 if (cost
.Failed()) return cost
;
2507 CommandCost ret
= RemoveBridgeTrack(tile
, TrackToOppositeTrack(track
), flags
);
2508 if (ret
.Failed()) return ret
;
2514 TileIndex other_tile
= GetOtherBridgeEnd(tile
);
2515 TrackBits other_remove
= GetTrackBits(other_tile
) & DiagdirReachesTracks(GetTunnelBridgeDirection(tile
));
2517 assert(other_remove
!= TRACK_BIT_NONE
);
2519 CommandCost ret
= EnsureNoTrainOnBridgeTrackBits(tile
, present
, other_tile
, other_remove
);
2520 if (ret
.Failed()) return ret
;
2522 uint len
= GetTunnelBridgeLength(tile
, other_tile
) + 2; // Don't forget the end tiles.
2524 CommandCost
cost(EXPENSES_CONSTRUCTION
, len
* _price
[PR_CLEAR_BRIDGE
]);
2525 cost
.AddCost((CountBits(present
) - 1) * RailClearCost(GetBridgeRailType(tile
)));
2527 /* Charge extra to remove signals on the track, if any */
2528 if (HasSignalOnTrack(tile
, FindFirstTrack(present
))) {
2529 cost
.AddCost(DoCommand(tile
, FindFirstTrack(present
), 0, flags
, CMD_REMOVE_SIGNALS
));
2532 int n
= CountBits(other_remove
);
2534 Track other_track
= FindFirstTrack(other_remove
);
2535 if (HasSignalOnTrack(other_tile
, other_track
)) {
2536 cost
.AddCost(DoCommand(other_tile
, other_track
, 0, flags
, CMD_REMOVE_SIGNALS
));
2539 cost
.AddCost((n
- 1) * RailClearCost(GetBridgeRailType(other_tile
)));
2542 if (flags
& DC_EXEC
) {
2543 RemoveRailBridge(tile
, present
, other_tile
, other_remove
);
2551 static int GetSlopePixelZ_Track(TileIndex tile
, uint x
, uint y
)
2554 Slope tileh
= GetTilePixelSlope(tile
, &z
);
2556 if (IsTileSubtype(tile
, TT_TRACK
)) {
2557 if (tileh
== SLOPE_FLAT
) return z
;
2558 z
+= ApplyPixelFoundationToSlope(GetRailFoundation(tileh
, GetTrackBits(tile
)), &tileh
);
2559 return z
+ GetPartialPixelZ(x
& 0xF, y
& 0xF, tileh
);
2560 } else if (IsExtendedRailBridge(tile
)) {
2561 return z
+ TILE_HEIGHT
;
2566 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
2568 z
+= ApplyPixelFoundationToSlope(GetBridgeFoundation(tileh
, DiagDirToAxis(dir
)), &tileh
);
2570 /* On the bridge ramp? */
2571 uint pos
= (DiagDirToAxis(dir
) == AXIS_X
? y
: x
);
2572 if (5 <= pos
&& pos
<= 10) {
2573 return z
+ ((tileh
== SLOPE_FLAT
) ? GetBridgePartialPixelZ(dir
, x
, y
) : TILE_HEIGHT
);
2576 return z
+ GetPartialPixelZ(x
, y
, tileh
);
2581 static uint32 _drawtile_track_palette
;
2583 static void DrawTrackFence_NW(const TileInfo
*ti
, SpriteID base_image
)
2585 RailFenceOffset rfo
= RFO_FLAT_X
;
2586 if (ti
->tileh
& SLOPE_NW
) rfo
= (ti
->tileh
& SLOPE_W
) ? RFO_SLOPE_SW
: RFO_SLOPE_NE
;
2587 AddSortableSpriteToDraw(base_image
+ rfo
, _drawtile_track_palette
,
2588 ti
->x
, ti
->y
+ 1, 16, 1, 4, ti
->z
);
2591 static void DrawTrackFence_SE(const TileInfo
*ti
, SpriteID base_image
)
2593 RailFenceOffset rfo
= RFO_FLAT_X
;
2594 if (ti
->tileh
& SLOPE_SE
) rfo
= (ti
->tileh
& SLOPE_S
) ? RFO_SLOPE_SW
: RFO_SLOPE_NE
;
2595 AddSortableSpriteToDraw(base_image
+ rfo
, _drawtile_track_palette
,
2596 ti
->x
, ti
->y
+ TILE_SIZE
- 1, 16, 1, 4, ti
->z
);
2599 static void DrawTrackFence_NW_SE(const TileInfo
*ti
, SpriteID base_image
)
2601 DrawTrackFence_NW(ti
, base_image
);
2602 DrawTrackFence_SE(ti
, base_image
);
2605 static void DrawTrackFence_NE(const TileInfo
*ti
, SpriteID base_image
)
2607 RailFenceOffset rfo
= RFO_FLAT_Y
;
2608 if (ti
->tileh
& SLOPE_NE
) rfo
= (ti
->tileh
& SLOPE_E
) ? RFO_SLOPE_SE
: RFO_SLOPE_NW
;
2609 AddSortableSpriteToDraw(base_image
+ rfo
, _drawtile_track_palette
,
2610 ti
->x
+ 1, ti
->y
, 1, 16, 4, ti
->z
);
2613 static void DrawTrackFence_SW(const TileInfo
*ti
, SpriteID base_image
)
2615 RailFenceOffset rfo
= RFO_FLAT_Y
;
2616 if (ti
->tileh
& SLOPE_SW
) rfo
= (ti
->tileh
& SLOPE_S
) ? RFO_SLOPE_SE
: RFO_SLOPE_NW
;
2617 AddSortableSpriteToDraw(base_image
+ rfo
, _drawtile_track_palette
,
2618 ti
->x
+ TILE_SIZE
- 1, ti
->y
, 1, 16, 4, ti
->z
);
2621 static void DrawTrackFence_NE_SW(const TileInfo
*ti
, SpriteID base_image
)
2623 DrawTrackFence_NE(ti
, base_image
);
2624 DrawTrackFence_SW(ti
, base_image
);
2628 * Draw fence at eastern side of track.
2630 static void DrawTrackFence_NS_1(const TileInfo
*ti
, SpriteID base_image
)
2632 int z
= ti
->z
+ GetSlopePixelZInCorner(RemoveHalftileSlope(ti
->tileh
), CORNER_W
);
2633 AddSortableSpriteToDraw(base_image
+ RFO_FLAT_VERT
, _drawtile_track_palette
,
2634 ti
->x
+ TILE_SIZE
/ 2, ti
->y
+ TILE_SIZE
/ 2, 1, 1, 4, z
);
2638 * Draw fence at western side of track.
2640 static void DrawTrackFence_NS_2(const TileInfo
*ti
, SpriteID base_image
)
2642 int z
= ti
->z
+ GetSlopePixelZInCorner(RemoveHalftileSlope(ti
->tileh
), CORNER_E
);
2643 AddSortableSpriteToDraw(base_image
+ RFO_FLAT_VERT
, _drawtile_track_palette
,
2644 ti
->x
+ TILE_SIZE
/ 2, ti
->y
+ TILE_SIZE
/ 2, 1, 1, 4, z
);
2648 * Draw fence at southern side of track.
2650 static void DrawTrackFence_WE_1(const TileInfo
*ti
, SpriteID base_image
)
2652 int z
= ti
->z
+ GetSlopePixelZInCorner(RemoveHalftileSlope(ti
->tileh
), CORNER_N
);
2653 AddSortableSpriteToDraw(base_image
+ RFO_FLAT_HORZ
, _drawtile_track_palette
,
2654 ti
->x
+ TILE_SIZE
/ 2, ti
->y
+ TILE_SIZE
/ 2, 1, 1, 4, z
);
2658 * Draw fence at northern side of track.
2660 static void DrawTrackFence_WE_2(const TileInfo
*ti
, SpriteID base_image
)
2662 int z
= ti
->z
+ GetSlopePixelZInCorner(RemoveHalftileSlope(ti
->tileh
), CORNER_S
);
2663 AddSortableSpriteToDraw(base_image
+ RFO_FLAT_HORZ
, _drawtile_track_palette
,
2664 ti
->x
+ TILE_SIZE
/ 2, ti
->y
+ TILE_SIZE
/ 2, 1, 1, 4, z
);
2668 static void DrawTrackDetails(const TileInfo
*ti
, TrackBits tracks
)
2670 const RailtypeInfo
*rti
;
2673 case TRACK_BIT_HORZ
:
2674 case TRACK_BIT_VERT
:
2675 return; /* these never have fences */
2677 case TRACK_BIT_LOWER
:
2678 case TRACK_BIT_RIGHT
:
2679 case TRACK_BIT_LOWER_RIGHT
:
2680 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TRACK_LOWER
));
2684 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TRACK_UPPER
));
2688 /* Base sprite for track fences.
2689 * Note: Halftile slopes only have fences on the upper part. */
2690 SpriteID base_image
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_FENCES
, IsHalftileSlope(ti
->tileh
) ? TCX_UPPER_HALFTILE
: TCX_NORMAL
);
2691 if (base_image
== 0) base_image
= SPR_TRACK_FENCE_FLAT_X
;
2693 switch (GetRailGroundType(ti
->tile
)) {
2694 case RAIL_GROUND_FENCE_NW
: DrawTrackFence_NW(ti
, base_image
); break;
2695 case RAIL_GROUND_FENCE_SE
: DrawTrackFence_SE(ti
, base_image
); break;
2696 case RAIL_GROUND_FENCE_SENW
: DrawTrackFence_NW_SE(ti
, base_image
); break;
2697 case RAIL_GROUND_FENCE_NE
: DrawTrackFence_NE(ti
, base_image
); break;
2698 case RAIL_GROUND_FENCE_SW
: DrawTrackFence_SW(ti
, base_image
); break;
2699 case RAIL_GROUND_FENCE_NESW
: DrawTrackFence_NE_SW(ti
, base_image
); break;
2700 case RAIL_GROUND_FENCE_VERT1
: DrawTrackFence_NS_1(ti
, base_image
); break;
2701 case RAIL_GROUND_FENCE_VERT2
: DrawTrackFence_NS_2(ti
, base_image
); break;
2702 case RAIL_GROUND_FENCE_HORIZ1
: DrawTrackFence_WE_1(ti
, base_image
); break;
2703 case RAIL_GROUND_FENCE_HORIZ2
: DrawTrackFence_WE_2(ti
, base_image
); break;
2704 case RAIL_GROUND_WATER
: {
2705 Corner track_corner
;
2706 if (IsHalftileSlope(ti
->tileh
)) {
2707 /* Steep slope or one-corner-raised slope with halftile foundation */
2708 track_corner
= GetHalftileSlopeCorner(ti
->tileh
);
2710 /* Three-corner-raised slope */
2711 track_corner
= OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti
->tileh
)));
2713 switch (track_corner
) {
2714 case CORNER_W
: DrawTrackFence_NS_1(ti
, base_image
); break;
2715 case CORNER_S
: DrawTrackFence_WE_2(ti
, base_image
); break;
2716 case CORNER_E
: DrawTrackFence_NS_2(ti
, base_image
); break;
2717 case CORNER_N
: DrawTrackFence_WE_1(ti
, base_image
); break;
2718 default: NOT_REACHED();
2726 /* SubSprite for drawing track halftiles. */
2727 static const int INF
= 1000; // big number compared to tilesprite size
2728 static const SubSprite _halftile_sub_sprite
[4] = {
2729 { -INF
, -INF
, 32 - 33, INF
}, // CORNER_W, clip 33 pixels from right
2730 { -INF
, 0 + 15, INF
, INF
}, // CORNER_S, clip 15 pixels from top
2731 { -31 + 33, -INF
, INF
, INF
}, // CORNER_E, clip 33 pixels from left
2732 { -INF
, -INF
, INF
, 30 - 15 } // CORNER_N, clip 15 pixels from bottom
2734 static const SubSprite _halftile_sub_sprite_upper
[4] = {
2735 { -INF
, -INF
, 32 - 33, INF
}, // CORNER_W, clip 33 pixels from right
2736 { -INF
, 0 + 7, INF
, INF
}, // CORNER_S, clip 7 pixels from top
2737 { -31 + 33, -INF
, INF
, INF
}, // CORNER_E, clip 33 pixels from left
2738 { -INF
, -INF
, INF
, 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2740 static const byte _corner_to_track_sprite
[] = {3, 1, 2, 0};
2742 static inline void DrawTrackSprite(SpriteID sprite
, PaletteID pal
, const TileInfo
*ti
, Slope s
)
2744 DrawGroundSprite(sprite
, pal
, NULL
, 0, (ti
->tileh
& s
) ? -8 : 0);
2747 static void DrawTrackGround(TileInfo
*ti
, RailGroundType rgt
, bool has_track
)
2749 if (rgt
== RAIL_GROUND_WATER
) {
2750 if (has_track
|| IsSteepSlope(ti
->tileh
)) {
2751 /* three-corner-raised slope or steep slope with track on upper part */
2752 DrawShoreTile(ti
->tileh
);
2754 /* single-corner-raised slope with track on upper part */
2755 DrawGroundSprite(SPR_FLAT_WATER_TILE
, PAL_NONE
);
2761 case RAIL_GROUND_BARREN
: image
= SPR_FLAT_BARE_LAND
; break;
2762 case RAIL_GROUND_ICE_DESERT
: image
= SPR_FLAT_SNOW_DESERT_TILE
; break;
2763 default: image
= SPR_FLAT_GRASS_TILE
; break;
2766 image
+= SlopeToSpriteOffset(ti
->tileh
);
2768 DrawGroundSprite(image
, PAL_NONE
);
2772 static void DrawTrackBitsOverlay(TileInfo
*ti
, TrackBits track
, const RailtypeInfo
*rti
)
2774 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
);
2775 SpriteID ground
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_GROUND
);
2776 TrackBits pbs
= _settings_client
.gui
.show_track_reservation
? GetRailReservationTrackBits(ti
->tile
) : TRACK_BIT_NONE
;
2778 if (track
== TRACK_BIT_NONE
) {
2779 /* Half-tile foundation, no track here? */
2780 } else if (ti
->tileh
== SLOPE_NW
&& track
== TRACK_BIT_Y
) {
2781 DrawGroundSprite(ground
+ RTO_SLOPE_NW
, PAL_NONE
);
2782 if (pbs
!= TRACK_BIT_NONE
) DrawGroundSprite(overlay
+ 9, PALETTE_CRASH
);
2783 } else if (ti
->tileh
== SLOPE_NE
&& track
== TRACK_BIT_X
) {
2784 DrawGroundSprite(ground
+ RTO_SLOPE_NE
, PAL_NONE
);
2785 if (pbs
!= TRACK_BIT_NONE
) DrawGroundSprite(overlay
+ 6, PALETTE_CRASH
);
2786 } else if (ti
->tileh
== SLOPE_SE
&& track
== TRACK_BIT_Y
) {
2787 DrawGroundSprite(ground
+ RTO_SLOPE_SE
, PAL_NONE
);
2788 if (pbs
!= TRACK_BIT_NONE
) DrawGroundSprite(overlay
+ 7, PALETTE_CRASH
);
2789 } else if (ti
->tileh
== SLOPE_SW
&& track
== TRACK_BIT_X
) {
2790 DrawGroundSprite(ground
+ RTO_SLOPE_SW
, PAL_NONE
);
2791 if (pbs
!= TRACK_BIT_NONE
) DrawGroundSprite(overlay
+ 8, PALETTE_CRASH
);
2794 /* Draw single ground sprite when not overlapping. No track overlay
2795 * is necessary for these sprites. */
2796 case TRACK_BIT_X
: DrawGroundSprite(ground
+ RTO_X
, PAL_NONE
); break;
2797 case TRACK_BIT_Y
: DrawGroundSprite(ground
+ RTO_Y
, PAL_NONE
); break;
2798 case TRACK_BIT_UPPER
: DrawTrackSprite(ground
+ RTO_N
, PAL_NONE
, ti
, SLOPE_N
); break;
2799 case TRACK_BIT_LOWER
: DrawTrackSprite(ground
+ RTO_S
, PAL_NONE
, ti
, SLOPE_S
); break;
2800 case TRACK_BIT_RIGHT
: DrawTrackSprite(ground
+ RTO_E
, PAL_NONE
, ti
, SLOPE_E
); break;
2801 case TRACK_BIT_LEFT
: DrawTrackSprite(ground
+ RTO_W
, PAL_NONE
, ti
, SLOPE_W
); break;
2802 case TRACK_BIT_CROSS
: DrawGroundSprite(ground
+ RTO_CROSSING_XY
, PAL_NONE
); break;
2803 case TRACK_BIT_HORZ
: DrawTrackSprite(ground
+ RTO_N
, PAL_NONE
, ti
, SLOPE_N
);
2804 DrawTrackSprite(ground
+ RTO_S
, PAL_NONE
, ti
, SLOPE_S
); break;
2805 case TRACK_BIT_VERT
: DrawTrackSprite(ground
+ RTO_E
, PAL_NONE
, ti
, SLOPE_E
);
2806 DrawTrackSprite(ground
+ RTO_W
, PAL_NONE
, ti
, SLOPE_W
); break;
2809 /* We're drawing a junction tile */
2810 if ((track
& TRACK_BIT_3WAY_NE
) == 0) {
2811 DrawGroundSprite(ground
+ RTO_JUNCTION_SW
, PAL_NONE
);
2812 } else if ((track
& TRACK_BIT_3WAY_SW
) == 0) {
2813 DrawGroundSprite(ground
+ RTO_JUNCTION_NE
, PAL_NONE
);
2814 } else if ((track
& TRACK_BIT_3WAY_NW
) == 0) {
2815 DrawGroundSprite(ground
+ RTO_JUNCTION_SE
, PAL_NONE
);
2816 } else if ((track
& TRACK_BIT_3WAY_SE
) == 0) {
2817 DrawGroundSprite(ground
+ RTO_JUNCTION_NW
, PAL_NONE
);
2819 DrawGroundSprite(ground
+ RTO_JUNCTION_NSEW
, PAL_NONE
);
2822 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2825 /* Draw regular track bits */
2826 if (track
& TRACK_BIT_X
) DrawGroundSprite(overlay
+ RTO_X
, PAL_NONE
);
2827 if (track
& TRACK_BIT_Y
) DrawGroundSprite(overlay
+ RTO_Y
, PAL_NONE
);
2828 if (track
& TRACK_BIT_UPPER
) DrawGroundSprite(overlay
+ RTO_N
, PAL_NONE
);
2829 if (track
& TRACK_BIT_LOWER
) DrawGroundSprite(overlay
+ RTO_S
, PAL_NONE
);
2830 if (track
& TRACK_BIT_RIGHT
) DrawGroundSprite(overlay
+ RTO_E
, PAL_NONE
);
2831 if (track
& TRACK_BIT_LEFT
) DrawGroundSprite(overlay
+ RTO_W
, PAL_NONE
);
2834 /* Draw reserved track bits */
2835 if (pbs
& TRACK_BIT_X
) DrawGroundSprite(overlay
+ RTO_X
, PALETTE_CRASH
);
2836 if (pbs
& TRACK_BIT_Y
) DrawGroundSprite(overlay
+ RTO_Y
, PALETTE_CRASH
);
2837 if (pbs
& TRACK_BIT_UPPER
) DrawTrackSprite(overlay
+ RTO_N
, PALETTE_CRASH
, ti
, SLOPE_N
);
2838 if (pbs
& TRACK_BIT_LOWER
) DrawTrackSprite(overlay
+ RTO_S
, PALETTE_CRASH
, ti
, SLOPE_S
);
2839 if (pbs
& TRACK_BIT_RIGHT
) DrawTrackSprite(overlay
+ RTO_E
, PALETTE_CRASH
, ti
, SLOPE_E
);
2840 if (pbs
& TRACK_BIT_LEFT
) DrawTrackSprite(overlay
+ RTO_W
, PALETTE_CRASH
, ti
, SLOPE_W
);
2844 static void DrawTrackBitsNonOverlay(TileInfo
*ti
, TrackBits track
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2847 PaletteID pal
= PAL_NONE
;
2848 const SubSprite
*sub
= NULL
;
2849 bool junction
= false;
2851 if (track
== TRACK_BIT_NONE
) return;
2853 if (ti
->tileh
!= SLOPE_FLAT
) {
2854 /* track on non-flat ground */
2855 image
= _track_sloped_sprites
[ti
->tileh
- 1] + rti
->base_sprites
.track_y
;
2857 /* track on flat ground */
2858 (image
= rti
->base_sprites
.track_y
, track
== TRACK_BIT_Y
) ||
2859 (image
++, track
== TRACK_BIT_X
) ||
2860 (image
++, track
== TRACK_BIT_UPPER
) ||
2861 (image
++, track
== TRACK_BIT_LOWER
) ||
2862 (image
++, track
== TRACK_BIT_RIGHT
) ||
2863 (image
++, track
== TRACK_BIT_LEFT
) ||
2864 (image
++, track
== TRACK_BIT_CROSS
) ||
2866 (image
= rti
->base_sprites
.track_ns
, track
== TRACK_BIT_HORZ
) ||
2867 (image
++, track
== TRACK_BIT_VERT
) ||
2869 (junction
= true, false) ||
2870 (image
= rti
->base_sprites
.ground
, (track
& TRACK_BIT_3WAY_NE
) == 0) ||
2871 (image
++, (track
& TRACK_BIT_3WAY_SW
) == 0) ||
2872 (image
++, (track
& TRACK_BIT_3WAY_NW
) == 0) ||
2873 (image
++, (track
& TRACK_BIT_3WAY_SE
) == 0) ||
2878 case RAIL_GROUND_BARREN
: pal
= PALETTE_TO_BARE_LAND
; break;
2879 case RAIL_GROUND_ICE_DESERT
: image
+= rti
->snow_offset
; break;
2880 case RAIL_GROUND_WATER
: {
2881 /* three-corner-raised slope */
2882 DrawShoreTile(ti
->tileh
);
2883 Corner track_corner
= OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti
->tileh
)));
2884 sub
= &_halftile_sub_sprite_upper
[track_corner
];
2890 DrawGroundSprite(image
, pal
, sub
);
2892 /* Draw track pieces individually for junction tiles */
2894 if (track
& TRACK_BIT_X
) DrawGroundSprite(rti
->base_sprites
.single_x
, PAL_NONE
);
2895 if (track
& TRACK_BIT_Y
) DrawGroundSprite(rti
->base_sprites
.single_y
, PAL_NONE
);
2896 if (track
& TRACK_BIT_UPPER
) DrawGroundSprite(rti
->base_sprites
.single_n
, PAL_NONE
);
2897 if (track
& TRACK_BIT_LOWER
) DrawGroundSprite(rti
->base_sprites
.single_s
, PAL_NONE
);
2898 if (track
& TRACK_BIT_LEFT
) DrawGroundSprite(rti
->base_sprites
.single_w
, PAL_NONE
);
2899 if (track
& TRACK_BIT_RIGHT
) DrawGroundSprite(rti
->base_sprites
.single_e
, PAL_NONE
);
2902 /* PBS debugging, draw reserved tracks darker */
2903 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
) {
2904 /* Get reservation, but mask track on halftile slope */
2905 TrackBits pbs
= GetRailReservationTrackBits(ti
->tile
) & track
;
2906 if (pbs
& TRACK_BIT_X
) {
2907 if (ti
->tileh
== SLOPE_FLAT
|| ti
->tileh
== SLOPE_ELEVATED
) {
2908 DrawGroundSprite(rti
->base_sprites
.single_x
, PALETTE_CRASH
);
2910 DrawGroundSprite(_track_sloped_sprites
[ti
->tileh
- 1] + rti
->base_sprites
.single_sloped
- 20, PALETTE_CRASH
);
2913 if (pbs
& TRACK_BIT_Y
) {
2914 if (ti
->tileh
== SLOPE_FLAT
|| ti
->tileh
== SLOPE_ELEVATED
) {
2915 DrawGroundSprite(rti
->base_sprites
.single_y
, PALETTE_CRASH
);
2917 DrawGroundSprite(_track_sloped_sprites
[ti
->tileh
- 1] + rti
->base_sprites
.single_sloped
- 20, PALETTE_CRASH
);
2920 if (pbs
& TRACK_BIT_UPPER
) DrawGroundSprite(rti
->base_sprites
.single_n
, PALETTE_CRASH
, NULL
, 0, ti
->tileh
& SLOPE_N
? -(int)TILE_HEIGHT
: 0);
2921 if (pbs
& TRACK_BIT_LOWER
) DrawGroundSprite(rti
->base_sprites
.single_s
, PALETTE_CRASH
, NULL
, 0, ti
->tileh
& SLOPE_S
? -(int)TILE_HEIGHT
: 0);
2922 if (pbs
& TRACK_BIT_LEFT
) DrawGroundSprite(rti
->base_sprites
.single_w
, PALETTE_CRASH
, NULL
, 0, ti
->tileh
& SLOPE_W
? -(int)TILE_HEIGHT
: 0);
2923 if (pbs
& TRACK_BIT_RIGHT
) DrawGroundSprite(rti
->base_sprites
.single_e
, PALETTE_CRASH
, NULL
, 0, ti
->tileh
& SLOPE_E
? -(int)TILE_HEIGHT
: 0);
2927 static void DrawTrackBits(TileInfo
*ti
, TrackBits track
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2929 if (rti
->UsesOverlay()) {
2930 DrawTrackBitsOverlay(ti
, track
, rti
);
2932 DrawTrackBitsNonOverlay(ti
, track
, rti
, rgt
);
2937 static void DrawHalftileOverlay(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2941 default: NOT_REACHED();
2942 case CORNER_N
: offset
= RTO_N
; break;
2943 case CORNER_S
: offset
= RTO_S
; break;
2944 case CORNER_E
: offset
= RTO_E
; break;
2945 case CORNER_W
: offset
= RTO_W
; break;
2948 DrawGroundSprite(offset
+ GetCustomRailSprite(rti
, ti
->tile
, RTSG_GROUND
), PAL_NONE
, &_halftile_sub_sprite
[corner
]);
2950 if (_settings_client
.gui
.show_track_reservation
&& HasReservedTracks(ti
->tile
, CornerToTrackBits(corner
))) {
2951 DrawGroundSprite(offset
+ GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
), PALETTE_CRASH
, &_halftile_sub_sprite
[corner
]);
2955 static void DrawHalftileNonOverlay(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2957 SpriteID image
= rti
->base_sprites
.track_y
+ 2;
2961 default: NOT_REACHED();
2962 case CORNER_W
: image
++; /* fall through */
2963 case CORNER_E
: image
++; /* fall through */
2964 case CORNER_S
: image
++; /* fall through */
2965 case CORNER_N
: break;
2969 case RAIL_GROUND_BARREN
: pal
= PALETTE_TO_BARE_LAND
; break;
2970 case RAIL_GROUND_ICE_DESERT
: image
+= rti
->snow_offset
; /* fall through */
2971 default: pal
= PAL_NONE
; break;
2974 DrawGroundSprite(image
, pal
, &_halftile_sub_sprite
[corner
]);
2976 /* PBS debugging, draw reserved tracks darker */
2977 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasReservedTracks(ti
->tile
, CornerToTrackBits(corner
))) {
2978 DrawGroundSprite(_corner_to_track_sprite
[corner
] + rti
->base_sprites
.single_n
, PALETTE_CRASH
, NULL
, 0, 0);
2982 static void DrawHalftile(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2984 if (rti
->UsesOverlay()) {
2985 DrawHalftileOverlay(ti
, corner
, rti
, rgt
);
2987 DrawHalftileNonOverlay(ti
, corner
, rti
, rgt
);
2991 static void DrawUpperHalftileOverlay(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2995 case RAIL_GROUND_BARREN
: image
= SPR_FLAT_BARE_LAND
; break;
2996 case RAIL_GROUND_ICE_DESERT
:
2997 case RAIL_GROUND_HALF_SNOW
: image
= SPR_FLAT_SNOW_DESERT_TILE
; break;
2998 default: image
= SPR_FLAT_GRASS_TILE
; break;
3001 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lighting. */
3002 Slope fake_slope
= SlopeWithThreeCornersRaised(OppositeCorner(corner
));
3004 image
+= SlopeToSpriteOffset(fake_slope
);
3006 DrawGroundSprite(image
, PAL_NONE
, &_halftile_sub_sprite_upper
[corner
]);
3008 TrackBits track
= CornerToTrackBits(corner
);
3010 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
, TCX_UPPER_HALFTILE
);
3011 SpriteID ground
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_GROUND
, TCX_UPPER_HALFTILE
);
3015 default: NOT_REACHED();
3016 case TRACK_BIT_UPPER
: offset
= RTO_N
; break;
3017 case TRACK_BIT_LOWER
: offset
= RTO_S
; break;
3018 case TRACK_BIT_RIGHT
: offset
= RTO_E
; break;
3019 case TRACK_BIT_LEFT
: offset
= RTO_W
; break;
3022 DrawTrackSprite(ground
+ offset
, PAL_NONE
, ti
, fake_slope
);
3023 if (_settings_client
.gui
.show_track_reservation
&& HasReservedTracks(ti
->tile
, track
)) {
3024 DrawTrackSprite(overlay
+ offset
, PALETTE_CRASH
, ti
, fake_slope
);
3028 static void DrawUpperHalftileNonOverlay(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
3030 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lighting. */
3031 Slope fake_slope
= SlopeWithThreeCornersRaised(OppositeCorner(corner
));
3032 SpriteID image
= _track_sloped_sprites
[fake_slope
- 1] + rti
->base_sprites
.track_y
;
3033 PaletteID pal
= PAL_NONE
;
3036 case RAIL_GROUND_BARREN
: pal
= PALETTE_TO_BARE_LAND
; break;
3037 case RAIL_GROUND_ICE_DESERT
:
3038 case RAIL_GROUND_HALF_SNOW
: image
+= rti
->snow_offset
; break; // higher part has snow in this case too
3042 DrawGroundSprite(image
, pal
, &_halftile_sub_sprite_upper
[corner
]);
3044 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasReservedTracks(ti
->tile
, CornerToTrackBits(corner
))) {
3045 DrawGroundSprite(_corner_to_track_sprite
[corner
] + rti
->base_sprites
.single_n
, PALETTE_CRASH
, NULL
, 0, -(int)TILE_HEIGHT
);
3049 static void DrawUpperHalftile(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
3051 DrawFoundation(ti
, HalftileFoundation(corner
));
3053 if (rti
->UsesOverlay()) {
3054 DrawUpperHalftileOverlay(ti
, corner
, rti
, rgt
);
3056 DrawUpperHalftileNonOverlay(ti
, corner
, rti
, rgt
);
3061 * Draw ground sprite and track bits
3062 * @param ti TileInfo
3063 * @param track TrackBits to draw
3065 static void DrawTrack(TileInfo
*ti
, TrackBits track
)
3067 RailGroundType rgt
= IsTileSubtype(ti
->tile
, TT_TRACK
) ? GetRailGroundType(ti
->tile
) :
3068 IsOnSnow(ti
->tile
) ? RAIL_GROUND_ICE_DESERT
: RAIL_GROUND_GRASS
;
3069 Foundation f
= IsTileSubtype(ti
->tile
, TT_TRACK
) ? GetRailFoundation(ti
->tileh
, track
) : FOUNDATION_LEVELED
;
3070 Corner halftile_corner
= CORNER_INVALID
;
3072 const RailtypeInfo
*rti
, *halftile_rti
;
3074 if (IsNonContinuousFoundation(f
)) {
3075 /* Save halftile corner */
3076 if (f
== FOUNDATION_STEEP_BOTH
) {
3077 halftile_corner
= GetHighestSlopeCorner(ti
->tileh
);
3078 f
= FOUNDATION_STEEP_LOWER
;
3080 halftile_corner
= GetHalftileFoundationCorner(f
);
3081 f
= FOUNDATION_NONE
;
3083 Track halftile_track
= TrackBitsToTrack(CornerToTrackBits(halftile_corner
));
3084 halftile_rti
= GetRailTypeInfo(GetRailType(ti
->tile
, halftile_track
));
3085 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TrackToOppositeTrack(halftile_track
)));
3086 /* Draw lower part first */
3087 track
&= ~CornerToTrackBits(halftile_corner
);
3088 /* Non-overlay railtypes need ground to be drawn if there is no lower halftile track */
3089 draw_ground
= rti
->UsesOverlay() || track
== TRACK_BIT_NONE
;
3092 case TRACK_BIT_LOWER
:
3093 case TRACK_BIT_RIGHT
:
3094 case TRACK_BIT_LOWER_RIGHT
:
3095 halftile_rti
= NULL
;
3096 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TRACK_LOWER
));
3097 draw_ground
= rti
->UsesOverlay();
3100 case TRACK_BIT_HORZ
:
3101 case TRACK_BIT_VERT
: {
3102 RailType halftile_rt
= GetRailType(ti
->tile
, TRACK_LOWER
);
3103 RailType rt
= GetRailType(ti
->tile
, TRACK_UPPER
);
3104 if (halftile_rt
!= rt
) {
3105 halftile_rti
= GetRailTypeInfo(halftile_rt
);
3106 rti
= GetRailTypeInfo(rt
);
3113 halftile_rti
= NULL
;
3114 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TRACK_UPPER
));
3115 draw_ground
= rti
->UsesOverlay();
3120 DrawFoundation(ti
, f
, IsTileSubtype(ti
->tile
, TT_BRIDGE
) ? GetTunnelBridgeDirection(ti
->tile
) : INVALID_DIAGDIR
);
3121 /* DrawFoundation modifies ti */
3125 DrawTrackGround(ti
, rgt
, track
!= TRACK_BIT_NONE
);
3128 if (IsValidCorner(halftile_corner
) || halftile_rti
== NULL
) {
3129 DrawTrackBits(ti
, track
, rti
, rgt
);
3131 if (IsValidCorner(halftile_corner
)) {
3132 DrawUpperHalftile(ti
, halftile_corner
, halftile_rti
, rgt
);
3134 } else if (track
== TRACK_BIT_HORZ
) {
3135 DrawHalftile(ti
, CORNER_S
, halftile_rti
, rgt
);
3136 DrawHalftile(ti
, CORNER_N
, rti
, rgt
);
3138 DrawHalftile(ti
, CORNER_W
, rti
, rgt
);
3139 DrawHalftile(ti
, CORNER_E
, halftile_rti
, rgt
);
3144 * Get surface height in point (x,y)
3145 * On tiles with halftile foundations move (x,y) to a safe point wrt. track
3147 static uint
GetSafeSlopePixelZ(TileIndex tile
, uint x
, uint y
, Track track
)
3150 case TRACK_UPPER
: x
&= ~0xF; y
&= ~0xF; break;
3151 case TRACK_LOWER
: x
|= 0xF; y
|= 0xF; break;
3152 case TRACK_LEFT
: x
|= 0xF; y
&= ~0xF; break;
3153 case TRACK_RIGHT
: x
&= ~0xF; y
|= 0xF; break;
3157 uint z
= GetSlopePixelZ_Track(tile
, x
, y
);
3159 if (IsTileSubtype(tile
, TT_BRIDGE
) && !IsExtendedRailBridge(tile
)) {
3160 assert(IsDiagonalTrack(track
));
3161 z
+= GetBridgePartialPixelZ(GetTunnelBridgeDirection(tile
), x
& 0xF, y
& 0xF);
3167 static void DrawSingleSignal(TileIndex tile
, Trackdir trackdir
)
3169 static const struct {
3170 Point pos
[2]; // signal position (left side, right side)
3171 SignalOffsets image
; // offset from base signal sprite
3173 { { {11, 3}, {11, 13} }, SIGNAL_TO_NORTHEAST
}, // TRACKDIR_X_NE
3174 { { { 3, 4}, {13, 4} }, SIGNAL_TO_SOUTHEAST
}, // TRACKDIR_Y_SE
3175 { { { 1, 0}, {10, 4} }, SIGNAL_TO_EAST
}, // TRACKDIR_UPPER_E
3176 { { {11, 4}, {14, 14} }, SIGNAL_TO_EAST
}, // TRACKDIR_LOWER_E
3177 { { { 8, 5}, {14, 1} }, SIGNAL_TO_SOUTH
}, // TRACKDIR_LEFT_S
3178 { { { 1, 14}, { 4, 6} }, SIGNAL_TO_SOUTH
}, // TRACKDIR_RIGHT_S
3179 { { { 0, 0}, { 0, 0} }, SIGNAL_TO_NORTHEAST
}, // TRACKDIR_RVREV_NE
3180 { { { 0, 0}, { 0, 0} }, SIGNAL_TO_NORTHEAST
}, // TRACKDIR_RVREV_SE
3181 { { { 4, 13}, { 4, 3} }, SIGNAL_TO_SOUTHWEST
}, // TRACKDIR_X_SW
3182 { { {11, 13}, { 3, 11} }, SIGNAL_TO_NORTHWEST
}, // TRACKDIR_Y_NW
3183 { { { 3, 10}, { 0, 1} }, SIGNAL_TO_WEST
}, // TRACKDIR_UPPER_W
3184 { { {14, 14}, { 5, 12} }, SIGNAL_TO_WEST
}, // TRACKDIR_LOWER_W
3185 { { {14, 1}, {12, 10} }, SIGNAL_TO_NORTH
}, // TRACKDIR_LEFT_N
3186 { { { 9, 11}, { 1, 14} }, SIGNAL_TO_NORTH
}, // TRACKDIR_RIGHT_N
3187 // { { { 0, 0}, { 0, 0} }, SIGNAL_TO_NORTHEAST }, // TRACKDIR_RVREV_SW
3188 // { { { 0, 0}, { 0, 0} }, SIGNAL_TO_NORTHEAST }, // TRACKDIR_RVREV_NW
3191 if (!HasSignalOnTrackdir(tile
, trackdir
)) return;
3193 Track track
= TrackdirToTrack(trackdir
);
3194 SignalType type
= GetSignalType(tile
, track
);
3195 SignalVariant variant
= GetSignalVariant(tile
, track
);
3196 SignalState condition
= GetSignalStateByTrackdir(tile
, trackdir
);
3198 SpriteID sprite
= GetCustomSignalSprite(GetRailTypeInfo(GetRailType(tile
, track
)), tile
, type
, variant
, condition
);
3199 SignalOffsets image
= SignalData
[trackdir
].image
;
3203 /* Normal electric signals are stored in a different sprite block than all other signals. */
3204 sprite
= (type
== SIGTYPE_NORMAL
&& variant
== SIG_ELECTRIC
) ? SPR_ORIGINAL_SIGNALS_BASE
: SPR_SIGNALS_BASE
- 16;
3205 sprite
+= type
* 16 + variant
* 64 + image
* 2 + condition
+ (type
> SIGTYPE_LAST_NOPBS
? 64 : 0);
3209 switch (_settings_game
.construction
.train_signal_side
) {
3210 case 0: side
= false; break; // left
3211 case 2: side
= true; break; // right
3212 default: side
= _settings_game
.vehicle
.road_side
!= 0; break; // driving side
3214 uint x
= TileX(tile
) * TILE_SIZE
+ SignalData
[trackdir
].pos
[side
].x
;
3215 uint y
= TileY(tile
) * TILE_SIZE
+ SignalData
[trackdir
].pos
[side
].y
;
3217 AddSortableSpriteToDraw(sprite
, PAL_NONE
, x
, y
, 1, 1, BB_HEIGHT_UNDER_BRIDGE
, GetSafeSlopePixelZ(tile
, x
, y
, track
));
3220 static void DrawSignals(TileIndex tile
, TrackBits rails
)
3222 if (rails
& TRACK_BIT_Y
) {
3223 DrawSingleSignal(tile
, TRACKDIR_Y_SE
);
3224 DrawSingleSignal(tile
, TRACKDIR_Y_NW
);
3225 } else if (rails
& TRACK_BIT_X
) {
3226 DrawSingleSignal(tile
, TRACKDIR_X_NE
);
3227 DrawSingleSignal(tile
, TRACKDIR_X_SW
);
3229 if (rails
& TRACK_BIT_LEFT
) {
3230 DrawSingleSignal(tile
, TRACKDIR_LEFT_S
);
3231 DrawSingleSignal(tile
, TRACKDIR_LEFT_N
);
3233 if (rails
& TRACK_BIT_RIGHT
) {
3234 DrawSingleSignal(tile
, TRACKDIR_RIGHT_S
);
3235 DrawSingleSignal(tile
, TRACKDIR_RIGHT_N
);
3237 if (rails
& TRACK_BIT_UPPER
) {
3238 DrawSingleSignal(tile
, TRACKDIR_UPPER_E
);
3239 DrawSingleSignal(tile
, TRACKDIR_UPPER_W
);
3241 if (rails
& TRACK_BIT_LOWER
) {
3242 DrawSingleSignal(tile
, TRACKDIR_LOWER_E
);
3243 DrawSingleSignal(tile
, TRACKDIR_LOWER_W
);
3248 static void DrawTile_Track(TileInfo
*ti
)
3250 if (IsTileSubtype(ti
->tile
, TT_TRACK
) || IsExtendedRailBridge(ti
->tile
)) {
3251 _drawtile_track_palette
= COMPANY_SPRITE_COLOUR(GetTileOwner(ti
->tile
));
3253 TrackBits rails
= GetTrackBits(ti
->tile
);
3255 DrawTrack(ti
, rails
);
3257 if (HasBit(_display_opt
, DO_FULL_DETAIL
) && IsTileSubtype(ti
->tile
, TT_TRACK
)) DrawTrackDetails(ti
, rails
);
3259 if (IsCatenaryDrawn()) DrawCatenary(ti
);
3261 DrawSignals(ti
->tile
, rails
);
3263 DrawBridgeGround(ti
);
3267 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(ti
->tile
));
3269 DiagDirection dir
= GetTunnelBridgeDirection(ti
->tile
);
3271 assert(rti
->bridge_offset
!= 8); // This one is used for roads
3272 const PalSpriteID
*psid
= GetBridgeRampSprite(GetRailBridgeType(ti
->tile
), rti
->bridge_offset
, ti
->tileh
, dir
);
3274 /* Draw PBS Reservation as SpriteCombine */
3275 StartSpriteCombine();
3277 /* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
3278 * it doesn't disappear behind it
3280 /* Bridge heads are drawn solid no matter how invisibility/transparency is set */
3281 AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, ti
->x
, ti
->y
, 16, 16, ti
->tileh
== SLOPE_FLAT
? 0 : 8, ti
->z
);
3283 if (rti
->UsesOverlay()) {
3284 SpriteID surface
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_BRIDGE
);
3286 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(dir
))) {
3287 AddSortableSpriteToDraw(surface
+ ((DiagDirToAxis(dir
) == AXIS_X
) ? RTBO_X
: RTBO_Y
), PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 0, ti
->z
+ 8);
3289 AddSortableSpriteToDraw(surface
+ RTBO_SLOPE
+ dir
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
3292 /* Don't fallback to non-overlay sprite -- the spec states that
3293 * if an overlay is present then the bridge surface must be
3297 /* PBS debugging, draw reserved tracks darker */
3298 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& GetRailReservationTrackBits(ti
->tile
) != TRACK_BIT_NONE
) {
3299 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(dir
))) {
3300 AddSortableSpriteToDraw(DiagDirToAxis(dir
) == AXIS_X
? rti
->base_sprites
.single_x
: rti
->base_sprites
.single_y
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 0, ti
->z
+ 8);
3302 AddSortableSpriteToDraw(rti
->base_sprites
.single_sloped
+ dir
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
3308 if (HasCatenaryDrawn(GetRailType(ti
->tile
))) {
3312 if (DiagDirToAxis(dir
) == AXIS_Y
) {
3313 DrawSingleSignal(ti
->tile
, TRACKDIR_Y_SE
);
3314 DrawSingleSignal(ti
->tile
, TRACKDIR_Y_NW
);
3316 DrawSingleSignal(ti
->tile
, TRACKDIR_X_NE
);
3317 DrawSingleSignal(ti
->tile
, TRACKDIR_X_SW
);
3321 DrawBridgeMiddle(ti
);
3324 static Foundation
GetFoundation_Track(TileIndex tile
, Slope tileh
)
3326 return IsTileSubtype(tile
, TT_TRACK
) ? GetRailFoundation(tileh
, GetTrackBits(tile
)) :
3327 IsExtendedRailBridge(tile
) ? FOUNDATION_LEVELED
:
3328 GetBridgeFoundation(tileh
, DiagDirToAxis(GetTunnelBridgeDirection(tile
)));
3331 static void TileLoop_Track(TileIndex tile
)
3333 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
3334 bool snow_or_desert
= IsOnSnow(tile
);
3335 switch (_settings_game
.game_creation
.landscape
) {
3339 /* As long as we do not have a snow density, we want to use the density
3340 * from the entry edge. For bridges this is the highest point.
3341 * (Independent of foundations) */
3342 if (snow_or_desert
== (GetTileMaxZ(tile
) > GetSnowLine())) return;
3346 if (GetTropicZone(tile
) != TROPICZONE_DESERT
|| snow_or_desert
) return;
3350 MarkTileDirtyByTile(tile
);
3354 RailGroundType old_ground
= GetRailGroundType(tile
);
3355 RailGroundType new_ground
;
3357 if (old_ground
== RAIL_GROUND_WATER
) {
3358 TileLoop_Water(tile
);
3362 switch (_settings_game
.game_creation
.landscape
) {
3365 Slope slope
= GetTileSlope(tile
, &z
);
3368 /* for non-flat track, use lower part of track
3369 * in other cases, use the highest part with track */
3370 TrackBits track
= GetTrackBits(tile
);
3371 Foundation f
= GetRailFoundation(slope
, track
);
3374 case FOUNDATION_NONE
:
3375 /* no foundation - is the track on the upper side of three corners raised tile? */
3376 if (IsSlopeWithThreeCornersRaised(slope
)) z
++;
3379 case FOUNDATION_INCLINED_X
:
3380 case FOUNDATION_INCLINED_Y
:
3381 /* sloped track - is it on a steep slope? */
3382 if (IsSteepSlope(slope
)) z
++;
3385 case FOUNDATION_STEEP_LOWER
:
3386 /* only lower part of steep slope */
3391 /* if it is a steep slope, then there is a track on higher part */
3392 if (IsSteepSlope(slope
)) z
++;
3397 half
= IsInsideMM(f
, FOUNDATION_STEEP_BOTH
, FOUNDATION_HALFTILE_N
+ 1);
3399 /* 'z' is now the lowest part of the highest track bit -
3400 * for sloped track, it is 'z' of lower part
3401 * for two track bits, it is 'z' of higher track bit
3402 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
3403 if (z
> GetSnowLine()) {
3404 if (half
&& z
- GetSnowLine() == 1) {
3405 /* track on non-continuous foundation, lower part is not under snow */
3406 new_ground
= RAIL_GROUND_HALF_SNOW
;
3408 new_ground
= RAIL_GROUND_ICE_DESERT
;
3416 if (GetTropicZone(tile
) == TROPICZONE_DESERT
) {
3417 new_ground
= RAIL_GROUND_ICE_DESERT
;
3423 new_ground
= RAIL_GROUND_GRASS
;
3425 if (old_ground
!= RAIL_GROUND_BARREN
) { // wait until bottom is green
3426 /* determine direction of fence */
3427 TrackBits rail
= GetTrackBits(tile
);
3429 Owner owner
= GetTileOwner(tile
);
3432 for (DiagDirection d
= DIAGDIR_BEGIN
; d
< DIAGDIR_END
; d
++) {
3433 static const TrackBits dir_to_trackbits
[DIAGDIR_END
] = {TRACK_BIT_3WAY_NE
, TRACK_BIT_3WAY_SE
, TRACK_BIT_3WAY_SW
, TRACK_BIT_3WAY_NW
};
3435 /* Track bit on this edge => no fence. */
3436 if ((rail
& dir_to_trackbits
[d
]) != TRACK_BIT_NONE
) continue;
3438 TileIndex tile2
= tile
+ TileOffsByDiagDir(d
);
3440 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
3441 if (!IsValidTile(tile2
) || IsHouseTile(tile2
) || IsIndustryTile(tile2
) ||
3442 (IsTileType(tile2
, TT_MISC
) && !IsRailDepotTile(tile2
)) ||
3443 IsRoadTile(tile2
) || (IsRailBridgeTile(tile2
) && !IsExtendedRailBridge(tile2
)) ||
3444 (IsObjectTile(tile2
) && !IsObjectType(tile2
, OBJECT_OWNED_LAND
)) || !IsTileOwner(tile2
, owner
)) {
3451 case (1 << DIAGDIR_NE
): new_ground
= RAIL_GROUND_FENCE_NE
; break;
3452 case (1 << DIAGDIR_SE
): new_ground
= RAIL_GROUND_FENCE_SE
; break;
3453 case (1 << DIAGDIR_SW
): new_ground
= RAIL_GROUND_FENCE_SW
; break;
3454 case (1 << DIAGDIR_NW
): new_ground
= RAIL_GROUND_FENCE_NW
; break;
3455 case (1 << DIAGDIR_NE
) | (1 << DIAGDIR_SW
): new_ground
= RAIL_GROUND_FENCE_NESW
; break;
3456 case (1 << DIAGDIR_SE
) | (1 << DIAGDIR_NW
): new_ground
= RAIL_GROUND_FENCE_SENW
; break;
3457 case (1 << DIAGDIR_NE
) | (1 << DIAGDIR_SE
): new_ground
= RAIL_GROUND_FENCE_VERT1
; break;
3458 case (1 << DIAGDIR_NE
) | (1 << DIAGDIR_NW
): new_ground
= RAIL_GROUND_FENCE_HORIZ2
; break;
3459 case (1 << DIAGDIR_SE
) | (1 << DIAGDIR_SW
): new_ground
= RAIL_GROUND_FENCE_HORIZ1
; break;
3460 case (1 << DIAGDIR_SW
) | (1 << DIAGDIR_NW
): new_ground
= RAIL_GROUND_FENCE_VERT2
; break;
3461 default: NOT_REACHED();
3466 if (old_ground
!= new_ground
) {
3467 SetRailGroundType(tile
, new_ground
);
3468 MarkTileDirtyByTile(tile
);
3473 static TrackStatus
GetTileRailwayStatus_Track(TileIndex tile
, DiagDirection side
)
3475 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
3476 if (side
== GetTunnelBridgeDirection(tile
)) return 0;
3479 TrackBits trackbits
= GetTrackBits(tile
);
3480 TrackdirBits red_signals
= TRACKDIR_BIT_NONE
;
3484 a
= GetPresentSignals(tile
, TRACK_UPPER
);
3485 /* When signals are not present (in neither direction),
3486 * we pretend them to be green. Otherwise, it depends on
3487 * the signal type. For signals that are only active from
3488 * one side, we set the missing signals explicitly to
3489 * `green'. Otherwise, they implicitly become `red'. */
3493 b
= GetSignalStates(tile
, TRACK_UPPER
) & a
;
3494 if (!IsOnewaySignal(GetSignalType(tile
, TRACK_UPPER
))) b
|= ~a
;
3497 if ((b
& 0x2) == 0) red_signals
|= (TRACKDIR_BIT_LEFT_N
| TRACKDIR_BIT_X_NE
| TRACKDIR_BIT_Y_SE
| TRACKDIR_BIT_UPPER_E
);
3498 if ((b
& 0x1) == 0) red_signals
|= (TRACKDIR_BIT_LEFT_S
| TRACKDIR_BIT_X_SW
| TRACKDIR_BIT_Y_NW
| TRACKDIR_BIT_UPPER_W
);
3500 a
= GetPresentSignals(tile
, TRACK_LOWER
);
3504 b
= GetSignalStates(tile
, TRACK_LOWER
) & a
;
3505 if (!IsOnewaySignal(GetSignalType(tile
, TRACK_LOWER
))) b
|= ~a
;
3508 if ((b
& 0x2) == 0) red_signals
|= (TRACKDIR_BIT_RIGHT_N
| TRACKDIR_BIT_LOWER_E
);
3509 if ((b
& 0x1) == 0) red_signals
|= (TRACKDIR_BIT_RIGHT_S
| TRACKDIR_BIT_LOWER_W
);
3511 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits
), red_signals
);
3514 static TrackdirBits
GetTileWaterwayStatus_Track(TileIndex tile
, DiagDirection side
)
3516 /* Case of half tile slope with water. */
3517 if (IsTileSubtype(tile
, TT_TRACK
) && GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(GetTileSlope(tile
))) {
3518 TrackBits tb
= GetTrackBits(tile
);
3520 default: NOT_REACHED();
3521 case TRACK_BIT_UPPER
: tb
= TRACK_BIT_LOWER
; break;
3522 case TRACK_BIT_LOWER
: tb
= TRACK_BIT_UPPER
; break;
3523 case TRACK_BIT_LEFT
: tb
= TRACK_BIT_RIGHT
; break;
3524 case TRACK_BIT_RIGHT
: tb
= TRACK_BIT_LEFT
; break;
3526 return TrackBitsToTrackdirBits(tb
);
3529 return TRACKDIR_BIT_NONE
;
3532 static bool ClickTile_Track(TileIndex tile
)
3537 static void GetTileDesc_Track(TileIndex tile
, TileDesc
*td
)
3539 static const StringID signal_type
[6][6] = {
3541 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS
,
3542 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS
,
3543 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS
,
3544 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS
,
3545 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS
,
3546 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
3549 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS
,
3550 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS
,
3551 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS
,
3552 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS
,
3553 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS
,
3554 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
3557 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS
,
3558 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS
,
3559 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS
,
3560 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS
,
3561 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS
,
3562 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
3565 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS
,
3566 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS
,
3567 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS
,
3568 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS
,
3569 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS
,
3570 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
3573 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS
,
3574 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS
,
3575 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS
,
3576 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS
,
3577 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS
,
3578 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
3581 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
,
3582 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
,
3583 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
,
3584 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
,
3585 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
,
3586 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
3590 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(tile
));
3591 td
->rail_speed
= rti
->max_speed
;
3592 td
->owner
[0] = GetTileOwner(tile
);
3594 if (IsTileSubtype(tile
, TT_TRACK
)) {
3595 SetDParamX(td
->dparam
, 0, rti
->strings
.name
);
3597 if (HasSignalOnTrack(tile
, TRACK_UPPER
)) {
3598 SignalType primary
= GetSignalType(tile
, TRACK_UPPER
);
3599 SignalType secondary
= HasSignalOnTrack(tile
, TRACK_LOWER
) ? GetSignalType(tile
, TRACK_LOWER
) : primary
;
3600 td
->str
= signal_type
[secondary
][primary
];
3601 } else if (HasSignalOnTrack(tile
, TRACK_LOWER
)) {
3602 SignalType signal
= GetSignalType(tile
, TRACK_LOWER
);
3603 td
->str
= signal_type
[signal
][signal
];
3605 td
->str
= STR_LAI_RAIL_DESCRIPTION_TRACK
;
3608 const BridgeSpec
*spec
= GetBridgeSpec(GetRailBridgeType(tile
));
3609 td
->str
= spec
->transport_name
[TRANSPORT_RAIL
];
3611 uint16 spd
= spec
->speed
;
3612 if (td
->rail_speed
== 0 || spd
< td
->rail_speed
) {
3613 td
->rail_speed
= spd
;
3618 static void ChangeTileOwner_Track(TileIndex tile
, Owner old_owner
, Owner new_owner
)
3620 if (!IsTileOwner(tile
, old_owner
)) return;
3622 if (new_owner
!= INVALID_OWNER
) {
3623 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
3624 TrackBits bits
= GetTrackBits(tile
);
3625 uint factor
= IsTileSubtype(tile
, TT_BRIDGE
) ? TUNNELBRIDGE_TRACKBIT_FACTOR
: 1;
3630 case TRACK_BIT_HORZ
:
3631 case TRACK_BIT_VERT
:
3632 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
3633 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
3634 rt
= GetSideRailType(tile
, dir
);
3635 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= TUNNELBRIDGE_TRACKBIT_FACTOR
;
3636 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += TUNNELBRIDGE_TRACKBIT_FACTOR
;
3637 rt
= GetSideRailType(tile
, ReverseDiagDir(dir
));
3639 rt
= GetRailType(tile
, TRACK_UPPER
);
3640 Company::Get(old_owner
)->infrastructure
.rail
[rt
]--;
3641 Company::Get(new_owner
)->infrastructure
.rail
[rt
]++;
3642 rt
= GetRailType(tile
, TRACK_LOWER
);
3644 Company::Get(old_owner
)->infrastructure
.rail
[rt
]--;
3645 Company::Get(new_owner
)->infrastructure
.rail
[rt
]++;
3646 num_sigs
= CountBits(GetPresentSignals(tile
, TRACK_UPPER
)) + CountBits(GetPresentSignals(tile
, TRACK_LOWER
));
3649 case TRACK_BIT_RIGHT
:
3650 case TRACK_BIT_LOWER
:
3651 rt
= GetRailType(tile
, TRACK_LOWER
);
3652 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= factor
;
3653 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += factor
;
3654 num_sigs
= CountBits(GetPresentSignals(tile
, TRACK_LOWER
));
3657 case TRACK_BIT_LOWER_RIGHT
:
3658 rt
= GetRailType(tile
, TRACK_LOWER
);
3659 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= 2 * 2 * factor
;
3660 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += 2 * 2 * factor
;
3665 rt
= GetRailType(tile
, TRACK_UPPER
);
3666 uint num_pieces
= CountBits(bits
);
3667 if (TracksOverlap(bits
)) {
3668 num_pieces
*= num_pieces
;
3671 num_sigs
= CountBits(GetPresentSignals(tile
, TRACK_UPPER
));
3673 num_pieces
*= factor
;
3674 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= num_pieces
;
3675 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += num_pieces
;
3680 Company::Get(old_owner
)->infrastructure
.signal
-= num_sigs
;
3681 Company::Get(new_owner
)->infrastructure
.signal
+= num_sigs
;
3683 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
3684 TileIndex other_end
= GetOtherBridgeEnd(tile
);
3685 if (tile
< other_end
) {
3686 uint num_pieces
= GetTunnelBridgeLength(tile
, other_end
) * TUNNELBRIDGE_TRACKBIT_FACTOR
;
3687 RailType rt
= GetBridgeRailType(tile
);
3688 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= num_pieces
;
3689 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += num_pieces
;
3693 SetTileOwner(tile
, new_owner
);
3695 DoCommand(tile
, 0, 0, DC_EXEC
| DC_BANKRUPT
, CMD_LANDSCAPE_CLEAR
);
3700 * Tests if autoslope is allowed.
3702 * @param tile The tile.
3703 * @param flags Terraform command flags.
3704 * @param z_old Old TileZ.
3705 * @param tileh_old Old TileSlope.
3706 * @param z_new New TileZ.
3707 * @param tileh_new New TileSlope.
3708 * @param rail_bits Trackbits.
3710 static CommandCost
TestAutoslopeOnRailTile(TileIndex tile
, uint flags
, int z_old
, Slope tileh_old
, int z_new
, Slope tileh_new
, TrackBits rail_bits
)
3712 if (!_settings_game
.construction
.build_on_slopes
|| !AutoslopeEnabled()) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
3714 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3715 if (CheckRailSlope(tileh_new
, rail_bits
, TRACK_BIT_NONE
, tile
).Failed()) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
3717 /* Get the slopes on top of the foundations */
3718 z_old
+= ApplyFoundationToSlope(GetRailFoundation(tileh_old
, rail_bits
), &tileh_old
);
3719 z_new
+= ApplyFoundationToSlope(GetRailFoundation(tileh_new
, rail_bits
), &tileh_new
);
3721 Corner track_corner
;
3722 switch (rail_bits
) {
3723 case TRACK_BIT_LEFT
: track_corner
= CORNER_W
; break;
3724 case TRACK_BIT_LOWER
: track_corner
= CORNER_S
; break;
3725 case TRACK_BIT_RIGHT
: track_corner
= CORNER_E
; break;
3726 case TRACK_BIT_UPPER
: track_corner
= CORNER_N
; break;
3728 /* Surface slope must not be changed */
3730 if (z_old
!= z_new
|| tileh_old
!= tileh_new
) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
3731 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
3734 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3735 z_old
+= GetSlopeZInCorner(RemoveHalftileSlope(tileh_old
), track_corner
);
3736 z_new
+= GetSlopeZInCorner(RemoveHalftileSlope(tileh_new
), track_corner
);
3737 if (z_old
!= z_new
) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
3739 CommandCost cost
= CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
3740 /* Make the ground dirty, if surface slope has changed */
3741 if (tileh_old
!= tileh_new
) {
3742 /* If there is flat water on the lower halftile add the cost for clearing it */
3743 if (GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(tileh_old
)) cost
.AddCost(_price
[PR_CLEAR_WATER
]);
3744 if ((flags
& DC_EXEC
) != 0) SetRailGroundType(tile
, RAIL_GROUND_BARREN
);
3749 static CommandCost
TerraformTile_Track(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
3752 Slope tileh_old
= GetTileSlope(tile
, &z_old
);
3754 if (IsTileSubtype(tile
, TT_TRACK
)) {
3755 TrackBits rail_bits
= GetTrackBits(tile
);
3756 /* Is there flat water on the lower halftile that must be cleared expensively? */
3757 bool was_water
= (GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(tileh_old
));
3759 /* Allow clearing the water only if there is no ship */
3761 VehicleTileFinder
iter (tile
);
3762 while (!iter
.finished()) {
3763 Vehicle
*v
= iter
.next();
3764 if (v
->type
== VEH_SHIP
) iter
.set_found();
3766 if (iter
.was_found()) return_cmd_error(STR_ERROR_SHIP_IN_THE_WAY
);
3769 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3770 CommandCost autoslope_result
= TestAutoslopeOnRailTile(tile
, flags
, z_old
, tileh_old
, z_new
, tileh_new
, rail_bits
);
3772 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3773 Corner allowed_corner
;
3774 switch (rail_bits
) {
3775 case TRACK_BIT_RIGHT
: allowed_corner
= CORNER_W
; break;
3776 case TRACK_BIT_UPPER
: allowed_corner
= CORNER_S
; break;
3777 case TRACK_BIT_LEFT
: allowed_corner
= CORNER_E
; break;
3778 case TRACK_BIT_LOWER
: allowed_corner
= CORNER_N
; break;
3779 default: return autoslope_result
;
3782 Foundation f_old
= GetRailFoundation(tileh_old
, rail_bits
);
3784 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3785 if (tileh_old
!= SLOPE_NS
&& tileh_old
!= SLOPE_EW
&& IsSpecialRailFoundation(f_old
)) return autoslope_result
;
3787 /* Everything is valid, which only changes allowed_corner */
3788 for (Corner corner
= (Corner
)0; corner
< CORNER_END
; corner
= (Corner
)(corner
+ 1)) {
3789 if (allowed_corner
== corner
) continue;
3790 if (z_old
+ GetSlopeZInCorner(tileh_old
, corner
) != z_new
+ GetSlopePixelZInCorner(tileh_new
, corner
)) return autoslope_result
;
3793 /* Make the ground dirty */
3794 if ((flags
& DC_EXEC
) != 0) SetRailGroundType(tile
, RAIL_GROUND_BARREN
);
3796 /* allow terraforming */
3797 return CommandCost(EXPENSES_CONSTRUCTION
, was_water
? _price
[PR_CLEAR_WATER
] : (Money
)0);
3799 if (_settings_game
.construction
.build_on_slopes
&& AutoslopeEnabled()) {
3800 DiagDirection direction
= GetTunnelBridgeDirection(tile
);
3802 if (IsExtendedRailBridge(tile
)) {
3803 if (IsValidRailBridgeBits(tileh_new
, direction
, GetTrackBits(tile
))) return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
3805 /* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
3806 CheckBridgeSlope(direction
, &tileh_old
, &z_old
);
3807 CommandCost res
= CheckBridgeSlope(direction
, &tileh_new
, &z_new
);
3809 /* Surface slope is valid and remains unchanged? */
3810 if (res
.Succeeded() && (z_old
== z_new
) && (tileh_old
== tileh_new
)) return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
3814 return DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
3819 extern const TileTypeProcs _tile_type_rail_procs
= {
3820 DrawTile_Track
, // draw_tile_proc
3821 GetSlopePixelZ_Track
, // get_slope_z_proc
3822 ClearTile_Track
, // clear_tile_proc
3823 NULL
, // add_accepted_cargo_proc
3824 GetTileDesc_Track
, // get_tile_desc_proc
3825 GetTileRailwayStatus_Track
, // get_tile_railway_status_proc
3826 NULL
, // get_tile_road_status_proc
3827 GetTileWaterwayStatus_Track
, // get_tile_waterway_status_proc
3828 ClickTile_Track
, // click_tile_proc
3829 NULL
, // animate_tile_proc
3830 TileLoop_Track
, // tile_loop_proc
3831 ChangeTileOwner_Track
, // change_tile_owner_proc
3832 NULL
, // add_produced_cargo_proc
3833 GetFoundation_Track
, // get_foundation_proc
3834 TerraformTile_Track
, // terraform_tile_proc