tunnelbridge_cmd.c
author celestar
Fri, 02 Jun 2006 13:05:41 +0000
changeset 3933 a5f08e17f4a0
parent 3900 4984308f9125
child 3938 73c210a59c43
permissions -rw-r--r--
(svn r5070) Merged the bridge branch
-Feature: Bridges can now be placed above:
Any railway track combination (excluding depots and waypoints)
Any road combination (excluding depots)
Clear tiles (duh), including fields
Tunnel entrances
Bridge heads

Thanks to Tron for idea and implementation, KUDr for the yapf synchronization and many others for hours of testing

There are still a number of visual problems remaining, especially when electric railways are on or under the bridge.
DO NOT REPORT THOSE BUGS FOR THE TIME BEING please.
/* $Id$ */

/** @file tunnelbridge_cmd.c
 * This file deals with tunnels and bridges (non-gui stuff)
 * @todo seperate this file into two
 */

#include "stdafx.h"
#include "openttd.h"
#include "bridge_map.h"
#include "rail_map.h"
#include "road_map.h"
#include "table/sprites.h"
#include "table/strings.h"
#include "functions.h"
#include "map.h"
#include "tile.h"
#include "tunnel_map.h"
#include "vehicle.h"
#include "viewport.h"
#include "command.h"
#include "player.h"
#include "town.h"
#include "sound.h"
#include "variables.h"
#include "bridge.h"
#include "train.h"
#include "water_map.h"
#include "yapf/yapf.h"

#include "table/bridge_land.h"

