/*
* This file is part of OpenTTD.
* 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.
* 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.
* 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 .
*/
/** @file rail_cmd.cpp Handling of rail tiles. */
#include "stdafx.h"
#include "cmd_helper.h"
#include "viewport_func.h"
#include "command_func.h"
#include "depot_base.h"
#include "pathfinder/yapf/yapf_cache.h"
#include "newgrf_debug.h"
#include "newgrf_railtype.h"
#include "train.h"
#include "autoslope.h"
#include "water.h"
#include "tunnelbridge_map.h"
#include "bridge_signal_map.h"
#include "vehicle_func.h"
#include "sound_func.h"
#include "tunnelbridge.h"
#include "elrail_func.h"
#include "town.h"
#include "pbs.h"
#include "company_base.h"
#include "core/backup_type.hpp"
#include "date_func.h"
#include "strings_func.h"
#include "company_gui.h"
#include "object_map.h"
#include "tracerestrict.h"
#include "programmable_signals.h"
#include "spritecache.h"
#include "core/container_func.hpp"
#include "news_func.h"
#include "scope.h"
#include "table/strings.h"
#include "table/railtypes.h"
#include "table/track_land.h"
#include
#include "safeguards.h"
/** Helper type for lists/vectors of trains */
typedef std::vector TrainList;
RailtypeInfo _railtypes[RAILTYPE_END];
std::vector _sorted_railtypes;
TileIndex _rail_track_endtile; ///< The end of a rail track; as hidden return from the rail build/remove command for GUI purposes.
RailTypes _railtypes_hidden_mask;
/**
* Reset all rail type information to its default values.
*/
void ResetRailTypes()
{
static_assert(lengthof(_original_railtypes) <= lengthof(_railtypes));
uint i = 0;
for (; i < lengthof(_original_railtypes); i++) _railtypes[i] = _original_railtypes[i];
static const RailtypeInfo empty_railtype = {
{0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,{}},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0},
0, RAILTYPES_NONE, RAILTYPES_NONE, RAILTYPES_NONE, 0, 0, 0, RTFB_NONE, 0, 0, 0, 0, 0, 0, 0,
RailTypeLabelList(), 0, 0, RAILTYPES_NONE, RAILTYPES_NONE, 0,
{}, {} };
for (; i < lengthof(_railtypes); i++) _railtypes[i] = empty_railtype;
_railtypes_hidden_mask = RAILTYPES_NONE;
}
void ResolveRailTypeGUISprites(RailtypeInfo *rti)
{
SpriteID cursors_base = GetCustomRailSprite(rti, INVALID_TILE, RTSG_CURSORS);
if (cursors_base != 0) {
rti->gui_sprites.build_ns_rail = cursors_base + 0;
rti->gui_sprites.build_x_rail = cursors_base + 1;
rti->gui_sprites.build_ew_rail = cursors_base + 2;
rti->gui_sprites.build_y_rail = cursors_base + 3;
rti->gui_sprites.auto_rail = cursors_base + 4;
rti->gui_sprites.build_depot = cursors_base + 5;
rti->gui_sprites.build_tunnel = cursors_base + 6;
rti->gui_sprites.convert_rail = cursors_base + 7;
rti->cursor.rail_ns = cursors_base + 8;
rti->cursor.rail_swne = cursors_base + 9;
rti->cursor.rail_ew = cursors_base + 10;
rti->cursor.rail_nwse = cursors_base + 11;
rti->cursor.autorail = cursors_base + 12;
rti->cursor.depot = cursors_base + 13;
rti->cursor.tunnel = cursors_base + 14;
rti->cursor.convert = cursors_base + 15;
}
/* Array of default GUI signal sprite numbers. */
const SpriteID _signal_lookup[2][SIGTYPE_END] = {
{SPR_IMG_SIGNAL_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_EXIT,
SPR_IMG_SIGNAL_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_PBS, SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY,
SPR_IMG_SIGNAL_ELECTRIC_PROG, SPR_IMG_SIGNAL_ELECTRIC_NO_ENTRY},
{SPR_IMG_SIGNAL_SEMAPHORE_NORM, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_EXIT,
SPR_IMG_SIGNAL_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_PBS, SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY,
SPR_IMG_SIGNAL_SEMAPHORE_PROG, SPR_IMG_SIGNAL_SEMAPHORE_NO_ENTRY},
};
for (SignalType type = SIGTYPE_NORMAL; type < SIGTYPE_END; type = (SignalType)(type + 1)) {
for (SignalVariant var = SIG_ELECTRIC; var <= SIG_SEMAPHORE; var = (SignalVariant)(var + 1)) {
PalSpriteID red = GetCustomSignalSprite(rti, INVALID_TILE, type, var, 0, true).sprite;
if (red.sprite != 0) {
rti->gui_sprites.signals[type][var][0] = { red.sprite + SIGNAL_TO_SOUTH, red.pal };
} else {
rti->gui_sprites.signals[type][var][0] = { _signal_lookup[var][type], PAL_NONE };
}
if (type == SIGTYPE_NO_ENTRY) {
rti->gui_sprites.signals[type][var][1] = rti->gui_sprites.signals[type][var][0];
continue;
}
PalSpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, 255, true).sprite;
if (green.sprite != 0) {
rti->gui_sprites.signals[type][var][1] = { green.sprite + SIGNAL_TO_SOUTH, green.pal };
} else {
rti->gui_sprites.signals[type][var][1] = { _signal_lookup[var][type] + 1, PAL_NONE };
}
}
}
}
/**
* Compare railtypes based on their sorting order.
* @param first The railtype to compare to.
* @param second The railtype to compare.
* @return True iff the first should be sorted before the second.
*/
static bool CompareRailTypes(const RailType &first, const RailType &second)
{
if (_settings_client.gui.sort_track_types_by_speed) {
RailType rt[2] = { first, second };
uint sort_value[2];
for (int i = 0; i < 2; ++i) {
// Last sort by speed
sort_value[i] = (GetRailTypeInfo(rt[i])->max_speed != 0) ? GetRailTypeInfo(rt[i])->max_speed : UINT16_MAX;
// Inside those categories filter by compatibility with eachother.
if (!HasPowerOnRail(rt[i], rt[(i + 1) % 2])) {
sort_value[i] += (1 << 16);
}
// We sort by Rail, Electric and others
if (!HasPowerOnRail(rt[i], RAILTYPE_RAIL)) {
sort_value[i] += (1 << 17);
if (!HasPowerOnRail(rt[i], RAILTYPE_ELECTRIC)) {
sort_value[i] += (1 << 18);
if (!HasPowerOnRail(rt[i], RAILTYPE_MONO) && HasPowerOnRail(rt[i], RAILTYPE_MAGLEV)) {
sort_value[i] += (1 << 19);
}
}
}
// Then Mono
if (HasPowerOnRail(rt[i], RAILTYPE_MONO)) {
sort_value[i] += (1 << 20);
}
// Maglev is second last
if (HasPowerOnRail(rt[i], RAILTYPE_MAGLEV)) {
sort_value[i] += (1 << 21);
}
// All no-speed tracks (like planning and lifted) go to the end
if (GetRailTypeInfo(rt[i])->max_speed == 0) {
sort_value[i] += (1 << 22);
}
}
return sort_value[0] < sort_value[1];
} else {
return GetRailTypeInfo(first)->sorting_order < GetRailTypeInfo(second)->sorting_order;
}
}
void SortRailTypes()
{
std::sort(_sorted_railtypes.begin(), _sorted_railtypes.end(), CompareRailTypes);
}
/**
* Resolve sprites of custom rail types
*/
void InitRailTypes()
{
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
RailtypeInfo *rti = &_railtypes[rt];
ResolveRailTypeGUISprites(rti);
if (HasBit(rti->flags, RTF_HIDDEN)) SetBit(_railtypes_hidden_mask, rt);
}
_sorted_railtypes.clear();
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
if (_railtypes[rt].label != 0 && !HasBit(_railtypes_hidden_mask, rt)) {
_sorted_railtypes.push_back(rt);
}
}
SortRailTypes();
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
_railtypes[rt].all_compatible_railtypes = _railtypes[rt].compatible_railtypes;
}
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
RailTypes compatible = _railtypes[rt].all_compatible_railtypes;
RailTypes to_check = compatible;
while (to_check) {
RailType i = (RailType)FindFirstBit64(to_check);
to_check = KillFirstBit(to_check);
RailTypes new_types = _railtypes[i].compatible_railtypes & (~compatible);
to_check |= new_types;
compatible |= new_types;
}
RailTypes to_update = compatible;
while (to_update) {
RailType i = (RailType)FindFirstBit64(to_update);
to_update = KillFirstBit(to_update);
_railtypes[i].all_compatible_railtypes = compatible;
}
}
}
/**
* Allocate a new rail type label
*/
RailType AllocateRailType(RailTypeLabel label)
{
for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
RailtypeInfo *rti = &_railtypes[rt];
if (rti->label == 0) {
/* Set up new rail type */
*rti = _original_railtypes[RAILTYPE_RAIL];
rti->label = label;
rti->alternate_labels.clear();
/* Make us compatible with ourself. */
rti->powered_railtypes = (RailTypes)(1LL << rt);
rti->compatible_railtypes = (RailTypes)(1LL << rt);
/* We also introduce ourself. */
rti->introduces_railtypes = (RailTypes)(1LL << rt);
/* Default sort order; order of allocation, but with some
* offsets so it's easier for NewGRF to pick a spot without
* changing the order of other (original) rail types.
* The << is so you can place other railtypes in between the
* other railtypes, the 7 is to be able to place something
* before the first (default) rail type. */
rti->sorting_order = rt << 4 | 7;
if (label == 'TELE' || label == 'PIPE' || label == 'WIRE') SetBit(rti->ctrl_flags, RTCF_NOREALISTICBRAKING);
return rt;
}
}
return INVALID_RAILTYPE;
}
static const byte _track_sloped_sprites[14] = {
14, 15, 22, 13,
0, 21, 17, 12,
23, 0, 18, 20,
19, 16
};
/* 4
* ---------
* |\ /|
* | \ 1/ |
* | \ / |
* | \ / |
* 16| \ |32
* | / \2 |
* | / \ |
* | / \ |
* |/ \|
* ---------
* 8
*/
/* MAP2 byte: abcd???? => Signal On? Same coding as map3lo
* MAP3LO byte: abcd???? => Signal Exists?
* a and b are for diagonals, upper and left,
* one for each direction. (ie a == NE->SW, b ==
* SW->NE, or v.v., I don't know. b and c are
* similar for lower and right.
* MAP2 byte: ????abcd => Type of ground.
* MAP3LO byte: ????abcd => Type of rail.
* MAP5: 00abcdef => rail
* 01abcdef => rail w/ signals
* 10uuuuuu => unused
* 11uuuudd => rail depot
*/
/**
* Tests if a vehicle interacts with the specified track.
* All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
*
* @param tile The tile.
* @param track The track.
* @return Succeeded command (no train found), or a failed command (a train was found).
*/
static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
{
TrackBits rail_bits = TrackToTrackBits(track);
return EnsureNoTrainOnTrackBits(tile, rail_bits);
}
/**
* Check that the new track bits may be built.
* @param tile %Tile to build on.
* @param to_build New track bits.
* @param railtype New rail type.
* @param disable_dual_rail_type Whether dual rail types are disabled.
* @param flags Flags of the operation.
* @return Succeeded or failed command.
*/
static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build, RailType railtype, bool disable_dual_rail_type, DoCommandFlag flags, bool auto_remove_signals)
{
if (!IsPlainRail(tile)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
/* So, we have a tile with tracks on it (and possibly signals). Let's see
* what tracks first */
TrackBits current = GetTrackBits(tile); // The current track layout.
TrackBits future = current | to_build; // The track layout we want to build.
/* Are we really building something new? */
if (current == future) {
/* Nothing new is being built */
if (IsCompatibleRail(GetTileRailTypeByTrackBit(tile, to_build), railtype)) {
return_cmd_error(STR_ERROR_ALREADY_BUILT);
} else {
return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
}
}
/* These combinations are always allowed, unless disable_dual_rail_type is set */
if ((future == TRACK_BIT_HORZ || future == TRACK_BIT_VERT) && !disable_dual_rail_type) {
if (flags & DC_EXEC) {
if (to_build & TRACK_BIT_RT_1) {
RailType current_rt = GetRailType(tile);
SetRailType(tile, railtype);
SetSecondaryRailType(tile, current_rt);
} else {
SetSecondaryRailType(tile, railtype);
}
}
return CommandCost();
}
/* Let's see if we may build this */
if (HasSignals(tile) && !auto_remove_signals) {
/* If we are not allowed to overlap (flag is on for ai companies or we have
* signals on the tile), check that */
if (future != TRACK_BIT_HORZ && future != TRACK_BIT_VERT) {
return_cmd_error(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
}
}
RailType rt = INVALID_RAILTYPE;
if (current == TRACK_BIT_HORZ || current == TRACK_BIT_VERT) {
RailType rt1 = GetRailType(tile);
if (!IsCompatibleRail(rt1, railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
RailType rt2 = GetSecondaryRailType(tile);
if (!IsCompatibleRail(rt2, railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
if (rt1 != rt2) {
/* Two different railtypes present */
if ((railtype == rt1 || HasPowerOnRail(rt1, railtype)) && (railtype == rt2 || HasPowerOnRail(rt2, railtype))) {
rt = railtype;
} else if ((railtype == rt1 || HasPowerOnRail(railtype, rt1)) && HasPowerOnRail(rt2, rt1)) {
rt = railtype = rt1;
} else if ((railtype == rt2 || HasPowerOnRail(railtype, rt2)) && HasPowerOnRail(rt1, rt2)) {
rt = railtype = rt2;
} else {
return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
}
} else if (railtype == rt1) {
/* Nothing to do */
rt = INVALID_RAILTYPE;
} else if (HasPowerOnRail(railtype, rt1)) {
/* Try to keep existing railtype */
railtype = rt1;
rt = INVALID_RAILTYPE;
} else if (HasPowerOnRail(rt1, railtype)) {
rt = railtype;
} else {
return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
}
} else {
rt = GetRailType(tile);
if (railtype == rt) {
/* Nothing to do */
rt = INVALID_RAILTYPE;
} else if (!IsCompatibleRail(rt, railtype)) {
return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
} else if (HasPowerOnRail(railtype, rt)) {
/* Try to keep existing railtype */
railtype = rt;
rt = INVALID_RAILTYPE;
} else if (HasPowerOnRail(rt, railtype)) {
rt = railtype;
} else {
return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
}
}
CommandCost ret;
if (rt != INVALID_RAILTYPE) {
ret = DoCommand(tile, tile, rt, flags, CMD_CONVERT_RAIL);
if (ret.Failed()) return ret;
}
if (flags & DC_EXEC) {
SetRailType(tile, railtype);
SetSecondaryRailType(tile, railtype);
}
return ret;
}
/** Valid TrackBits on a specific (non-steep)-slope without foundation */
static const TrackBits _valid_tracks_without_foundation[15] = {
TRACK_BIT_ALL,
TRACK_BIT_RIGHT,
TRACK_BIT_UPPER,
TRACK_BIT_X,
TRACK_BIT_LEFT,
TRACK_BIT_NONE,
TRACK_BIT_Y,
TRACK_BIT_LOWER,
TRACK_BIT_LOWER,
TRACK_BIT_Y,
TRACK_BIT_NONE,
TRACK_BIT_LEFT,
TRACK_BIT_X,
TRACK_BIT_UPPER,
TRACK_BIT_RIGHT,
};
/** Valid TrackBits on a specific (non-steep)-slope with leveled foundation */
static const TrackBits _valid_tracks_on_leveled_foundation[15] = {
TRACK_BIT_NONE,
TRACK_BIT_LEFT,
TRACK_BIT_LOWER,
TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_LEFT,
TRACK_BIT_RIGHT,
TRACK_BIT_ALL,
TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_RIGHT,
TRACK_BIT_ALL,
TRACK_BIT_UPPER,
TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_LEFT,
TRACK_BIT_ALL,
TRACK_BIT_ALL,
TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_RIGHT,
TRACK_BIT_ALL,
TRACK_BIT_ALL
};
/**
* Checks if a track combination is valid on a specific slope and returns the needed foundation.
*
* @param tileh Tile slope.
* @param bits Trackbits.
* @return Needed foundation or FOUNDATION_INVALID if track/slope combination is not allowed.
*/
Foundation GetRailFoundation(Slope tileh, TrackBits bits)
{
if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE;
if (IsSteepSlope(tileh)) {
/* Test for inclined foundations */
if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X;
if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y;
/* Get higher track */
Corner highest_corner = GetHighestSlopeCorner(tileh);
TrackBits higher_track = CornerToTrackBits(highest_corner);
/* Only higher track? */
if (bits == higher_track) return HalftileFoundation(highest_corner);
/* Overlap with higher track? */
if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID;
/* either lower track or both higher and lower track */
return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER);
} else {
if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE;
bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0);
Corner track_corner;
switch (bits) {
case TRACK_BIT_LEFT: track_corner = CORNER_W; break;
case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
case TRACK_BIT_HORZ:
if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
case TRACK_BIT_VERT:
if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
case TRACK_BIT_X:
if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_X;
return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
case TRACK_BIT_Y:
if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_Y;
return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
default:
return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
}
/* Single diagonal track */
/* Track must be at least valid on leveled foundation */
if (!valid_on_leveled) return FOUNDATION_INVALID;
/* If slope has three raised corners, build leveled foundation */
if (IsSlopeWithThreeCornersRaised(tileh)) return FOUNDATION_LEVELED;
/* If neighboured corners of track_corner are lowered, build halftile foundation */
if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
/* else special anti-zig-zag foundation */
return SpecialRailFoundation(track_corner);
}
}
/**
* Tests if a track can be build on a tile.
*
* @param tileh Tile slope.
* @param rail_bits Tracks to build.
* @param existing Tracks already built.
* @param tile Tile (used for water test)
* @return Error message or cost for foundation building.
*/
static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
{
/* don't allow building on the lower side of a coast */
if (GetFloodingBehaviour(tile) != FLOOD_NONE) {
if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
}
Foundation f_new = GetRailFoundation(tileh, rail_bits | existing);
/* check track/slope combination */
if ((f_new == FOUNDATION_INVALID) ||
((f_new != FOUNDATION_NONE) && (!_settings_game.construction.build_on_slopes))) {
return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
}
Foundation f_old = GetRailFoundation(tileh, existing);
return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price[PR_BUILD_FOUNDATION] : (Money)0);
}
bool IsValidFlatRailBridgeHeadTrackBits(Slope normalised_slope, DiagDirection bridge_direction, TrackBits tracks)
{
/* bridge_direction c1 c2
* 0 0 1
* 1 0 3
* 2 2 3
* 3 2 1
*/
const Corner c1 = (Corner) (bridge_direction & 2);
const Corner c2 = (Corner) (((bridge_direction + 1) & 2) + 1);
auto test_corner = [&](Corner c) -> bool {
if (normalised_slope & SlopeWithOneCornerRaised(c)) return true;
Slope effective_slope = normalised_slope | SlopeWithOneCornerRaised(OppositeCorner(c));
assert(effective_slope < lengthof(_valid_tracks_on_leveled_foundation));
return (_valid_tracks_on_leveled_foundation[effective_slope] & tracks) == tracks;
};
return test_corner(c1) && test_corner(c2);
}
/* Validate functions for rail building */
static inline bool ValParamTrackOrientation(Track track)
{
return IsValidTrack(track);
}
/**
* Build a single piece of rail
* @param tile tile to build on
* @param flags operation to perform
* @param p1 railtype of being built piece (normal, mono, maglev)
* @param p2 various bitstuffed elements
* - (bit 0- 2) - track-orientation, valid values: 0-5 (@see Track)
* - (bit 3) - 0 = error on signal in the way, 1 = auto remove signals when in the way
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
RailType railtype = Extract(p1);
Track track = Extract