rail_cmd.c
author Darkvater
Thu, 12 May 2005 00:11:37 +0000
changeset 1793 b9a37c98b468
parent 1782 48749e4c2dc6
child 1796 614d996f6be7
permissions -rw-r--r--
(svn r2297) - CodeChange: server-check the next batch of commands.
- CodeChange: since only the server will be able to modify difficulty settings, leave the checking of correct values besides, and trust users will join legit servers.
- CodeChange: for renaming signs, only check if GetDParam(); eg _decode_parameters is empty ('\0') or not, instead of the extra check of players, etc. That basically does the same thing. Also dirty sign two times when renaming, once before, once after the action. Because if the name becomes shorter and you update only after, garbage remains on the screen.
- CodeChange: made GetMaskOfTownActions() available to the town-cmd to double-check if the action was available to the player. For this purpose the hardcoded _local_player has been removed from the function and is now passed as a parameter.
#include "stdafx.h"
#include "ttd.h"
#include "table/sprites.h"
#include "table/strings.h"
#include "gfx.h"
#include "map.h"
#include "tile.h"
#include "vehicle.h"
#include "viewport.h"
#include "command.h"
#include "pathfind.h"
#include "town.h"
#include "sound.h"
#include "station.h"
#include "sprite.h"
#include "depot.h"
#include "waypoint.h"

extern uint16 _custom_sprites_base;

void ShowTrainDepotWindow(uint tile);

enum { /* These values are bitmasks for the map5 byte */
	RAIL_TYPE_NORMAL = 0,
	RAIL_TYPE_SIGNALS = 0x40,
	RAIL_TYPE_SPECIAL = 0x80, // If this bit is set, then it's not a regular track.
	RAIL_TYPE_DEPOT = 0xC0,
	RAIL_TYPE_MASK = 0xC0,

	RAIL_BIT_DIAG1 = 1,  // 0
	RAIL_BIT_DIAG2 = 2,  // 1
	RAIL_BIT_UPPER = 4,  // 2
	RAIL_BIT_LOWER = 8,  // 3
	RAIL_BIT_LEFT  = 16, // 4
	RAIL_BIT_RIGHT = 32, // 5
	RAIL_BIT_MASK = 0x3F,

	RAIL_DEPOT_TRACK_MASK = 1,
	RAIL_DEPOT_DIR = 3,

	RAIL_SUBTYPE_MASK     = 0x3C,
	RAIL_SUBTYPE_DEPOT    = 0x00,
	RAIL_SUBTYPE_WAYPOINT = 0x04
};

static inline bool IsRailDepot(byte m5)
{
	return
		(m5 & RAIL_TYPE_MASK) == RAIL_TYPE_DEPOT &&
		(m5 & RAIL_SUBTYPE_MASK) == RAIL_SUBTYPE_DEPOT;
}

/* Format of rail map5 byte.
 * 00 abcdef  => Normal rail
 * 01 abcdef  => Rail with signals
 * 10 ??????  => Unused
 * 11 ????dd  => Depot
 *
 * abcdef is a bitmask, which contains ones for all present tracks. Below the
 * value for each track is given.
 */

/*         4
 *     ---------
 *    |\       /|
 *    | \    1/ |
 *    |  \   /  |
 *    |   \ /   |
 *  16|    \    |32
 *    |   / \2  |
 *    |  /   \  |
 *    | /     \ |
 *    |/       \|
 *     ---------
 *         8
 */


// Constants for lower part of Map2 byte.
enum RailMap2Lower4 {
	RAIL_MAP2LO_GROUND_MASK = 0xF,
	RAIL_GROUND_BROWN = 0,
	RAIL_GROUND_GREEN = 1,
	RAIL_GROUND_FENCE_NW = 2,
	RAIL_GROUND_FENCE_SE = 3,
	RAIL_GROUND_FENCE_SENW = 4,
	RAIL_GROUND_FENCE_NE = 5,
	RAIL_GROUND_FENCE_SW = 6,
	RAIL_GROUND_FENCE_NESW = 7,
	RAIL_GROUND_FENCE_VERT1 = 8,
	RAIL_GROUND_FENCE_VERT2 = 9,
	RAIL_GROUND_FENCE_HORIZ1 = 10,
	RAIL_GROUND_FENCE_HORIZ2 = 11,
	RAIL_GROUND_ICE_DESERT = 12,
};


/* 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
 */

static bool CheckTrackCombination(uint map5, uint trackbits, uint flags)
{
	_error_message = STR_1001_IMPOSSIBLE_TRACK_COMBINATION;

	switch (map5 & RAIL_TYPE_MASK) {
		case RAIL_TYPE_NORMAL:
			if (map5 & trackbits) {
				_error_message = STR_1007_ALREADY_BUILT;
				return false;
			}

			if (flags & DC_NO_RAIL_OVERLAP) {
				// Computer players are not allowed to intersect pieces of rail.
				map5 |= trackbits;
				return
					map5 == (RAIL_BIT_UPPER | RAIL_BIT_LOWER) ||
					map5 == (RAIL_BIT_LEFT  | RAIL_BIT_RIGHT);
			} else {
				return true;
			}

		case RAIL_TYPE_SIGNALS:
			if (map5 & trackbits) {
				_error_message = STR_1007_ALREADY_BUILT;
				return false;
			}

			map5 |= trackbits;
			return
				map5 == (RAIL_TYPE_SIGNALS | RAIL_BIT_UPPER | RAIL_BIT_LOWER) ||
				map5 == (RAIL_TYPE_SIGNALS | RAIL_BIT_LEFT  | RAIL_BIT_RIGHT);

		default:
			return false;
	}
}


static const byte _valid_tileh_slopes[4][15] = {

// set of normal ones
{
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
	RAIL_BIT_RIGHT,
	RAIL_BIT_UPPER,
	RAIL_BIT_DIAG1,

	RAIL_BIT_LEFT,
	0,
	RAIL_BIT_DIAG2,
	RAIL_BIT_LOWER,

	RAIL_BIT_LOWER,
	RAIL_BIT_DIAG2,
	0,
	RAIL_BIT_LEFT,

	RAIL_BIT_DIAG1,
	RAIL_BIT_UPPER,
	RAIL_BIT_RIGHT,
},

// allowed rail for an evenly raised platform
{
	0,
	RAIL_BIT_LEFT,
	RAIL_BIT_LOWER,
	RAIL_BIT_DIAG2 | RAIL_BIT_LOWER | RAIL_BIT_LEFT,

	RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1 | RAIL_BIT_LOWER | RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,

	RAIL_BIT_UPPER,
	RAIL_BIT_DIAG1 | RAIL_BIT_UPPER | RAIL_BIT_LEFT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,

	RAIL_BIT_DIAG2 | RAIL_BIT_UPPER | RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
},

// allowed rail on coast tile
{
	0,
	RAIL_BIT_LEFT,
	RAIL_BIT_LOWER,
	RAIL_BIT_DIAG2|RAIL_BIT_LEFT|RAIL_BIT_LOWER,

	RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_RIGHT|RAIL_BIT_LOWER,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,

	RAIL_BIT_UPPER,
	RAIL_BIT_DIAG1|RAIL_BIT_LEFT|RAIL_BIT_UPPER,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,

	RAIL_BIT_DIAG2|RAIL_BIT_RIGHT|RAIL_BIT_UPPER,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
	RAIL_BIT_DIAG1|RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LOWER|RAIL_BIT_LEFT|RAIL_BIT_RIGHT,
	},

	// valid railway crossings on slopes
	{
		1, 0, 0, // 0, 1, 2
		0, 0, 1, // 3, 4, 5
		0, 1, 0, // 6, 7, 8
		0, 1, 1, // 9, 10, 11
		0, 1, 1, // 12, 13, 14
	}
};

uint GetRailFoundation(uint tileh, uint bits)
{
	int i;

	if ((~_valid_tileh_slopes[0][tileh] & bits) == 0)
		return 0;

	if ((~_valid_tileh_slopes[1][tileh] & bits) == 0)
		return tileh;

	if ( ((i=0, tileh == 1) || (i+=2, tileh == 2) || (i+=2, tileh == 4) || (i+=2, tileh == 8)) && (bits == RAIL_BIT_DIAG1 || (i++, bits == RAIL_BIT_DIAG2)))
		return i + 15;

	return 0;
}

//
static uint32 CheckRailSlope(uint tileh, uint rail_bits, uint existing, TileIndex tile)
{
	// never allow building on top of steep tiles
	if (!(tileh & 0x10)) {
		rail_bits |= existing;

		// don't allow building on the lower side of a coast
		if (IsTileType(tile, MP_WATER) &&
				~_valid_tileh_slopes[2][tileh] & rail_bits) {
			return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER);
		}

		// no special foundation
		if ((~_valid_tileh_slopes[0][tileh] & rail_bits) == 0)
			return 0;

		if ((~_valid_tileh_slopes[1][tileh] & rail_bits) == 0 || ( // whole tile is leveled up
					(rail_bits == RAIL_BIT_DIAG1 || rail_bits == RAIL_BIT_DIAG2) &&
					(tileh == 1 || tileh == 2 || tileh == 4 || tileh == 8)
				)) { // partly up
			if (existing != 0) {
				return 0;
			} else if (!_patches.build_on_slopes ||
					(_is_ai_player && !_patches.ainew_active)) {
				return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
			} else {
				return _price.terraform;
			}
		}
	}
	return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
}