const Bridge orig_bridge[] = {
/*
	   year of availablity
	   |  minimum length
	   |  |   maximum length
	   |  |   |    price
	   |  |   |    |    maximum speed
	   |  |   |    |    |  sprite to use in GUI                string with description
	   |  |   |    |    |  |                                   |                            */
	{  0, 0, 16,  80,  32, 0xA24                             , STR_5012_WOODEN             , NULL, 0 },
	{  0, 0,  2, 112,  48, 0xA26 | PALETTE_TO_STRUCT_RED     , STR_5013_CONCRETE           , NULL, 0 },
	{ 10, 0,  5, 144,  64, 0xA25                             , STR_500F_GIRDER_STEEL       , NULL, 0 },
	{  0, 2, 10, 168,  80, 0xA22 | PALETTE_TO_STRUCT_CONCRETE, STR_5011_SUSPENSION_CONCRETE, NULL, 0 },
	{ 10, 3, 16, 185,  96, 0xA22                             , STR_500E_SUSPENSION_STEEL   , NULL, 0 },
	{ 10, 3, 16, 192, 112, 0xA22 | PALETTE_TO_STRUCT_YELLOW  , STR_500E_SUSPENSION_STEEL   , NULL, 0 },
	{ 10, 3,  7, 224, 160, 0xA23                             , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
	{ 10, 3,  8, 232, 208, 0xA23 | PALETTE_TO_STRUCT_BROWN   , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
	{ 10, 3,  9, 248, 240, 0xA23 | PALETTE_TO_STRUCT_RED     , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
	{ 10, 0,  2, 240, 256, 0xA27                             , STR_500F_GIRDER_STEEL       , NULL, 0 },
	{ 75, 2, 16, 255, 320, 0xA28                             , STR_5014_TUBULAR_STEEL      , NULL, 0 },
	{ 85, 2, 32, 380, 512, 0xA28 | PALETTE_TO_STRUCT_YELLOW  , STR_5014_TUBULAR_STEEL      , NULL, 0 },
	{ 90, 2, 32, 510, 608, 0xA28 | PALETTE_TO_STRUCT_GREY    , STR_BRIDGE_TUBULAR_SILICON  , NULL, 0 }
};

Bridge _bridge[MAX_BRIDGES];


// calculate the price factor for building a long bridge.
// basically the cost delta is 1,1, 1, 2,2, 3,3,3, 4,4,4,4, 5,5,5,5,5, 6,6,6,6,6,6,  7,7,7,7,7,7,7,  8,8,8,8,8,8,8,8,
int CalcBridgeLenCostFactor(int x)
{
	int n;
	int r;

	if (x < 2) return x;
	x -= 2;
	for (n = 0, r = 2;; n++) {
		if (x <= n) return r + x * n;
		r += n * n;
		x -= n;
	}
}

#define M(x) (1 << (x))
enum {
	// foundation, whole tile is leveled up --> 3 corners raised
	BRIDGE_FULL_LEVELED_FOUNDATION = M(SLOPE_WSE) | M(SLOPE_NWS) | M(SLOPE_ENW) | M(SLOPE_SEN),
	// foundation, tile is partly leveled up --> 1 corner raised
	BRIDGE_PARTLY_LEVELED_FOUNDATION = M(SLOPE_W) | M(SLOPE_S) | M(SLOPE_E) | M(SLOPE_N),
	// no foundations (X,Y direction)
	BRIDGE_NO_FOUNDATION = M(SLOPE_FLAT) | M(SLOPE_SW) | M(SLOPE_SE) | M(SLOPE_NW) | M(SLOPE_NE),
	BRIDGE_HORZ_RAMP = (BRIDGE_PARTLY_LEVELED_FOUNDATION | BRIDGE_NO_FOUNDATION) & ~M(SLOPE_FLAT)
};
#undef M

static inline const PalSpriteID *GetBridgeSpriteTable(int index, byte table)
{
	const Bridge *bridge = &_bridge[index];
	assert(table < 7);
	if (bridge->sprite_table == NULL || bridge->sprite_table[table] == NULL) {
		return _bridge_sprite_table[index][table];
	} else {
		return bridge->sprite_table[table];
	}
}

static inline byte GetBridgeFlags(int index) { return _bridge[index].flags;}

/**	check if bridge can be built on slope
 *	direction 0 = X-axis, direction 1 = Y-axis
 *	is_start_tile = false		<-- end tile
 *	is_start_tile = true		<-- start tile
 */
static uint32 CheckBridgeSlope(Axis direction, Slope tileh, bool is_start_tile)
{
	if (IsSteepSlope(tileh)) return CMD_ERROR;

	if (is_start_tile) {
		/* check slope at start tile
				- no extra cost
		*/
#define M(x) (1 << (x))
		if (HASBIT(M(SLOPE_FLAT) | (direction == AXIS_X ? M(SLOPE_NE) : M(SLOPE_NW)), tileh)) return 0;

		// disallow certain start tiles to avoid certain crooked bridges
		if (tileh == SLOPE_S) return CMD_ERROR;
	} else {
		/*	check slope at end tile
				- no extra cost
		*/
		if (HASBIT(M(SLOPE_FLAT) | (direction == AXIS_X ? M(SLOPE_SW) : M(SLOPE_SE)), tileh)) return 0;
#undef M

		// disallow certain end tiles to avoid certain crooked bridges
		if (tileh == SLOPE_N) return CMD_ERROR;
	}

	/*	disallow common start/end tiles to avoid certain crooked bridges e.g.
	 *	start-tile:	X 2,1 Y 2,4 (2 was disabled before)
	 *	end-tile:		X 8,4 Y 8,1 (8 was disabled before)
	 */
	if ((tileh == SLOPE_W && is_start_tile != (direction != AXIS_X)) ||
			(tileh == SLOPE_E && is_start_tile == (direction != AXIS_X))) {
		return CMD_ERROR;
	}

	// slope foundations
	if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION | BRIDGE_PARTLY_LEVELED_FOUNDATION, tileh))
		return _price.terraform;

	return CMD_ERROR;
}

uint32 GetBridgeLength(TileIndex begin, TileIndex end)
{
	int x1 = TileX(begin);
	int y1 = TileY(begin);
	int x2 = TileX(end);
	int y2 = TileY(end);

	return abs(x2 + y2 - x1 - y1) - 1;
}

bool CheckBridge_Stuff(byte bridge_type, uint bridge_len)
{
	const Bridge *b = &_bridge[bridge_type];
	uint max; // max possible length of a bridge (with patch 100)

	if (bridge_type >= MAX_BRIDGES) return false;
	if (b->avail_year > _cur_year) return false;

	max = b->max_length;
	if (max >= 16 && _patches.longbridges) max = 100;

	return b->min_length <= bridge_len && bridge_len <= max;
}

/** Build a Bridge
 * @param end_tile end tile
 * @param p1 packed start tile coords (~ dx)
 * @param p2 various bitstuffed elements
 * - p2 = (bit 0- 7) - bridge type (hi bh)
 * - p2 = (bit 8-..) - rail type. bit15 ((x>>8)&0x80) means road bridge.
 */
int32 CmdBuildBridge(TileIndex end_tile, uint32 flags, uint32 p1, uint32 p2)
{
	int bridge_type;
	TransportType transport;
	RailType railtype;
	uint x;
	uint y;
	uint sx;
	uint sy;
	TileIndex tile_start;
	TileIndex tile_end;
	Slope tileh_start;
	Slope tileh_end;
	uint z_start;
	uint z_end;
	TileIndex tile;
	TileIndexDiff delta;
	uint bridge_len;
	Axis direction;
	uint i;
	int32 cost, terraformcost, ret;
	bool allow_on_slopes;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	/* unpack parameters */
	bridge_type = GB(p2, 0, 8);

	if (p1 >= MapSize()) return CMD_ERROR;

	// type of bridge
	if (HASBIT(p2, 15)) {
		railtype = 0;
		transport = TRANSPORT_ROAD;
	} else {
		if (!ValParamRailtype(GB(p2, 8, 8))) return CMD_ERROR;
		railtype = GB(p2, 8, 8);
		transport = TRANSPORT_RAIL;
	}

	x = TileX(end_tile);
	y = TileY(end_tile);
	sx = TileX(p1);
	sy = TileY(p1);

	/* check if valid, and make sure that (x,y) are smaller than (sx,sy) */
	if (x == sx) {
		if (y == sy) return_cmd_error(STR_5008_CANNOT_START_AND_END_ON);
		direction = AXIS_Y;
		if (y > sy) uintswap(y,sy);
	} else if (y == sy) {
		direction = AXIS_X;
		if (x > sx) uintswap(x,sx);
	} else {
		return_cmd_error(STR_500A_START_AND_END_MUST_BE_IN);
	}

	/* set and test bridge length, availability */
	bridge_len = (sx + sy - x - y) - 1;
	if (!CheckBridge_Stuff(bridge_type, bridge_len)) return_cmd_error(STR_5015_CAN_T_BUILD_BRIDGE_HERE);

	/* retrieve landscape height and ensure it's on land */
	tile_start = TileXY(x, y);
	tile_end = TileXY(sx, sy);
	if (IsClearWaterTile(tile_start) || IsClearWaterTile(tile_end)) return_cmd_error(STR_02A0_ENDS_OF_BRIDGE_MUST_BOTH);

	tileh_start = GetTileSlope(tile_start, &z_start);
	tileh_end = GetTileSlope(tile_end, &z_end);

	if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh_start)) {
		z_start += TILE_HEIGHT;
		tileh_start = SLOPE_FLAT;
	}

	if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh_end)) {
		z_end += TILE_HEIGHT;
		tileh_end = SLOPE_FLAT;
	}

	if (z_start != z_end) return_cmd_error(STR_5009_LEVEL_LAND_OR_WATER_REQUIRED);

	// Towns are not allowed to use bridges on slopes.
	allow_on_slopes = (!_is_old_ai_player
	                   && _current_player != OWNER_TOWN && _patches.build_on_slopes);

	/* Try and clear the start landscape */

	ret = DoCommand(tile_start, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
	if (CmdFailed(ret)) return ret;
	cost = ret;

	// true - bridge-start-tile, false - bridge-end-tile
	terraformcost = CheckBridgeSlope(direction, tileh_start, true);
	if (CmdFailed(terraformcost) || (terraformcost && !allow_on_slopes))
		return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
	cost += terraformcost;

	/* Try and clear the end landscape */

	ret = DoCommand(tile_end, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
	if (CmdFailed(ret)) return ret;
	cost += ret;

	// false - end tile slope check
	terraformcost = CheckBridgeSlope(direction, tileh_end, false);
	if (CmdFailed(terraformcost) || (terraformcost && !allow_on_slopes))
		return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
	cost += terraformcost;

	{
		TileIndex Heads[] = {tile_start, tile_end};
		int i;

		for (i = 0; i < 2; i++) {
			if (MayHaveBridgeAbove(Heads[i])) {
				if (IsBridgeAbove(Heads[i])) {
					TileIndex north_head = GetNorthernBridgeEnd(Heads[i]);
					int z1, z2;

					if (direction == GetBridgeAxis(Heads[i])) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);

					z1 = GetBridgeHeight(north_head, GetBridgeAxis(Heads[i]));
					z2 = GetBridgeHeight(Heads[i], direction);

					if (z1 == z2) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
				}
			}
		}
	}

	/* do the drill? */
	if (flags & DC_EXEC) {
		DiagDirection dir = AxisToDiagDir(direction);

		if (transport == TRANSPORT_RAIL) {
			MakeRailBridgeRamp(tile_start, _current_player, bridge_type, dir, railtype);
			MakeRailBridgeRamp(tile_end,   _current_player, bridge_type, ReverseDiagDir(dir), railtype);
		} else {
			MakeRoadBridgeRamp(tile_start, _current_player, bridge_type, dir);
			MakeRoadBridgeRamp(tile_end,   _current_player, bridge_type, ReverseDiagDir(dir));
		}
		MarkTileDirtyByTile(tile_start);
		MarkTileDirtyByTile(tile_end);
	}

	tile = tile_start;
	delta = (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
	for (i = 0; i != bridge_len; i++) {
		uint z;

		tile += delta;

		if (GetTileSlope(tile, &z) != SLOPE_FLAT && z >= z_start) return_cmd_error(STR_5009_LEVEL_LAND_OR_WATER_REQUIRED);

		if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) {
			/* Disallow crossing bridges for the time being */
			return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
		}

		switch (GetTileType(tile)) {
			case MP_WATER:
				if (!EnsureNoVehicle(tile)) return_cmd_error(STR_980E_SHIP_IN_THE_WAY);
				if (!(IsWater(tile) || IsCoast(tile))) goto not_valid_below;
				break;

			case MP_RAILWAY:
				if (!IsPlainRailTile(tile)) goto not_valid_below;
				break;

			case MP_STREET:
				if (GetRoadTileType(tile) != ROAD_TILE_NORMAL) goto not_valid_below;
				break;

			case MP_TUNNELBRIDGE:
				if (IsTunnel(tile)) break;
				if (direction == DiagDirToAxis(GetBridgeRampDirection(tile))) goto not_valid_below;
				if (GetBridgeHeight(tile_start, direction) == GetBridgeHeight(tile, DiagDirToAxis(GetBridgeRampDirection(tile)))) goto not_valid_below;
				break;

			case MP_CLEAR:
				if (IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
				break;

			default:
not_valid_below:;
				/* try and clear the middle landscape */
				ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
				if (CmdFailed(ret)) return ret;
				cost += ret;
				break;
		}

		if (flags & DC_EXEC) {
			SetBridgeMiddle(tile, direction);
			MarkTileDirtyByTile(tile);
		}
	}

	SetSignalsOnBothDir(tile_start, direction == AXIS_X ? TRACK_X : TRACK_Y);
	YapfNotifyTrackLayoutChange(tile_start, direction == AXIS_X ? TRACK_X : TRACK_Y);

	/*	for human player that builds the bridge he gets a selection to choose from bridges (DC_QUERY_COST)
			It's unnecessary to execute this command every time for every bridge. So it is done only
			and cost is computed in "bridge_gui.c". For AI, Towns this has to be of course calculated
	*/
	if (!(flags & DC_QUERY_COST)) {
		const Bridge *b = &_bridge[bridge_type];

		bridge_len += 2;	// begin and end tiles/ramps

		if (_current_player < MAX_PLAYERS && !_is_old_ai_player)
			bridge_len = CalcBridgeLenCostFactor(bridge_len);

		cost += (int64)bridge_len * _price.build_bridge * b->price >> 8;
	}

	return cost;
}


/** Build Tunnel.
 * @param tile start tile of tunnel
 * @param p1 railtype, 0x200 for road tunnel
 * @param p2 unused
 */
int32 CmdBuildTunnel(TileIndex start_tile, uint32 flags, uint32 p1, uint32 p2)
{
	TileIndexDiff delta;
	TileIndex end_tile;
	DiagDirection direction;
	Slope start_tileh;
	Slope end_tileh;
	uint start_z;
	uint end_z;
	int32 cost;
	int32 ret;

	_build_tunnel_endtile = 0;

	if (p1 != 0x200 && !ValParamRailtype(p1)) return CMD_ERROR;

	start_tileh = GetTileSlope(start_tile, &start_z);

	switch (start_tileh) {
		case SLOPE_SW: direction = DIAGDIR_SW; break;
		case SLOPE_SE: direction = DIAGDIR_SE; break;
		case SLOPE_NW: direction = DIAGDIR_NW; break;
		case SLOPE_NE: direction = DIAGDIR_NE; break;
		default: return_cmd_error(STR_500B_SITE_UNSUITABLE_FOR_TUNNEL);
	}

	ret = DoCommand(start_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
	if (CmdFailed(ret)) return ret;
	cost = _price.build_tunnel + ret;

	delta = TileOffsByDir(direction);
	end_tile = start_tile;
	for (;;) {
		end_tile += delta;
		end_tileh = GetTileSlope(end_tile, &end_z);

		if (start_z == end_z) break;

		if (!_cheats.crossing_tunnels.value && IsTunnelInWay(end_tile, start_z)) {
			return_cmd_error(STR_5003_ANOTHER_TUNNEL_IN_THE_WAY);
		}

		cost += _price.build_tunnel;
		cost += cost >> 3;
		if (cost >= 400000000) cost = 400000000;
	}

	// if the command fails from here on we want the end tile to be highlighted
	_build_tunnel_endtile = end_tile;

	// slope of end tile must be complementary to the slope of the start tile
	if (end_tileh != ComplementSlope(start_tileh)) {
		ret = DoCommand(end_tile, end_tileh & start_tileh, 0, flags, CMD_TERRAFORM_LAND);
		if (CmdFailed(ret)) return_cmd_error(STR_5005_UNABLE_TO_EXCAVATE_LAND);
	} else {
		ret = DoCommand(end_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
		if (CmdFailed(ret)) return ret;
	}
	cost += _price.build_tunnel + ret;

	if (flags & DC_EXEC) {
		if (GB(p1, 9, 1) == TRANSPORT_RAIL) {
			MakeRailTunnel(start_tile, _current_player, direction,                 GB(p1, 0, 4));
			MakeRailTunnel(end_tile,   _current_player, ReverseDiagDir(direction), GB(p1, 0, 4));
			UpdateSignalsOnSegment(start_tile, direction);
			YapfNotifyTrackLayoutChange(start_tile, DiagDirToAxis(direction) == AXIS_X ? TRACK_X : TRACK_Y);
		} else {
			MakeRoadTunnel(start_tile, _current_player, direction);
			MakeRoadTunnel(end_tile,   _current_player, ReverseDiagDir(direction));
		}

	}

	return cost;
}

TileIndex CheckTunnelBusy(TileIndex tile, uint *length)
{
	uint z = GetTileZ(tile);
	DiagDirection dir = GetTunnelDirection(tile);
	TileIndexDiff delta = TileOffsByDir(dir);
	uint len = 0;
	TileIndex starttile = tile;
	Vehicle *v;

	do {
		tile += delta;
		len++;
	} while (
		!IsTunnelTile(tile) ||
		ReverseDiagDir(GetTunnelDirection(tile)) != dir ||
		GetTileZ(tile) != z
	);

	v = FindVehicleBetween(starttile, tile, z);
	if (v != NULL) {
		_error_message = v->type == VEH_Train ?
			STR_5000_TRAIN_IN_TUNNEL : STR_5001_ROAD_VEHICLE_IN_TUNNEL;
		return INVALID_TILE;
	}

	if (length != NULL) *length = len;
	return tile;
}

static int32 DoClearTunnel(TileIndex tile, uint32 flags)
{
	Town *t;
	TileIndex endtile;
	uint length;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	// in scenario editor you can always destroy tunnels
	if (_game_mode != GM_EDITOR && !CheckTileOwnership(tile)) {
		if (!(_patches.extra_dynamite || _cheats.magic_bulldozer.value) || !IsTileOwner(tile, OWNER_TOWN))
			return CMD_ERROR;
	}

	endtile = CheckTunnelBusy(tile, &length);
	if (endtile == INVALID_TILE) return CMD_ERROR;

	_build_tunnel_endtile = endtile;

	t = ClosestTownFromTile(tile, (uint)-1); //needed for town rating penalty
	// check if you're allowed to remove the tunnel owned by a town
	// removal allowal depends on difficulty settings
	if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
		if (!CheckforTownRating(flags, t, TUNNELBRIDGE_REMOVE)) {
			SetDParam(0, t->index);
			return_cmd_error(STR_2009_LOCAL_AUTHORITY_REFUSES);
		}
	}

	if (flags & DC_EXEC) {
		// We first need to request the direction before calling DoClearSquare
		//  else the direction is always 0.. dah!! ;)
		DiagDirection dir = GetTunnelDirection(tile);

		// Adjust the town's player rating. Do this before removing the tile owner info.
		if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR)
			ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM);

		DoClearSquare(tile);
		DoClearSquare(endtile);
		UpdateSignalsOnSegment(tile, ReverseDiagDir(dir));
		UpdateSignalsOnSegment(endtile, dir);
		YapfNotifyTrackLayoutChange(tile, DiagDirToAxis(dir) == AXIS_X ? TRACK_X : TRACK_Y);
		YapfNotifyTrackLayoutChange(endtile, DiagDirToAxis(dir) == AXIS_X ? TRACK_X : TRACK_Y);
	}
	return _price.clear_tunnel * (length + 1);
}

