bridge_cmd.c
author KUDr
Sun, 31 Dec 2006 23:48:04 +0000
branchcustombridgeheads
changeset 5618 a7db50b9f817
parent 5613 8b68de175f44
child 5621 6ce400c0a2f4
permissions -rw-r--r--
(svn r7710) [cbh] - Fix: [YAPF] one more assert fixed. Call from the TrainController() added by (r7705) has broken YAPF because it was called when vehicle was already on the next tile (with cbh choice). Before it was always called before the train entered tile with choice.
/* $Id$ */

/** @file bridge_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 "unmovable_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 "date.h"
#include "newgrf_sound.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 },
	{ 1930, 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 },
	{ 1930, 3, 16, 185,  96, 0xA22                             , STR_500E_SUSPENSION_STEEL   , NULL, 0 },
	{ 1930, 3, 16, 192, 112, 0xA22 | PALETTE_TO_STRUCT_YELLOW  , STR_500E_SUSPENSION_STEEL   , NULL, 0 },
	{ 1930, 3,  7, 224, 160, 0xA23                             , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
	{ 1930, 3,  8, 232, 208, 0xA23 | PALETTE_TO_STRUCT_BROWN   , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
	{ 1930, 3,  9, 248, 240, 0xA23 | PALETTE_TO_STRUCT_RED     , STR_5010_CANTILEVER_STEEL   , NULL, 0 },
	{ 1930, 0,  2, 240, 256, 0xA27                             , STR_500F_GIRDER_STEEL       , NULL, 0 },
	{ 1995, 2, 16, 255, 320, 0xA28                             , STR_5014_TUBULAR_STEEL      , NULL, 0 },
	{ 2005, 2, 32, 380, 512, 0xA28 | PALETTE_TO_STRUCT_YELLOW  , STR_5014_TUBULAR_STEEL      , NULL, 0 },
	{ 2010, 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;
	}
}

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 the slope at the bridge ramps in three easy steps:
 * - valid slopes without foundation
 * - valid slopes with foundation
 * - rest is invalid
 */
#define M(x) (1 << (x))
static int32 CheckBridgeSlopeNorth(Axis axis, Slope tileh)
{
	uint32 valid;

	valid = M(SLOPE_FLAT) | (axis == AXIS_X ? M(SLOPE_NE) : M(SLOPE_NW));
	if (HASBIT(valid, tileh)) return 0;

	valid =
		BRIDGE_FULL_LEVELED_FOUNDATION | M(SLOPE_N) | M(SLOPE_STEEP_N) |
		(axis == AXIS_X ? M(SLOPE_E) | M(SLOPE_STEEP_E) : M(SLOPE_W) | M(SLOPE_STEEP_W));
	if (HASBIT(valid, tileh)) return _price.terraform;

	return CMD_ERROR;
}

static int32 CheckBridgeSlopeSouth(Axis axis, Slope tileh)
{
	uint32 valid;

	valid = M(SLOPE_FLAT) | (axis == AXIS_X ? M(SLOPE_SW) : M(SLOPE_SE));
	if (HASBIT(valid, tileh)) return 0;

	valid =
		BRIDGE_FULL_LEVELED_FOUNDATION | M(SLOPE_S) | M(SLOPE_STEEP_S) |
		(axis == AXIS_X ? M(SLOPE_W) | M(SLOPE_STEEP_W) : M(SLOPE_E) | M(SLOPE_STEEP_E));
	if (HASBIT(valid, tileh)) return _price.terraform;

	return CMD_ERROR;
}
#undef M


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;
	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 (IsSteepSlope(tileh_start)) z_start += TILE_HEIGHT;
	if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh_start)) {
		z_start += TILE_HEIGHT;
		tileh_start = SLOPE_FLAT;
	}

	if (IsSteepSlope(tileh_end)) z_end += TILE_HEIGHT;
	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;

	terraformcost = CheckBridgeSlopeNorth(direction, tileh_start);
	if (CmdFailed(terraformcost) || (terraformcost != 0 && !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 = CheckBridgeSlopeSouth(direction, tileh_end);
	if (CmdFailed(terraformcost) || (terraformcost != 0 && !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]);

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

					if (z_start + TILE_HEIGHT == GetBridgeHeight(north_head)) {
						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);
	}

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

		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_DEPOT) goto not_valid_below;
				break;

			case MP_TUNNEL:
				break;

			case MP_STREET_BRIDGE:
			case MP_RAILWAY_BRIDGE:
				if (direction == DiagDirToAxis(GetBridgeRampDirection(tile))) goto not_valid_below;
				if (z_start < GetBridgeHeight(tile)) goto not_valid_below;
				break;

			case MP_UNMOVABLE:
				if (!IsOwnedLand(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, AxisToTrack(direction));
	YapfNotifyTrackLayoutChange(tile_start, AxisToTrack(direction));

	/* 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 (IsValidPlayer(_current_player) && !_is_old_ai_player)
			bridge_len = CalcBridgeLenCostFactor(bridge_len);

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

	return cost;
}


static inline bool CheckAllowRemoveBridge(TileIndex tile)
{
	/* Floods can remove anything as well as the scenario editor */
	if (_current_player == OWNER_WATER || _game_mode == GM_EDITOR) return true;
	/* Obviously if the bridge/tunnel belongs to us, or no-one, we can remove it */
	if (CheckTileOwnership(tile) || IsTileOwner(tile, OWNER_NONE)) return true;
	/* Otherwise we can only remove town-owned stuff with extra patch-settings, or cheat */
	if (IsTileOwner(tile, OWNER_TOWN) && (_patches.extra_dynamite || _cheats.magic_bulldozer.value)) return true;
	return false;
}