/* Validate functions for rail building */
static inline bool ValParamTrackOrientation(uint32 rail) {return rail <= 5;}

/** Build a single piece of rail
 * @param x,y coordinates on where to build
 * @param p1 railtype of being built piece (normal, mono, maglev)
 * @param p2 rail combination to build
 */
int32 CmdBuildSingleRail(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	TileIndex tile;
	uint tileh;
	uint m5;
	uint rail_bit;
	int32 cost = 0;
	int32 ret;

	if (!ValParamRailtype(p1) || !ValParamTrackOrientation(p2)) return CMD_ERROR;

	tile = TILE_FROM_XY(x, y);
	tileh = GetTileSlope(tile, NULL);
	m5 = _map5[tile];
	rail_bit = 1 << p2;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	switch (GetTileType(tile)) {
		case MP_TUNNELBRIDGE:
			if ((m5 & 0xC0) != 0xC0 || // not bridge middle part?
					(m5 & 0x01 ? 1 : 2) != rail_bit) { // wrong direction?
				// Get detailed error message
				return DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
			}

			switch (m5 & 0x38) { // what's under the bridge?
				case 0x00: // clear land
					ret = CheckRailSlope(tileh, rail_bit, 0, tile);
					if (CmdFailed(ret)) return ret;
					cost += ret;

					if (flags & DC_EXEC) {
						_map_owner[tile] = _current_player;
						_map3_lo[tile] &= ~0x0F;
						_map3_lo[tile] |= p1;
						_map5[tile] = (m5 & 0xC7) | 0x20; // railroad under bridge
					}
					break;

				case 0x20: // rail already there
					return_cmd_error(STR_1007_ALREADY_BUILT);

				default:
					// Get detailed error message
					return DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
			}
			break;

		case MP_RAILWAY:
			if (!CheckTrackCombination(m5, rail_bit, flags) ||
					!EnsureNoVehicle(tile)) {
				return CMD_ERROR;
			}
			if (m5 & RAIL_TYPE_SPECIAL ||
					_map_owner[tile] != _current_player ||
					(_map3_lo[tile] & 0xFU) != p1) {
				// Get detailed error message
				return DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
			}

			ret = CheckRailSlope(tileh, rail_bit, m5 & RAIL_BIT_MASK, tile);
			if (CmdFailed(ret)) return ret;
			cost += ret;

			if (flags & DC_EXEC) {
				_map2[tile] &= ~RAIL_MAP2LO_GROUND_MASK; // Bare land
				_map5[tile] = m5 | rail_bit;
			}
			break;

		case MP_STREET:
			if (!_valid_tileh_slopes[3][tileh]) // prevent certain slopes
				return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
			if (!EnsureNoVehicle(tile)) return CMD_ERROR;

			if ((m5 & 0xF0) == 0 && ( // normal road?
						(rail_bit == 1 && m5 == 0x05) ||
						(rail_bit == 2 && m5 == 0x0A) // correct direction?
					)) {
				if (flags & DC_EXEC) {
					_map3_lo[tile] = _map_owner[tile];
					_map_owner[tile] = _current_player;
					_map3_hi[tile] = p1;
					_map5[tile] = 0x10 | (rail_bit == 1 ? 0x08 : 0x00); // level crossing
				}
				break;
			}

			if ((m5 & 0xF0) == 0x10 && (m5 & 0x08 ? 1 : 2) == rail_bit)
				return_cmd_error(STR_1007_ALREADY_BUILT);
			/* FALLTHROUGH */

		default:
			ret = CheckRailSlope(tileh, rail_bit, 0, tile);
			if (CmdFailed(ret)) return ret;
			cost += ret;

			ret = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
			if (CmdFailed(ret)) return ret;
			cost += ret;

			if (flags & DC_EXEC) {
				SetTileType(tile, MP_RAILWAY);
				_map_owner[tile] = _current_player;
				_map2[tile] = 0; // Bare land
				_map3_lo[tile] = p1; // No signals, rail type
				_map5[tile] = rail_bit;
			}
			break;
	}

	if (flags & DC_EXEC) {
		MarkTileDirtyByTile(tile);
		SetSignalsOnBothDir(tile, p2);
	}

	return cost + _price.build_rail;
}

static const byte _signals_table[] = {
	0x40, 0x40, 0x40, 0x10, 0x80, 0x20, 0, 0, // direction 1
	0x80, 0x80, 0x80, 0x20, 0x40, 0x10, 0, 0  // direction 2
};

static const byte _signals_table_other[] = {
	0x80, 0x80, 0x80, 0x20, 0x40, 0x10, 0, 0, // direction 1
	0x40, 0x40, 0x40, 0x10, 0x80, 0x20, 0, 0  // direction 2
};

static const byte _signals_table_both[] = {
	0xC0, 0xC0, 0xC0, 0x30, 0xC0, 0x30, 0, 0,	// both directions combined
	0xC0, 0xC0, 0xC0, 0x30, 0xC0, 0x30, 0, 0
};


/** Remove a single piece of track
 * @param x,y coordinates for removal of track
 * @param p1 unused
 * @param p2 rail orientation
 */
int32 CmdRemoveSingleRail(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	TileInfo ti;
	TileIndex tile;
	byte rail_bit = 1 << p2;

	if (!ValParamTrackOrientation(p2)) return CMD_ERROR;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	FindLandscapeHeight(&ti, x, y);

	tile = ti.tile;

	if (!((1<<ti.type) & ((1<<MP_TUNNELBRIDGE)|(1<<MP_STREET)|(1<<MP_RAILWAY))))
		return CMD_ERROR;

	if (_current_player != OWNER_WATER && !CheckTileOwnership(tile))
		return CMD_ERROR;

	// allow building rail under bridge
	if (ti.type != MP_TUNNELBRIDGE && !EnsureNoVehicle(tile))
		return CMD_ERROR;

	if (ti.type == MP_TUNNELBRIDGE) {
		if (!EnsureNoVehicleZ(tile, TilePixelHeight(tile)))
			return CMD_ERROR;

		if ((ti.map5 & 0xF8) != 0xE0)
			return CMD_ERROR;

		if ( ((ti.map5 & 1) ? 1 : 2) != rail_bit )
			return CMD_ERROR;

		if (!(flags & DC_EXEC))
			return _price.remove_rail;

		_map_owner[tile] = OWNER_NONE;
		_map5[tile] = ti.map5 & 0xC7;
	} else if (ti.type == MP_STREET) {
		byte m5;
		if (!(ti.map5 & 0xF0))
			return CMD_ERROR;

		if (ti.map5 & 0xE0)
			return CMD_ERROR;

		if (ti.map5 & 8) {
			m5 = 5;
			if (rail_bit != 1)
				return CMD_ERROR;
		} else {
			m5 = 10;
			if (rail_bit != 2)
				return CMD_ERROR;
		}

		if (!(flags & DC_EXEC))
			return _price.remove_rail;

		_map5[tile] = m5;
		_map_owner[tile] = _map3_lo[tile];
		_map2[tile] = 0;
	} else {
		assert(ti.type == MP_RAILWAY);

		if (ti.map5 & RAIL_TYPE_SPECIAL)
			return CMD_ERROR;

		if (!(ti.map5 & rail_bit))
			return CMD_ERROR;

		// don't allow remove if there are signals on the track
		if (ti.map5 & RAIL_TYPE_SIGNALS) {
			if (_map3_lo[tile] & _signals_table_both[p2])
				return CMD_ERROR;
		}

		if (!(flags & DC_EXEC))
			return _price.remove_rail;

		if ( (_map5[tile] ^= rail_bit) == 0) {
			DoClearSquare(tile);
			goto skip_mark_dirty;
		}
	}

	/* mark_dirty */
	MarkTileDirtyByTile(tile);

skip_mark_dirty:;

	SetSignalsOnBothDir(tile, (byte)p2);

	return _price.remove_rail;
}

static const struct {
	int8 xinc[16];
	int8 yinc[16];
} _railbit = {{
//  0   1   2   3   4   5
	-16,  0,-16,  0, 16,  0,    0,  0,
	 16,  0,  0, 16,  0,-16,    0,  0,
},{
	  0, 16,  0, 16,  0, 16,    0,  0,
	  0,-16,-16,  0,-16,  0,    0,  0,
}};

