Remove SIGTYPE_LAST_NOPBS
[openttd/fttd.git] / src / rail_cmd.cpp
blobee5253fadd27b152d367b5352327ccab68bf9cc6
1 /* $Id$ */
3 /*
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/>.
8 */
10 /** @file rail_cmd.cpp Handling of rail tiles. */
12 #include "stdafx.h"
13 #include "map/zoneheight.h"
14 #include "map/road.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"
22 #include "train.h"
23 #include "autoslope.h"
24 #include "water.h"
25 #include "vehicle_func.h"
26 #include "sound_func.h"
27 #include "tunnelbridge.h"
28 #include "elrail_func.h"
29 #include "town.h"
30 #include "pbs.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"
37 #include "bridge.h"
38 #include "signal_func.h"
39 #include "object.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. */
52 enum SignalOffsets {
53 SIGNAL_TO_NORTHEAST,
54 SIGNAL_TO_SOUTHWEST,
55 SIGNAL_TO_SOUTHEAST,
56 SIGNAL_TO_NORTHWEST,
57 SIGNAL_TO_WEST,
58 SIGNAL_TO_EAST,
59 SIGNAL_TO_NORTH,
60 SIGNAL_TO_SOUTH,
63 /**
64 * Reset all rail type information to its default values.
66 void ResetRailTypes()
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
116 void InitRailTypes()
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));
135 rti->label = label;
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;
154 return rt;
158 return INVALID_RAILTYPE;
161 static const byte _track_sloped_sprites[14] = {
162 14, 15, 22, 13,
163 0, 21, 17, 12,
164 23, 0, 18, 20,
165 19, 16
169 /* 4
170 * ---------
171 * |\ /|
172 * | \ 1/ |
173 * | \ / |
174 * | \ / |
175 * 16| \ |32
176 * | / \2 |
177 * | / \ |
178 * | / \ |
179 * |/ \|
180 * ---------
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);
220 } else {
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);
247 if (rt1 != rt2) {
248 /* Two different railtypes present */
249 if ((railtype == rt1 || HasPowerOnRail(rt1, railtype)) && (railtype == rt2 || HasPowerOnRail(rt2, railtype))) {
250 rt = railtype;
251 } else if ((railtype == rt1 || HasPowerOnRail(railtype, rt1)) && HasPowerOnRail(rt2, rt1)) {
252 rt = railtype = rt1;
253 } else if ((railtype == rt2 || HasPowerOnRail(railtype, rt2)) && HasPowerOnRail(rt1, rt2)) {
254 rt = railtype = rt2;
255 } else {
256 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
258 } else if (railtype == rt1) {
259 /* Nothing to do */
260 rt = INVALID_RAILTYPE;
261 } else if (HasPowerOnRail(railtype, rt1)) {
262 /* Try to keep existing railtype */
263 railtype = rt1;
264 rt = INVALID_RAILTYPE;
265 } else if (HasPowerOnRail(rt1, railtype)) {
266 rt = railtype;
267 } else {
268 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
270 } else {
271 rt = GetRailType(tile, FindFirstTrack(current));
273 if (railtype == rt) {
274 /* Nothing to do */
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 */
280 railtype = rt;
281 rt = INVALID_RAILTYPE;
282 } else if (HasPowerOnRail(rt, railtype)) {
283 rt = railtype;
284 } else {
285 return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
289 CommandCost ret;
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);
303 return ret;
307 /** Valid TrackBits on a specific (non-steep)-slope without foundation */
308 static const TrackBits _valid_tracks_without_foundation[15] = {
309 TRACK_BIT_ALL,
310 TRACK_BIT_RIGHT,
311 TRACK_BIT_UPPER,
312 TRACK_BIT_X,
314 TRACK_BIT_LEFT,
315 TRACK_BIT_NONE,
316 TRACK_BIT_Y,
317 TRACK_BIT_LOWER,
319 TRACK_BIT_LOWER,
320 TRACK_BIT_Y,
321 TRACK_BIT_NONE,
322 TRACK_BIT_LEFT,
324 TRACK_BIT_X,
325 TRACK_BIT_UPPER,
326 TRACK_BIT_RIGHT,
329 /** Valid TrackBits on a specific (non-steep)-slope with leveled foundation */
330 static const TrackBits _valid_tracks_on_leveled_foundation[15] = {
331 TRACK_BIT_NONE,
332 TRACK_BIT_LEFT,
333 TRACK_BIT_LOWER,
334 TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_LEFT,
336 TRACK_BIT_RIGHT,
337 TRACK_BIT_ALL,
338 TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_RIGHT,
339 TRACK_BIT_ALL,
341 TRACK_BIT_UPPER,
342 TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_LEFT,
343 TRACK_BIT_ALL,
344 TRACK_BIT_ALL,
346 TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_RIGHT,
347 TRACK_BIT_ALL,
348 TRACK_BIT_ALL
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);
379 } else {
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);
384 Corner track_corner;
385 switch (bits) {
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;
391 case TRACK_BIT_HORZ:
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);
396 case TRACK_BIT_VERT:
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);
401 case TRACK_BIT_X:
402 if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_X;
403 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
405 case TRACK_BIT_Y:
406 if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_Y;
407 return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
409 default:
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);
475 switch (diff) {
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
489 * @param text unused
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)) {
504 case TT_RAILWAY: {
505 CommandCost ret = CheckTileOwnership(tile);
506 if (ret.Failed()) return ret;
508 ret = CheckTrackCombination(tile, track, railtype, flags);
509 if (ret.Failed()) return ret;
510 cost.AddCost(ret);
512 if (IsTileSubtype(tile, TT_TRACK)) {
513 ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
514 if (ret.Failed()) return ret;
515 cost.AddCost(ret);
516 } else {
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]++;
533 } else {
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;
537 } else {
538 uint pieces = CountBits(bits);
539 pieces *= pieces;
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));
545 pieces *= pieces;
546 if (IsTileSubtype(tile, TT_BRIDGE)) pieces *= TUNNELBRIDGE_TRACKBIT_FACTOR;
547 Company::Get(owner)->infrastructure.rail[rt] += pieces;
549 DirtyCompanyInfrastructureWindows(owner);
551 break;
554 case TT_ROAD: {
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);
572 switch (roadtypes) {
573 default: break;
574 case ROADTYPES_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);
579 if (c != NULL) {
580 /* A full diagonal tile has two road bits. */
581 c->infrastructure.road[ROADTYPE_ROAD] += 2;
582 DirtyCompanyInfrastructureWindows(c->index);
585 roadtypes |= ROADTYPES_ROAD;
586 break;
588 case ROADTYPES_ALL:
589 if (road != tram) return CMD_ERROR;
590 break;
593 road |= tram;
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);
603 break;
606 goto try_clear;
609 case TT_MISC:
610 if (IsLevelCrossingTile(tile) && GetCrossingRailBits(tile) == trackbit) {
611 return_cmd_error(STR_ERROR_ALREADY_BUILT);
613 /* FALL THROUGH */
615 try_clear:
616 default: {
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;
622 cost.AddCost(ret);
624 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
625 if (ret.Failed()) return ret;
626 cost.AddCost(ret);
628 if (water_ground) {
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);
639 break;
643 if (flags & DC_EXEC) {
644 MarkTileDirtyByTile(tile);
645 AddTrackToSignalBuffer(tile, track, _current_company);
646 YapfNotifyTrackLayoutChange(tile, track);
649 cost.AddCost(RailBuildCost(railtype));
650 return cost;
653 static void NotifyTrackRemoval(TileIndex tile, Track track, bool was_crossing, Owner owner)
655 if (was_crossing) {
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);
664 } else {
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) {
703 Train *v = NULL;
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);
715 pieces *= pieces;
716 if (IsTileSubtype(tile, TT_BRIDGE)) pieces *= TUNNELBRIDGE_TRACKBIT_FACTOR;
717 Company::Get(owner)->infrastructure.rail[rt] -= pieces;
718 /* Add new infrastructure count. */
719 present ^= trackbit;
720 if (present == TRACK_BIT_HORZ || present == TRACK_BIT_VERT) {
721 pieces = IsTileSubtype(tile, TT_BRIDGE) ? TUNNELBRIDGE_TRACKBIT_FACTOR + 1 : 2;
722 } else {
723 pieces = CountBits(present);
724 pieces *= pieces;
725 if (IsTileSubtype(tile, TT_BRIDGE)) pieces *= TUNNELBRIDGE_TRACKBIT_FACTOR;
727 Company::Get(owner)->infrastructure.rail[rt] += pieces;
728 } else {
729 Company::Get(owner)->infrastructure.rail[rt]--;
730 present ^= trackbit;
732 DirtyCompanyInfrastructureWindows(owner);
734 if (present == 0) {
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)) {
738 MakeShore(tile);
739 } else {
740 DoClearSquare(tile);
742 DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile);
743 } else {
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);
754 return cost;
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;
773 bits &= ~remove;
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)))]--;
780 } else {
781 bits &= ~remove;
782 Company::Get(owner)->infrastructure.rail[rt] -= TUNNELBRIDGE_TRACKBIT_FACTOR;
785 if (bits == TRACK_BIT_NONE) {
786 DoClearSquare(tile);
787 DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile);
788 } else {
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);
887 if (n == 1) {
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));
892 } else {
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);
901 return cost;
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);
926 Train *v = NULL;
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);
947 return cost;
951 * Remove a single piece of track
952 * @param tile tile to remove track from
953 * @param flags operation to perform
954 * @param p1 unused
955 * @param p2 rail orientation
956 * @param text unused
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)) {
966 case TT_MISC:
967 if (!IsLevelCrossingTile(tile) || GetCrossingRailTrack(tile) != track) break;
968 return RemoveCrossingTrack(tile, flags);
970 case TT_RAILWAY:
971 if (IsTileSubtype(tile, TT_BRIDGE)) {
972 return RemoveBridgeTrack(tile, track, flags);
973 } else {
974 return RemoveRailTrack(tile, track, flags);
977 default: break;
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) {
1012 MakeShore(t);
1013 MarkTileDirtyByTile(t);
1014 return flooded;
1018 if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
1019 flooded = true;
1020 SetRailGroundType(t, RAIL_GROUND_WATER);
1021 MarkTileDirtyByTile(t);
1023 } else {
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)) {
1027 flooded = true;
1028 SetRailGroundType(t, RAIL_GROUND_WATER);
1029 MarkTileDirtyByTile(t);
1033 return flooded;
1036 static const CoordDiff _trackdelta[] = {
1037 { -1, 0 }, { 0, 1 }, { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, 1 },
1038 { 0, 0 },
1039 { 0, 0 },
1040 { 1, 0 }, { 0, -1 }, { 0, -1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
1041 { 0, 0 },
1042 { 0, 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 */
1056 int dx = ex - x;
1057 int dy = ey - y;
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
1075 trdx = -trdx;
1076 trdy = -trdy;
1077 } else { // other direction is invalid too, invalid drag
1078 return CMD_ERROR;
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;
1124 for (;;) {
1125 if (seen_bridgehead && IsRailBridgeTile(tile) && DiagDirToDiagTrackdir(ReverseDiagDir(GetTunnelBridgeDirection(tile))) == trackdir) {
1126 seen_bridgehead = false;
1127 } else {
1128 CommandCost ret = DoCommand(tile, remove ? 0 : railtype, TrackdirToTrack(trackdir), flags, remove ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL);
1130 if (ret.Failed()) {
1131 last_error = ret;
1132 if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
1133 if (HasBit(p2, 8)) return last_error;
1134 break;
1137 /* Ownership errors are more important. */
1138 if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
1139 } else {
1140 had_success = true;
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;
1158 return last_error;
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)
1231 )) {
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);
1248 MakeDefaultName(d);
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));
1259 return cost;
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 */
1288 SignalPair signals;
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;
1319 } else {
1320 if (sigtype != SIGTYPE_NORMAL || p2 == 0 || p2 > 2) return CMD_ERROR;
1322 } else {
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);
1337 } else {
1338 return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1341 CommandCost cost (EXPENSES_CONSTRUCTION);
1342 switch (mode) {
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);
1366 break;
1368 /* build new signals--fall through */
1369 case SIGNALS_BUILD:
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);
1382 } else {
1383 assert(signalpair_has_signal(&signals, false));
1384 sigtype = SIGTYPE_NORMAL;
1385 signalpair_set_present(&signals, 2);
1386 signalpair_set_type(&signals, SIGTYPE_NORMAL);
1388 } else {
1389 /* build new signals */
1390 cost.AddCost(_price[PR_BUILD_SIGNALS]);
1392 uint present;
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;
1398 present = 2;
1399 } else {
1400 assert(sigtype == SIGTYPE_NORMAL || sigtype == SIGTYPE_PBS_ONEWAY);
1401 present = 1;
1403 signalpair_set_present(&signals, present);
1404 signalpair_set_type_variant(&signals, sigtype, sigvar);
1405 signalpair_set_states(&signals, 3);
1407 break;
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();
1412 /* fall through */
1413 case SIGNALS_COPY:
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]);
1421 } else {
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);
1429 break;
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]);
1439 } else {
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);
1447 break;
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);
1454 break;
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);
1470 } else {
1471 other_signals = 0; // 0 means no changes
1473 } else {
1474 if (signalpair_has_signal(&other_signals, false)) {
1475 signalpair_set_present(&other_signals, 2);
1476 signalpair_set_type(&other_signals, SIGTYPE_NORMAL);
1477 } else {
1478 other_signals = 0; // 0 means no changes
1481 } else {
1482 other_signals = 0;
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));
1506 } else {
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;
1528 } else {
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++) {
1544 if (v[i] != NULL) {
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);
1554 return cost;
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)) {
1578 case TT_RAILWAY:
1579 if (!IsTileSubtype(tile, TT_TRACK)) goto bridge;
1580 if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1581 signal_ctr++;
1582 if (IsDiagonalTrackdir(trackdir)) {
1583 signal_ctr++;
1584 /* Ensure signal_ctr even so X and Y pieces get signals */
1585 ClrBit(signal_ctr, 0);
1587 return true;
1589 case TT_MISC:
1590 if (IsLevelCrossingTile(tile)) {
1591 signal_ctr += 2;
1592 return true;
1593 } else if (!IsTunnelTile(tile)) return false;
1595 if (GetTunnelTransportType(tile) != TRANSPORT_RAIL) return false;
1596 bridge:;{
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;
1606 return true;
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;
1664 byte signals_ref;
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) {
1670 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
1689 **********
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 */
1695 int signal_ctr = 0;
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;
1702 for (;;) {
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) {
1713 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) {
1733 signals ^= 3;
1736 ret = DoCommand(last_suitable_tile, p1, signals, flags, remove ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);
1739 /* Collect cost. */
1740 if (!test_only) {
1741 /* Be user-friendly and try placing signals as much as possible */
1742 if (ret.Succeeded()) {
1743 had_success = true;
1744 total_cost.AddCost(ret);
1745 last_used_ctr = last_suitable_ctr;
1746 last_suitable_tile = INVALID_TILE;
1747 } else {
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) {
1751 last_error = ret;
1757 if (autofill) {
1758 if (!CheckSignalAutoFill(tile, trackdir, signal_ctr, remove)) break;
1760 /* Prevent possible loops */
1761 if (tile == start_tile && trackdir == start_trackdir) break;
1762 } else {
1763 if (tile == end_tile) break;
1765 tile += ToTileIndexDiff(_trackdelta[trackdir]);
1766 signal_ctr++;
1768 /* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1769 if (IsDiagonalTrackdir(trackdir)) {
1770 signal_ctr++;
1771 } else {
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);
1804 * Remove signals
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
1811 * @param p2 unused
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);
1835 } else {
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;
1865 /* Do it? */
1866 if (flags & DC_EXEC) {
1867 Train *v = NULL;
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,
1876 * so
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
1880 * the tunnel.
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
1885 * other end).
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
1889 * tunnel.
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);
1970 return v;
1973 template <typename T>
1974 static inline void FindUnpoweredReservationTrains(T *vector, TileIndex tile, RailType rt)
1976 TrackBits reserved = GetReservedTrackbits(tile);
1977 Track track;
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));
2022 break;
2025 /* fall through */
2026 case TRACK_BIT_RIGHT:
2027 case TRACK_BIT_LOWER:
2028 case TRACK_BIT_LOWER_RIGHT:
2029 type = GetRailType(tile, TRACK_LOWER);
2030 /* fall through */
2031 default:
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));
2041 break;
2044 return cost;
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));
2066 uint num_pieces;
2068 TrackBits bits = GetTrackBits(tile);
2069 switch (bits) {
2070 case TRACK_BIT_HORZ:
2071 case TRACK_BIT_VERT:
2072 num_pieces = 2;
2073 c->infrastructure.rail[GetRailType(tile, TRACK_UPPER)]--;
2074 c->infrastructure.rail[GetRailType(tile, TRACK_LOWER)]--;
2075 break;
2077 case TRACK_BIT_RIGHT:
2078 case TRACK_BIT_LOWER:
2079 num_pieces = 1;
2080 c->infrastructure.rail[GetRailType(tile, TRACK_LOWER)]--;
2081 break;
2083 case TRACK_BIT_LOWER_RIGHT:
2084 num_pieces = 2 * 2;
2085 c->infrastructure.rail[GetRailType(tile, TRACK_LOWER)] -= 2 * 2;
2086 break;
2088 default:
2089 num_pieces = CountBits(bits);
2090 if (TracksOverlap(bits)) num_pieces *= num_pieces;
2091 c->infrastructure.rail[GetRailType(tile, TRACK_UPPER)] -= num_pieces;
2092 break;
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);
2114 return ret;
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;
2133 cost.AddCost(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]++;
2165 num_pieces++;
2166 } else {
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]++;
2175 num_pieces++;
2176 } else {
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);
2209 return cost;
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));
2245 Train *v = NULL;
2246 if (HasTunnelHeadReservation(tile)) {
2247 v = FindUnpoweredReservationTrain(tile, track, totype);
2250 Train *w = NULL;
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
2310 Train *v = NULL;
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)) {
2362 CommandCost ret;
2364 Track track = INVALID_TRACK;
2365 bool reserved;
2367 /* Check if there is any track on tile */
2368 switch (GetTileType(tile)) {
2369 case TT_RAILWAY:
2370 if (IsTileSubtype(tile, TT_TRACK)) {
2371 ret = ConvertTrack(tile, totype, &affected_trains, flags);
2372 } else {
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);
2380 break;
2382 case TT_MISC:
2383 switch (GetTileSubtype(tile)) {
2384 default: continue;
2386 case TT_MISC_CROSSING:
2387 if (RailNoLevelCrossings(totype)) {
2388 err.MakeError(STR_ERROR_CROSSING_DISALLOWED);
2389 continue;
2391 track = GetCrossingRailTrack(tile);
2392 reserved = HasCrossingReservation(tile);
2393 break;
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);
2404 break;
2407 case TT_MISC_DEPOT:
2408 if (!IsRailDepot(tile)) continue;
2409 track = GetRailDepotTrack(tile);
2410 reserved = HasDepotReservation(tile);
2411 break;
2413 break;
2415 case TT_STATION:
2416 if (!HasStationRail(tile)) continue;
2417 track = GetRailStationTrack(tile);
2418 reserved = HasStationReservation(tile);
2419 break;
2421 default: continue;
2424 if (track != INVALID_TRACK) {
2425 ret = ConvertGeneric(tile, totype, track, reserved, &affected_trains, flags);
2428 if (ret.Failed()) {
2429 err = ret;
2430 } else {
2431 cost.AddCost(ret);
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);
2448 delete iter;
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);
2459 } else {
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;
2476 cost.AddCost(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]);
2490 return cost;
2491 } else {
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;
2508 cost.AddCost(ret);
2509 return cost;
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);
2531 if (n == 1) {
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));
2536 } else {
2537 cost.AddCost((n - 1) * RailClearCost(GetBridgeRailType(other_tile)));
2540 if (flags & DC_EXEC) {
2541 RemoveRailBridge(tile, present, other_tile, other_remove);
2544 return cost;
2549 static int GetSlopePixelZ_Track(TileIndex tile, uint x, uint y)
2551 int z;
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;
2560 } else {
2561 x &= 0xF;
2562 y &= 0xF;
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;
2670 switch (tracks) {
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));
2679 break;
2681 default:
2682 rti = GetRailTypeInfo(GetRailType(ti->tile, TRACK_UPPER));
2683 break;
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);
2707 } else {
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();
2718 break;
2720 default: break;
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);
2751 } else {
2752 /* single-corner-raised slope with track on upper part */
2753 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
2755 } else {
2756 SpriteID image;
2758 switch (rgt) {
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);
2790 } else {
2791 switch (track) {
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;
2806 default:
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);
2816 } else {
2817 DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2820 /* Mask out PBS bits as we shall draw them afterwards anyway. */
2821 track &= ~pbs;
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)
2844 SpriteID image;
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;
2854 } else {
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) ||
2872 (image++, true);
2875 switch (rgt) {
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];
2883 break;
2885 default: break;
2888 DrawGroundSprite(image, pal, sub);
2890 /* Draw track pieces individually for junction tiles */
2891 if (junction) {
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);
2907 } else {
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);
2914 } else {
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);
2929 } else {
2930 DrawTrackBitsNonOverlay(ti, track, rti, rgt);
2935 static void DrawHalftileOverlay(TileInfo *ti, Corner corner, const RailtypeInfo *rti, RailGroundType rgt)
2937 SpriteID offset;
2938 switch (corner) {
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;
2956 PaletteID pal;
2958 switch (corner) {
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;
2966 switch (rgt) {
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);
2984 } else {
2985 DrawHalftileNonOverlay(ti, corner, rti, rgt);
2989 static void DrawUpperHalftileOverlay(TileInfo *ti, Corner corner, const RailtypeInfo *rti, RailGroundType rgt)
2991 SpriteID image;
2992 switch (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);
3011 int offset;
3012 switch (track) {
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;
3033 switch (rgt) {
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
3037 default: break;
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);
3053 } else {
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;
3069 bool draw_ground;
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;
3077 } else {
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;
3088 } else {
3089 switch (track) {
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();
3096 break;
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);
3105 draw_ground = true;
3106 break;
3109 /* fall through */
3110 default:
3111 halftile_rti = NULL;
3112 rti = GetRailTypeInfo(GetRailType(ti->tile, TRACK_UPPER));
3113 draw_ground = rti->UsesOverlay();
3114 break;
3118 DrawFoundation(ti, f, IsTileSubtype(ti->tile, TT_BRIDGE) ? GetTunnelBridgeDirection(ti->tile) : INVALID_DIAGDIR);
3119 /* DrawFoundation modifies ti */
3121 if (draw_ground) {
3122 /* Draw ground */
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);
3135 } else {
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)
3147 switch (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;
3152 default: 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);
3162 return z;
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
3170 } SignalData[] = {
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;
3198 if (sprite != 0) {
3199 sprite += image;
3200 } else {
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);
3206 bool side;
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);
3226 } else {
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);
3260 } else {
3261 DrawBridgeGround(ti);
3263 /* draw ramp */
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);
3283 if (surface != 0) {
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);
3286 } else {
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
3292 * present. */
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);
3299 } else {
3300 AddSortableSpriteToDraw(rti->base_sprites.single_sloped + dir, PALETTE_CRASH, ti->x, ti->y, 16, 16, 8, ti->z);
3304 EndSpriteCombine();
3306 if (HasCatenaryDrawn(GetRailType(ti->tile))) {
3307 DrawCatenary(ti);
3310 if (DiagDirToAxis(dir) == AXIS_Y) {
3311 DrawSingleSignal(ti->tile, TRACKDIR_Y_SE);
3312 DrawSingleSignal(ti->tile, TRACKDIR_Y_NW);
3313 } else {
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) {
3334 default: return;
3336 case LT_ARCTIC:
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;
3341 break;
3343 case LT_TROPIC:
3344 if (GetTropicZone(tile) != TROPICZONE_DESERT || snow_or_desert) return;
3345 break;
3347 ToggleSnow(tile);
3348 MarkTileDirtyByTile(tile);
3349 return;
3352 RailGroundType old_ground = GetRailGroundType(tile);
3353 RailGroundType new_ground;
3355 if (old_ground == RAIL_GROUND_WATER) {
3356 TileLoop_Water(tile);
3357 return;
3360 switch (_settings_game.game_creation.landscape) {
3361 case LT_ARCTIC: {
3362 int z;
3363 Slope slope = GetTileSlope(tile, &z);
3364 bool half = false;
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);
3371 switch (f) {
3372 case FOUNDATION_NONE:
3373 /* no foundation - is the track on the upper side of three corners raised tile? */
3374 if (IsSlopeWithThreeCornersRaised(slope)) z++;
3375 break;
3377 case FOUNDATION_INCLINED_X:
3378 case FOUNDATION_INCLINED_Y:
3379 /* sloped track - is it on a steep slope? */
3380 if (IsSteepSlope(slope)) z++;
3381 break;
3383 case FOUNDATION_STEEP_LOWER:
3384 /* only lower part of steep slope */
3385 z++;
3386 break;
3388 default:
3389 /* if it is a steep slope, then there is a track on higher part */
3390 if (IsSteepSlope(slope)) z++;
3391 z++;
3392 break;
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;
3405 } else {
3406 new_ground = RAIL_GROUND_ICE_DESERT;
3408 goto set_ground;
3410 break;
3413 case LT_TROPIC:
3414 if (GetTropicZone(tile) == TROPICZONE_DESERT) {
3415 new_ground = RAIL_GROUND_ICE_DESERT;
3416 goto set_ground;
3418 break;
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);
3428 byte fences = 0;
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)) {
3443 fences |= 1 << d;
3447 switch (fences) {
3448 case 0: break;
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();
3463 set_ground:
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;
3480 uint a, b;
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'. */
3488 if (a == 0) {
3489 b = 3;
3490 } else {
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);
3499 if (a == 0) {
3500 b = 3;
3501 } else {
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);
3517 switch (tb) {
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)
3532 return false;
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];
3602 } else {
3603 td->str = STR_LAI_RAIL_DESCRIPTION_TRACK;
3605 } else {
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;
3624 RailType rt;
3625 uint num_sigs;
3627 switch (bits) {
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));
3636 } else {
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));
3645 break;
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));
3653 break;
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;
3659 num_sigs = 0;
3660 break;
3662 default: {
3663 rt = GetRailType(tile, TRACK_UPPER);
3664 uint num_pieces = CountBits(bits);
3665 if (TracksOverlap(bits)) {
3666 num_pieces *= num_pieces;
3667 num_sigs = 0;
3668 } else {
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;
3674 break;
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);
3692 } else {
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 */
3727 default:
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);
3744 return cost;
3747 static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
3749 int z_old;
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 */
3758 if (was_water) {
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);
3796 } else {
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]);
3802 } else {
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