static bool IsVehicleOnBridge(TileIndex starttile, TileIndex endtile, uint z)
{
	const Vehicle *v;
	FOR_ALL_VEHICLES(v) {
		if ((v->tile == starttile || v->tile == endtile) && v->z_pos == z) {
			_error_message = VehicleInTheWayErrMsg(v);
			return true;
		}
	}
	return false;
}

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

	SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);

	if (!CheckAllowRemoveBridge(tile)) return CMD_ERROR;

	endtile = GetOtherBridgeEnd(tile);

	if (!EnsureNoVehicle(tile) ||
			!EnsureNoVehicle(endtile) ||
			IsVehicleOnBridge(tile, endtile, GetBridgeHeight(tile))) {
		return CMD_ERROR;
	}

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

	if (IsTileOwner(tile, OWNER_TOWN) && _game_mode != GM_EDITOR) {
		t = ClosestTownFromTile(tile, (uint)-1); // town penalty rating

		/* Check if you are allowed to remove the bridge owned by a town
		 * Removal depends on difficulty settings */
		if (!CheckforTownRating(flags, t, TUNNELBRIDGE_REMOVE)) {
			SetDParam(0, t->index);
			return_cmd_error(STR_2009_LOCAL_AUTHORITY_REFUSES);
		}
	}

	if (flags & DC_EXEC) {
		TileIndex c;
		Track track;

		//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);
		track = AxisToTrack(DiagDirToAxis(direction));
		YapfNotifyTrackLayoutChange(tile, track);
		YapfNotifyTrackLayoutChange(endtile, track);
	}

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


static int32 ClearTile_Bridge(TileIndex tile, byte flags)
{
	assert(IsBridgeTile(tile));
	if (flags & DC_AUTO) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
	return DoClearBridge(tile, flags);
}