static int32 ValidateAutoDrag(byte *railbit, int x, int y, int ex, int ey)
{
	int dx, dy, trdx, trdy;

	if (!ValParamTrackOrientation(*railbit)) return CMD_ERROR;

	// calculate delta x,y from start to end tile
	dx = ex - x;
	dy = ey - y;

	// calculate delta x,y for the first direction
	trdx = _railbit.xinc[*railbit];
	trdy = _railbit.yinc[*railbit];

	if (*railbit & 0x6) {
		trdx += _railbit.xinc[*railbit ^ 1];
		trdy += _railbit.yinc[*railbit ^ 1];
	}

	// validate the direction
	while (((trdx <= 0) && (dx > 0)) || ((trdx >= 0) && (dx < 0)) ||
	       ((trdy <= 0) && (dy > 0)) || ((trdy >= 0) && (dy < 0))) {
		if (*railbit < 8) { // first direction is invalid, try the other
			SETBIT(*railbit, 3);
			trdx = -trdx;
			trdy = -trdy;
		} else // other direction is invalid too, invalid drag
			return CMD_ERROR;
	}

	// (for diagonal tracks, this is already made sure of by above test), but:
	// for non-diagonal tracks, check if the start and end tile are on 1 line
	if (*railbit & 0x6) {
		trdx = _railbit.xinc[*railbit];
		trdy = _railbit.yinc[*railbit];
		if ((abs(dx) != abs(dy)) && (abs(dx) + abs(trdy) != abs(dy) + abs(trdx)))
			return CMD_ERROR;
	}

	return 0;
}

/** Build a stretch of railroad tracks.
 * @param x,y start tile of drag
 * @param p1 end tile of drag
 * @param p2 various bitstuffed elements
 * - p2 = (bit 0-3) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev)
 * - p2 = (bit 4-6) - track-orientation, valid values: 0-5
 * - p2 = (bit 7)   - 0 = build, 1 = remove tracks
 */
static int32 CmdRailTrackHelper(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	int ex, ey;
	int32 ret, total_cost = 0;
	byte railbit = (p2 >> 4) & 7;
	byte mode = HASBIT(p2, 7);

	if (!ValParamRailtype(p2 & 0x3) || !ValParamTrackOrientation(railbit)) return CMD_ERROR;
	if (p1 > MapSize()) return CMD_ERROR;

	/* unpack end point */
	ex = TileX(p1) * 16;
	ey = TileY(p1) * 16;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	if (CmdFailed(ValidateAutoDrag(&railbit, x, y, ex, ey))) return CMD_ERROR;

	if (flags & DC_EXEC) SndPlayTileFx(SND_20_SPLAT_2, TILE_FROM_XY(x,y));

	for(;;) {
		ret = DoCommand(x, y, p2 & 0x3, railbit & 7, flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL);

		if (CmdFailed(ret)) {
			if ((_error_message != STR_1007_ALREADY_BUILT) && (mode == 0))
				break;
		} else
			total_cost += ret;

		if (x == ex && y == ey)
			break;

		x += _railbit.xinc[railbit];
		y += _railbit.yinc[railbit];

		// toggle railbit for the non-diagonal tracks
		if (railbit & 0x6) railbit ^= 1;
	}

	return (total_cost == 0) ? CMD_ERROR : total_cost;
}

int32 CmdBuildRailroadTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	return CmdRailTrackHelper(x, y, flags, p1, CLRBIT(p2, 7));
}

int32 CmdRemoveRailroadTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	return CmdRailTrackHelper(x, y, flags, p1, SETBIT(p2, 7));
}

/** Build a train depot
 * @param x,y position of the train depot
 * @param p1 rail type
 * @param p2 depot direction (0 through 3), where 0 is NW, 1 is NE, etc.
 */
int32 CmdBuildTrainDepot(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	Depot *d;
	TileIndex tile = TILE_FROM_XY(x,y);
	int32 cost, ret;
	uint tileh;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	if (!EnsureNoVehicle(tile)) return CMD_ERROR;
	/* check railtype and valid direction for depot (0 through 3), 4 in total */
	if (!ValParamRailtype(p1) || p2 > 3) return CMD_ERROR;

	tileh = GetTileSlope(tile, NULL);
	if (tileh != 0) {
		if ((!_patches.ainew_active && _is_ai_player) || !_patches.build_on_slopes || (tileh & 0x10 || !((0x4C >> p2) & tileh) ))
			return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
	}

	ret = DoCommandByTile(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
	if (CmdFailed(ret)) return CMD_ERROR;
	cost = ret;

	d = AllocateDepot();
	if (d == NULL)
		return CMD_ERROR;

	if (flags & DC_EXEC) {
		if (_current_player == _local_player)
			_last_built_train_depot_tile = (TileIndex)tile;

		ModifyTile(tile,
			MP_SETTYPE(MP_RAILWAY) |
			MP_MAP3LO | MP_MAPOWNER_CURRENT | MP_MAP5,
			p1, /* map3_lo */
			p2 | RAIL_TYPE_DEPOT /* map5 */
		);

		d->xy = tile;
		d->town_index = ClosestTownFromTile(tile, (uint)-1)->index;

		SetSignalsOnBothDir(tile, (p2&1) ? 2 : 1);

	}

	return cost + _price.build_train_depot;
}

/** Build signals, alternate between double/single, signal/semaphore,
 * pre/exit/combo-signals, and what-else not
 * @param x,y coordinates where signals is being built
 * @param p1 various bitstuffed elements
 * - p1 = (bit 0-2) - track-orientation, valid values: 0-5
 * - p1 = (bit 3)   - choose semaphores/signals or cycle normal/pre/exit/combo depending on context
 * @param p2 used for CmdBuildManySignals() to copy style of first signal
 */
int32 CmdBuildSingleSignal(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	TileIndex tile = TILE_FROM_XY(x, y);
	bool semaphore;
	bool pre_signal;
	uint track = p1 & 0x7;
	byte m5;
	int32 cost;

	if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoVehicle(tile))
		return CMD_ERROR;

	/* Protect against invalid signal copying */
	if (p2 != 0 && (p2 & _signals_table_both[track]) == 0) return CMD_ERROR;

	m5 = _map5[tile];

	/* You can't build signals in a depot, and the selected track must exist */
	if (m5 & 0x80 || !HASBIT(m5, track)) return CMD_ERROR;

	if (!CheckTileOwnership(tile)) return CMD_ERROR;

	_error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK;

	{
		byte m = m5 & RAIL_BIT_MASK;
		if (m != RAIL_BIT_DIAG1 &&
				m != RAIL_BIT_DIAG2 &&
				m != RAIL_BIT_UPPER &&
				m != RAIL_BIT_LOWER &&
				m != RAIL_BIT_LEFT &&
				m != RAIL_BIT_RIGHT &&
				m != (RAIL_BIT_UPPER | RAIL_BIT_LOWER) &&
				m != (RAIL_BIT_LEFT | RAIL_BIT_RIGHT))
			return CMD_ERROR;
	}

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	// Same bit, used in different contexts
	semaphore = pre_signal = HASBIT(p1, 3);

	if ((_map3_lo[tile] & _signals_table_both[track]) == 0) {
		// build new signals
		cost = _price.build_signals;
	} else {
		if (p2 != 0 &&
				((semaphore && !HASBIT(_map3_hi[tile], 2)) ||
				(!semaphore && HASBIT(_map3_hi[tile], 2)))) {
			// convert signals <-> semaphores
			cost = _price.build_signals + _price.remove_signals;
		} else {
			// it is free to change orientation/pre-exit-combo signals
			cost = 0;
		}
	}

	if (flags & DC_EXEC) {
		if (!(m5 & RAIL_TYPE_SIGNALS)) {
			// there are no signals at all on this tile yet
			_map5[tile] |= RAIL_TYPE_SIGNALS; // change into signals
			_map2[tile] |= 0xF0;              // all signals are on
			_map3_lo[tile] &= ~0xF0;          // no signals built by default
			_map3_hi[tile] = semaphore ? 4 : 0;
		}

		if (p2 == 0) {
			if ((_map3_lo[tile] & _signals_table_both[track]) == 0) {
				// build new signals
				_map3_lo[tile] |= _signals_table_both[track];
			} else {
				if (pre_signal) {
					// cycle between normal -> pre -> exit -> combo -> ...
					byte type = (_map3_hi[tile] + 1) & 0x03;
					_map3_hi[tile] &= ~0x03;
					_map3_hi[tile] |= type;
				} else {
					// cycle between two-way -> one-way -> one-way -> ...
					switch (track) {
						case 3:
						case 5: {
							byte signal = (_map3_lo[tile] - 0x10) & 0x30;
							if (signal == 0) signal = 0x30;
							_map3_lo[tile] &= ~0x30;
							_map3_lo[tile] |= signal;
							break;
						}

						default: {
							byte signal = (_map3_lo[tile] - 0x40) & 0xC0;
							if (signal == 0) signal = 0xC0;
							_map3_lo[tile] &= ~0xC0;
							_map3_lo[tile] |= signal;
							break;
						}
					}
				}
			}
		} else {
			/* If CmdBuildManySignals is called with copying signals, just copy the
			 * style of the first signal given as parameter by CmdBuildManySignals */
			_map3_lo[tile] &= ~_signals_table_both[track];
			_map3_lo[tile] |= p2 & _signals_table_both[track];
			// convert between signal<->semaphores when dragging
			if (semaphore)
				SETBIT(_map3_hi[tile], 2);
			else
				CLRBIT(_map3_hi[tile], 2);
		}

		MarkTileDirtyByTile(tile);
		SetSignalsOnBothDir(tile, track);
	}

	return cost;
}