#if 0
static uint GetBridgeHeightRamp(TileIndex t)
{
	/* Return the height there (the height of the NORTH CORNER)
	 * If the end of the bridge is on a tile with all corners except the north corner raised,
	 * the z coordinate is 1 height level too low. Compensate for that */
	return TilePixelHeight(t) + (GetTileSlope(t, NULL) == SLOPE_WSE ? TILE_HEIGHT : 0);
}
#endif


static int32 DoClearBridge(TileIndex tile, uint32 flags)
{
	DiagDirection direction;
	TileIndexDiff delta;
	TileIndex endtile;
	Town *t;

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	// floods, scenario editor can always destroy bridges
	if (_current_player != OWNER_WATER && _game_mode != GM_EDITOR && !CheckTileOwnership(tile)) {
		if (!(_patches.extra_dynamite || _cheats.magic_bulldozer.value) || !IsTileOwner(tile, OWNER_TOWN))
			return CMD_ERROR;
	}

	endtile = GetOtherBridgeEnd(tile);

	if (!EnsureNoVehicle(tile) || !EnsureNoVehicle(endtile)) return CMD_ERROR;

	direction = GetBridgeRampDirection(tile);
	delta = TileOffsByDir(direction);

	t = ClosestTownFromTile(tile, (uint)-1); //needed for town rating penalty
	// check if you're allowed to remove the bridge owned by a town.
	// removal allowal depends on difficulty settings
	if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
		if (!CheckforTownRating(flags, t, TUNNELBRIDGE_REMOVE)) return CMD_ERROR;
	}

	if (flags & DC_EXEC) {
		TileIndex c;

		//checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
		// you have a "Poor" (0) town rating
		if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR)
			ChangeTownRating(t, RATING_TUNNEL_BRIDGE_DOWN_STEP, RATING_TUNNEL_BRIDGE_MINIMUM);

		DoClearSquare(tile);
		DoClearSquare(endtile);
		for (c = tile + delta; c != endtile; c += delta) {
			ClearBridgeMiddle(c);
			MarkTileDirtyByTile(c);
		}

		UpdateSignalsOnSegment(tile, ReverseDiagDir(direction));
		UpdateSignalsOnSegment(endtile, direction);
		YapfNotifyTrackLayoutChange(tile, DiagDirToAxis(direction) == AXIS_X ? TRACK_X : TRACK_Y);
		YapfNotifyTrackLayoutChange(endtile, DiagDirToAxis(direction) == AXIS_X ? TRACK_X : TRACK_Y);
	}

	return (DistanceManhattan(tile, endtile) + 1) * _price.clear_bridge;
}