int32 DoConvertBridgeRail(TileIndex tile, RailType totype, bool exec)
{
	TileIndex endtile;

	if (GetBridgeTransportType(tile) == TRANSPORT_RAIL) {
		if (!CheckTileOwnership(tile)) return CMD_ERROR;

		endtile = GetOtherBridgeEnd(tile);

		if (!EnsureNoVehicle(tile) ||
				!EnsureNoVehicle(endtile) ||
				IsVehicleOnBridge(tile, endtile, GetBridgeHeight(tile))) {
			return CMD_ERROR;
		}

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

		if (exec) {
			TileIndexDiff delta;
			Track track;

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

			track = AxisToTrack(DiagDirToAxis(GetBridgeRampDirection(tile)));
			YapfNotifyTrackLayoutChange(tile, track);
			YapfNotifyTrackLayoutChange(endtile, track);

			delta = TileOffsByDiagDir(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,  2, 0, 9 },
			{ 1, 8, 4, 2,   2, 16, 9, 0 },
			{ 4, 8, 1, 2,  16,  2, 0, 9 },
			{ 2, 4, 8, 1,   2, 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) {
			/* HACK set height of the BB of pillars to 1, because the origin of the
			 * sprites is at the top
			 */
			if (z >= front_height) { // front facing pillar
				AddSortableSpriteToDraw(image, x, y, p[4], p[5], 1, 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], 1, 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:
		case SLOPE_STEEP_W: i = 0; break;
		case SLOPE_S:
		case SLOPE_STEEP_S: i = 2; break;
		case SLOPE_E:
		case SLOPE_STEEP_E: i = 4; break;
		case SLOPE_N:
		case SLOPE_STEEP_N: i = 6; break;
		default: return 0;
	}
	if (axis != AXIS_X) ++i;
	return i + 15;
}


/**
 * Draws a bridge tile.
 * 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_Bridge(TileInfo *ti)
{
	uint32 image;

	int base_offset;
	bool ice = HasBridgeSnowOrDesert(ti->tile);

	if (GetBridgeTransportType(ti->tile) == TRANSPORT_RAIL) {
		if (GetTrackBits(ti->tile) != TRACK_BIT_X && GetTrackBits(ti->tile) != TRACK_BIT_Y) {
			DrawTile_Track(ti);
			return;
		}
		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 != 0) 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);
	/* HACK set the height of the BB of a sloped ramp to 1 so a vehicle on
	 * it doesn't disappear behind it
	 */
	AddSortableSpriteToDraw(
		image, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 1 : 8, ti->z
	);

	DrawBridgeMiddle(ti);
}


/** Compute bridge piece. Computes the bridge piece to display depending on the position inside the bridge.
 * 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)
 * @param north Northernmost tile of bridge
 * @param south Southernmost tile of bridge
 * @return Index of bridge piece
 */
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) - 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 bool IsCustomBridgeHead(TileIndex tile)
{
	assert(IsBridgeTile(tile));

	if (IsTileType(tile, MP_RAILWAY_BRIDGE)) {
		return !(GetTrackBits(tile) == TRACK_BIT_X || GetTrackBits(tile) == TRACK_BIT_Y);
	}

	return false; /* TODO - street bridges */
}


/** Gets the absolute z coordinate of a point inside a bridge tile
 *  When we're on the track (that means between position 5 and 10)
 *  on the coordinate perpendicular to the track it returns the base
 *  height of the ramp
 *  Outside this range (from 0 to 4 and from 11 to 15) it returns the
 *  "true" Z coordinate of the tile by taking the slope into account
 *  For custom bridge heads the entire bridge head is flat and has the
 *  same z coordinate
 *  @param tile The index of the tile we are talking about
 *  @param x Absolute or relative x coordinate
 *  @param y Absolute or relative y coordinate
 *  @return Absolute z coordinate
 */
static uint GetSlopeZ_Bridge(TileIndex tile, uint x, uint y)
{
	uint z, pos;
	Slope tileh = GetTileSlope(tile, &z);
	DiagDirection dir = GetBridgeRampDirection(tile);

	x &= 0xF;
	y &= 0xF;

	pos = (DiagDirToAxis(dir) == AXIS_X ? y : x);

	// On the bridge ramp or flat bridge head?
	if ( (5 <= pos && pos <= 10) || IsCustomBridgeHead(tile)) {
		uint delta;

		if (IsSteepSlope(tileh)) return z + TILE_HEIGHT * 2;

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

		if (HASBIT(BRIDGE_FULL_LEVELED_FOUNDATION, tileh)) z += TILE_HEIGHT;
		switch (dir) {
			default: NOT_REACHED();
			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 (IsSteepSlope(tileh)) {
				z += TILE_HEIGHT;
			} else if (f < 15) {
				return z + TILE_HEIGHT;
			}
			tileh = _inclined_tileh[f - 15];
		}
	}
	return z + GetPartialZ(x, y, tileh);
}

static Slope GetSlopeTileh_Bridge(TileIndex tile, Slope tileh)
{
	if (HASBIT(BRIDGE_NO_FOUNDATION, tileh)) {
		return tileh;
	} else {
		uint f = GetBridgeFoundation(tileh, DiagDirToAxis(GetBridgeRampDirection(tile)));

		if (f == 0) return tileh;
		if (f < 15) return SLOPE_FLAT;
		return _inclined_tileh[f - 15];
	}
}

static void GetAcceptedCargo_Bridge(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_Bridge(TileIndex tile, TileDesc *td)
{
	td->str = _bridge_tile_str[GetBridgeTransportType(tile) << 4 | GetBridgeType(tile)];
	td->owner = GetTileOwner(tile);
}


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


static void TileLoop_Bridge(TileIndex tile)
{
	bool snow_or_desert = HasBridgeSnowOrDesert(tile);
	switch (_opt.landscape) {
		case LT_HILLY:
			if (snow_or_desert != (GetTileZ(tile) > _opt.snow_line)) {
				SetBridgeSnowOrDesert(tile, !snow_or_desert);
				MarkTileDirtyByTile(tile);
			}
			break;

		case LT_DESERT:
			if (GetTropicZone(tile) == TROPICZONE_DESERT && !snow_or_desert) {
				SetBridgeSnowOrDesert(tile, true);
				MarkTileDirtyByTile(tile);
			}
			break;
	}
}

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


static uint32 GetTileTrackStatus_Bridge(TileIndex tile, TransportType mode)
{
	if (GetBridgeTransportType(tile) != mode) return 0;

	if (IsTileType(tile, MP_RAILWAY_BRIDGE)) return GetTileTrackStatus_Track(tile, mode);
	return AxisToTrackBits(DiagDirToAxis(GetBridgeRampDirection(tile))) * 0x101;
}


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

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


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

	DiagDirection dir;

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

	if (v->type == VEH_Road) {
		/* modify speed of vehicle */
		uint16 spd = _bridge[GetBridgeType(tile)].speed * 2;
		if (v->cur_speed > spd) v->cur_speed = spd;
	}

	dir = GetBridgeRampDirection(tile);
	if (DirToDiagDir(v->direction) == dir) {
		switch (dir) {
			default: NOT_REACHED();
			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;
		}
		v->u.road.state = 0xFF;
		return 4;
	} else if (DirToDiagDir(v->direction) == ReverseDiagDir(dir)) {
		v->tile = tile;
		if (v->u.road.state == 0xFF) {
			static const byte road_exit_bridge_state[4] = {8, 9, 0, 1};
			v->u.road.state = road_exit_bridge_state[dir];
			v->u.road.frame = 0;
			return 4;
		}
		return 0;
	}
	return 0;
}

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

	DiagDirection dir;

	if (IsCustomBridgeHead(tile)) {
		uint h;
		GetTileSlope(tile, &h);

		z = h + TILE_HEIGHT - v->z_pos;
	}

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

	if (IsFrontEngine(v)) {
		/* modify speed of vehicle */
		uint16 spd = _bridge[GetBridgeType(tile)].speed;
		if (v->cur_speed > spd) v->cur_speed = spd;
	}

	dir = GetBridgeRampDirection(tile);
	if (v->direction == DiagDirToDir(ReverseDiagDir(dir))) {
		/* leaving the bridge */
		v->tile = tile;
		if (v->u.rail.track == 0x40) {
			v->u.rail.track = (DiagDirToAxis(dir) == AXIS_X ? 1 : 2);
			return 4;
		}
		return 0;
	}
	return 0;
}