/*	Build many signals by dragging: AutoSignals
 * x,y = start tile
 * p1  = end tile
 * p2  = (bit 0)			- 0 = build, 1 = remove signals
 * p2  = (bit 3)			- 0 = signals, 1 = semaphores
 * p2  = (bit 4-6)		- track-orientation, valid values: 0-5
 * p2  = (bit 24-31)	- user defined signals_density
 */
static int32 CmdSignalTrackHelper(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	int ex, ey;
	byte railbit = (p2 >> 4) & 7;
	bool error = true;
	TileIndex tile = TILE_FROM_XY(x, y);
	int32 ret, total_cost, signal_ctr;
	byte m5, semaphores = (HASBIT(p2, 3)) ? 8 : 0;
	int mode = p2 & 0x1;
	// for vertical/horizontal tracks, double the given signals density
	// since the original amount will be too dense (shorter tracks)
	byte signal_density = (railbit & 0x6) ? (p2 >> 24) * 2: (p2 >> 24);
	byte signals;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	// unpack end tile
	ex = TileX(p1) * 16;
	ey = TileY(p1) * 16;

	if (ValidateAutoDrag(&railbit, x, y, ex, ey) == CMD_ERROR)
		return CMD_ERROR;

	// copy the signal-style of the first rail-piece if existing
	m5 = _map5[tile];
	if (!(m5 & RAIL_TYPE_SPECIAL) && (m5 & RAIL_BIT_MASK) && (m5 & RAIL_TYPE_SIGNALS)) {
		signals = _map3_lo[tile] & _signals_table_both[railbit];
		if (signals == 0) signals = _signals_table_both[railbit];

		semaphores = (_map3_hi[tile] & ~3) ? 8 : 0; // copy signal/semaphores style (independent of CTRL)
	} else { // no signals exist, drag a two-way signal stretch
		signals = _signals_table_both[railbit];
	}

	/* signal_density_ctr	- amount of tiles already processed
	 * signals_density		- patch setting to put signal on every Nth tile (double space on |, -- tracks)
	 **********
	 * railbit		- direction of autorail
	 * semaphores	- semaphores or signals
	 * signals		- is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
									and convert all others to semaphore/signal
	 * mode				- 1 remove signals, 0 build signals */
	signal_ctr = total_cost = 0;
	for(;;) {
		// only build/remove signals with the specified density
		if ((signal_ctr %	signal_density) == 0 ) {
			ret = DoCommand(x, y, (railbit & 7) | semaphores, signals, flags, (mode == 1) ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);

			/* Abort placement for any other error than NOT_SUITABLE_TRACK
			 * This includes vehicles on track, competitor's tracks, etc. */
			if (ret == CMD_ERROR) {
				if (_error_message != STR_1005_NO_SUITABLE_RAILROAD_TRACK && mode != 1) {
					return CMD_ERROR;
				}
			} else {
				error = false;
				total_cost += ret;
			}
		}

		if (ex == x && ey == y) // reached end of drag
			break;

		x += _railbit.xinc[railbit];
		y += _railbit.yinc[railbit];
		signal_ctr++;

		// toggle railbit for the non-diagonal tracks (|, -- tracks)
		if (railbit & 0x6) railbit ^= 1;
	}

	return (error) ? CMD_ERROR : total_cost;
}

/* Stub for the unified Signal builder/remover */
int32 CmdBuildSignalTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	return CmdSignalTrackHelper(x, y, flags, p1, p2);
}

/** Remove signals
 * @param x,y coordinates where signal is being deleted from
 * @param p1 track combination to remove signal from
 */
int32 CmdRemoveSingleSignal(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	TileIndex tile = TILE_FROM_XY(x, y);
	uint track = p1 & 0x7;

	if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoVehicle(tile))
		return CMD_ERROR;

	if ((_map5[tile] & RAIL_TYPE_MASK) != RAIL_TYPE_SIGNALS ||
			(_map3_lo[tile] & _signals_table_both[track]) == 0) // signals on track?
		return CMD_ERROR;

	/* Only water can remove signals from anyone */
	if (_current_player != OWNER_WATER && !CheckTileOwnership(tile)) return CMD_ERROR;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	/* Do it? */
	if (flags & DC_EXEC) {
		_map3_lo[tile] &= ~_signals_table_both[track];

		/* removed last signal from tile? */
		if ((_map3_lo[tile] & 0xF0) == 0) {
			_map5[tile] &= ~RAIL_TYPE_SIGNALS;
			_map2[tile] &= ~0xF0;
			CLRBIT(_map3_hi[tile], 2); // remove any possible semaphores
		}

		SetSignalsOnBothDir(tile, track);

		MarkTileDirtyByTile(tile);
	}

	return _price.remove_signals;
}

/* Stub for the unified Signal builder/remover */
int32 CmdRemoveSignalTrack(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
	return CmdSignalTrackHelper(x, y, flags, p1, SETBIT(p2, 0));
}

typedef int32 DoConvertRailProc(uint tile, uint totype, bool exec);

static int32 DoConvertRail(uint tile, uint totype, bool exec)
{
	if (!CheckTileOwnership(tile) || !EnsureNoVehicle(tile))
		return CMD_ERROR;

	// tile is already of requested type?
	if ( (uint)(_map3_lo[tile] & 0xF) == totype)
		return CMD_ERROR;

	// change type.
	if (exec) {
		_map3_lo[tile] = (_map3_lo[tile] & 0xF0) + totype;
		MarkTileDirtyByTile(tile);
	}

	return _price.build_rail >> 1;
}

extern int32 DoConvertStationRail(uint tile, uint totype, bool exec);
extern int32 DoConvertStreetRail(uint tile, uint totype, bool exec);
extern int32 DoConvertTunnelBridgeRail(uint tile, uint totype, bool exec);

/** Convert one rail type to the other. You can convert normal rail to
 * monorail/maglev easily or vice-versa.
 * @param ex,ey end tile of rail conversion drag
 * @param p1 start tile of drag
 * @param p2 new railtype to convert to
 */
int32 CmdConvertRail(int ex, int ey, uint32 flags, uint32 p1, uint32 p2)
{
	int32 ret, cost, money;
	int sx, sy, x, y;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	if (!ValParamRailtype(p2)) return CMD_ERROR;
	if (p1 > MapSize()) return CMD_ERROR;

	// make sure sx,sy are smaller than ex,ey
	sx = TileX(p1) * 16;
	sy = TileY(p1) * 16;
	if (ex < sx) intswap(ex, sx);
	if (ey < sy) intswap(ey, sy);

	money = GetAvailableMoneyForCommand();
	cost = 0;

	for (x = sx; x <= ex; x += 16) {
		for (y = sy; y <= ey; y += 16) {
			TileIndex tile = TILE_FROM_XY(x,y);
			DoConvertRailProc *proc;

			if (IsTileType(tile, MP_RAILWAY)) proc = DoConvertRail;
			else if (IsTileType(tile, MP_STATION)) proc = DoConvertStationRail;
			else if (IsTileType(tile, MP_STREET)) proc = DoConvertStreetRail;
			else if (IsTileType(tile, MP_TUNNELBRIDGE)) proc = DoConvertTunnelBridgeRail;
			else continue;

			ret = proc(tile, p2, false);
			if (CmdFailed(ret)) continue;
			cost += ret;

			if (flags & DC_EXEC) {
				if ( (money -= ret) < 0) { _additional_cash_required = ret; return cost - ret; }
				proc(tile, p2, true);
			}
		}
	}

	return (cost == 0) ? CMD_ERROR : cost;
}

static int32 RemoveTrainDepot(uint tile, uint32 flags)
{
	if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER)
		return CMD_ERROR;

	if (!EnsureNoVehicle(tile))
		return CMD_ERROR;

	if (flags & DC_EXEC) {
		int track = _map5[tile] & RAIL_DEPOT_TRACK_MASK;

		DoDeleteDepot(tile);
		SetSignalsOnBothDir(tile, track);
	}

	return _price.remove_train_depot;
}

static int32 ClearTile_Track(TileIndex tile, byte flags)
{
	int32 cost;
	int32 ret;
	byte m5;

	m5 = _map5[tile];

	if (flags & DC_AUTO) {
		if (m5 & RAIL_TYPE_SPECIAL)
			return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);

		if (_map_owner[tile] != _current_player)
			return_cmd_error(STR_1024_AREA_IS_OWNED_BY_ANOTHER);

		return_cmd_error(STR_1008_MUST_REMOVE_RAILROAD_TRACK);
	}

	cost = 0;

	switch (m5 & RAIL_TYPE_MASK) {
		case RAIL_TYPE_SIGNALS:
			if (_map3_lo[tile] & _signals_table_both[0]) {
				ret = DoCommandByTile(tile, 0, 0, flags, CMD_REMOVE_SIGNALS);
				if (ret == CMD_ERROR) return CMD_ERROR;
				cost += ret;
			}
			if (_map3_lo[tile] & _signals_table_both[3]) {
				ret = DoCommandByTile(tile, 3, 0, flags, CMD_REMOVE_SIGNALS);
				if (ret == CMD_ERROR) return CMD_ERROR;
				cost += ret;
			}

			m5 &= RAIL_BIT_MASK;
			if (!(flags & DC_EXEC)) {
				for (; m5 != 0; m5 >>= 1) if (m5 & 1) cost += _price.remove_rail;
				return cost;
			}
			/* FALLTHROUGH */

		case RAIL_TYPE_NORMAL: {
			uint i;

			for (i = 0; m5 != 0; i++, m5 >>= 1) {
				if (m5 & 1) {
					ret = DoCommandByTile(tile, 0, i, flags, CMD_REMOVE_SINGLE_RAIL);
					if (ret == CMD_ERROR) return CMD_ERROR;
					cost += ret;
				}
			}
			return cost;
		}

		case RAIL_TYPE_DEPOT:
			switch (m5 & RAIL_SUBTYPE_MASK) {
				case RAIL_SUBTYPE_DEPOT:
					return RemoveTrainDepot(tile, flags);

				case RAIL_SUBTYPE_WAYPOINT:
					return RemoveTrainWaypoint(tile, flags, false);

				default:
					return CMD_ERROR;
			}

		default:
			return CMD_ERROR;
	}
}