static int32 ClearTile_TunnelBridge(TileIndex tile, byte flags)
{
	if (IsTunnel(tile)) {
		if (flags & DC_AUTO) return_cmd_error(STR_5006_MUST_DEMOLISH_TUNNEL_FIRST);
		return DoClearTunnel(tile, flags);
	} else if (IsBridge(tile)) { // XXX Is this necessary?
		if (flags & DC_AUTO) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
		return DoClearBridge(tile, flags);
	}

	return CMD_ERROR;
}

int32 DoConvertTunnelBridgeRail(TileIndex tile, RailType totype, bool exec)
{
	TileIndex endtile;
	uint length;
	Vehicle *v;

	if (IsTunnel(tile) && GetTunnelTransportType(tile) == TRANSPORT_RAIL) {
		if (!CheckTileOwnership(tile)) return CMD_ERROR;

		if (GetRailType(tile) == totype) return CMD_ERROR;

		endtile = CheckTunnelBusy(tile, &length);
		if (endtile == INVALID_TILE) return CMD_ERROR;

		if (exec) {
			Track track, endtrack;
			SetRailType(tile, totype);
			SetRailType(endtile, totype);
			MarkTileDirtyByTile(tile);
			MarkTileDirtyByTile(endtile);

			// notify YAPF about the track layout change
			track = TrackdirToTrack(DiagdirToDiagTrackdir(GetTunnelDirection(tile)));
			endtrack = TrackdirToTrack(DiagdirToDiagTrackdir(GetTunnelDirection(endtile)));
			YapfNotifyTrackLayoutChange(tile, track);
			YapfNotifyTrackLayoutChange(endtile, endtrack);
		}
		return (length + 1) * (_price.build_rail >> 1);
	} else if (IsBridge(tile) && GetBridgeTransportType(tile) == TRANSPORT_RAIL) {
		uint z = TilePixelHeight(tile);

		z += TILE_HEIGHT;

		if (!CheckTileOwnership(tile)) return CMD_ERROR;

		endtile = GetOtherBridgeEnd(tile);
		// Make sure there's no vehicle on the bridge
		v = FindVehicleBetween(tile, endtile, z);
		if (v != NULL) {
			return_cmd_error(VehicleInTheWayErrMsg(v));
		}

		if (!EnsureNoVehicle(tile) || !EnsureNoVehicle(endtile)) {
			return_cmd_error(STR_8803_TRAIN_IN_THE_WAY);
		}

		if (GetRailType(tile) == totype) return CMD_ERROR;

		if (exec) {
			TileIndexDiff delta;

			SetRailType(tile, totype);
			SetRailType(endtile, totype);
			MarkTileDirtyByTile(tile);
			MarkTileDirtyByTile(endtile);

			delta = TileOffsByDir(GetBridgeRampDirection(tile));
			for (tile += delta; tile != endtile; tile += delta) {
				MarkTileDirtyByTile(tile); // TODO encapsulate this into a function
			}
		}

		return (DistanceManhattan(tile, endtile) + 1) * (_price.build_rail >> 1);
	} else
		return CMD_ERROR;
}


