mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-02 09:40:35 +00:00
25a63ec7af
-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.
1258 lines
37 KiB
C
1258 lines
37 KiB
C
/* $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 */
|
|
};
|