#include "table/track_land.h"

// used for presignals
static const SpriteID _signal_base_sprites[16] = {
	0x4FB,
	0x1323,
	0x1333,
	0x1343,

	// use semaphores instead of signals?
	0x1353,
	0x1363,
	0x1373,
	0x1383,

	// mirrored versions
	0x4FB,
	0x1323,
	0x1333,
	0x1343,

	0x13C6,
	0x13D6,
	0x13E6,
	0x13F6,
};

// used to determine the side of the road for the signal
static const byte _signal_position[24] = {
	/* original: left side position */
	0x58,0x1E,0xE1,0xB9,0x01,0xA3,0x4B,0xEE,0x3B,0xD4,0x43,0xBD,
	/* patch: ride side position */
	0x1E,0xAC,0x64,0xE1,0x4A,0x10,0xEE,0xC5,0xDB,0x34,0x4D,0xB3
};

static void DrawSignalHelper(TileInfo *ti, byte condition, uint32 image_and_pos)
{
	bool otherside = _opt.road_side & _patches.signal_side;

	uint v = _signal_position[(image_and_pos & 0xF) + (otherside ? 12 : 0)];
	uint x = ti->x | (v&0xF);
	uint y = ti->y | (v>>4);
	uint sprite = _signal_base_sprites[(_map3_hi[ti->tile] & 7) + (otherside ? 8 : 0)] + (image_and_pos>>4) + ((condition != 0) ? 1 : 0);
	AddSortableSpriteToDraw(sprite, x, y, 1, 1, 10, GetSlopeZ(x,y));
}

static uint32 _drawtile_track_palette;


static void DrawTrackFence_NW(TileInfo *ti)
{
	uint32 image = 0x515;
	if (ti->tileh != 0) {
		image = 0x519;
		if (!(ti->tileh & 2)) {
			image = 0x51B;
		}
	}
	AddSortableSpriteToDraw(image | _drawtile_track_palette,
		ti->x, ti->y+1, 16, 1, 4, ti->z);
}

static void DrawTrackFence_SE(TileInfo *ti)
{
	uint32 image = 0x515;
	if (ti->tileh != 0) {
		image = 0x519;
		if (!(ti->tileh & 2)) {
			image = 0x51B;
		}
	}
	AddSortableSpriteToDraw(image | _drawtile_track_palette,
		ti->x, ti->y+15, 16, 1, 4, ti->z);
}

static void DrawTrackFence_NW_SE(TileInfo *ti)
{
	DrawTrackFence_NW(ti);
	DrawTrackFence_SE(ti);
}

static void DrawTrackFence_NE(TileInfo *ti)
{
	uint32 image = 0x516;
	if (ti->tileh != 0) {
		image = 0x51A;
		if (!(ti->tileh & 2)) {
			image = 0x51C;
		}
	}
	AddSortableSpriteToDraw(image | _drawtile_track_palette,
		ti->x+1, ti->y, 1, 16, 4, ti->z);
}

static void DrawTrackFence_SW(TileInfo *ti)
{
	uint32 image = 0x516;
	if (ti->tileh != 0) {
		image = 0x51A;
		if (!(ti->tileh & 2)) {
			image = 0x51C;
		}
	}
	AddSortableSpriteToDraw(image | _drawtile_track_palette,
		ti->x+15, ti->y, 1, 16, 4, ti->z);
}

static void DrawTrackFence_NE_SW(TileInfo *ti)
{
	DrawTrackFence_NE(ti);
	DrawTrackFence_SW(ti);
}

static void DrawTrackFence_NS_1(TileInfo *ti)
{
	int z = ti->z;
	if (ti->tileh & 1)
		z += 8;
	AddSortableSpriteToDraw(0x517 | _drawtile_track_palette,
		ti->x + 8, ti->y + 8, 1, 1, 4, z);
}

static void DrawTrackFence_NS_2(TileInfo *ti)
{
	int z = ti->z;
	if (ti->tileh & 4)
		z += 8;
	AddSortableSpriteToDraw(0x517 | _drawtile_track_palette,
		ti->x + 8, ti->y + 8, 1, 1, 4, z);
}

static void DrawTrackFence_WE_1(TileInfo *ti)
{
	int z = ti->z;
	if (ti->tileh & 8)
		z += 8;
	AddSortableSpriteToDraw(0x518 | _drawtile_track_palette,
		ti->x + 8, ti->y + 8, 1, 1, 4, z);
}

static void DrawTrackFence_WE_2(TileInfo *ti)
{
	int z = ti->z;
	if (ti->tileh & 2)
		z += 8;
	AddSortableSpriteToDraw(0x518 | _drawtile_track_palette,
		ti->x + 8, ti->y + 8, 1, 1, 4, z);
}

static void DetTrackDrawProc_Null(TileInfo *ti)
{
	/* nothing should be here */
}

typedef void DetailedTrackProc(TileInfo *ti);
DetailedTrackProc * const _detailed_track_proc[16] = {
	DetTrackDrawProc_Null,
	DetTrackDrawProc_Null,

	DrawTrackFence_NW,
	DrawTrackFence_SE,
	DrawTrackFence_NW_SE,

	DrawTrackFence_NE,
	DrawTrackFence_SW,
	DrawTrackFence_NE_SW,

	DrawTrackFence_NS_1,
	DrawTrackFence_NS_2,

	DrawTrackFence_WE_1,
	DrawTrackFence_WE_2,

	DetTrackDrawProc_Null,
	DetTrackDrawProc_Null,
	DetTrackDrawProc_Null,
	DetTrackDrawProc_Null,
};

static void DrawSpecialBuilding(uint32 image, uint32 tracktype_offs,
                                TileInfo *ti,
                                byte x, byte y, byte z,
                                byte xsize, byte ysize, byte zsize)
{
	if (image & 0x8000)
		image |= _drawtile_track_palette;
	image += tracktype_offs;
	if (_display_opt & DO_TRANS_BUILDINGS) // show transparent depots
		image = (image & 0x3FFF) | 0x3224000;
	AddSortableSpriteToDraw(image, ti->x + x, ti->y + y, xsize, ysize, zsize, ti->z + z);
}