static void DrawBridgePillars(PalSpriteID image, const TileInfo* ti, Axis axis, uint type, int x, int y, int z)
{
	if (image != 0) {
		bool drawfarpillar = !HASBIT(GetBridgeFlags(type), 0);
		int back_height, front_height;
		int i = z;
		const byte *p;

		static const byte _tileh_bits[4][8] = {
			{2,1,8,4,  16,11,0,9},
			{1,8,4,2,  11,16,9,0},
			{4,8,1,2,  16,11,0,9},
			{2,4,8,1,  11,16,9,0},
		};

		if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);

		p = _tileh_bits[(image & 1) * 2 + (axis == AXIS_X ? 0 : 1)];
		front_height = ti->z + (ti->tileh & p[0] ? TILE_HEIGHT : 0);
		back_height  = ti->z + (ti->tileh & p[1] ? TILE_HEIGHT : 0);

		if (IsSteepSlope(ti->tileh)) {
			if (!(ti->tileh & p[2])) front_height += TILE_HEIGHT;
			if (!(ti->tileh & p[3])) back_height  += TILE_HEIGHT;
		}

		for (; z >= front_height || z >= back_height; z -= TILE_HEIGHT) {
			if (z >= front_height) { // front facing pillar
				AddSortableSpriteToDraw(image, x,y, p[4], p[5], 0x28, z);
			}

			if (drawfarpillar && z >= back_height && z < i - TILE_HEIGHT) { // back facing pillar
				AddSortableSpriteToDraw(image, x - p[6], y - p[7], p[4], p[5], 0x28, z);
			}
		}
	}
}

uint GetBridgeFoundation(Slope tileh, Axis axis)
{
	uint i;

	if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh)) return tileh;

	// inclined sloped building
	switch (tileh) {
		case SLOPE_W: i = 0; break;
		case SLOPE_S: i = 2; break;
		case SLOPE_E: i = 4; break;
		case SLOPE_N: i = 6; break;
		default:      return 0;
	}
	if (axis != AXIS_X) ++i;
	return i + 15;
}

/**
  * Draws a tunnel of bridge tile.
  * For tunnels, this is rather simple, as you only needa draw the entrance.
  * Bridges are a bit more complex. base_offset is where the sprite selection comes into play
  * and it works a bit like a bitmask.<p> For bridge heads:
  * <ul><li>Bit 0: direction</li>
  * <li>Bit 1: northern or southern heads</li>
  * <li>Bit 2: Set if the bridge head is sloped</li>
  * <li>Bit 3 and more: Railtype Specific subset</li>
  * </ul>
  * Please note that in this code, "roads" are treated as railtype 1, whilst the real railtypes are 0, 2 and 3
  */