static uint32 VehicleEnter_Bridge(Vehicle *v, TileIndex tile, int x, int y)
{
	if (v->type == VEH_Train) return VehicleEnter_Railway_Bridge(v, tile, x, y);

	if (v->type == VEH_Road) return VehicleEnter_Street_Bridge(v, tile, x, y);

	NOT_REACHED();
}

const TileTypeProcs _tile_type_bridge_procs = {
	DrawTile_Bridge,           /* draw_tile_proc */
	GetSlopeZ_Bridge,          /* get_slope_z_proc */
	ClearTile_Bridge,          /* clear_tile_proc */
	GetAcceptedCargo_Bridge,   /* get_accepted_cargo_proc */
	GetTileDesc_Bridge,        /* get_tile_desc_proc */
	GetTileTrackStatus_Bridge, /* get_tile_track_status_proc */
	ClickTile_Bridge,          /* click_tile_proc */
	AnimateTile_Bridge,        /* animate_tile_proc */
	TileLoop_Bridge,           /* tile_loop_clear */
	ChangeTileOwner_Bridge,    /* change_tile_owner_clear */
	NULL,                      /* get_produced_cargo_proc */
	VehicleEnter_Bridge,       /* vehicle_enter_tile_proc */
	GetSlopeTileh_Bridge,      /* get_slope_tileh_proc */
};