static void DrawTile_Track(TileInfo *ti)
{
	uint32 tracktype_offs, image;
	byte m5;

	_drawtile_track_palette = SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_map_owner[ti->tile]));

	tracktype_offs = (_map3_lo[ti->tile] & 0xF) * TRACKTYPE_SPRITE_PITCH;

	m5 = (byte)ti->map5;
	if (!(m5 & RAIL_TYPE_SPECIAL)) {
		bool special;

		m5 &= RAIL_BIT_MASK;

		special = false;

		// select the sprite to use based on the map5 byte.
		(image = 0x3F3, m5 == RAIL_BIT_DIAG2) ||
		(image++,				m5 == RAIL_BIT_DIAG1) ||
		(image++,				m5 == RAIL_BIT_UPPER) ||
		(image++,				m5 == RAIL_BIT_LOWER) ||
		(image++,				m5 == RAIL_BIT_RIGHT) ||
		(image++,				m5 == RAIL_BIT_LEFT) ||
		(image++,				m5 == (RAIL_BIT_DIAG1|RAIL_BIT_DIAG2)) ||

		(image = 0x40B, m5 == (RAIL_BIT_UPPER|RAIL_BIT_LOWER)) ||
		(image++,				m5 == (RAIL_BIT_LEFT|RAIL_BIT_RIGHT)) ||

		(special=true, false) ||

		(image = 0x3FA, !(m5 & (RAIL_BIT_RIGHT|RAIL_BIT_UPPER|RAIL_BIT_DIAG1))) ||
		(image++,				!(m5 & (RAIL_BIT_LEFT|RAIL_BIT_LOWER|RAIL_BIT_DIAG1))) ||
		(image++,				!(m5 & (RAIL_BIT_LEFT|RAIL_BIT_UPPER|RAIL_BIT_DIAG2))) ||
		(image++,				!(m5 & (RAIL_BIT_RIGHT|RAIL_BIT_LOWER|RAIL_BIT_DIAG2))) ||
		(image++, true);

		if (ti->tileh != 0) {
			int f = GetRailFoundation(ti->tileh, ti->map5 & 0x3F);
			if (f) DrawFoundation(ti, f);

			// default sloped sprites..
			if (ti->tileh != 0) image = _track_sloped_sprites[ti->tileh - 1] + 0x3F3;
		}

		if ((_map2[ti->tile] & RAIL_MAP2LO_GROUND_MASK)==RAIL_GROUND_BROWN)
			image = (image & 0xFFFF) | 0x3178000; // use a brown palette
		else if ((_map2[ti->tile] & RAIL_MAP2LO_GROUND_MASK)==RAIL_GROUND_ICE_DESERT)
			image += 26;

		DrawGroundSprite(image + tracktype_offs);

		if (special) {
			if (m5 & RAIL_BIT_DIAG1) DrawGroundSprite(0x3ED + tracktype_offs);
			if (m5 & RAIL_BIT_DIAG2) DrawGroundSprite(0x3EE + tracktype_offs);
			if (m5 & RAIL_BIT_UPPER) DrawGroundSprite(0x3EF + tracktype_offs);
			if (m5 & RAIL_BIT_LOWER) DrawGroundSprite(0x3F0 + tracktype_offs);
			if (m5 & RAIL_BIT_LEFT)  DrawGroundSprite(0x3F2 + tracktype_offs);
			if (m5 & RAIL_BIT_RIGHT) DrawGroundSprite(0x3F1 + tracktype_offs);
		}

		if (_display_opt & DO_FULL_DETAIL) {
			_detailed_track_proc[_map2[ti->tile] & RAIL_MAP2LO_GROUND_MASK](ti);
		}

		/* draw signals also? */
		if (!(ti->map5 & RAIL_TYPE_SIGNALS))
			return;

		{
			byte m23;

			m23 = (_map3_lo[ti->tile] >> 4) | (_map2[ti->tile] & 0xF0);

#define HAS_SIGNAL(x) (m23 & (byte)(0x1 << (x)))
#define ISON_SIGNAL(x) (m23 & (byte)(0x10 << (x)))
#define MAYBE_DRAW_SIGNAL(x,y,z) if (HAS_SIGNAL(x)) DrawSignalHelper(ti, ISON_SIGNAL(x), ((y-0x4FB) << 4)|(z))

		if (!(m5 & RAIL_BIT_DIAG2)) {
			if (!(m5 & RAIL_BIT_DIAG1)) {
				if (m5 & RAIL_BIT_LEFT) {
					MAYBE_DRAW_SIGNAL(2, 0x509, 0);
					MAYBE_DRAW_SIGNAL(3, 0x507, 1);
				}
				if (m5 & RAIL_BIT_RIGHT) {
					MAYBE_DRAW_SIGNAL(0, 0x509, 2);
					MAYBE_DRAW_SIGNAL(1, 0x507, 3);
				}
				if (m5 & RAIL_BIT_UPPER) {
					MAYBE_DRAW_SIGNAL(3, 0x505, 4);
					MAYBE_DRAW_SIGNAL(2, 0x503, 5);
				}
				if (m5 & RAIL_BIT_LOWER) {
					MAYBE_DRAW_SIGNAL(1, 0x505, 6);
					MAYBE_DRAW_SIGNAL(0, 0x503, 7);
				}
			} else {
				MAYBE_DRAW_SIGNAL(3, 0x4FB, 8);
				MAYBE_DRAW_SIGNAL(2, 0x4FD, 9);
			}
		} else {
			MAYBE_DRAW_SIGNAL(3, 0x4FF, 10);
			MAYBE_DRAW_SIGNAL(2, 0x501, 11);
		}
		}
	} else {
		/* draw depots / waypoints */
		const DrawTrackSeqStruct *drss;
		byte type = m5 & 0x3F; // 0-3: depots, 4-5: waypoints

		if (!(m5 & (RAIL_TYPE_MASK&~RAIL_TYPE_SPECIAL)))
			return;

		if (ti->tileh != 0) { DrawFoundation(ti, ti->tileh); }

		if (IsRailWaypoint(m5) && _map3_lo[ti->tile] & 16) {
			// look for customization
			StationSpec *stat = GetCustomStation(STAT_CLASS_WAYP, _map3_hi[ti->tile]);

			if (stat) {
				DrawTileSeqStruct const *seq;
				// emulate station tile - open with building
				DrawTileSprites *cust = &stat->renderdata[2 + (m5 & 0x1)];
				uint32 relocation = GetCustomStationRelocation(stat, ComposeWaypointStation(ti->tile), 0);
				int railtype=(_map3_lo[ti->tile] & 0xF);

				/* We don't touch the 0x8000 bit. In all this
				 * waypoint code, it is used to indicate that
				 * we should offset by railtype, but we always
				 * do that for custom ground sprites and never
				 * for station sprites. And in the drawing
				 * code, it is used to indicate that the sprite
				 * should be drawn in company colors, and it's
				 * up to the GRF file to decide that. */

				image = cust->ground_sprite;
				image += railtype*((image<_custom_sprites_base)?TRACKTYPE_SPRITE_PITCH:1);

				DrawGroundSprite(image);

				foreach_draw_tile_seq(seq, cust->seq) {
					uint32 image = seq->image + relocation;
					DrawSpecialBuilding(image, 0, ti,
					                    seq->delta_x, seq->delta_y, seq->delta_z,
					                    seq->width, seq->height, seq->unk);
				}
				return;
			}
		}

		drss = _track_depot_layout_table[type];

		image = drss++->image;
		if (image & 0x8000) image = (image & 0x7FFF) + tracktype_offs;

		// adjust ground tile for desert
		// (don't adjust for arctic depots, because snow in depots looks weird)
		if ((_map2[ti->tile] & RAIL_MAP2LO_GROUND_MASK)==RAIL_GROUND_ICE_DESERT && (_opt.landscape == LT_DESERT || type>=4))
		{
			if(image!=3981)
				image += 26; // tile with tracks
			else
				image = 4550; // flat ground
		}

		DrawGroundSprite(image);

		while ((image=drss->image) != 0) {
			DrawSpecialBuilding(image, type < 4 ? tracktype_offs : 0, ti,
			                    drss->subcoord_x, drss->subcoord_y, 0,
			                    drss->width, drss->height, 0x17);
			drss++;
		}
	}
}

void DrawTrainDepotSprite(int x, int y, int image, int railtype)
{
	uint32 ormod, img;
	const DrawTrackSeqStruct *dtss;

	/* baseimage */
	railtype *= TRACKTYPE_SPRITE_PITCH;

	ormod = PLAYER_SPRITE_COLOR(_local_player);

	dtss = _track_depot_layout_table[image];

	x+=33;
	y+=17;

	img = dtss++->image;
	if (img & 0x8000) img = (img & 0x7FFF) + railtype;
	DrawSprite(img, x, y);

	for (; dtss->image != 0; dtss++) {
		Point pt = RemapCoords(dtss->subcoord_x, dtss->subcoord_y, 0);
		image = dtss->image;
		if (image & 0x8000) image |= ormod;
		DrawSprite(image + railtype, x + pt.x, y + pt.y);
	}
}

#define NUM_SSD_ENTRY 256
#define NUM_SSD_STACK 32

typedef struct SetSignalsData {
	int cur;
	int cur_stack;
	bool stop;
	bool has_presignal;

	// presignal info
	int presignal_exits;
	int presignal_exits_free;

	// these are used to keep track of the signals that change.
	byte bit[NUM_SSD_ENTRY];
	TileIndex tile[NUM_SSD_ENTRY];

	// these are used to keep track of the stack that modifies presignals recursively
	TileIndex next_tile[NUM_SSD_STACK];
	byte next_dir[NUM_SSD_STACK];

} SetSignalsData;

static bool SetSignalsEnumProc(uint tile, SetSignalsData *ssd, int track, uint length, byte *state)
{
	// the tile has signals?
	if (IsTileType(tile, MP_RAILWAY)) {
		if ((_map5[tile]&RAIL_TYPE_MASK) == RAIL_TYPE_SIGNALS) {
			if ((_map3_lo[tile] & _signals_table_both[track]) != 0) {

				// is the signal pointing in to the segment existing?
				if ((_map3_lo[tile] & _signals_table[track]) != 0) {
					// yes, add the signal to the list of signals
					if (ssd->cur != NUM_SSD_ENTRY) {
						ssd->tile[ssd->cur] = tile; // remember the tile index
						ssd->bit[ssd->cur] = track; // and the controlling bit number
						ssd->cur++;
					}

					// remember if this block has a presignal.
					ssd->has_presignal |= (_map3_hi[tile]&1);
				}

				// is this an exit signal that points out from the segment?
				if ((_map3_hi[tile]&2) && _map3_lo[tile]&_signals_table_other[track]) {
					ssd->presignal_exits++;
					if ((_map2[tile]&_signals_table_other[track]) != 0)
						ssd->presignal_exits_free++;
				}

				return true;
			}
		} else if (IsRailDepot(_map5[tile]))
			return true; // don't look further if the tile is a depot
	}
	return false;
}

/* Struct to parse data from VehicleFromPos to SignalVehicleCheckProc */
typedef struct SignalVehicleCheckStruct {
	TileIndex tile;
	uint track;
} SignalVehicleCheckStruct;