static void DrawTile_TunnelBridge(TileInfo *ti)
{
	uint32 image;
	bool ice = _m[ti->tile].m4 & 0x80;

	if (IsTunnel(ti->tile)) {
		if (GetTunnelTransportType(ti->tile) == TRANSPORT_RAIL) {
			image = GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.tunnel;
		} else {
			image = SPR_TUNNEL_ENTRY_REAR_ROAD;
		}

		if (ice) image += 32;

		image += GetTunnelDirection(ti->tile) * 2;
		DrawGroundSprite(image);
		if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);

		AddSortableSpriteToDraw(image+1, ti->x + TILE_SIZE - 1, ti->y + TILE_SIZE - 1, 1, 1, 8, (byte)ti->z);
		DrawBridgeMiddle(ti);
	} else if (IsBridge(ti->tile)) { // XXX is this necessary?
		int base_offset;

		if (GetBridgeTransportType(ti->tile) == TRANSPORT_RAIL) {
			base_offset = GetRailTypeInfo(GetRailType(ti->tile))->bridge_offset;
			assert(base_offset != 8); /* This one is used for roads */
		} else {
			base_offset = 8;
		}

		/* as the lower 3 bits are used for other stuff, make sure they are clear */
		assert( (base_offset & 0x07) == 0x00);

		if (!HASBIT(BRIDGE_NO_FOUNDATION, ti->tileh)) {
			int f = GetBridgeFoundation(ti->tileh, DiagDirToAxis(GetBridgeRampDirection(ti->tile)));
			if (f) DrawFoundation(ti, f);
		}

		// HACK Wizardry to convert the bridge ramp direction into a sprite offset
		base_offset += (6 - GetBridgeRampDirection(ti->tile)) % 4;

		if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head

		/* Table number 6 always refers to the bridge heads for any bridge type */
		image = GetBridgeSpriteTable(GetBridgeType(ti->tile), 6)[base_offset];

		if (!ice) {
			DrawClearLandTile(ti, 3);
		} else {
			DrawGroundSprite(SPR_FLAT_SNOWY_TILE + _tileh_to_sprite[ti->tileh]);
		}

		if (GetRailType(ti->tile) == RAILTYPE_ELECTRIC) DrawCatenary(ti);

		// draw ramp
		if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
		AddSortableSpriteToDraw(image, ti->x, ti->y, 16, 16, 7, ti->z);

		DrawBridgeMiddle(ti);
	}
}


//bridges pieces sequence (middle parts)
// bridge len 1: 0
// bridge len 2: 0 1
// bridge len 3: 0 4 1
// bridge len 4: 0 2 3 1
// bridge len 5: 0 2 5 3 1
// bridge len 6: 0 2 3 2 3 1
// bridge len 7: 0 2 3 4 2 3 1
// #0 - always as first, #1 - always as last (if len>1)
// #2,#3 are to pair in order
// for odd bridges: #5 is going in the bridge middle if on even position, #4 on odd (counting from 0)
static uint CalcBridgePiece(uint north, uint south)
{
	if (north == 1) {
		return 0;
	} else if (south == 1) {
		return 1;
	} else if (north < south) {
		return north & 1 ? 3 : 2;
	} else if (north > south) {
		return south & 1 ? 2 : 3;
	} else {
		return north & 1 ? 5 : 4;
	}
}

void DrawBridgeMiddle(const TileInfo* ti)
{
	const PalSpriteID* b;
	PalSpriteID image;
	uint base_offset;
	TileIndex rampnorth;
	TileIndex rampsouth;
	Axis axis;
	uint piece;
	uint type;
	int x;
	int y;
	uint z;

	if (!IsBridgeAbove(ti->tile)) return;

	rampnorth = GetNorthernBridgeEnd(ti->tile);
	rampsouth = GetSouthernBridgeEnd(ti->tile);

	axis = GetBridgeAxis(ti->tile);
	piece = CalcBridgePiece(
		DistanceManhattan(ti->tile, rampnorth),
		DistanceManhattan(ti->tile, rampsouth)
	);
	type = GetBridgeType(rampsouth);

	if (GetBridgeTransportType(rampsouth) == TRANSPORT_RAIL) {
		base_offset = GetRailTypeInfo(GetRailType(rampsouth))->bridge_offset;
	} else {
		base_offset = 8;
	}

	b = base_offset + GetBridgeSpriteTable(type, piece);
	if (axis != AXIS_X) b += 4;

	x = ti->x;
	y = ti->y;
	z = GetBridgeHeight(rampsouth, axis) - 3;

	image = b[0];
	if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
	if (axis == AXIS_X) {
		AddSortableSpriteToDraw(image, x, y, 16, 11, 1, z);
	} else {
		AddSortableSpriteToDraw(image, x, y, 11, 16, 1, z);
	}

	image = b[1];
	if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);

	// draw roof, the component of the bridge which is logically between the vehicle and the camera
	if (axis == AXIS_X) {
		y += 12;
		if (image & SPRITE_MASK) AddSortableSpriteToDraw(image, x, y, 16, 1, 0x28, z);
	} else {
		x += 12;
		if (image & SPRITE_MASK) AddSortableSpriteToDraw(image, x, y, 1, 16, 0x28, z);
	}

	if (GetRailType(rampsouth) == RAILTYPE_ELECTRIC) DrawCatenary(ti);

	if (ti->z + 5 == z) {
		// draw poles below for small bridges
		image = b[2];
		if (image != 0) {
			if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image);
			DrawGroundSpriteAt(image, x, y, z);
		}
	} else if (_patches.bridge_pillars) {
		// draw pillars below for high bridges
		DrawBridgePillars(b[2], ti, axis, type, x, y, z);
	}
}


uint SetSpeedLimitOnBridge(Vehicle *v)
{
	uint bridge_speed;
	if (v->vehstatus & VS_HIDDEN) return v->max_speed; /* in tunnel */

	bridge_speed = _bridge[GetBridgeType(v->tile)].speed;

	if (v->type == VEH_Road) bridge_speed *= 2; /* XXX give vehicles proper speeds */

	if (v->cur_speed > bridge_speed) v->cur_speed = bridge_speed;
	return bridge_speed;
}



