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.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), only used for building
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 ((!remove
&& !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
, remove
? 0 : 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), only used for building
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
);
2351 bool rotated
= HasBit(p2
, 4);
2353 if (!ValParamRailtype(totype
)) return CMD_ERROR
;
2354 if (p1
>= MapSize()) return CMD_ERROR
;
2356 TrainList affected_trains
;
2358 CommandCost
cost(EXPENSES_CONSTRUCTION
);
2359 CommandCost err
= CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK
); // by default, there is no track to convert.
2360 TileAreaIterator
*iter
= rotated
? (TileAreaIterator
*)new DiagonalTileAreaIterator(tile
, p1
) : new OrthogonalTileAreaIterator(tile
, p1
);
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
&& iter
->Contains(endtile
)) continue;
2378 ret
= ConvertBridge(tile
, endtile
, totype
, &affected_trains
, flags
);
2383 switch (GetTileSubtype(tile
)) {
2386 case TT_MISC_CROSSING
:
2387 if (RailNoLevelCrossings(totype
)) {
2388 err
.MakeError(STR_ERROR_CROSSING_DISALLOWED
);
2391 track
= GetCrossingRailTrack(tile
);
2392 reserved
= HasCrossingReservation(tile
);
2395 case TT_MISC_TUNNEL
: {
2396 if (GetTunnelTransportType(tile
) != TRANSPORT_RAIL
) continue;
2398 /* If both ends of tunnel are in the range, do not try to convert twice -
2399 * it would cause assert because of different test and exec runs */
2400 TileIndex endtile
= GetOtherTunnelEnd(tile
);
2401 if (endtile
< tile
&& iter
->Contains(endtile
)) continue;
2403 ret
= ConvertTunnel(tile
, endtile
, totype
, &affected_trains
, flags
);
2408 if (!IsRailDepot(tile
)) continue;
2409 track
= GetRailDepotTrack(tile
);
2410 reserved
= HasDepotReservation(tile
);
2416 if (!HasStationRail(tile
)) continue;
2417 track
= GetRailStationTrack(tile
);
2418 reserved
= HasStationReservation(tile
);
2424 if (track
!= INVALID_TRACK
) {
2425 ret
= ConvertGeneric(tile
, totype
, track
, reserved
, &affected_trains
, flags
);
2433 if (IsRailDepotTile(tile
) && (flags
& DC_EXEC
)) {
2434 /* Update build vehicle window related to this depot */
2435 InvalidateWindowData(WC_VEHICLE_DEPOT
, tile
);
2436 InvalidateWindowData(WC_BUILD_VEHICLE
, tile
);
2441 if (flags
& DC_EXEC
) {
2442 /* Railtype changed, update trains as when entering different track */
2443 for (Train
**v
= affected_trains
.Begin(); v
!= affected_trains
.End(); v
++) {
2444 (*v
)->ConsistChanged(CCF_TRACK
);
2449 return (cost
.GetCost() == 0) ? err
: cost
;
2452 static CommandCost
ClearTile_Track(TileIndex tile
, DoCommandFlag flags
)
2454 if (flags
& DC_AUTO
) {
2455 if (!IsTileOwner(tile
, _current_company
)) {
2456 return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER
);
2457 } else if (IsTileSubtype(tile
, TT_BRIDGE
)) {
2458 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
2460 return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
2464 if (IsTileSubtype(tile
, TT_TRACK
)) {
2465 CommandCost
cost(EXPENSES_CONSTRUCTION
);
2467 Slope tileh
= GetTileSlope(tile
);
2468 /* Is there flat water on the lower halftile that gets cleared expensively? */
2469 bool water_ground
= (GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(tileh
));
2471 TrackBits tracks
= GetTrackBits(tile
);
2472 while (tracks
!= TRACK_BIT_NONE
) {
2473 Track track
= RemoveFirstTrack(&tracks
);
2474 CommandCost ret
= DoCommand(tile
, 0, track
, flags
, CMD_REMOVE_SINGLE_RAIL
);
2475 if (ret
.Failed()) return ret
;
2479 /* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
2480 * Same holds for non-companies clearing the tile, e.g. disasters. */
2481 if (water_ground
&& !(flags
& DC_BANKRUPT
) && Company::IsValidID(_current_company
)) {
2482 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
2483 if (ret
.Failed()) return ret
;
2485 /* The track was removed, and left a coast tile. Now also clear the water. */
2486 if (flags
& DC_EXEC
) DoClearSquare(tile
);
2487 cost
.AddCost(_price
[PR_CLEAR_WATER
]);
2492 if (_current_company
!= OWNER_WATER
&& _game_mode
!= GM_EDITOR
) {
2493 CommandCost ret
= CheckOwnership(GetTileOwner(tile
));
2494 if (ret
.Failed()) return ret
;
2497 TrackBits present
= GetTrackBits(tile
);
2499 if ((present
== TRACK_BIT_HORZ
) || (present
== TRACK_BIT_VERT
)) {
2500 Track track
= FindFirstTrack(DiagdirReachesTracks(GetTunnelBridgeDirection(tile
)) & present
);
2502 CommandCost cost
= DoCommand(tile
, 0, track
, flags
, CMD_REMOVE_SINGLE_RAIL
);
2503 if (cost
.Failed()) return cost
;
2505 CommandCost ret
= RemoveBridgeTrack(tile
, TrackToOppositeTrack(track
), flags
);
2506 if (ret
.Failed()) return ret
;
2512 TileIndex other_tile
= GetOtherBridgeEnd(tile
);
2513 TrackBits other_remove
= GetTrackBits(other_tile
) & DiagdirReachesTracks(GetTunnelBridgeDirection(tile
));
2515 assert(other_remove
!= TRACK_BIT_NONE
);
2517 CommandCost ret
= EnsureNoTrainOnBridgeTrackBits(tile
, present
, other_tile
, other_remove
);
2518 if (ret
.Failed()) return ret
;
2520 uint len
= GetTunnelBridgeLength(tile
, other_tile
) + 2; // Don't forget the end tiles.
2522 CommandCost
cost(EXPENSES_CONSTRUCTION
, len
* _price
[PR_CLEAR_BRIDGE
]);
2523 cost
.AddCost((CountBits(present
) - 1) * RailClearCost(GetBridgeRailType(tile
)));
2525 /* Charge extra to remove signals on the track, if any */
2526 if (HasSignalOnTrack(tile
, FindFirstTrack(present
))) {
2527 cost
.AddCost(DoCommand(tile
, FindFirstTrack(present
), 0, flags
, CMD_REMOVE_SIGNALS
));
2530 int n
= CountBits(other_remove
);
2532 Track other_track
= FindFirstTrack(other_remove
);
2533 if (HasSignalOnTrack(other_tile
, other_track
)) {
2534 cost
.AddCost(DoCommand(other_tile
, other_track
, 0, flags
, CMD_REMOVE_SIGNALS
));
2537 cost
.AddCost((n
- 1) * RailClearCost(GetBridgeRailType(other_tile
)));
2540 if (flags
& DC_EXEC
) {
2541 RemoveRailBridge(tile
, present
, other_tile
, other_remove
);
2549 static int GetSlopePixelZ_Track(TileIndex tile
, uint x
, uint y
)
2552 Slope tileh
= GetTilePixelSlope(tile
, &z
);
2554 if (IsTileSubtype(tile
, TT_TRACK
)) {
2555 if (tileh
== SLOPE_FLAT
) return z
;
2556 z
+= ApplyPixelFoundationToSlope(GetRailFoundation(tileh
, GetTrackBits(tile
)), &tileh
);
2557 return z
+ GetPartialPixelZ(x
& 0xF, y
& 0xF, tileh
);
2558 } else if (IsExtendedRailBridge(tile
)) {
2559 return z
+ TILE_HEIGHT
;
2564 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
2566 z
+= ApplyPixelFoundationToSlope(GetBridgeFoundation(tileh
, DiagDirToAxis(dir
)), &tileh
);
2568 /* On the bridge ramp? */
2569 uint pos
= (DiagDirToAxis(dir
) == AXIS_X
? y
: x
);
2570 if (5 <= pos
&& pos
<= 10) {
2571 return z
+ ((tileh
== SLOPE_FLAT
) ? GetBridgePartialPixelZ(dir
, x
, y
) : TILE_HEIGHT
);
2574 return z
+ GetPartialPixelZ(x
, y
, tileh
);
2579 static uint32 _drawtile_track_palette
;
2581 static void DrawTrackFence_NW(const TileInfo
*ti
, SpriteID base_image
)
2583 RailFenceOffset rfo
= RFO_FLAT_X
;
2584 if (ti
->tileh
& SLOPE_NW
) rfo
= (ti
->tileh
& SLOPE_W
) ? RFO_SLOPE_SW
: RFO_SLOPE_NE
;
2585 AddSortableSpriteToDraw(base_image
+ rfo
, _drawtile_track_palette
,
2586 ti
->x
, ti
->y
+ 1, 16, 1, 4, ti
->z
);
2589 static void DrawTrackFence_SE(const TileInfo
*ti
, SpriteID base_image
)
2591 RailFenceOffset rfo
= RFO_FLAT_X
;
2592 if (ti
->tileh
& SLOPE_SE
) rfo
= (ti
->tileh
& SLOPE_S
) ? RFO_SLOPE_SW
: RFO_SLOPE_NE
;
2593 AddSortableSpriteToDraw(base_image
+ rfo
, _drawtile_track_palette
,
2594 ti
->x
, ti
->y
+ TILE_SIZE
- 1, 16, 1, 4, ti
->z
);
2597 static void DrawTrackFence_NW_SE(const TileInfo
*ti
, SpriteID base_image
)
2599 DrawTrackFence_NW(ti
, base_image
);
2600 DrawTrackFence_SE(ti
, base_image
);
2603 static void DrawTrackFence_NE(const TileInfo
*ti
, SpriteID base_image
)
2605 RailFenceOffset rfo
= RFO_FLAT_Y
;
2606 if (ti
->tileh
& SLOPE_NE
) rfo
= (ti
->tileh
& SLOPE_E
) ? RFO_SLOPE_SE
: RFO_SLOPE_NW
;
2607 AddSortableSpriteToDraw(base_image
+ rfo
, _drawtile_track_palette
,
2608 ti
->x
+ 1, ti
->y
, 1, 16, 4, ti
->z
);
2611 static void DrawTrackFence_SW(const TileInfo
*ti
, SpriteID base_image
)
2613 RailFenceOffset rfo
= RFO_FLAT_Y
;
2614 if (ti
->tileh
& SLOPE_SW
) rfo
= (ti
->tileh
& SLOPE_S
) ? RFO_SLOPE_SE
: RFO_SLOPE_NW
;
2615 AddSortableSpriteToDraw(base_image
+ rfo
, _drawtile_track_palette
,
2616 ti
->x
+ TILE_SIZE
- 1, ti
->y
, 1, 16, 4, ti
->z
);
2619 static void DrawTrackFence_NE_SW(const TileInfo
*ti
, SpriteID base_image
)
2621 DrawTrackFence_NE(ti
, base_image
);
2622 DrawTrackFence_SW(ti
, base_image
);
2626 * Draw fence at eastern side of track.
2628 static void DrawTrackFence_NS_1(const TileInfo
*ti
, SpriteID base_image
)
2630 int z
= ti
->z
+ GetSlopePixelZInCorner(RemoveHalftileSlope(ti
->tileh
), CORNER_W
);
2631 AddSortableSpriteToDraw(base_image
+ RFO_FLAT_VERT
, _drawtile_track_palette
,
2632 ti
->x
+ TILE_SIZE
/ 2, ti
->y
+ TILE_SIZE
/ 2, 1, 1, 4, z
);
2636 * Draw fence at western side of track.
2638 static void DrawTrackFence_NS_2(const TileInfo
*ti
, SpriteID base_image
)
2640 int z
= ti
->z
+ GetSlopePixelZInCorner(RemoveHalftileSlope(ti
->tileh
), CORNER_E
);
2641 AddSortableSpriteToDraw(base_image
+ RFO_FLAT_VERT
, _drawtile_track_palette
,
2642 ti
->x
+ TILE_SIZE
/ 2, ti
->y
+ TILE_SIZE
/ 2, 1, 1, 4, z
);
2646 * Draw fence at southern side of track.
2648 static void DrawTrackFence_WE_1(const TileInfo
*ti
, SpriteID base_image
)
2650 int z
= ti
->z
+ GetSlopePixelZInCorner(RemoveHalftileSlope(ti
->tileh
), CORNER_N
);
2651 AddSortableSpriteToDraw(base_image
+ RFO_FLAT_HORZ
, _drawtile_track_palette
,
2652 ti
->x
+ TILE_SIZE
/ 2, ti
->y
+ TILE_SIZE
/ 2, 1, 1, 4, z
);
2656 * Draw fence at northern side of track.
2658 static void DrawTrackFence_WE_2(const TileInfo
*ti
, SpriteID base_image
)
2660 int z
= ti
->z
+ GetSlopePixelZInCorner(RemoveHalftileSlope(ti
->tileh
), CORNER_S
);
2661 AddSortableSpriteToDraw(base_image
+ RFO_FLAT_HORZ
, _drawtile_track_palette
,
2662 ti
->x
+ TILE_SIZE
/ 2, ti
->y
+ TILE_SIZE
/ 2, 1, 1, 4, z
);
2666 static void DrawTrackDetails(const TileInfo
*ti
, TrackBits tracks
)
2668 const RailtypeInfo
*rti
;
2671 case TRACK_BIT_HORZ
:
2672 case TRACK_BIT_VERT
:
2673 return; /* these never have fences */
2675 case TRACK_BIT_LOWER
:
2676 case TRACK_BIT_RIGHT
:
2677 case TRACK_BIT_LOWER_RIGHT
:
2678 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TRACK_LOWER
));
2682 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TRACK_UPPER
));
2686 /* Base sprite for track fences.
2687 * Note: Halftile slopes only have fences on the upper part. */
2688 SpriteID base_image
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_FENCES
, IsHalftileSlope(ti
->tileh
) ? TCX_UPPER_HALFTILE
: TCX_NORMAL
);
2689 if (base_image
== 0) base_image
= SPR_TRACK_FENCE_FLAT_X
;
2691 switch (GetRailGroundType(ti
->tile
)) {
2692 case RAIL_GROUND_FENCE_NW
: DrawTrackFence_NW(ti
, base_image
); break;
2693 case RAIL_GROUND_FENCE_SE
: DrawTrackFence_SE(ti
, base_image
); break;
2694 case RAIL_GROUND_FENCE_SENW
: DrawTrackFence_NW_SE(ti
, base_image
); break;
2695 case RAIL_GROUND_FENCE_NE
: DrawTrackFence_NE(ti
, base_image
); break;
2696 case RAIL_GROUND_FENCE_SW
: DrawTrackFence_SW(ti
, base_image
); break;
2697 case RAIL_GROUND_FENCE_NESW
: DrawTrackFence_NE_SW(ti
, base_image
); break;
2698 case RAIL_GROUND_FENCE_VERT1
: DrawTrackFence_NS_1(ti
, base_image
); break;
2699 case RAIL_GROUND_FENCE_VERT2
: DrawTrackFence_NS_2(ti
, base_image
); break;
2700 case RAIL_GROUND_FENCE_HORIZ1
: DrawTrackFence_WE_1(ti
, base_image
); break;
2701 case RAIL_GROUND_FENCE_HORIZ2
: DrawTrackFence_WE_2(ti
, base_image
); break;
2702 case RAIL_GROUND_WATER
: {
2703 Corner track_corner
;
2704 if (IsHalftileSlope(ti
->tileh
)) {
2705 /* Steep slope or one-corner-raised slope with halftile foundation */
2706 track_corner
= GetHalftileSlopeCorner(ti
->tileh
);
2708 /* Three-corner-raised slope */
2709 track_corner
= OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti
->tileh
)));
2711 switch (track_corner
) {
2712 case CORNER_W
: DrawTrackFence_NS_1(ti
, base_image
); break;
2713 case CORNER_S
: DrawTrackFence_WE_2(ti
, base_image
); break;
2714 case CORNER_E
: DrawTrackFence_NS_2(ti
, base_image
); break;
2715 case CORNER_N
: DrawTrackFence_WE_1(ti
, base_image
); break;
2716 default: NOT_REACHED();
2724 /* SubSprite for drawing track halftiles. */
2725 static const int INF
= 1000; // big number compared to tilesprite size
2726 static const SubSprite _halftile_sub_sprite
[4] = {
2727 { -INF
, -INF
, 32 - 33, INF
}, // CORNER_W, clip 33 pixels from right
2728 { -INF
, 0 + 15, INF
, INF
}, // CORNER_S, clip 15 pixels from top
2729 { -31 + 33, -INF
, INF
, INF
}, // CORNER_E, clip 33 pixels from left
2730 { -INF
, -INF
, INF
, 30 - 15 } // CORNER_N, clip 15 pixels from bottom
2732 static const SubSprite _halftile_sub_sprite_upper
[4] = {
2733 { -INF
, -INF
, 32 - 33, INF
}, // CORNER_W, clip 33 pixels from right
2734 { -INF
, 0 + 7, INF
, INF
}, // CORNER_S, clip 7 pixels from top
2735 { -31 + 33, -INF
, INF
, INF
}, // CORNER_E, clip 33 pixels from left
2736 { -INF
, -INF
, INF
, 30 - 23 } // CORNER_N, clip 23 pixels from bottom
2738 static const byte _corner_to_track_sprite
[] = {3, 1, 2, 0};
2740 static inline void DrawTrackSprite(SpriteID sprite
, PaletteID pal
, const TileInfo
*ti
, Slope s
)
2742 DrawGroundSprite(sprite
, pal
, NULL
, 0, (ti
->tileh
& s
) ? -8 : 0);
2745 static void DrawTrackGround(TileInfo
*ti
, RailGroundType rgt
, bool has_track
)
2747 if (rgt
== RAIL_GROUND_WATER
) {
2748 if (has_track
|| IsSteepSlope(ti
->tileh
)) {
2749 /* three-corner-raised slope or steep slope with track on upper part */
2750 DrawShoreTile(ti
->tileh
);
2752 /* single-corner-raised slope with track on upper part */
2753 DrawGroundSprite(SPR_FLAT_WATER_TILE
, PAL_NONE
);
2759 case RAIL_GROUND_BARREN
: image
= SPR_FLAT_BARE_LAND
; break;
2760 case RAIL_GROUND_ICE_DESERT
: image
= SPR_FLAT_SNOW_DESERT_TILE
; break;
2761 default: image
= SPR_FLAT_GRASS_TILE
; break;
2764 image
+= SlopeToSpriteOffset(ti
->tileh
);
2766 DrawGroundSprite(image
, PAL_NONE
);
2770 static void DrawTrackBitsOverlay(TileInfo
*ti
, TrackBits track
, const RailtypeInfo
*rti
)
2772 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
);
2773 SpriteID ground
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_GROUND
);
2774 TrackBits pbs
= _settings_client
.gui
.show_track_reservation
? GetRailReservationTrackBits(ti
->tile
) : TRACK_BIT_NONE
;
2776 if (track
== TRACK_BIT_NONE
) {
2777 /* Half-tile foundation, no track here? */
2778 } else if (ti
->tileh
== SLOPE_NW
&& track
== TRACK_BIT_Y
) {
2779 DrawGroundSprite(ground
+ RTO_SLOPE_NW
, PAL_NONE
);
2780 if (pbs
!= TRACK_BIT_NONE
) DrawGroundSprite(overlay
+ 9, PALETTE_CRASH
);
2781 } else if (ti
->tileh
== SLOPE_NE
&& track
== TRACK_BIT_X
) {
2782 DrawGroundSprite(ground
+ RTO_SLOPE_NE
, PAL_NONE
);
2783 if (pbs
!= TRACK_BIT_NONE
) DrawGroundSprite(overlay
+ 6, PALETTE_CRASH
);
2784 } else if (ti
->tileh
== SLOPE_SE
&& track
== TRACK_BIT_Y
) {
2785 DrawGroundSprite(ground
+ RTO_SLOPE_SE
, PAL_NONE
);
2786 if (pbs
!= TRACK_BIT_NONE
) DrawGroundSprite(overlay
+ 7, PALETTE_CRASH
);
2787 } else if (ti
->tileh
== SLOPE_SW
&& track
== TRACK_BIT_X
) {
2788 DrawGroundSprite(ground
+ RTO_SLOPE_SW
, PAL_NONE
);
2789 if (pbs
!= TRACK_BIT_NONE
) DrawGroundSprite(overlay
+ 8, PALETTE_CRASH
);
2792 /* Draw single ground sprite when not overlapping. No track overlay
2793 * is necessary for these sprites. */
2794 case TRACK_BIT_X
: DrawGroundSprite(ground
+ RTO_X
, PAL_NONE
); break;
2795 case TRACK_BIT_Y
: DrawGroundSprite(ground
+ RTO_Y
, PAL_NONE
); break;
2796 case TRACK_BIT_UPPER
: DrawTrackSprite(ground
+ RTO_N
, PAL_NONE
, ti
, SLOPE_N
); break;
2797 case TRACK_BIT_LOWER
: DrawTrackSprite(ground
+ RTO_S
, PAL_NONE
, ti
, SLOPE_S
); break;
2798 case TRACK_BIT_RIGHT
: DrawTrackSprite(ground
+ RTO_E
, PAL_NONE
, ti
, SLOPE_E
); break;
2799 case TRACK_BIT_LEFT
: DrawTrackSprite(ground
+ RTO_W
, PAL_NONE
, ti
, SLOPE_W
); break;
2800 case TRACK_BIT_CROSS
: DrawGroundSprite(ground
+ RTO_CROSSING_XY
, PAL_NONE
); break;
2801 case TRACK_BIT_HORZ
: DrawTrackSprite(ground
+ RTO_N
, PAL_NONE
, ti
, SLOPE_N
);
2802 DrawTrackSprite(ground
+ RTO_S
, PAL_NONE
, ti
, SLOPE_S
); break;
2803 case TRACK_BIT_VERT
: DrawTrackSprite(ground
+ RTO_E
, PAL_NONE
, ti
, SLOPE_E
);
2804 DrawTrackSprite(ground
+ RTO_W
, PAL_NONE
, ti
, SLOPE_W
); break;
2807 /* We're drawing a junction tile */
2808 if ((track
& TRACK_BIT_3WAY_NE
) == 0) {
2809 DrawGroundSprite(ground
+ RTO_JUNCTION_SW
, PAL_NONE
);
2810 } else if ((track
& TRACK_BIT_3WAY_SW
) == 0) {
2811 DrawGroundSprite(ground
+ RTO_JUNCTION_NE
, PAL_NONE
);
2812 } else if ((track
& TRACK_BIT_3WAY_NW
) == 0) {
2813 DrawGroundSprite(ground
+ RTO_JUNCTION_SE
, PAL_NONE
);
2814 } else if ((track
& TRACK_BIT_3WAY_SE
) == 0) {
2815 DrawGroundSprite(ground
+ RTO_JUNCTION_NW
, PAL_NONE
);
2817 DrawGroundSprite(ground
+ RTO_JUNCTION_NSEW
, PAL_NONE
);
2820 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2823 /* Draw regular track bits */
2824 if (track
& TRACK_BIT_X
) DrawGroundSprite(overlay
+ RTO_X
, PAL_NONE
);
2825 if (track
& TRACK_BIT_Y
) DrawGroundSprite(overlay
+ RTO_Y
, PAL_NONE
);
2826 if (track
& TRACK_BIT_UPPER
) DrawGroundSprite(overlay
+ RTO_N
, PAL_NONE
);
2827 if (track
& TRACK_BIT_LOWER
) DrawGroundSprite(overlay
+ RTO_S
, PAL_NONE
);
2828 if (track
& TRACK_BIT_RIGHT
) DrawGroundSprite(overlay
+ RTO_E
, PAL_NONE
);
2829 if (track
& TRACK_BIT_LEFT
) DrawGroundSprite(overlay
+ RTO_W
, PAL_NONE
);
2832 /* Draw reserved track bits */
2833 if (pbs
& TRACK_BIT_X
) DrawGroundSprite(overlay
+ RTO_X
, PALETTE_CRASH
);
2834 if (pbs
& TRACK_BIT_Y
) DrawGroundSprite(overlay
+ RTO_Y
, PALETTE_CRASH
);
2835 if (pbs
& TRACK_BIT_UPPER
) DrawTrackSprite(overlay
+ RTO_N
, PALETTE_CRASH
, ti
, SLOPE_N
);
2836 if (pbs
& TRACK_BIT_LOWER
) DrawTrackSprite(overlay
+ RTO_S
, PALETTE_CRASH
, ti
, SLOPE_S
);
2837 if (pbs
& TRACK_BIT_RIGHT
) DrawTrackSprite(overlay
+ RTO_E
, PALETTE_CRASH
, ti
, SLOPE_E
);
2838 if (pbs
& TRACK_BIT_LEFT
) DrawTrackSprite(overlay
+ RTO_W
, PALETTE_CRASH
, ti
, SLOPE_W
);
2842 static void DrawTrackBitsNonOverlay(TileInfo
*ti
, TrackBits track
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2845 PaletteID pal
= PAL_NONE
;
2846 const SubSprite
*sub
= NULL
;
2847 bool junction
= false;
2849 if (track
== TRACK_BIT_NONE
) return;
2851 if (ti
->tileh
!= SLOPE_FLAT
) {
2852 /* track on non-flat ground */
2853 image
= _track_sloped_sprites
[ti
->tileh
- 1] + rti
->base_sprites
.track_y
;
2855 /* track on flat ground */
2856 (image
= rti
->base_sprites
.track_y
, track
== TRACK_BIT_Y
) ||
2857 (image
++, track
== TRACK_BIT_X
) ||
2858 (image
++, track
== TRACK_BIT_UPPER
) ||
2859 (image
++, track
== TRACK_BIT_LOWER
) ||
2860 (image
++, track
== TRACK_BIT_RIGHT
) ||
2861 (image
++, track
== TRACK_BIT_LEFT
) ||
2862 (image
++, track
== TRACK_BIT_CROSS
) ||
2864 (image
= rti
->base_sprites
.track_ns
, track
== TRACK_BIT_HORZ
) ||
2865 (image
++, track
== TRACK_BIT_VERT
) ||
2867 (junction
= true, false) ||
2868 (image
= rti
->base_sprites
.ground
, (track
& TRACK_BIT_3WAY_NE
) == 0) ||
2869 (image
++, (track
& TRACK_BIT_3WAY_SW
) == 0) ||
2870 (image
++, (track
& TRACK_BIT_3WAY_NW
) == 0) ||
2871 (image
++, (track
& TRACK_BIT_3WAY_SE
) == 0) ||
2876 case RAIL_GROUND_BARREN
: pal
= PALETTE_TO_BARE_LAND
; break;
2877 case RAIL_GROUND_ICE_DESERT
: image
+= rti
->snow_offset
; break;
2878 case RAIL_GROUND_WATER
: {
2879 /* three-corner-raised slope */
2880 DrawShoreTile(ti
->tileh
);
2881 Corner track_corner
= OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti
->tileh
)));
2882 sub
= &_halftile_sub_sprite_upper
[track_corner
];
2888 DrawGroundSprite(image
, pal
, sub
);
2890 /* Draw track pieces individually for junction tiles */
2892 if (track
& TRACK_BIT_X
) DrawGroundSprite(rti
->base_sprites
.single_x
, PAL_NONE
);
2893 if (track
& TRACK_BIT_Y
) DrawGroundSprite(rti
->base_sprites
.single_y
, PAL_NONE
);
2894 if (track
& TRACK_BIT_UPPER
) DrawGroundSprite(rti
->base_sprites
.single_n
, PAL_NONE
);
2895 if (track
& TRACK_BIT_LOWER
) DrawGroundSprite(rti
->base_sprites
.single_s
, PAL_NONE
);
2896 if (track
& TRACK_BIT_LEFT
) DrawGroundSprite(rti
->base_sprites
.single_w
, PAL_NONE
);
2897 if (track
& TRACK_BIT_RIGHT
) DrawGroundSprite(rti
->base_sprites
.single_e
, PAL_NONE
);
2900 /* PBS debugging, draw reserved tracks darker */
2901 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
) {
2902 /* Get reservation, but mask track on halftile slope */
2903 TrackBits pbs
= GetRailReservationTrackBits(ti
->tile
) & track
;
2904 if (pbs
& TRACK_BIT_X
) {
2905 if (ti
->tileh
== SLOPE_FLAT
|| ti
->tileh
== SLOPE_ELEVATED
) {
2906 DrawGroundSprite(rti
->base_sprites
.single_x
, PALETTE_CRASH
);
2908 DrawGroundSprite(_track_sloped_sprites
[ti
->tileh
- 1] + rti
->base_sprites
.single_sloped
- 20, PALETTE_CRASH
);
2911 if (pbs
& TRACK_BIT_Y
) {
2912 if (ti
->tileh
== SLOPE_FLAT
|| ti
->tileh
== SLOPE_ELEVATED
) {
2913 DrawGroundSprite(rti
->base_sprites
.single_y
, PALETTE_CRASH
);
2915 DrawGroundSprite(_track_sloped_sprites
[ti
->tileh
- 1] + rti
->base_sprites
.single_sloped
- 20, PALETTE_CRASH
);
2918 if (pbs
& TRACK_BIT_UPPER
) DrawGroundSprite(rti
->base_sprites
.single_n
, PALETTE_CRASH
, NULL
, 0, ti
->tileh
& SLOPE_N
? -(int)TILE_HEIGHT
: 0);
2919 if (pbs
& TRACK_BIT_LOWER
) DrawGroundSprite(rti
->base_sprites
.single_s
, PALETTE_CRASH
, NULL
, 0, ti
->tileh
& SLOPE_S
? -(int)TILE_HEIGHT
: 0);
2920 if (pbs
& TRACK_BIT_LEFT
) DrawGroundSprite(rti
->base_sprites
.single_w
, PALETTE_CRASH
, NULL
, 0, ti
->tileh
& SLOPE_W
? -(int)TILE_HEIGHT
: 0);
2921 if (pbs
& TRACK_BIT_RIGHT
) DrawGroundSprite(rti
->base_sprites
.single_e
, PALETTE_CRASH
, NULL
, 0, ti
->tileh
& SLOPE_E
? -(int)TILE_HEIGHT
: 0);
2925 static void DrawTrackBits(TileInfo
*ti
, TrackBits track
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2927 if (rti
->UsesOverlay()) {
2928 DrawTrackBitsOverlay(ti
, track
, rti
);
2930 DrawTrackBitsNonOverlay(ti
, track
, rti
, rgt
);
2935 static void DrawHalftileOverlay(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2939 default: NOT_REACHED();
2940 case CORNER_N
: offset
= RTO_N
; break;
2941 case CORNER_S
: offset
= RTO_S
; break;
2942 case CORNER_E
: offset
= RTO_E
; break;
2943 case CORNER_W
: offset
= RTO_W
; break;
2946 DrawGroundSprite(offset
+ GetCustomRailSprite(rti
, ti
->tile
, RTSG_GROUND
), PAL_NONE
, &_halftile_sub_sprite
[corner
]);
2948 if (_settings_client
.gui
.show_track_reservation
&& HasReservedTracks(ti
->tile
, CornerToTrackBits(corner
))) {
2949 DrawGroundSprite(offset
+ GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
), PALETTE_CRASH
, &_halftile_sub_sprite
[corner
]);
2953 static void DrawHalftileNonOverlay(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2955 SpriteID image
= rti
->base_sprites
.track_y
+ 2;
2959 default: NOT_REACHED();
2960 case CORNER_W
: image
++; /* fall through */
2961 case CORNER_E
: image
++; /* fall through */
2962 case CORNER_S
: image
++; /* fall through */
2963 case CORNER_N
: break;
2967 case RAIL_GROUND_BARREN
: pal
= PALETTE_TO_BARE_LAND
; break;
2968 case RAIL_GROUND_ICE_DESERT
: image
+= rti
->snow_offset
; /* fall through */
2969 default: pal
= PAL_NONE
; break;
2972 DrawGroundSprite(image
, pal
, &_halftile_sub_sprite
[corner
]);
2974 /* PBS debugging, draw reserved tracks darker */
2975 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasReservedTracks(ti
->tile
, CornerToTrackBits(corner
))) {
2976 DrawGroundSprite(_corner_to_track_sprite
[corner
] + rti
->base_sprites
.single_n
, PALETTE_CRASH
, NULL
, 0, 0);
2980 static void DrawHalftile(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2982 if (rti
->UsesOverlay()) {
2983 DrawHalftileOverlay(ti
, corner
, rti
, rgt
);
2985 DrawHalftileNonOverlay(ti
, corner
, rti
, rgt
);
2989 static void DrawUpperHalftileOverlay(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
2993 case RAIL_GROUND_BARREN
: image
= SPR_FLAT_BARE_LAND
; break;
2994 case RAIL_GROUND_ICE_DESERT
:
2995 case RAIL_GROUND_HALF_SNOW
: image
= SPR_FLAT_SNOW_DESERT_TILE
; break;
2996 default: image
= SPR_FLAT_GRASS_TILE
; break;
2999 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lighting. */
3000 Slope fake_slope
= SlopeWithThreeCornersRaised(OppositeCorner(corner
));
3002 image
+= SlopeToSpriteOffset(fake_slope
);
3004 DrawGroundSprite(image
, PAL_NONE
, &_halftile_sub_sprite_upper
[corner
]);
3006 TrackBits track
= CornerToTrackBits(corner
);
3008 SpriteID overlay
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_OVERLAY
, TCX_UPPER_HALFTILE
);
3009 SpriteID ground
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_GROUND
, TCX_UPPER_HALFTILE
);
3013 default: NOT_REACHED();
3014 case TRACK_BIT_UPPER
: offset
= RTO_N
; break;
3015 case TRACK_BIT_LOWER
: offset
= RTO_S
; break;
3016 case TRACK_BIT_RIGHT
: offset
= RTO_E
; break;
3017 case TRACK_BIT_LEFT
: offset
= RTO_W
; break;
3020 DrawTrackSprite(ground
+ offset
, PAL_NONE
, ti
, fake_slope
);
3021 if (_settings_client
.gui
.show_track_reservation
&& HasReservedTracks(ti
->tile
, track
)) {
3022 DrawTrackSprite(overlay
+ offset
, PALETTE_CRASH
, ti
, fake_slope
);
3026 static void DrawUpperHalftileNonOverlay(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
3028 /* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lighting. */
3029 Slope fake_slope
= SlopeWithThreeCornersRaised(OppositeCorner(corner
));
3030 SpriteID image
= _track_sloped_sprites
[fake_slope
- 1] + rti
->base_sprites
.track_y
;
3031 PaletteID pal
= PAL_NONE
;
3034 case RAIL_GROUND_BARREN
: pal
= PALETTE_TO_BARE_LAND
; break;
3035 case RAIL_GROUND_ICE_DESERT
:
3036 case RAIL_GROUND_HALF_SNOW
: image
+= rti
->snow_offset
; break; // higher part has snow in this case too
3040 DrawGroundSprite(image
, pal
, &_halftile_sub_sprite_upper
[corner
]);
3042 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& HasReservedTracks(ti
->tile
, CornerToTrackBits(corner
))) {
3043 DrawGroundSprite(_corner_to_track_sprite
[corner
] + rti
->base_sprites
.single_n
, PALETTE_CRASH
, NULL
, 0, -(int)TILE_HEIGHT
);
3047 static void DrawUpperHalftile(TileInfo
*ti
, Corner corner
, const RailtypeInfo
*rti
, RailGroundType rgt
)
3049 DrawFoundation(ti
, HalftileFoundation(corner
));
3051 if (rti
->UsesOverlay()) {
3052 DrawUpperHalftileOverlay(ti
, corner
, rti
, rgt
);
3054 DrawUpperHalftileNonOverlay(ti
, corner
, rti
, rgt
);
3059 * Draw ground sprite and track bits
3060 * @param ti TileInfo
3061 * @param track TrackBits to draw
3063 static void DrawTrack(TileInfo
*ti
, TrackBits track
)
3065 RailGroundType rgt
= IsTileSubtype(ti
->tile
, TT_TRACK
) ? GetRailGroundType(ti
->tile
) :
3066 IsOnSnow(ti
->tile
) ? RAIL_GROUND_ICE_DESERT
: RAIL_GROUND_GRASS
;
3067 Foundation f
= IsTileSubtype(ti
->tile
, TT_TRACK
) ? GetRailFoundation(ti
->tileh
, track
) : FOUNDATION_LEVELED
;
3068 Corner halftile_corner
= CORNER_INVALID
;
3070 const RailtypeInfo
*rti
, *halftile_rti
;
3072 if (IsNonContinuousFoundation(f
)) {
3073 /* Save halftile corner */
3074 if (f
== FOUNDATION_STEEP_BOTH
) {
3075 halftile_corner
= GetHighestSlopeCorner(ti
->tileh
);
3076 f
= FOUNDATION_STEEP_LOWER
;
3078 halftile_corner
= GetHalftileFoundationCorner(f
);
3079 f
= FOUNDATION_NONE
;
3081 Track halftile_track
= TrackBitsToTrack(CornerToTrackBits(halftile_corner
));
3082 halftile_rti
= GetRailTypeInfo(GetRailType(ti
->tile
, halftile_track
));
3083 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TrackToOppositeTrack(halftile_track
)));
3084 /* Draw lower part first */
3085 track
&= ~CornerToTrackBits(halftile_corner
);
3086 /* Non-overlay railtypes need ground to be drawn if there is no lower halftile track */
3087 draw_ground
= rti
->UsesOverlay() || track
== TRACK_BIT_NONE
;
3090 case TRACK_BIT_LOWER
:
3091 case TRACK_BIT_RIGHT
:
3092 case TRACK_BIT_LOWER_RIGHT
:
3093 halftile_rti
= NULL
;
3094 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TRACK_LOWER
));
3095 draw_ground
= rti
->UsesOverlay();
3098 case TRACK_BIT_HORZ
:
3099 case TRACK_BIT_VERT
: {
3100 RailType halftile_rt
= GetRailType(ti
->tile
, TRACK_LOWER
);
3101 RailType rt
= GetRailType(ti
->tile
, TRACK_UPPER
);
3102 if (halftile_rt
!= rt
) {
3103 halftile_rti
= GetRailTypeInfo(halftile_rt
);
3104 rti
= GetRailTypeInfo(rt
);
3111 halftile_rti
= NULL
;
3112 rti
= GetRailTypeInfo(GetRailType(ti
->tile
, TRACK_UPPER
));
3113 draw_ground
= rti
->UsesOverlay();
3118 DrawFoundation(ti
, f
, IsTileSubtype(ti
->tile
, TT_BRIDGE
) ? GetTunnelBridgeDirection(ti
->tile
) : INVALID_DIAGDIR
);
3119 /* DrawFoundation modifies ti */
3123 DrawTrackGround(ti
, rgt
, track
!= TRACK_BIT_NONE
);
3126 if (IsValidCorner(halftile_corner
) || halftile_rti
== NULL
) {
3127 DrawTrackBits(ti
, track
, rti
, rgt
);
3129 if (IsValidCorner(halftile_corner
)) {
3130 DrawUpperHalftile(ti
, halftile_corner
, halftile_rti
, rgt
);
3132 } else if (track
== TRACK_BIT_HORZ
) {
3133 DrawHalftile(ti
, CORNER_S
, halftile_rti
, rgt
);
3134 DrawHalftile(ti
, CORNER_N
, rti
, rgt
);
3136 DrawHalftile(ti
, CORNER_W
, rti
, rgt
);
3137 DrawHalftile(ti
, CORNER_E
, halftile_rti
, rgt
);
3142 * Get surface height in point (x,y)
3143 * On tiles with halftile foundations move (x,y) to a safe point wrt. track
3145 static uint
GetSafeSlopePixelZ(TileIndex tile
, uint x
, uint y
, Track track
)
3148 case TRACK_UPPER
: x
&= ~0xF; y
&= ~0xF; break;
3149 case TRACK_LOWER
: x
|= 0xF; y
|= 0xF; break;
3150 case TRACK_LEFT
: x
|= 0xF; y
&= ~0xF; break;
3151 case TRACK_RIGHT
: x
&= ~0xF; y
|= 0xF; break;
3155 uint z
= GetSlopePixelZ_Track(tile
, x
, y
);
3157 if (IsTileSubtype(tile
, TT_BRIDGE
) && !IsExtendedRailBridge(tile
)) {
3158 assert(IsDiagonalTrack(track
));
3159 z
+= GetBridgePartialPixelZ(GetTunnelBridgeDirection(tile
), x
& 0xF, y
& 0xF);
3165 static void DrawSingleSignal(TileIndex tile
, Trackdir trackdir
)
3167 static const struct {
3168 Point pos
[2]; // signal position (left side, right side)
3169 SignalOffsets image
; // offset from base signal sprite
3171 { { {11, 3}, {11, 13} }, SIGNAL_TO_NORTHEAST
}, // TRACKDIR_X_NE
3172 { { { 3, 4}, {13, 4} }, SIGNAL_TO_SOUTHEAST
}, // TRACKDIR_Y_SE
3173 { { { 1, 0}, {10, 4} }, SIGNAL_TO_EAST
}, // TRACKDIR_UPPER_E
3174 { { {11, 4}, {14, 14} }, SIGNAL_TO_EAST
}, // TRACKDIR_LOWER_E
3175 { { { 8, 5}, {14, 1} }, SIGNAL_TO_SOUTH
}, // TRACKDIR_LEFT_S
3176 { { { 1, 14}, { 4, 6} }, SIGNAL_TO_SOUTH
}, // TRACKDIR_RIGHT_S
3177 { { { 0, 0}, { 0, 0} }, SIGNAL_TO_NORTHEAST
}, // TRACKDIR_RVREV_NE
3178 { { { 0, 0}, { 0, 0} }, SIGNAL_TO_NORTHEAST
}, // TRACKDIR_RVREV_SE
3179 { { { 4, 13}, { 4, 3} }, SIGNAL_TO_SOUTHWEST
}, // TRACKDIR_X_SW
3180 { { {11, 13}, { 3, 11} }, SIGNAL_TO_NORTHWEST
}, // TRACKDIR_Y_NW
3181 { { { 3, 10}, { 0, 1} }, SIGNAL_TO_WEST
}, // TRACKDIR_UPPER_W
3182 { { {14, 14}, { 5, 12} }, SIGNAL_TO_WEST
}, // TRACKDIR_LOWER_W
3183 { { {14, 1}, {12, 10} }, SIGNAL_TO_NORTH
}, // TRACKDIR_LEFT_N
3184 { { { 9, 11}, { 1, 14} }, SIGNAL_TO_NORTH
}, // TRACKDIR_RIGHT_N
3185 // { { { 0, 0}, { 0, 0} }, SIGNAL_TO_NORTHEAST }, // TRACKDIR_RVREV_SW
3186 // { { { 0, 0}, { 0, 0} }, SIGNAL_TO_NORTHEAST }, // TRACKDIR_RVREV_NW
3189 if (!HasSignalOnTrackdir(tile
, trackdir
)) return;
3191 Track track
= TrackdirToTrack(trackdir
);
3192 SignalType type
= GetSignalType(tile
, track
);
3193 SignalVariant variant
= GetSignalVariant(tile
, track
);
3194 SignalState condition
= GetSignalStateByTrackdir(tile
, trackdir
);
3196 SpriteID sprite
= GetCustomSignalSprite(GetRailTypeInfo(GetRailType(tile
, track
)), tile
, type
, variant
, condition
);
3197 SignalOffsets image
= SignalData
[trackdir
].image
;
3201 /* Normal electric signals are stored in a different sprite block than all other signals. */
3202 sprite
= (type
== SIGTYPE_NORMAL
&& variant
== SIG_ELECTRIC
) ? SPR_ORIGINAL_SIGNALS_BASE
: SPR_SIGNALS_BASE
- 16;
3203 sprite
+= type
* 16 + variant
* 64 + image
* 2 + condition
+ (IsPbsSignal(type
) ? 64 : 0);
3207 switch (_settings_game
.construction
.train_signal_side
) {
3208 case 0: side
= false; break; // left
3209 case 2: side
= true; break; // right
3210 default: side
= _settings_game
.vehicle
.road_side
!= 0; break; // driving side
3212 uint x
= TileX(tile
) * TILE_SIZE
+ SignalData
[trackdir
].pos
[side
].x
;
3213 uint y
= TileY(tile
) * TILE_SIZE
+ SignalData
[trackdir
].pos
[side
].y
;
3215 AddSortableSpriteToDraw(sprite
, PAL_NONE
, x
, y
, 1, 1, BB_HEIGHT_UNDER_BRIDGE
, GetSafeSlopePixelZ(tile
, x
, y
, track
));
3218 static void DrawSignals(TileIndex tile
, TrackBits rails
)
3220 if (rails
& TRACK_BIT_Y
) {
3221 DrawSingleSignal(tile
, TRACKDIR_Y_SE
);
3222 DrawSingleSignal(tile
, TRACKDIR_Y_NW
);
3223 } else if (rails
& TRACK_BIT_X
) {
3224 DrawSingleSignal(tile
, TRACKDIR_X_NE
);
3225 DrawSingleSignal(tile
, TRACKDIR_X_SW
);
3227 if (rails
& TRACK_BIT_LEFT
) {
3228 DrawSingleSignal(tile
, TRACKDIR_LEFT_S
);
3229 DrawSingleSignal(tile
, TRACKDIR_LEFT_N
);
3231 if (rails
& TRACK_BIT_RIGHT
) {
3232 DrawSingleSignal(tile
, TRACKDIR_RIGHT_S
);
3233 DrawSingleSignal(tile
, TRACKDIR_RIGHT_N
);
3235 if (rails
& TRACK_BIT_UPPER
) {
3236 DrawSingleSignal(tile
, TRACKDIR_UPPER_E
);
3237 DrawSingleSignal(tile
, TRACKDIR_UPPER_W
);
3239 if (rails
& TRACK_BIT_LOWER
) {
3240 DrawSingleSignal(tile
, TRACKDIR_LOWER_E
);
3241 DrawSingleSignal(tile
, TRACKDIR_LOWER_W
);
3246 static void DrawTile_Track(TileInfo
*ti
)
3248 if (IsTileSubtype(ti
->tile
, TT_TRACK
) || IsExtendedRailBridge(ti
->tile
)) {
3249 _drawtile_track_palette
= COMPANY_SPRITE_COLOUR(GetTileOwner(ti
->tile
));
3251 TrackBits rails
= GetTrackBits(ti
->tile
);
3253 DrawTrack(ti
, rails
);
3255 if (HasBit(_display_opt
, DO_FULL_DETAIL
) && IsTileSubtype(ti
->tile
, TT_TRACK
)) DrawTrackDetails(ti
, rails
);
3257 if (IsCatenaryDrawn()) DrawCatenary(ti
);
3259 DrawSignals(ti
->tile
, rails
);
3261 DrawBridgeGround(ti
);
3265 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(ti
->tile
));
3267 DiagDirection dir
= GetTunnelBridgeDirection(ti
->tile
);
3269 assert(rti
->bridge_offset
!= 8); // This one is used for roads
3270 const PalSpriteID
*psid
= GetBridgeRampSprite(GetRailBridgeType(ti
->tile
), rti
->bridge_offset
, ti
->tileh
, dir
);
3272 /* Draw PBS Reservation as SpriteCombine */
3273 StartSpriteCombine();
3275 /* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
3276 * it doesn't disappear behind it
3278 /* Bridge heads are drawn solid no matter how invisibility/transparency is set */
3279 AddSortableSpriteToDraw(psid
->sprite
, psid
->pal
, ti
->x
, ti
->y
, 16, 16, ti
->tileh
== SLOPE_FLAT
? 0 : 8, ti
->z
);
3281 if (rti
->UsesOverlay()) {
3282 SpriteID surface
= GetCustomRailSprite(rti
, ti
->tile
, RTSG_BRIDGE
);
3284 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(dir
))) {
3285 AddSortableSpriteToDraw(surface
+ ((DiagDirToAxis(dir
) == AXIS_X
) ? RTBO_X
: RTBO_Y
), PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 0, ti
->z
+ 8);
3287 AddSortableSpriteToDraw(surface
+ RTBO_SLOPE
+ dir
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
3290 /* Don't fallback to non-overlay sprite -- the spec states that
3291 * if an overlay is present then the bridge surface must be
3295 /* PBS debugging, draw reserved tracks darker */
3296 if (_game_mode
!= GM_MENU
&& _settings_client
.gui
.show_track_reservation
&& GetRailReservationTrackBits(ti
->tile
) != TRACK_BIT_NONE
) {
3297 if (HasBridgeFlatRamp(ti
->tileh
, DiagDirToAxis(dir
))) {
3298 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);
3300 AddSortableSpriteToDraw(rti
->base_sprites
.single_sloped
+ dir
, PALETTE_CRASH
, ti
->x
, ti
->y
, 16, 16, 8, ti
->z
);
3306 if (HasCatenaryDrawn(GetRailType(ti
->tile
))) {
3310 if (DiagDirToAxis(dir
) == AXIS_Y
) {
3311 DrawSingleSignal(ti
->tile
, TRACKDIR_Y_SE
);
3312 DrawSingleSignal(ti
->tile
, TRACKDIR_Y_NW
);
3314 DrawSingleSignal(ti
->tile
, TRACKDIR_X_NE
);
3315 DrawSingleSignal(ti
->tile
, TRACKDIR_X_SW
);
3319 DrawBridgeMiddle(ti
);
3322 static Foundation
GetFoundation_Track(TileIndex tile
, Slope tileh
)
3324 return IsTileSubtype(tile
, TT_TRACK
) ? GetRailFoundation(tileh
, GetTrackBits(tile
)) :
3325 IsExtendedRailBridge(tile
) ? FOUNDATION_LEVELED
:
3326 GetBridgeFoundation(tileh
, DiagDirToAxis(GetTunnelBridgeDirection(tile
)));
3329 static void TileLoop_Track(TileIndex tile
)
3331 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
3332 bool snow_or_desert
= IsOnSnow(tile
);
3333 switch (_settings_game
.game_creation
.landscape
) {
3337 /* As long as we do not have a snow density, we want to use the density
3338 * from the entry edge. For bridges this is the highest point.
3339 * (Independent of foundations) */
3340 if (snow_or_desert
== (GetTileMaxZ(tile
) > GetSnowLine())) return;
3344 if (GetTropicZone(tile
) != TROPICZONE_DESERT
|| snow_or_desert
) return;
3348 MarkTileDirtyByTile(tile
);
3352 RailGroundType old_ground
= GetRailGroundType(tile
);
3353 RailGroundType new_ground
;
3355 if (old_ground
== RAIL_GROUND_WATER
) {
3356 TileLoop_Water(tile
);
3360 switch (_settings_game
.game_creation
.landscape
) {
3363 Slope slope
= GetTileSlope(tile
, &z
);
3366 /* for non-flat track, use lower part of track
3367 * in other cases, use the highest part with track */
3368 TrackBits track
= GetTrackBits(tile
);
3369 Foundation f
= GetRailFoundation(slope
, track
);
3372 case FOUNDATION_NONE
:
3373 /* no foundation - is the track on the upper side of three corners raised tile? */
3374 if (IsSlopeWithThreeCornersRaised(slope
)) z
++;
3377 case FOUNDATION_INCLINED_X
:
3378 case FOUNDATION_INCLINED_Y
:
3379 /* sloped track - is it on a steep slope? */
3380 if (IsSteepSlope(slope
)) z
++;
3383 case FOUNDATION_STEEP_LOWER
:
3384 /* only lower part of steep slope */
3389 /* if it is a steep slope, then there is a track on higher part */
3390 if (IsSteepSlope(slope
)) z
++;
3395 half
= IsInsideMM(f
, FOUNDATION_STEEP_BOTH
, FOUNDATION_HALFTILE_N
+ 1);
3397 /* 'z' is now the lowest part of the highest track bit -
3398 * for sloped track, it is 'z' of lower part
3399 * for two track bits, it is 'z' of higher track bit
3400 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
3401 if (z
> GetSnowLine()) {
3402 if (half
&& z
- GetSnowLine() == 1) {
3403 /* track on non-continuous foundation, lower part is not under snow */
3404 new_ground
= RAIL_GROUND_HALF_SNOW
;
3406 new_ground
= RAIL_GROUND_ICE_DESERT
;
3414 if (GetTropicZone(tile
) == TROPICZONE_DESERT
) {
3415 new_ground
= RAIL_GROUND_ICE_DESERT
;
3421 new_ground
= RAIL_GROUND_GRASS
;
3423 if (old_ground
!= RAIL_GROUND_BARREN
) { // wait until bottom is green
3424 /* determine direction of fence */
3425 TrackBits rail
= GetTrackBits(tile
);
3427 Owner owner
= GetTileOwner(tile
);
3430 for (DiagDirection d
= DIAGDIR_BEGIN
; d
< DIAGDIR_END
; d
++) {
3431 static const TrackBits dir_to_trackbits
[DIAGDIR_END
] = {TRACK_BIT_3WAY_NE
, TRACK_BIT_3WAY_SE
, TRACK_BIT_3WAY_SW
, TRACK_BIT_3WAY_NW
};
3433 /* Track bit on this edge => no fence. */
3434 if ((rail
& dir_to_trackbits
[d
]) != TRACK_BIT_NONE
) continue;
3436 TileIndex tile2
= tile
+ TileOffsByDiagDir(d
);
3438 /* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
3439 if (!IsValidTile(tile2
) || IsHouseTile(tile2
) || IsIndustryTile(tile2
) ||
3440 (IsTileType(tile2
, TT_MISC
) && !IsRailDepotTile(tile2
)) ||
3441 IsRoadTile(tile2
) || (IsRailBridgeTile(tile2
) && !IsExtendedRailBridge(tile2
)) ||
3442 (IsObjectTile(tile2
) && !IsObjectType(tile2
, OBJECT_OWNED_LAND
)) || !IsTileOwner(tile2
, owner
)) {
3449 case (1 << DIAGDIR_NE
): new_ground
= RAIL_GROUND_FENCE_NE
; break;
3450 case (1 << DIAGDIR_SE
): new_ground
= RAIL_GROUND_FENCE_SE
; break;
3451 case (1 << DIAGDIR_SW
): new_ground
= RAIL_GROUND_FENCE_SW
; break;
3452 case (1 << DIAGDIR_NW
): new_ground
= RAIL_GROUND_FENCE_NW
; break;
3453 case (1 << DIAGDIR_NE
) | (1 << DIAGDIR_SW
): new_ground
= RAIL_GROUND_FENCE_NESW
; break;
3454 case (1 << DIAGDIR_SE
) | (1 << DIAGDIR_NW
): new_ground
= RAIL_GROUND_FENCE_SENW
; break;
3455 case (1 << DIAGDIR_NE
) | (1 << DIAGDIR_SE
): new_ground
= RAIL_GROUND_FENCE_VERT1
; break;
3456 case (1 << DIAGDIR_NE
) | (1 << DIAGDIR_NW
): new_ground
= RAIL_GROUND_FENCE_HORIZ2
; break;
3457 case (1 << DIAGDIR_SE
) | (1 << DIAGDIR_SW
): new_ground
= RAIL_GROUND_FENCE_HORIZ1
; break;
3458 case (1 << DIAGDIR_SW
) | (1 << DIAGDIR_NW
): new_ground
= RAIL_GROUND_FENCE_VERT2
; break;
3459 default: NOT_REACHED();
3464 if (old_ground
!= new_ground
) {
3465 SetRailGroundType(tile
, new_ground
);
3466 MarkTileDirtyByTile(tile
);
3471 static TrackStatus
GetTileRailwayStatus_Track(TileIndex tile
, DiagDirection side
)
3473 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
3474 if (side
== GetTunnelBridgeDirection(tile
)) return 0;
3477 TrackBits trackbits
= GetTrackBits(tile
);
3478 TrackdirBits red_signals
= TRACKDIR_BIT_NONE
;
3482 a
= GetPresentSignals(tile
, TRACK_UPPER
);
3483 /* When signals are not present (in neither direction),
3484 * we pretend them to be green. Otherwise, it depends on
3485 * the signal type. For signals that are only active from
3486 * one side, we set the missing signals explicitly to
3487 * `green'. Otherwise, they implicitly become `red'. */
3491 b
= GetSignalStates(tile
, TRACK_UPPER
) & a
;
3492 if (!IsOnewaySignal(GetSignalType(tile
, TRACK_UPPER
))) b
|= ~a
;
3495 if ((b
& 0x2) == 0) red_signals
|= (TRACKDIR_BIT_LEFT_N
| TRACKDIR_BIT_X_NE
| TRACKDIR_BIT_Y_SE
| TRACKDIR_BIT_UPPER_E
);
3496 if ((b
& 0x1) == 0) red_signals
|= (TRACKDIR_BIT_LEFT_S
| TRACKDIR_BIT_X_SW
| TRACKDIR_BIT_Y_NW
| TRACKDIR_BIT_UPPER_W
);
3498 a
= GetPresentSignals(tile
, TRACK_LOWER
);
3502 b
= GetSignalStates(tile
, TRACK_LOWER
) & a
;
3503 if (!IsOnewaySignal(GetSignalType(tile
, TRACK_LOWER
))) b
|= ~a
;
3506 if ((b
& 0x2) == 0) red_signals
|= (TRACKDIR_BIT_RIGHT_N
| TRACKDIR_BIT_LOWER_E
);
3507 if ((b
& 0x1) == 0) red_signals
|= (TRACKDIR_BIT_RIGHT_S
| TRACKDIR_BIT_LOWER_W
);
3509 return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits
), red_signals
);
3512 static TrackdirBits
GetTileWaterwayStatus_Track(TileIndex tile
, DiagDirection side
)
3514 /* Case of half tile slope with water. */
3515 if (IsTileSubtype(tile
, TT_TRACK
) && GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(GetTileSlope(tile
))) {
3516 TrackBits tb
= GetTrackBits(tile
);
3518 default: NOT_REACHED();
3519 case TRACK_BIT_UPPER
: tb
= TRACK_BIT_LOWER
; break;
3520 case TRACK_BIT_LOWER
: tb
= TRACK_BIT_UPPER
; break;
3521 case TRACK_BIT_LEFT
: tb
= TRACK_BIT_RIGHT
; break;
3522 case TRACK_BIT_RIGHT
: tb
= TRACK_BIT_LEFT
; break;
3524 return TrackBitsToTrackdirBits(tb
);
3527 return TRACKDIR_BIT_NONE
;
3530 static bool ClickTile_Track(TileIndex tile
)
3535 static void GetTileDesc_Track(TileIndex tile
, TileDesc
*td
)
3537 static const StringID signal_type
[6][6] = {
3539 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS
,
3540 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS
,
3541 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS
,
3542 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS
,
3543 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS
,
3544 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
3547 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS
,
3548 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS
,
3549 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS
,
3550 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS
,
3551 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS
,
3552 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
3555 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS
,
3556 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS
,
3557 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS
,
3558 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS
,
3559 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS
,
3560 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
3563 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS
,
3564 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS
,
3565 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS
,
3566 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS
,
3567 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS
,
3568 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
3571 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS
,
3572 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS
,
3573 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS
,
3574 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS
,
3575 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS
,
3576 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
3579 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
,
3580 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
,
3581 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
,
3582 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
,
3583 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
,
3584 STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
3588 const RailtypeInfo
*rti
= GetRailTypeInfo(GetRailType(tile
));
3589 td
->rail_speed
= rti
->max_speed
;
3590 td
->owner
[0] = GetTileOwner(tile
);
3592 if (IsTileSubtype(tile
, TT_TRACK
)) {
3593 SetDParamX(td
->dparam
, 0, rti
->strings
.name
);
3595 if (HasSignalOnTrack(tile
, TRACK_UPPER
)) {
3596 SignalType primary
= GetSignalType(tile
, TRACK_UPPER
);
3597 SignalType secondary
= HasSignalOnTrack(tile
, TRACK_LOWER
) ? GetSignalType(tile
, TRACK_LOWER
) : primary
;
3598 td
->str
= signal_type
[secondary
][primary
];
3599 } else if (HasSignalOnTrack(tile
, TRACK_LOWER
)) {
3600 SignalType signal
= GetSignalType(tile
, TRACK_LOWER
);
3601 td
->str
= signal_type
[signal
][signal
];
3603 td
->str
= STR_LAI_RAIL_DESCRIPTION_TRACK
;
3606 const BridgeSpec
*spec
= GetBridgeSpec(GetRailBridgeType(tile
));
3607 td
->str
= spec
->transport_name
[TRANSPORT_RAIL
];
3609 uint16 spd
= spec
->speed
;
3610 if (td
->rail_speed
== 0 || spd
< td
->rail_speed
) {
3611 td
->rail_speed
= spd
;
3616 static void ChangeTileOwner_Track(TileIndex tile
, Owner old_owner
, Owner new_owner
)
3618 if (!IsTileOwner(tile
, old_owner
)) return;
3620 if (new_owner
!= INVALID_OWNER
) {
3621 /* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
3622 TrackBits bits
= GetTrackBits(tile
);
3623 uint factor
= IsTileSubtype(tile
, TT_BRIDGE
) ? TUNNELBRIDGE_TRACKBIT_FACTOR
: 1;
3628 case TRACK_BIT_HORZ
:
3629 case TRACK_BIT_VERT
:
3630 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
3631 DiagDirection dir
= GetTunnelBridgeDirection(tile
);
3632 rt
= GetSideRailType(tile
, dir
);
3633 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= TUNNELBRIDGE_TRACKBIT_FACTOR
;
3634 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += TUNNELBRIDGE_TRACKBIT_FACTOR
;
3635 rt
= GetSideRailType(tile
, ReverseDiagDir(dir
));
3637 rt
= GetRailType(tile
, TRACK_UPPER
);
3638 Company::Get(old_owner
)->infrastructure
.rail
[rt
]--;
3639 Company::Get(new_owner
)->infrastructure
.rail
[rt
]++;
3640 rt
= GetRailType(tile
, TRACK_LOWER
);
3642 Company::Get(old_owner
)->infrastructure
.rail
[rt
]--;
3643 Company::Get(new_owner
)->infrastructure
.rail
[rt
]++;
3644 num_sigs
= CountBits(GetPresentSignals(tile
, TRACK_UPPER
)) + CountBits(GetPresentSignals(tile
, TRACK_LOWER
));
3647 case TRACK_BIT_RIGHT
:
3648 case TRACK_BIT_LOWER
:
3649 rt
= GetRailType(tile
, TRACK_LOWER
);
3650 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= factor
;
3651 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += factor
;
3652 num_sigs
= CountBits(GetPresentSignals(tile
, TRACK_LOWER
));
3655 case TRACK_BIT_LOWER_RIGHT
:
3656 rt
= GetRailType(tile
, TRACK_LOWER
);
3657 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= 2 * 2 * factor
;
3658 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += 2 * 2 * factor
;
3663 rt
= GetRailType(tile
, TRACK_UPPER
);
3664 uint num_pieces
= CountBits(bits
);
3665 if (TracksOverlap(bits
)) {
3666 num_pieces
*= num_pieces
;
3669 num_sigs
= CountBits(GetPresentSignals(tile
, TRACK_UPPER
));
3671 num_pieces
*= factor
;
3672 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= num_pieces
;
3673 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += num_pieces
;
3678 Company::Get(old_owner
)->infrastructure
.signal
-= num_sigs
;
3679 Company::Get(new_owner
)->infrastructure
.signal
+= num_sigs
;
3681 if (IsTileSubtype(tile
, TT_BRIDGE
)) {
3682 TileIndex other_end
= GetOtherBridgeEnd(tile
);
3683 if (tile
< other_end
) {
3684 uint num_pieces
= GetTunnelBridgeLength(tile
, other_end
) * TUNNELBRIDGE_TRACKBIT_FACTOR
;
3685 RailType rt
= GetBridgeRailType(tile
);
3686 Company::Get(old_owner
)->infrastructure
.rail
[rt
] -= num_pieces
;
3687 Company::Get(new_owner
)->infrastructure
.rail
[rt
] += num_pieces
;
3691 SetTileOwner(tile
, new_owner
);
3693 DoCommand(tile
, 0, 0, DC_EXEC
| DC_BANKRUPT
, CMD_LANDSCAPE_CLEAR
);
3698 * Tests if autoslope is allowed.
3700 * @param tile The tile.
3701 * @param flags Terraform command flags.
3702 * @param z_old Old TileZ.
3703 * @param tileh_old Old TileSlope.
3704 * @param z_new New TileZ.
3705 * @param tileh_new New TileSlope.
3706 * @param rail_bits Trackbits.
3708 static CommandCost
TestAutoslopeOnRailTile(TileIndex tile
, uint flags
, int z_old
, Slope tileh_old
, int z_new
, Slope tileh_new
, TrackBits rail_bits
)
3710 if (!_settings_game
.construction
.build_on_slopes
|| !AutoslopeEnabled()) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
3712 /* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3713 if (CheckRailSlope(tileh_new
, rail_bits
, TRACK_BIT_NONE
, tile
).Failed()) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
3715 /* Get the slopes on top of the foundations */
3716 z_old
+= ApplyFoundationToSlope(GetRailFoundation(tileh_old
, rail_bits
), &tileh_old
);
3717 z_new
+= ApplyFoundationToSlope(GetRailFoundation(tileh_new
, rail_bits
), &tileh_new
);
3719 Corner track_corner
;
3720 switch (rail_bits
) {
3721 case TRACK_BIT_LEFT
: track_corner
= CORNER_W
; break;
3722 case TRACK_BIT_LOWER
: track_corner
= CORNER_S
; break;
3723 case TRACK_BIT_RIGHT
: track_corner
= CORNER_E
; break;
3724 case TRACK_BIT_UPPER
: track_corner
= CORNER_N
; break;
3726 /* Surface slope must not be changed */
3728 if (z_old
!= z_new
|| tileh_old
!= tileh_new
) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
3729 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
3732 /* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3733 z_old
+= GetSlopeZInCorner(RemoveHalftileSlope(tileh_old
), track_corner
);
3734 z_new
+= GetSlopeZInCorner(RemoveHalftileSlope(tileh_new
), track_corner
);
3735 if (z_old
!= z_new
) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK
);
3737 CommandCost cost
= CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
3738 /* Make the ground dirty, if surface slope has changed */
3739 if (tileh_old
!= tileh_new
) {
3740 /* If there is flat water on the lower halftile add the cost for clearing it */
3741 if (GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(tileh_old
)) cost
.AddCost(_price
[PR_CLEAR_WATER
]);
3742 if ((flags
& DC_EXEC
) != 0) SetRailGroundType(tile
, RAIL_GROUND_BARREN
);
3747 static CommandCost
TerraformTile_Track(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
3750 Slope tileh_old
= GetTileSlope(tile
, &z_old
);
3752 if (IsTileSubtype(tile
, TT_TRACK
)) {
3753 TrackBits rail_bits
= GetTrackBits(tile
);
3754 /* Is there flat water on the lower halftile that must be cleared expensively? */
3755 bool was_water
= (GetRailGroundType(tile
) == RAIL_GROUND_WATER
&& IsSlopeWithOneCornerRaised(tileh_old
));
3757 /* Allow clearing the water only if there is no ship */
3759 VehicleTileFinder
iter (tile
);
3760 while (!iter
.finished()) {
3761 Vehicle
*v
= iter
.next();
3762 if (v
->type
== VEH_SHIP
) iter
.set_found();
3764 if (iter
.was_found()) return_cmd_error(STR_ERROR_SHIP_IN_THE_WAY
);
3767 /* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3768 CommandCost autoslope_result
= TestAutoslopeOnRailTile(tile
, flags
, z_old
, tileh_old
, z_new
, tileh_new
, rail_bits
);
3770 /* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3771 Corner allowed_corner
;
3772 switch (rail_bits
) {
3773 case TRACK_BIT_RIGHT
: allowed_corner
= CORNER_W
; break;
3774 case TRACK_BIT_UPPER
: allowed_corner
= CORNER_S
; break;
3775 case TRACK_BIT_LEFT
: allowed_corner
= CORNER_E
; break;
3776 case TRACK_BIT_LOWER
: allowed_corner
= CORNER_N
; break;
3777 default: return autoslope_result
;
3780 Foundation f_old
= GetRailFoundation(tileh_old
, rail_bits
);
3782 /* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3783 if (tileh_old
!= SLOPE_NS
&& tileh_old
!= SLOPE_EW
&& IsSpecialRailFoundation(f_old
)) return autoslope_result
;
3785 /* Everything is valid, which only changes allowed_corner */
3786 for (Corner corner
= (Corner
)0; corner
< CORNER_END
; corner
= (Corner
)(corner
+ 1)) {
3787 if (allowed_corner
== corner
) continue;
3788 if (z_old
+ GetSlopeZInCorner(tileh_old
, corner
) != z_new
+ GetSlopePixelZInCorner(tileh_new
, corner
)) return autoslope_result
;
3791 /* Make the ground dirty */
3792 if ((flags
& DC_EXEC
) != 0) SetRailGroundType(tile
, RAIL_GROUND_BARREN
);
3794 /* allow terraforming */
3795 return CommandCost(EXPENSES_CONSTRUCTION
, was_water
? _price
[PR_CLEAR_WATER
] : (Money
)0);
3797 if (_settings_game
.construction
.build_on_slopes
&& AutoslopeEnabled()) {
3798 DiagDirection direction
= GetTunnelBridgeDirection(tile
);
3800 if (IsExtendedRailBridge(tile
)) {
3801 if (IsValidRailBridgeBits(tileh_new
, direction
, GetTrackBits(tile
))) return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
3803 /* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */
3804 CheckBridgeSlope(direction
, &tileh_old
, &z_old
);
3805 CommandCost res
= CheckBridgeSlope(direction
, &tileh_new
, &z_new
);
3807 /* Surface slope is valid and remains unchanged? */
3808 if (res
.Succeeded() && (z_old
== z_new
) && (tileh_old
== tileh_new
)) return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_FOUNDATION
]);
3812 return DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
3817 extern const TileTypeProcs _tile_type_rail_procs
= {
3818 DrawTile_Track
, // draw_tile_proc
3819 GetSlopePixelZ_Track
, // get_slope_z_proc
3820 ClearTile_Track
, // clear_tile_proc
3821 NULL
, // add_accepted_cargo_proc
3822 GetTileDesc_Track
, // get_tile_desc_proc
3823 GetTileRailwayStatus_Track
, // get_tile_railway_status_proc
3824 NULL
, // get_tile_road_status_proc
3825 GetTileWaterwayStatus_Track
, // get_tile_waterway_status_proc
3826 ClickTile_Track
, // click_tile_proc
3827 NULL
, // animate_tile_proc
3828 TileLoop_Track
, // tile_loop_proc
3829 ChangeTileOwner_Track
, // change_tile_owner_proc
3830 NULL
, // add_produced_cargo_proc
3831 GetFoundation_Track
, // get_foundation_proc
3832 TerraformTile_Track
, // terraform_tile_proc