static void *SignalVehicleCheckProc(Vehicle *v, void *data)
{
	SignalVehicleCheckStruct *dest = data;
	TileIndex tile;

	if (v->type != VEH_Train)
		return NULL;

	/* Find the tile outside the tunnel, for signalling */
	if (v->u.rail.track == 0x40)
		tile = GetVehicleOutOfTunnelTile(v);
	else
		tile = v->tile;

	/* Wrong tile, or no train? Not a match */
	if (tile != dest->tile)
		return NULL;

	/* Are we on the same piece of track? */
	if (dest->track & (v->u.rail.track + (v->u.rail.track<<8)))
		return v;

	return NULL;
}

/* Special check for SetSignalsAfterProc, to see if there is a vehicle on this tile */
bool SignalVehicleCheck(TileIndex tile, uint track)
{
	SignalVehicleCheckStruct dest;

	dest.tile = tile;
	dest.track = track;

	return VehicleFromPos(tile, &dest, SignalVehicleCheckProc) != NULL;
}

static void SetSignalsAfterProc(TrackPathFinder *tpf)
{
	SetSignalsData *ssd = tpf->userdata;
	TrackPathFinderLink *link;
	uint offs;
	uint i;

	ssd->stop = false;

	/* Go through all the PF tiles */
	for (i = 0; i < lengthof(tpf->hash_head); i++) {
		/* Empty hash item */
		if (tpf->hash_head[i] == 0)
			continue;

		/* If 0x8000 is not set, there is only 1 item */
		if (!(tpf->hash_head[i] & 0x8000)) {
			/* Check if there is a vehicle on this tile */
			if (SignalVehicleCheck(tpf->hash_tile[i], tpf->hash_head[i])) {
				ssd->stop = true;
				return;
			}
		} else {
			/* There are multiple items, where hash_tile points to the first item in the list */
			offs = tpf->hash_tile[i];
			do {
				/* Find the next item */
				link = PATHFIND_GET_LINK_PTR(tpf, offs);
				/* Check if there is a vehicle on this tile */
				if (SignalVehicleCheck(link->tile, link->flags)) {
					ssd->stop = true;
					return;
				}
				/* Goto the next item */
			} while ((offs=link->next) != 0xFFFF);
		}
	}
}

static const byte _dir_from_track[14] = {
	0,1,0,1,2,1, 0,0,
	2,3,3,2,3,0,
};


static void ChangeSignalStates(SetSignalsData *ssd)
{
	int i;

	// thinking about presignals...
	// the presignal is green if,
	//   if no train is in the segment AND
	//   there is at least one green exit signal OR
	//   there are no exit signals in the segment

	// then mark the signals in the segment accordingly
	for(i=0; i!=ssd->cur; i++) {
		uint tile = ssd->tile[i];
		byte bit = _signals_table[ssd->bit[i]];
		uint16 m2 = _map2[tile];

		// presignals don't turn green if there is at least one presignal exit and none are free
		if (_map3_hi[tile] & 1) {
			int ex = ssd->presignal_exits, exfree = ssd->presignal_exits_free;

			// subtract for dual combo signals so they don't count themselves
			if (_map3_hi[tile]&2 && _map3_lo[tile]&_signals_table_other[ssd->bit[i]]) {
				ex--;
				if ((_map2[tile]&_signals_table_other[ssd->bit[i]]) != 0) exfree--;
			}

			// if we have exits and none are free, make red.
			if (ex && !exfree) goto make_red;
		}

		// check if the signal is unaffected.
		if (ssd->stop) {
make_red:
			// turn red
			if ( (bit&m2) == 0 )
				continue;
		} else {
			// turn green
			if ( (bit&m2) != 0 )
				continue;
		}

		// Update signals on the other side of this exit signal, it changed.
		// If this segment has presignals, then we treat exit signals going into the segment as normal signals.
		if (_map3_hi[tile]&2 && (_map3_hi[tile]&1 || !ssd->has_presignal)) {
			if (ssd->cur_stack != NUM_SSD_STACK) {
				ssd->next_tile[ssd->cur_stack] = tile;
				ssd->next_dir[ssd->cur_stack] = _dir_from_track[ssd->bit[i]];
				ssd->cur_stack++;
			} else {
				printf("NUM_SSD_STACK too small\n");
			}
		}

		// it changed, so toggle it
		_map2[tile] = m2 ^ bit;
		MarkTileDirtyByTile(tile);
	}
}


bool UpdateSignalsOnSegment(uint tile, byte direction)
{
	SetSignalsData ssd;
	int result = -1;

	ssd.cur_stack = 0;
	direction>>=1;

	for(;;) {
		// go through one segment and update all signals pointing into that segment.
		ssd.cur = ssd.presignal_exits = ssd.presignal_exits_free = 0;
		ssd.has_presignal = false;

		FollowTrack(tile, 0xC000 | TRANSPORT_RAIL, direction, (TPFEnumProc*)SetSignalsEnumProc, SetSignalsAfterProc, &ssd);
		ChangeSignalStates(&ssd);

		// remember the result only for the first iteration.
		if (result < 0) result = ssd.stop;

		// if any exit signals were changed, we need to keep going to modify the stuff behind those.
		if(!ssd.cur_stack)
			break;

		// one or more exit signals were changed, so we need to update another segment too.
		tile = ssd.next_tile[--ssd.cur_stack];
		direction = ssd.next_dir[ssd.cur_stack];
	}

	return (bool)result;
}

void SetSignalsOnBothDir(uint tile, byte track)
{
	static const byte _search_dir_1[6] = {1, 3, 1, 3, 5, 3};
	static const byte _search_dir_2[6] = {5, 7, 7, 5, 7, 1};

	UpdateSignalsOnSegment(tile, _search_dir_1[track]);
	UpdateSignalsOnSegment(tile, _search_dir_2[track]);
}

static uint GetSlopeZ_Track(TileInfo *ti)
{
	uint z = ti->z;
	int th = ti->tileh;

	// check if it's a foundation
	if (ti->tileh != 0) {
		if ((ti->map5 & 0x80) == 0) {
			uint f = GetRailFoundation(ti->tileh, ti->map5 & 0x3F);
			if (f != 0) {
				if (f < 15) {
					// leveled foundation
					return z + 8;
				}
				// inclined foundation
				th = _inclined_tileh[f - 15];
			}
		} else if ((ti->map5 & 0xC0) == 0xC0) {
			// depot or waypoint
			return z + 8;
		}
		return GetPartialZ(ti->x&0xF, ti->y&0xF, th) + z;
	}
	return z;
}

static uint GetSlopeTileh_Track(TileInfo *ti)
{
	// check if it's a foundation
	if (ti->tileh != 0) {
		if ((ti->map5 & 0x80) == 0) {
			uint f = GetRailFoundation(ti->tileh, ti->map5 & 0x3F);
			if (f != 0) {
				if (f < 15) {
					// leveled foundation
					return 0;
				}
				// inclined foundation
				return _inclined_tileh[f - 15];
			}
		} else if ((ti->map5 & 0xC0) == 0xC0) {
			// depot or waypoint
			return 0;
		}
	}
	return ti->tileh;
}

static void GetAcceptedCargo_Track(uint tile, AcceptedCargo ac)
{
	/* not used */
}

static void AnimateTile_Track(uint tile)
{
	/* not used */
}