static uint GetSlopeZ_TunnelBridge(const TileInfo* ti)
{
	TileIndex tile = ti->tile;
	uint z = ti->z;
	uint x = ti->x & 0xF;
	uint y = ti->y & 0xF;
	Slope tileh = ti->tileh;

	if (IsTunnel(tile)) {
		uint pos = (DiagDirToAxis(GetTunnelDirection(tile)) == AXIS_X ? y : x);

		// In the tunnel entrance?
		if (5 <= pos && pos <= 10) return z;
	} else {
		DiagDirection dir = GetBridgeRampDirection(tile);
		uint pos = (DiagDirToAxis(dir) == AXIS_X ? y : x);

		// On the bridge ramp?
		if (5 <= pos && pos <= 10) {
			uint delta;

			if (HASBIT(BRIDGE_HORZ_RAMP, tileh)) return z + TILE_HEIGHT;

			if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh)) z += TILE_HEIGHT;
			switch (dir) {
				default:
				case DIAGDIR_NE: delta = (TILE_SIZE - 1 - x) / 2; break;
				case DIAGDIR_SE: delta = y / 2; break;
				case DIAGDIR_SW: delta = x / 2; break;
				case DIAGDIR_NW: delta = (TILE_SIZE - 1 - y) / 2; break;
			}
			return z + 1 + delta;
		} else {
			uint f = GetBridgeFoundation(tileh, DiagDirToAxis(dir));

			if (f != 0) {
				if (f < 15) return z + TILE_HEIGHT;
				tileh = _inclined_tileh[f - 15];
			}
		}
	}

	return z + GetPartialZ(x, y, tileh);
}

static Slope GetSlopeTileh_TunnelBridge(TileIndex tile, Slope tileh)
{
	// not accurate, but good enough for slope graphics drawing
	return SLOPE_FLAT;
}


static void GetAcceptedCargo_TunnelBridge(TileIndex tile, AcceptedCargo ac)
{
	/* not used */
}

static const StringID _bridge_tile_str[(MAX_BRIDGES + 3) + (MAX_BRIDGES + 3)] = {
	STR_501F_WOODEN_RAIL_BRIDGE,
	STR_5020_CONCRETE_RAIL_BRIDGE,
	STR_501C_STEEL_GIRDER_RAIL_BRIDGE,
	STR_501E_REINFORCED_CONCRETE_SUSPENSION,
	STR_501B_STEEL_SUSPENSION_RAIL_BRIDGE,
	STR_501B_STEEL_SUSPENSION_RAIL_BRIDGE,
	STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE,
	STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE,
	STR_501D_STEEL_CANTILEVER_RAIL_BRIDGE,
	STR_501C_STEEL_GIRDER_RAIL_BRIDGE,
	STR_5027_TUBULAR_RAIL_BRIDGE,
	STR_5027_TUBULAR_RAIL_BRIDGE,
	STR_5027_TUBULAR_RAIL_BRIDGE,
	0,0,0,

	STR_5025_WOODEN_ROAD_BRIDGE,
	STR_5026_CONCRETE_ROAD_BRIDGE,
	STR_5022_STEEL_GIRDER_ROAD_BRIDGE,
	STR_5024_REINFORCED_CONCRETE_SUSPENSION,
	STR_5021_STEEL_SUSPENSION_ROAD_BRIDGE,
	STR_5021_STEEL_SUSPENSION_ROAD_BRIDGE,
	STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE,
	STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE,
	STR_5023_STEEL_CANTILEVER_ROAD_BRIDGE,
	STR_5022_STEEL_GIRDER_ROAD_BRIDGE,
	STR_5028_TUBULAR_ROAD_BRIDGE,
	STR_5028_TUBULAR_ROAD_BRIDGE,
	STR_5028_TUBULAR_ROAD_BRIDGE,
	0,0,0,
};

static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
{
	if (IsTunnel(tile)) {
		td->str = (GetTunnelTransportType(tile) == TRANSPORT_RAIL) ?
			STR_5017_RAILROAD_TUNNEL : STR_5018_ROAD_TUNNEL;
	} else {
		td->str = _bridge_tile_str[GetBridgeTransportType(tile) << 4 | GetBridgeType(tile)];
	}
	td->owner = GetTileOwner(tile);
}


static void AnimateTile_TunnelBridge(TileIndex tile)
{
	/* not used */
}

static void TileLoop_TunnelBridge(TileIndex tile)
{
	switch (_opt.landscape) {
		case LT_HILLY:
			if (GetTileZ(tile) > _opt.snow_line) {
				if (!(_m[tile].m4 & 0x80)) {
					_m[tile].m4 |= 0x80;
					MarkTileDirtyByTile(tile);
				}
			} else {
				if (_m[tile].m4 & 0x80) {
					_m[tile].m4 &= ~0x80;
					MarkTileDirtyByTile(tile);
				}
			}
			break;

		case LT_DESERT:
			if (GetTropicZone(tile) == TROPICZONE_DESERT && !(_m[tile].m4 & 0x80)) {
				_m[tile].m4 |= 0x80;
				MarkTileDirtyByTile(tile);
			}
			break;
	}
}

static void ClickTile_TunnelBridge(TileIndex tile)
{
	/* not used */
}


static uint32 GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode)
{
	if (IsTunnel(tile)) {
		if (GetTunnelTransportType(tile) == mode) {
			return DiagDirToAxis(GetTunnelDirection(tile)) == AXIS_X ? 0x101 : 0x202;
		}
	} else if (IsBridge(tile)) { // XXX is this necessary?
		if (GetBridgeTransportType(tile) != mode) return 0;
		return (DiagDirToAxis(GetBridgeRampDirection(tile)) == AXIS_X ? TRACK_BIT_X : TRACK_BIT_Y) * 0x101;
	} else {
		assert(0); /* This should never occur */
	}
	return 0;
}

static void ChangeTileOwner_TunnelBridge(TileIndex tile, PlayerID old_player, PlayerID new_player)
{
	if (!IsTileOwner(tile, old_player)) return;

	if (new_player != OWNER_SPECTATOR) {
		SetTileOwner(tile, new_player);
	}	else {
		DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
	}
}


