1
0
mirror of https://github.com/OpenTTD/OpenTTD synced 2026-01-23 04:04:09 +01:00

Change: Allow bridges over locks. (#14595)

The bridge must be at least 2 levels higher than the lock.
This commit is contained in:
Peter Nelson
2025-09-10 18:41:56 +01:00
committed by GitHub
parent eb09d18418
commit e10200efa4
5 changed files with 65 additions and 29 deletions

View File

@@ -5268,6 +5268,7 @@ STR_ERROR_BRIDGE_TOO_LOW_FOR_DOCK :{WHITE}Bridge i
STR_ERROR_BRIDGE_TOO_LOW_FOR_BUOY :{WHITE}Bridge is too low for buoy
STR_ERROR_BRIDGE_TOO_LOW_FOR_RAIL_WAYPOINT :{WHITE}Bridge is too low for rail waypoint
STR_ERROR_BRIDGE_TOO_LOW_FOR_ROAD_WAYPOINT :{WHITE}Bridge is too low for road waypoint
STR_ERROR_BRIDGE_TOO_LOW_FOR_LOCK :{WHITE}Bridge is too low for lock
# Tunnel related errors
STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Can't build tunnel here...

View File

@@ -410,6 +410,7 @@ enum SaveLoadVersion : uint16_t {
SLV_STATIONS_UNDER_BRIDGES, ///< 359 PR#14477 Allow stations under bridges.
SLV_DOCKS_UNDER_BRIDGES, ///< 360 PR#14594 Allow docks under bridges.
SLV_LOCKS_UNDER_BRIDGES, ///< 361 PR#14595 Allow locks under bridges.
SL_MAX_VERSION, ///< Highest possible saveload version
};

View File

@@ -55,64 +55,71 @@ static const DrawTileSpriteSpan _shipdepot_display_data[][DEPOT_PART_END] = {
},
};
static constexpr uint8_t LOCK_HEIGHT_LOWER_REAR = 6; ///< Sub-tile height of rear wall of lower part.
static constexpr uint8_t LOCK_HEIGHT_LOWER_FRONT = 10; ///< Sub-tile height of front wall of lower part.
static constexpr uint8_t LOCK_HEIGHT_MIDDLE_REAR = 6; ///< Sub-tile height of rear wall of middle part.
static constexpr uint8_t LOCK_HEIGHT_MIDDLE_FRONT = 10; ///< Sub-tile height of front wall of middle part.
static constexpr uint8_t LOCK_HEIGHT_UPPER_REAR = 6; ///< Sub-tile height of rear wall of upper part.
static constexpr uint8_t LOCK_HEIGHT_UPPER_FRONT = 6; ///< Sub-tile height of front wall of upper part.
static const DrawTileSeqStruct _lock_display_middle_ne_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 0x10, 1, 0x14, 0 + 1)
TILE_SEQ_LINE( 0, 0xF, 0, 0x10, 1, 0x14, 4 + 1)
TILE_SEQ_LINE(0, 0, 0, TILE_SIZE, 1, LOCK_HEIGHT_MIDDLE_REAR, 0 + 1)
TILE_SEQ_LINE(0, 15, 0, TILE_SIZE, 1, LOCK_HEIGHT_MIDDLE_FRONT, 4 + 1)
};
static const DrawTileSeqStruct _lock_display_middle_se_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 1, 0x10, 0x14, 0)
TILE_SEQ_LINE( 0xF, 0, 0, 1, 0x10, 0x14, 4)
TILE_SEQ_LINE( 0, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_MIDDLE_REAR, 0)
TILE_SEQ_LINE(15, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_MIDDLE_FRONT, 4)
};
static const DrawTileSeqStruct _lock_display_middle_sw_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 0x10, 1, 0x14, 0 + 2)
TILE_SEQ_LINE( 0, 0xF, 0, 0x10, 1, 0x14, 4 + 2)
TILE_SEQ_LINE(0, 0, 0, TILE_SIZE, 1, LOCK_HEIGHT_MIDDLE_REAR, 0 + 2)
TILE_SEQ_LINE(0, 15, 0, TILE_SIZE, 1, LOCK_HEIGHT_MIDDLE_FRONT, 4 + 2)
};
static const DrawTileSeqStruct _lock_display_middle_nw_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 1, 0x10, 0x14, 0 + 3)
TILE_SEQ_LINE( 0xF, 0, 0, 1, 0x10, 0x14, 4 + 3)
TILE_SEQ_LINE( 0, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_MIDDLE_REAR, 0 + 3)
TILE_SEQ_LINE(15, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_MIDDLE_FRONT, 4 + 3)
};
static const DrawTileSeqStruct _lock_display_lower_ne_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 0x10, 1, 0x14, 8 + 1)
TILE_SEQ_LINE( 0, 0xF, 0, 0x10, 1, 0x14, 12 + 1)
TILE_SEQ_LINE(0, 0, 0, TILE_SIZE, 1, LOCK_HEIGHT_LOWER_REAR, 8 + 1)
TILE_SEQ_LINE(0, 15, 0, TILE_SIZE, 1, LOCK_HEIGHT_LOWER_FRONT, 12 + 1)
};
static const DrawTileSeqStruct _lock_display_lower_se_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 0x1, 0x10, 0x14, 8)
TILE_SEQ_LINE( 0xF, 0, 0, 0x1, 0x10, 0x14, 12)
TILE_SEQ_LINE( 0, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_LOWER_REAR, 8)
TILE_SEQ_LINE(15, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_LOWER_FRONT, 12)
};
static const DrawTileSeqStruct _lock_display_lower_sw_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 0x10, 1, 0x14, 8 + 2)
TILE_SEQ_LINE( 0, 0xF, 0, 0x10, 1, 0x14, 12 + 2)
TILE_SEQ_LINE(0, 0, 0, TILE_SIZE, 1, LOCK_HEIGHT_LOWER_REAR, 8 + 2)
TILE_SEQ_LINE(0, 15, 0, TILE_SIZE, 1, LOCK_HEIGHT_LOWER_FRONT, 12 + 2)
};
static const DrawTileSeqStruct _lock_display_lower_nw_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 1, 0x10, 0x14, 8 + 3)
TILE_SEQ_LINE( 0xF, 0, 0, 1, 0x10, 0x14, 12 + 3)
TILE_SEQ_LINE( 0, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_LOWER_REAR, 8 + 3)
TILE_SEQ_LINE(15, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_LOWER_FRONT, 12 + 3)
};
static const DrawTileSeqStruct _lock_display_upper_ne_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 0x10, 1, 0x14, 16 + 1)
TILE_SEQ_LINE( 0, 0xF, 0, 0x10, 1, 0x14, 20 + 1)
TILE_SEQ_LINE(0, 0, 0, TILE_SIZE, 1, LOCK_HEIGHT_UPPER_REAR, 16 + 1)
TILE_SEQ_LINE(0, 15, 0, TILE_SIZE, 1, LOCK_HEIGHT_UPPER_FRONT, 20 + 1)
};
static const DrawTileSeqStruct _lock_display_upper_se_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 0x1, 0x10, 0x14, 16)
TILE_SEQ_LINE( 0xF, 0, 0, 0x1, 0x10, 0x14, 20)
TILE_SEQ_LINE( 0, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_UPPER_REAR, 16)
TILE_SEQ_LINE(15, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_UPPER_FRONT, 20)
};
static const DrawTileSeqStruct _lock_display_upper_sw_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 0x10, 1, 0x14, 16 + 2)
TILE_SEQ_LINE( 0, 0xF, 0, 0x10, 1, 0x14, 20 + 2)
TILE_SEQ_LINE(0, 0, 0, TILE_SIZE, 1, LOCK_HEIGHT_UPPER_REAR, 16 + 2)
TILE_SEQ_LINE(0, 15, 0, TILE_SIZE, 1, LOCK_HEIGHT_UPPER_FRONT, 20 + 2)
};
static const DrawTileSeqStruct _lock_display_upper_nw_seq[] = {
TILE_SEQ_LINE( 0, 0, 0, 1, 0x10, 0x14, 16 + 3)
TILE_SEQ_LINE( 0xF, 0, 0, 1, 0x10, 0x14, 20 + 3)
TILE_SEQ_LINE( 0, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_UPPER_REAR, 16 + 3)
TILE_SEQ_LINE(15, 0, 0, 1, TILE_SIZE, LOCK_HEIGHT_UPPER_FRONT, 20 + 3)
};
static const DrawTileSpriteSpan _lock_display_data[][DIAGDIR_END] = {

View File

@@ -301,6 +301,21 @@ static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlags flags)
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
}
/**
* Get the minimal height required for a bridge above a lock part.
* @param lock_part the lock part.
* @return the minimal bridge height.
*/
static uint8_t GetLockPartMinimalBridgeHeight(LockPart lock_part)
{
static constexpr uint8_t MINIMAL_BRIDGE_HEIGHT[LOCK_PART_END] = {
2, // LOCK_PART_MIDDLE
3, // LOCK_PART_LOWER
2, // LOCK_PART_UPPER
};
return MINIMAL_BRIDGE_HEIGHT[to_underlying(lock_part)];
}
/**
* Builds a lock.
* @param tile Central tile of the lock.
@@ -348,8 +363,11 @@ static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlags
}
WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
if (IsBridgeAbove(tile) || IsBridgeAbove(tile - delta) || IsBridgeAbove(tile + delta)) {
return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
for (LockPart lock_part = LOCK_PART_MIDDLE; TileIndex t : {tile, tile - delta, tile + delta}) {
if (IsBridgeAbove(t) && GetBridgeHeight(GetSouthernBridgeEnd(t)) < GetTileMaxZ(t) + GetLockPartMinimalBridgeHeight(lock_part)) {
return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_LOCK);
}
++lock_part;
}
if (flags.Test(DoCommandFlag::Execute)) {
@@ -939,6 +957,9 @@ static void DrawTile_Water(TileInfo *ti)
case WATER_TILE_LOCK:
DrawWaterLock(ti);
DrawBridgeMiddle(ti, DiagDirToAxis(GetLockDirection(ti->tile)) == AXIS_X
? BridgePillarFlags{BridgePillarFlag::EdgeNE, BridgePillarFlag::EdgeSW}
: BridgePillarFlags{BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE});
break;
case WATER_TILE_DEPOT:
@@ -1412,9 +1433,13 @@ static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlags flags, int
return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
}
static CommandCost CheckBuildAbove_Water(TileIndex tile, DoCommandFlags flags, Axis, int)
static CommandCost CheckBuildAbove_Water(TileIndex tile, DoCommandFlags flags, Axis, int height)
{
if (IsWater(tile) || IsCoast(tile)) return CommandCost();
if (IsLock(tile)) {
if (GetTileMaxZ(tile) + GetLockPartMinimalBridgeHeight(GetLockPart(tile)) <= height) return CommandCost();
return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_LOCK);
}
return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
}

View File

@@ -66,7 +66,9 @@ enum LockPart : uint8_t {
LOCK_PART_MIDDLE = 0, ///< Middle part of a lock.
LOCK_PART_LOWER = 1, ///< Lower part of a lock.
LOCK_PART_UPPER = 2, ///< Upper part of a lock.
LOCK_PART_END,
};
DECLARE_INCREMENT_DECREMENT_OPERATORS(LockPart);
bool IsPossibleDockingTile(Tile t);
@@ -323,10 +325,10 @@ inline DiagDirection GetLockDirection(Tile t)
* @return The part.
* @pre IsTileType(t, MP_WATER) && IsLock(t)
*/
inline uint8_t GetLockPart(Tile t)
inline LockPart GetLockPart(Tile t)
{
assert(IsLock(t));
return GB(t.m5(), WBL_LOCK_PART_BEGIN, WBL_LOCK_PART_COUNT);
return static_cast<LockPart>(GB(t.m5(), WBL_LOCK_PART_BEGIN, WBL_LOCK_PART_COUNT));
}
/**