|
@@ -13,48 +13,50 @@
|
|
|
#include "table/sprites.h"
|
|
|
#include "table/strings.h"
|
|
|
#include "strings.h"
|
|
|
#include "functions.h"
|
|
|
#include "window.h"
|
|
|
#include "map.h"
|
|
|
#include "landscape.h"
|
|
|
#include "tile.h"
|
|
|
#include "town_map.h"
|
|
|
#include "vehicle.h"
|
|
|
#include "viewport.h"
|
|
|
#include "command.h"
|
|
|
#include "player.h"
|
|
|
#include "town.h"
|
|
|
#include "gfx.h"
|
|
|
#include "sound.h"
|
|
|
#include "yapf/yapf.h"
|
|
|
#include "depot.h"
|
|
|
#include "newgrf.h"
|
|
|
#include "station_map.h"
|
|
|
#include "tunnel_map.h"
|
|
|
#include "misc/autoptr.hpp"
|
|
|
#include "autoslope.h"
|
|
|
#include "transparency.h"
|
|
|
#include "tunnelbridge_map.h"
|
|
|
|
|
|
|
|
|
#define M(x) (1 << (x))
|
|
|
/* Level crossings may only be built on these slopes */
|
|
|
static const uint32 VALID_LEVEL_CROSSING_SLOPES = (M(SLOPE_SEN) | M(SLOPE_ENW) | M(SLOPE_NWS) | M(SLOPE_NS) | M(SLOPE_WSE) | M(SLOPE_EW) | M(SLOPE_FLAT));
|
|
|
#undef M
|
|
|
|
|
|
bool CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, bool *edge_road, RoadType rt)
|
|
|
{
|
|
|
RoadBits present;
|
|
|
RoadBits n;
|
|
|
*edge_road = true;
|
|
|
|
|
|
if (_game_mode == GM_EDITOR || remove == ROAD_NONE) return true;
|
|
|
|
|
|
/* Water can always flood and towns can always remove "normal" road pieces.
|
|
|
* Towns are not be allowed to remove non "normal" road pieces, like tram
|
|
|
* tracks as that would result in trams that cannot turn. */
|
|
|
if (_current_player == OWNER_WATER ||
|
|
|
(rt == ROADTYPE_ROAD && !IsValidPlayer(_current_player))) return true;
|
|
|
|
|
|
/* Only do the special processing if the road is owned
|
|
|
* by a town */
|
|
|
if (owner != OWNER_TOWN) return (owner == OWNER_NONE) || CheckOwnership(owner);
|
|
|
|
|
@@ -112,91 +114,91 @@ CommandCost CmdRemoveRoad(TileIndex tile
|
|
|
* false if it was a center piece. Affects town ratings drop */
|
|
|
bool edge_road;
|
|
|
|
|
|
SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION);
|
|
|
|
|
|
RoadType rt = (RoadType)GB(p1, 4, 2);
|
|
|
if (!IsValidRoadType(rt)) return CMD_ERROR;
|
|
|
|
|
|
Town *t = NULL;
|
|
|
switch (GetTileType(tile)) {
|
|
|
case MP_ROAD:
|
|
|
if (_game_mode != GM_EDITOR && GetRoadOwner(tile, rt) == OWNER_TOWN) t = GetTownByTile(tile);
|
|
|
if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
case MP_STATION:
|
|
|
if (!IsDriveThroughStopTile(tile)) return CMD_ERROR;
|
|
|
if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
case MP_TUNNELBRIDGE:
|
|
|
{
|
|
|
TileIndex endtile;
|
|
|
if (IsTunnel(tile)) {
|
|
|
if (GetTunnelTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR;
|
|
|
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR;
|
|
|
endtile = GetOtherTunnelEnd(tile);
|
|
|
} else {
|
|
|
if (GetBridgeTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR;
|
|
|
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR;
|
|
|
endtile = GetOtherBridgeEnd(tile);
|
|
|
}
|
|
|
|
|
|
if (GetVehicleTunnelBridge(tile, endtile) != NULL) return CMD_ERROR;
|
|
|
} break;
|
|
|
|
|
|
default:
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
RoadBits pieces = Extract<RoadBits, 0>(p1);
|
|
|
RoadTypes rts = GetRoadTypes(tile);
|
|
|
/* The tile doesn't have the given road type */
|
|
|
if (!HasBit(rts, rt)) return CMD_ERROR;
|
|
|
|
|
|
if (!CheckAllowRemoveRoad(tile, pieces, &edge_road, rt)) return CMD_ERROR;
|
|
|
|
|
|
/* check if you're allowed to remove the street owned by a town
|
|
|
* removal allowance depends on difficulty setting */
|
|
|
if (!CheckforTownRating(flags, t, ROAD_REMOVE)) return CMD_ERROR;
|
|
|
|
|
|
if (!IsTileType(tile, MP_ROAD)) {
|
|
|
/* If it's the last roadtype, just clear the whole tile */
|
|
|
if (rts == RoadTypeToRoadTypes(rt)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
|
|
|
|
|
CommandCost cost;
|
|
|
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
|
|
|
TileIndex other_end = IsTunnel(tile) ? GetOtherTunnelEnd(tile) : GetOtherBridgeEnd(tile);
|
|
|
/* Pay for *every* tile of the bridge or tunnel */
|
|
|
cost.AddCost((DistanceManhattan(IsTunnel(tile) ? GetOtherTunnelEnd(tile) : GetOtherBridgeEnd(tile), tile) + 1) * _price.remove_road);
|
|
|
if (flags & DC_EXEC) {
|
|
|
SetRoadTypes(other_end, GetRoadTypes(other_end) & ~RoadTypeToRoadTypes(rt));
|
|
|
SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt));
|
|
|
|
|
|
/* Mark tiles diry that have been repaved */
|
|
|
MarkTileDirtyByTile(tile);
|
|
|
MarkTileDirtyByTile(other_end);
|
|
|
if (IsBridge(tile)) {
|
|
|
TileIndexDiff delta = TileOffsByDiagDir(GetBridgeRampDirection(tile));
|
|
|
TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile));
|
|
|
|
|
|
for (TileIndex t = tile + delta; t != other_end; t += delta) MarkTileDirtyByTile(t);
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
cost.AddCost(_price.remove_road);
|
|
|
if (flags & DC_EXEC) {
|
|
|
SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt));
|
|
|
MarkTileDirtyByTile(tile);
|
|
|
}
|
|
|
}
|
|
|
return CommandCost(cost);
|
|
|
}
|
|
|
|
|
|
switch (GetRoadTileType(tile)) {
|
|
|
case ROAD_TILE_NORMAL: {
|
|
|
RoadBits present = GetRoadBits(tile, rt);
|
|
|
RoadBits c = pieces;
|
|
|
|
|
|
if (HasRoadWorks(tile)) return_cmd_error(STR_ROAD_WORKS_IN_PROGRESS);
|
|
|
|
|
|
if (GetTileSlope(tile, NULL) != SLOPE_FLAT &&
|
|
|
(present == ROAD_Y || present == ROAD_X)) {
|
|
|
c |= (RoadBits)((c & 0xC) >> 2);
|
|
@@ -493,52 +495,52 @@ CommandCost CmdBuildRoad(TileIndex tile,
|
|
|
}
|
|
|
|
|
|
if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
YapfNotifyTrackLayoutChange(tile, FindFirstTrack(GetTrackBits(tile)));
|
|
|
/* Always add road to the roadtypes (can't draw without it) */
|
|
|
MakeRoadCrossing(tile, _current_player, _current_player, _current_player, GetTileOwner(tile), roaddir, GetRailType(tile), RoadTypeToRoadTypes(rt) | ROADTYPES_ROAD, p2);
|
|
|
MarkTileDirtyByTile(tile);
|
|
|
}
|
|
|
return CommandCost(_price.build_road * (rt == ROADTYPE_ROAD ? 2 : 4));
|
|
|
}
|
|
|
|
|
|
case MP_STATION:
|
|
|
if (!IsDriveThroughStopTile(tile)) return CMD_ERROR;
|
|
|
if (HasBit(GetRoadTypes(tile), rt)) return_cmd_error(STR_1007_ALREADY_BUILT);
|
|
|
/* Don't allow "upgrading" the roadstop when vehicles are already driving on it */
|
|
|
if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
|
|
|
break;
|
|
|
|
|
|
case MP_TUNNELBRIDGE:
|
|
|
{
|
|
|
TileIndex endtile;
|
|
|
if (IsTunnel(tile)) {
|
|
|
if (GetTunnelTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR;
|
|
|
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR;
|
|
|
endtile = GetOtherTunnelEnd(tile);
|
|
|
} else {
|
|
|
if (GetBridgeTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR;
|
|
|
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR;
|
|
|
endtile = GetOtherBridgeEnd(tile);
|
|
|
}
|
|
|
if (HasBit(GetRoadTypes(tile), rt)) return_cmd_error(STR_1007_ALREADY_BUILT);
|
|
|
/* Don't allow "upgrading" the bridge/tunnel when vehicles are already driving on it */
|
|
|
if (GetVehicleTunnelBridge(tile, endtile) != NULL) return CMD_ERROR;
|
|
|
} break;
|
|
|
|
|
|
default:
|
|
|
do_clear:;
|
|
|
ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
|
|
if (CmdFailed(ret)) return ret;
|
|
|
cost.AddCost(ret);
|
|
|
}
|
|
|
|
|
|
if (all_bits != pieces) {
|
|
|
/* Check the foundation/slopes when adding road/tram bits */
|
|
|
ret = CheckRoadSlope(tileh, &pieces, all_bits | existing);
|
|
|
/* Return an error if we need to build a foundation (ret != 0) but the
|
|
|
* current patch-setting is turned off (or stupid AI@work) */
|
|
|
if (CmdFailed(ret) || (ret.GetCost() != 0 && (!_patches.build_on_slopes || _is_old_ai_player))) {
|
|
|
return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
|
|
|
}
|
|
|
cost.AddCost(ret);
|
|
|
}
|
|
@@ -555,49 +557,49 @@ do_clear:;
|
|
|
}
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
switch (GetTileType(tile)) {
|
|
|
case MP_ROAD: {
|
|
|
RoadTileType rtt = GetRoadTileType(tile);
|
|
|
if (existing == ROAD_NONE || rtt == ROAD_TILE_CROSSING) {
|
|
|
SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt));
|
|
|
SetRoadOwner(tile, rt, _current_player);
|
|
|
if (_current_player == OWNER_TOWN && rt == ROADTYPE_ROAD) SetTownIndex(tile, p2);
|
|
|
}
|
|
|
if (rtt != ROAD_TILE_CROSSING) SetRoadBits(tile, existing | pieces, rt);
|
|
|
} break;
|
|
|
|
|
|
case MP_TUNNELBRIDGE: {
|
|
|
TileIndex other_end = IsTunnel(tile) ? GetOtherTunnelEnd(tile) : GetOtherBridgeEnd(tile);
|
|
|
|
|
|
SetRoadTypes(other_end, GetRoadTypes(other_end) | RoadTypeToRoadTypes(rt));
|
|
|
SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt));
|
|
|
|
|
|
/* Mark tiles diry that have been repaved */
|
|
|
MarkTileDirtyByTile(other_end);
|
|
|
MarkTileDirtyByTile(tile);
|
|
|
if (IsBridge(tile)) {
|
|
|
TileIndexDiff delta = TileOffsByDiagDir(GetBridgeRampDirection(tile));
|
|
|
TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile));
|
|
|
|
|
|
for (TileIndex t = tile + delta; t != other_end; t += delta) MarkTileDirtyByTile(t);
|
|
|
}
|
|
|
} break;
|
|
|
|
|
|
case MP_STATION:
|
|
|
SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt));
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
MakeRoadNormal(tile, pieces, RoadTypeToRoadTypes(rt), p2, _current_player, _current_player, _current_player);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (rt != ROADTYPE_TRAM && IsTileType(tile, MP_ROAD) && GetRoadTileType(tile) == ROAD_TILE_NORMAL) {
|
|
|
existing |= pieces;
|
|
|
SetDisallowedRoadDirections(tile, (existing == ROAD_X || existing == ROAD_Y) ?
|
|
|
GetDisallowedRoadDirections(tile) ^ toggle_drd : DRD_NONE);
|
|
|
}
|
|
|
|
|
|
MarkTileDirtyByTile(tile);
|
|
|
}
|
|
|
return cost;
|
|
|
}
|
|
@@ -670,54 +672,54 @@ CommandCost CmdBuildLongRoad(TileIndex e
|
|
|
/* On the X-axis, we have to swap the initial bits, so they
|
|
|
* will be interpreted correctly in the GTTS. Futhermore
|
|
|
* when you just 'click' on one tile to build them. */
|
|
|
if (HasBit(p2, 2) == (start_tile == end_tile && HasBit(p2, 0) == HasBit(p2, 1))) drd ^= DRD_BOTH;
|
|
|
/* No disallowed direction bits have to be toggled */
|
|
|
if (!HasBit(p2, 5)) drd = DRD_NONE;
|
|
|
|
|
|
tile = start_tile;
|
|
|
/* Start tile is the small number. */
|
|
|
for (;;) {
|
|
|
RoadBits bits = HasBit(p2, 2) ? ROAD_Y : ROAD_X;
|
|
|
|
|
|
if (tile == end_tile && !HasBit(p2, 1)) bits &= ROAD_NW | ROAD_NE;
|
|
|
if (tile == start_tile && HasBit(p2, 0)) bits &= ROAD_SE | ROAD_SW;
|
|
|
|
|
|
ret = DoCommand(tile, drd << 6 | rt << 4 | bits, 0, flags, CMD_BUILD_ROAD);
|
|
|
if (CmdFailed(ret)) {
|
|
|
if (_error_message != STR_1007_ALREADY_BUILT) return CMD_ERROR;
|
|
|
_error_message = INVALID_STRING_ID;
|
|
|
} else {
|
|
|
had_success = true;
|
|
|
/* Only pay for the upgrade on one side of the bridges and tunnels */
|
|
|
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
|
|
|
if (IsBridge(tile)) {
|
|
|
if ((!had_bridge || GetBridgeRampDirection(tile) == DIAGDIR_SE || GetBridgeRampDirection(tile) == DIAGDIR_SW)) {
|
|
|
if ((!had_bridge || GetTunnelBridgeDirection(tile) == DIAGDIR_SE || GetTunnelBridgeDirection(tile) == DIAGDIR_SW)) {
|
|
|
cost.AddCost(ret);
|
|
|
}
|
|
|
had_bridge = true;
|
|
|
} else {
|
|
|
if ((!had_tunnel || GetTunnelDirection(tile) == DIAGDIR_SE || GetTunnelDirection(tile) == DIAGDIR_SW)) {
|
|
|
if ((!had_tunnel || GetTunnelBridgeDirection(tile) == DIAGDIR_SE || GetTunnelBridgeDirection(tile) == DIAGDIR_SW)) {
|
|
|
cost.AddCost(ret);
|
|
|
}
|
|
|
had_tunnel = true;
|
|
|
}
|
|
|
} else {
|
|
|
cost.AddCost(ret);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (tile == end_tile) break;
|
|
|
|
|
|
tile += HasBit(p2, 2) ? TileDiffXY(0, 1) : TileDiffXY(1, 0);
|
|
|
}
|
|
|
|
|
|
return !had_success ? CMD_ERROR : cost;
|
|
|
}
|
|
|
|
|
|
/** Remove a long piece of road.
|
|
|
* @param end_tile end tile of drag
|
|
|
* @param flags operation to perform
|
|
|
* @param p1 start tile of drag
|
|
|
* @param p2 various bitstuffed elements
|
|
|
* - p2 = (bit 0) - start tile starts in the 2nd half of tile (p2 & 1)
|
|
|
* - p2 = (bit 1) - end tile starts in the 2nd half of tile (p2 & 2)
|