static const byte _tunnel_fractcoord_1[4] = {0x8E,0x18,0x81,0xE8};
static const byte _tunnel_fractcoord_2[4] = {0x81,0x98,0x87,0x38};
static const byte _tunnel_fractcoord_3[4] = {0x82,0x88,0x86,0x48};
static const byte _exit_tunnel_track[4] = {1,2,1,2};

static const byte _road_exit_tunnel_state[4] = {8, 9, 0, 1};
static const byte _road_exit_tunnel_frame[4] = {2, 7, 9, 4};

static const byte _tunnel_fractcoord_4[4] = {0x52, 0x85, 0x98, 0x29};
static const byte _tunnel_fractcoord_5[4] = {0x92, 0x89, 0x58, 0x25};
static const byte _tunnel_fractcoord_6[4] = {0x92, 0x89, 0x56, 0x45};
static const byte _tunnel_fractcoord_7[4] = {0x52, 0x85, 0x96, 0x49};

static uint32 VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y)
{
	int z = GetSlopeZ(x, y) - v->z_pos;

	if (myabs(z) > 2) return 8;

	if (IsTunnel(tile)) {
		byte fc;
		DiagDirection dir;
		DiagDirection vdir;

		if (v->type == VEH_Train) {
			fc = (x & 0xF) + (y << 4);

			dir = GetTunnelDirection(tile);
			vdir = DirToDiagDir(v->direction);

			if (v->u.rail.track != 0x40 && dir == vdir) {
				if (IsFrontEngine(v) && fc == _tunnel_fractcoord_1[dir]) {
					if (v->spritenum < 4)
						SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
					return 0;
				}
				if (fc == _tunnel_fractcoord_2[dir]) {
					v->tile = tile;
					v->u.rail.track = 0x40;
					v->vehstatus |= VS_HIDDEN;
					return 4;
				}
			}

			if (dir == ReverseDiagDir(vdir) && fc == _tunnel_fractcoord_3[dir] && z == 0) {
				/* We're at the tunnel exit ?? */
				v->tile = tile;
				v->u.rail.track = _exit_tunnel_track[dir];
				assert(v->u.rail.track);
				v->vehstatus &= ~VS_HIDDEN;
				return 4;
			}
		} else if (v->type == VEH_Road) {
			fc = (x & 0xF) + (y << 4);
			dir = GetTunnelDirection(tile);
			vdir = DirToDiagDir(v->direction);

			// Enter tunnel?
			if (v->u.road.state != 0xFF && dir == vdir) {
				if (fc == _tunnel_fractcoord_4[dir] ||
						fc == _tunnel_fractcoord_5[dir]) {
					v->tile = tile;
					v->u.road.state = 0xFF;
					v->vehstatus |= VS_HIDDEN;
					return 4;
				} else {
					return 0;
				}
			}

			if (dir == ReverseDiagDir(vdir) && (
						/* We're at the tunnel exit ?? */
						fc == _tunnel_fractcoord_6[dir] ||
						fc == _tunnel_fractcoord_7[dir]
					) &&
					z == 0) {
				v->tile = tile;
				v->u.road.state = _road_exit_tunnel_state[dir];
				v->u.road.frame = _road_exit_tunnel_frame[dir];
				v->vehstatus &= ~VS_HIDDEN;
				return 4;
			}
		}
	} else if (IsBridge(tile)) { // XXX is this necessary?
		DiagDirection dir;

		if (v->type == VEH_Road || (v->type == VEH_Train && IsFrontEngine(v))) {
			/* modify speed of vehicle */
			uint16 spd = _bridge[GetBridgeType(tile)].speed;

			if (v->type == VEH_Road) spd *= 2;
			if (v->cur_speed > spd) v->cur_speed = spd;
		}

		dir = GetBridgeRampDirection(tile);
		if (DirToDiagDir(v->direction) == dir) {
			switch (dir) {
				default:
				case DIAGDIR_NE: if ((x & 0xF) != 0)             return 0; break;
				case DIAGDIR_SE: if ((y & 0xF) != TILE_SIZE - 1) return 0; break;
				case DIAGDIR_SW: if ((x & 0xF) != TILE_SIZE - 1) return 0; break;
				case DIAGDIR_NW: if ((y & 0xF) != 0)             return 0; break;
			}
			if (v->type == VEH_Train) {
				v->u.rail.track = 0x40;
				CLRBIT(v->u.rail.flags, VRF_GOINGUP);
				CLRBIT(v->u.rail.flags, VRF_GOINGDOWN);
			} else {
				v->u.road.state = 0xFF;
			}
			return 4;
		} else if (DirToDiagDir(v->direction) == ReverseDiagDir(dir)) {
			v->tile = tile;
			if (v->type == VEH_Train) {
				if (v->u.rail.track == 0x40) {
					v->u.rail.track = (DiagDirToAxis(dir) == AXIS_X ? 1 : 2);
					return 4;
				}
			} else {
				if (v->u.road.state == 0xFF) {
					v->u.road.state = _road_exit_tunnel_state[dir];
					v->u.road.frame = 0;
					return 4;
				}
			}
			return 0;
		}
	}
	return 0;
}

const TileTypeProcs _tile_type_tunnelbridge_procs = {
	DrawTile_TunnelBridge,					/* draw_tile_proc */
	GetSlopeZ_TunnelBridge,					/* get_slope_z_proc */
	ClearTile_TunnelBridge,					/* clear_tile_proc */
	GetAcceptedCargo_TunnelBridge,	/* get_accepted_cargo_proc */
	GetTileDesc_TunnelBridge,				/* get_tile_desc_proc */
	GetTileTrackStatus_TunnelBridge,/* get_tile_track_status_proc */
	ClickTile_TunnelBridge,					/* click_tile_proc */
	AnimateTile_TunnelBridge,				/* animate_tile_proc */
	TileLoop_TunnelBridge,					/* tile_loop_clear */
	ChangeTileOwner_TunnelBridge,		/* change_tile_owner_clear */
	NULL,														/* get_produced_cargo_proc */
	VehicleEnter_TunnelBridge,			/* vehicle_enter_tile_proc */
	GetSlopeTileh_TunnelBridge,			/* get_slope_tileh_proc */
};