static void TileLoop_Track(uint tile)
{
	byte a2;
	byte rail;
	uint16 m2;
	byte owner;

	m2 = _map2[tile] & 0xF;

	/* special code for alps landscape */
	if (_opt.landscape == LT_HILLY) {
		/* convert into snow? */
		if (GetTileZ(tile) > _opt.snow_line) {
			a2 = RAIL_GROUND_ICE_DESERT;
			goto modify_me;
		}

	/* special code for desert landscape */
	} else if (_opt.landscape == LT_DESERT) {
		/* convert into desert? */
		if (GetMapExtraBits(tile) == 1) {
			a2 = RAIL_GROUND_ICE_DESERT;
			goto modify_me;
		}
	}

	// Don't continue tile loop for depots
	if (_map5[tile] & RAIL_TYPE_SPECIAL)
		return;

	a2 = RAIL_GROUND_GREEN;

	if (m2 != RAIL_GROUND_BROWN) { /* wait until bottom is green */
		/* determine direction of fence */
		rail = _map5[tile] & RAIL_BIT_MASK;

		if (rail == RAIL_BIT_UPPER) {
			a2 = RAIL_GROUND_FENCE_HORIZ1;
		} else if (rail == RAIL_BIT_LOWER) {
			a2 = RAIL_GROUND_FENCE_HORIZ2;
		} else if (rail == RAIL_BIT_LEFT) {
			a2 = RAIL_GROUND_FENCE_VERT1;
		} else if (rail == RAIL_BIT_RIGHT) {
			a2 = RAIL_GROUND_FENCE_VERT2;
		} else {
			owner = _map_owner[tile];

			if ( (!(rail&(RAIL_BIT_DIAG2|RAIL_BIT_UPPER|RAIL_BIT_LEFT)) && (rail&RAIL_BIT_DIAG1)) || rail==(RAIL_BIT_LOWER|RAIL_BIT_RIGHT)) {
				if (!IsTileType(tile + TILE_XY(0,-1), MP_RAILWAY) ||
						owner != _map_owner[tile + TILE_XY(0,-1)] ||
						(_map5[tile + TILE_XY(0,-1)]==RAIL_BIT_UPPER || _map5[tile + TILE_XY(0,-1)]==RAIL_BIT_LEFT))
							a2 = RAIL_GROUND_FENCE_NW;
			}

			if ( (!(rail&(RAIL_BIT_DIAG2|RAIL_BIT_LOWER|RAIL_BIT_RIGHT)) && (rail&RAIL_BIT_DIAG1)) || rail==(RAIL_BIT_UPPER|RAIL_BIT_LEFT)) {
				if (!IsTileType(tile + TILE_XY(0,1), MP_RAILWAY) ||
						owner != _map_owner[tile + TILE_XY(0,1)] ||
						(_map5[tile + TILE_XY(0,1)]==RAIL_BIT_LOWER || _map5[tile + TILE_XY(0,1)]==RAIL_BIT_RIGHT))
							a2 = (a2 == RAIL_GROUND_FENCE_NW) ? RAIL_GROUND_FENCE_SENW : RAIL_GROUND_FENCE_SE;
			}

			if ( (!(rail&(RAIL_BIT_DIAG1|RAIL_BIT_UPPER|RAIL_BIT_RIGHT)) && (rail&RAIL_BIT_DIAG2)) || rail==(RAIL_BIT_LOWER|RAIL_BIT_LEFT)) {
				if (!IsTileType(tile + TILE_XY(-1,0), MP_RAILWAY) ||
						owner != _map_owner[tile + TILE_XY(-1,0)] ||
						(_map5[tile + TILE_XY(-1,0)]==RAIL_BIT_UPPER || _map5[tile + TILE_XY(-1,0)]==RAIL_BIT_RIGHT))
							a2 = RAIL_GROUND_FENCE_NE;
			}

			if ( (!(rail&(RAIL_BIT_DIAG1|RAIL_BIT_LOWER|RAIL_BIT_LEFT)) && (rail&RAIL_BIT_DIAG2)) || rail==(RAIL_BIT_UPPER|RAIL_BIT_RIGHT)) {
				if (!IsTileType(tile + TILE_XY(1,0), MP_RAILWAY) ||
						owner != _map_owner[tile + TILE_XY(1,0)] ||
						(_map5[tile + TILE_XY(1,0)]==RAIL_BIT_LOWER || _map5[tile + TILE_XY(1,0)]==RAIL_BIT_LEFT))
							a2 = (a2 == RAIL_GROUND_FENCE_NE) ? RAIL_GROUND_FENCE_NESW : RAIL_GROUND_FENCE_SW;
			}
		}
	}

modify_me:;
	/* tile changed? */
	if ( m2 != a2) {
		_map2[tile] = (_map2[tile] & ~RAIL_MAP2LO_GROUND_MASK) | a2;
		MarkTileDirtyByTile(tile);
	}
}


static uint32 GetTileTrackStatus_Track(uint tile, TransportType mode) {
	byte m5, a;
	uint16 b;
	uint32 ret;

	if (mode != TRANSPORT_RAIL)
		return 0;

	m5 = _map5[tile];

	if (!(m5 & RAIL_TYPE_SPECIAL)) {
		ret = (m5 | (m5 << 8)) & 0x3F3F;
		if (!(m5 & RAIL_TYPE_SIGNALS)) {
			if ( (ret & 0xFF) == 3)
			/* Diagonal crossing? */
				ret |= 0x40;
		} else {
			/* has_signals */

			a = _map3_lo[tile];
			b = _map2[tile];

			b &= a;

			/* When signals are not present (in neither
			 * direction), we pretend them to be green. (So if
			 * signals are only one way, the other way will
			 * implicitely become `red' */
			if ((a & 0xC0) == 0) { b |= 0xC0; }
			if ((a & 0x30) == 0) { b |= 0x30; }

			if ( (b & 0x80) == 0)	ret |= 0x10070000;
			if ( (b & 0x40) == 0)	ret |= 0x7100000;
			if ( (b & 0x20) == 0)	ret |= 0x20080000;
			if ( (b & 0x10) == 0)	ret |= 0x8200000;
		}
	} else if (m5 & 0x40) {
		static const byte _train_spec_tracks[6] = {1,2,1,2,1,2};
		m5 = _train_spec_tracks[m5 & 0x3F];
		ret = (m5 << 8) + m5;
	} else
		return 0;
	return ret;
}

static void ClickTile_Track(uint tile)
{
	if (IsRailDepot(_map5[tile]))
		ShowTrainDepotWindow(tile);
	else if (IsRailWaypoint(_map5[tile]))
		ShowRenameWaypointWindow(GetWaypointByTile(tile));

}

static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
{
	td->owner = _map_owner[tile];
	switch (_map5[tile] & RAIL_TYPE_MASK) {
		case RAIL_TYPE_NORMAL:
			td->str = STR_1021_RAILROAD_TRACK;
			break;

		case RAIL_TYPE_SIGNALS: {
			const StringID signal_type[] = {
				STR_RAILROAD_TRACK_WITH_NORMAL_SIGNALS,
				STR_RAILROAD_TRACK_WITH_PRESIGNALS,
				STR_RAILROAD_TRACK_WITH_EXITSIGNALS,
				STR_RAILROAD_TRACK_WITH_COMBOSIGNALS
			};

			td->str = signal_type[_map3_hi[tile] & 0x03];
			break;
		}

		case RAIL_TYPE_DEPOT:
		default:
			td->str = ((_map5[tile] & RAIL_SUBTYPE_MASK) == RAIL_SUBTYPE_DEPOT) ?
				STR_1023_RAILROAD_TRAIN_DEPOT : STR_LANDINFO_WAYPOINT;
			break;
	}
}

static void ChangeTileOwner_Track(uint tile, byte old_player, byte new_player)
{
	if (_map_owner[tile] != old_player)
		return;

	if (new_player != 255) {
		_map_owner[tile] = new_player;
	}	else {
		DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
	}
}

static const byte _fractcoords_behind[4] = { 0x8F, 0x8, 0x80, 0xF8 };
static const byte _fractcoords_enter[4] = { 0x8A, 0x48, 0x84, 0xA8 };
static const byte _fractcoords_leave[4] = { 0x81, 0xD8, 0x8D, 0x18 };
static const byte _enter_directions[4] = {5, 7, 1, 3};
static const byte _leave_directions[4] = {1, 3, 5, 7};
static const byte _depot_track_mask[4] = {1, 2, 1, 2};

static uint32 VehicleEnter_Track(Vehicle *v, uint tile, int x, int y)
{
	byte fract_coord;
	int dir;

	// this routine applies only to trains in depot tiles
	if (v->type != VEH_Train || !IsRailDepot(_map5[tile]))
		return 0;

	/* depot direction */
	dir = _map5[tile] & RAIL_DEPOT_DIR;

	fract_coord = (x & 0xF) + ((y & 0xF) << 4);
	if (_fractcoords_behind[dir] == fract_coord) {
		/* make sure a train is not entering the tile from behind */
		return 8;
	} else if (_fractcoords_enter[dir] == fract_coord) {
		if (_enter_directions[dir] == v->direction) {
			/* enter the depot */
			v->u.rail.track = 0x80,
			v->vehstatus |= VS_HIDDEN; /* hide it */
			v->direction ^= 4;
			if (v->next == NULL)
				TrainEnterDepot(v, tile);
			v->tile = tile;
			InvalidateWindow(WC_VEHICLE_DEPOT, tile);
			return 4;
		}
	} else if (_fractcoords_leave[dir] == fract_coord) {
		if (_leave_directions[dir] == v->direction) {
			/* leave the depot? */
			if ((v=v->next) != NULL) {
				v->vehstatus &= ~VS_HIDDEN;
				v->u.rail.track = _depot_track_mask[dir];
				assert(v->u.rail.track);
			}
		}
	}

	return 0;
}

void InitializeRail(void)
{
	_last_built_train_depot_tile = 0;
}

const TileTypeProcs _tile_type_rail_procs = {
	DrawTile_Track,						/* draw_tile_proc */
	GetSlopeZ_Track,					/* get_slope_z_proc */
	ClearTile_Track,					/* clear_tile_proc */
	GetAcceptedCargo_Track,		/* get_accepted_cargo_proc */
	GetTileDesc_Track,				/* get_tile_desc_proc */
	GetTileTrackStatus_Track,	/* get_tile_track_status_proc */
	ClickTile_Track,					/* click_tile_proc */
	AnimateTile_Track,				/* animate_tile_proc */
	TileLoop_Track,						/* tile_loop_clear */
	ChangeTileOwner_Track,		/* change_tile_owner_clear */
	NULL,											/* get_produced_cargo_proc */
	VehicleEnter_Track,				/* vehicle_enter_tile_proc */
	NULL,											/* vehicle_leave_tile_proc */
	GetSlopeTileh_Track,			/* get_slope_tileh_proc */
};