diff --git a/docs/landscape.html b/docs/landscape.html
index cba92906aa..c8886be7d8 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -1741,6 +1741,7 @@
m6 bit 6: set = PBS signals, clear = block signals
m6 bit 1: set = semaphore signals, clear = light signals
m6 bit 0: set = entrance signal shows green, clear = entrance signal shows red
+ m8 bits 15..12: signal simulation spacing
m5 bit 4: pbs reservation state for railway (tunnel only)
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html
index cf5000358e..428ac71f3b 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -292,7 +292,7 @@ the array so you can quickly see what is used and what is not.
OPPX XX XX |
PPOO OOPP |
PPPX XXXX |
- OPPP XXXX XXXX XXXX |
+ PPPP XXXX XXXX XXXX |
bridge ramp - rail |
@@ -300,7 +300,7 @@ the array so you can quickly see what is used and what is not.
OOPP PPPP |
1PPO 00 XX |
PPXX XXPP |
- OOOO PPPP PPXX XXXX |
+ PPPP PPPP PPXX XXXX |
bridge ramp - road |
diff --git a/src/bridge_map.h b/src/bridge_map.h
index b6f37cb36b..fb72170e55 100644
--- a/src/bridge_map.h
+++ b/src/bridge_map.h
@@ -189,6 +189,7 @@ static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetyp
SB(_me[t].m6, 0, 2, GB(m6_backup, 0, 2));
SB(_me[t].m6, 6, 1, GB(m6_backup, 6, 1));
SB(_me[t].m8, 6, 6, GB(m8_backup, 6, 6));
+ SB(_me[t].m8, 12, 4, GB(m8_backup, 12, 4));
} else {
/* Set bridge head tracks to axial track only. */
SB(_m[t].m4, 0, 6, DiagDirToDiagTrackBits(d));
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 9366f6add4..fbf201e398 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1879,6 +1879,7 @@ STR_CONFIG_SETTING_MIN_YEARS_FOR_SHARES_HELPTEXT :Set the minimum
STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE :Percentage of leg profit to pay in feeder systems: {STRING2}
STR_CONFIG_SETTING_FEEDER_PAYMENT_SHARE_HELPTEXT :Percentage of income given to the intermediate legs in feeder systems, giving more control over the income
STR_CONFIG_SETTING_SIMULATE_SIGNALS :Simulate signals in tunnels, bridges every: {STRING2}
+STR_CONFIG_SETTING_SIMULATE_SIGNALS_HELPTEXT :This sets the target signal spacing for newly signalled bridges and tunnels. The actual spacing may slightly differ from this to avoid uneven spacing. Changing this setting does not re-signal existing signalled bridges and tunnels.
STR_CONFIG_SETTING_SIMULATE_SIGNALS_VALUE :{COMMA} tile{P 0 "" s}
STR_CONFIG_SETTING_DAY_LENGTH_FACTOR :Day length factor: {STRING2}
STR_CONFIG_SETTING_DAY_LENGTH_FACTOR_HELPTEXT :Game pace is slowed by this factor
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 640c4b59f9..125b54ed98 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -1527,8 +1527,11 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
Company * const c = Company::Get(GetTileOwner(tile));
std::vector re_reserve_trains;
if (IsTunnelBridgeWithSignalSimulation(tile)) {
- c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(c, tile, tile_exit);
+ c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, tile_exit);
} else {
+ uint spacing = GetBestTunnelBridgeSignalSimulationSpacing(GetTileOwner(tile), tile, tile_exit);
+ SetTunnelBridgeSignalSimulationSpacing(tile, spacing);
+ SetTunnelBridgeSignalSimulationSpacing(tile_exit, spacing);
for (TileIndex t : { tile, tile_exit }) {
if (HasAcrossTunnelBridgeReservation(t)) {
Train *re_reserve_train = GetTrainForReservation(t, FindFirstTrack(GetAcrossTunnelBridgeReservationTrackBits(t)));
@@ -1602,7 +1605,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
AddSideToSignalBuffer(tile_exit, INVALID_DIAGDIR, GetTileOwner(tile));
YapfNotifyTrackLayoutChange(tile, track);
YapfNotifyTrackLayoutChange(tile_exit, track);
- if (IsTunnelBridgeWithSignalSimulation(tile)) c->infrastructure.signal += GetTunnelBridgeSignalSimulationSignalCount(c, tile, tile_exit);
+ if (IsTunnelBridgeWithSignalSimulation(tile)) c->infrastructure.signal += GetTunnelBridgeSignalSimulationSignalCount(tile, tile_exit);
DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
for (Train *re_reserve_train : re_reserve_trains) {
ReReserveTrainPath(re_reserve_train);
@@ -2095,7 +2098,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1
}
if (flags & DC_EXEC) {
Company *c = Company::Get(GetTileOwner(tile));
- c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(c, tile, end);
+ c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, end);
ClearBridgeTunnelSignalSimulation(end, tile);
ClearBridgeTunnelSignalSimulation(tile, end);
MarkBridgeOrTunnelDirty(tile);
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index a10485da66..706b16ff7e 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -29,6 +29,7 @@
#include "../station_base.h"
#include "../waypoint_base.h"
#include "../roadstop_base.h"
+#include "../tunnelbridge.h"
#include "../tunnelbridge_map.h"
#include "../pathfinder/yapf/yapf_cache.h"
#include "../elrail_func.h"
@@ -3637,6 +3638,22 @@ bool AfterLoadGame()
c->settings.simulated_wormhole_signals = _settings_game.construction.old_simulated_wormhole_signals;
}
}
+ if (SlXvIsFeaturePresent(XSLFI_SIG_TUNNEL_BRIDGE, 1, 8)) {
+ /* spacing made per tunnel/bridge */
+ for (TileIndex t = 0; t < map_size; t++) {
+ if (IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL && IsTunnelBridgeWithSignalSimulation(t)) {
+ DiagDirection dir = GetTunnelBridgeDirection(t);
+ if (dir == DIAGDIR_NE || dir == DIAGDIR_SE) {
+ TileIndex other = GetOtherTunnelBridgeEnd(t);
+ uint spacing = GetBestTunnelBridgeSignalSimulationSpacing(GetTileOwner(t), t, other);
+ SetTunnelBridgeSignalSimulationSpacing(t, spacing);
+ SetTunnelBridgeSignalSimulationSpacing(other, spacing);
+ }
+ }
+ }
+ /* force aspect re-calculation */
+ _extra_aspects = 0;
+ }
if (SlXvIsFeatureMissing(XSLFI_CUSTOM_BRIDGE_HEADS)) {
/* ensure that previously unused custom bridge-head bits are cleared */
diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index 8cfad01739..40c83824a1 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -85,7 +85,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_DEPARTURE_BOARDS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "departure_boards", nullptr, nullptr, nullptr },
{ XSLFI_TIMETABLES_START_TICKS, XSCF_NULL, 2, 2, "timetable_start_ticks", nullptr, nullptr, nullptr },
{ XSLFI_TOWN_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 2, 2, "town_cargo_adj", nullptr, nullptr, nullptr },
- { XSLFI_SIG_TUNNEL_BRIDGE, XSCF_NULL, 8, 8, "signal_tunnel_bridge", nullptr, nullptr, "XBSS" },
+ { XSLFI_SIG_TUNNEL_BRIDGE, XSCF_NULL, 9, 9, "signal_tunnel_bridge", nullptr, nullptr, "XBSS" },
{ XSLFI_IMPROVED_BREAKDOWNS, XSCF_NULL, 7, 7, "improved_breakdowns", nullptr, nullptr, nullptr },
{ XSLFI_CONSIST_BREAKDOWN_FLAG, XSCF_NULL, 1, 1, "consist_breakdown_flag", nullptr, nullptr, nullptr },
{ XSLFI_TT_WAIT_IN_DEPOT, XSCF_NULL, 1, 1, "tt_wait_in_depot", nullptr, nullptr, nullptr },
diff --git a/src/settings.cpp b/src/settings.cpp
index e9ce4ef8dd..3ec5efe75c 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1482,14 +1482,6 @@ static bool InvalidateCompanyWindow(int32 p1)
return true;
}
-static bool SimulatedWormholeSignalsChanged(int32 p1)
-{
- extern void AfterLoadCompanyStats();
- AfterLoadCompanyStats();
- MarkWholeScreenDirty();
- return true;
-}
-
static bool EnableSingleVehSharedOrderGuiChanged(int32)
{
for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
diff --git a/src/table/company_settings.ini b/src/table/company_settings.ini
index 99ec3f871a..aaaa60f7fb 100644
--- a/src/table/company_settings.ini
+++ b/src/table/company_settings.ini
@@ -11,7 +11,6 @@ static bool UpdateIntervalTrains(int32 p1);
static bool UpdateIntervalRoadVeh(int32 p1);
static bool UpdateIntervalShips(int32 p1);
static bool UpdateIntervalAircraft(int32 p1);
-static bool SimulatedWormholeSignalsChanged(int32 p1);
static const SettingDesc _company_settings[] = {
[post-amble]
@@ -269,8 +268,8 @@ def = 4
min = 1
max = 16
str = STR_CONFIG_SETTING_SIMULATE_SIGNALS
+strhelp = STR_CONFIG_SETTING_SIMULATE_SIGNALS_HELPTEXT
strval = STR_CONFIG_SETTING_SIMULATE_SIGNALS_VALUE
-proc = SimulatedWormholeSignalsChanged
cat = SC_ADVANCED
patxname = ""simulated_wormhole_signals""
diff --git a/src/tunnelbridge.h b/src/tunnelbridge.h
index 417899ba7a..c2d1fc879a 100644
--- a/src/tunnelbridge.h
+++ b/src/tunnelbridge.h
@@ -13,8 +13,9 @@
#include "map_func.h"
#include "tile_map.h"
-uint GetTunnelBridgeSignalSimulationSpacing(TileIndex tile);
-uint GetTunnelBridgeSignalSimulationSignalCount(Company *c, TileIndex begin, TileIndex end);
+uint GetTunnelBridgeSignalSimulationSpacingTarget(Owner owner);
+uint GetBestTunnelBridgeSignalSimulationSpacing(Owner owner, TileIndex begin, TileIndex end);
+uint GetTunnelBridgeSignalSimulationSignalCount(TileIndex begin, TileIndex end);
/**
* Calculates the length of a tunnel or a bridge (without end tiles)
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index 49e92930b1..225256e619 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -131,9 +131,8 @@ void MarkBridgeOrTunnelDirtyOnReservationChange(TileIndex tile, ViewportMarkDirt
}
}
-uint GetTunnelBridgeSignalSimulationSpacing(TileIndex tile)
+uint GetTunnelBridgeSignalSimulationSpacingTarget(Owner owner)
{
- Owner owner = GetTileOwner(tile);
if (Company::IsValidID(owner)) {
return Company::Get(owner)->settings.simulated_wormhole_signals;
} else {
@@ -141,16 +140,37 @@ uint GetTunnelBridgeSignalSimulationSpacing(TileIndex tile)
}
}
+uint GetBestTunnelBridgeSignalSimulationSpacing(Owner owner, TileIndex begin, TileIndex end)
+{
+ int target = GetTunnelBridgeSignalSimulationSpacingTarget(owner);
+ if (target <= 2) return target;
+ int length = GetTunnelBridgeLength(begin, end);
+ if (target > length || ((length + 1) % target) == 0) return target;
+
+ int lower = target - (target / 4);
+ int upper = std::min(16, target + (target / 3));
+
+ int best_gap = -1;
+ int best_spacing = 0;
+ for (int i = lower; i <= upper; i++) {
+ int gap = length % i;
+ if (gap > best_gap) {
+ best_gap = gap;
+ best_spacing = i;
+ }
+ }
+ return best_spacing;
+}
+
/**
* Get number of signals on bridge or tunnel with signal simulation.
- * @param c Company to use.
* @param begin The begin of the tunnel or bridge.
* @param end The end of the tunnel or bridge.
* @pre IsTunnelBridgeWithSignalSimulation(begin)
*/
-uint GetTunnelBridgeSignalSimulationSignalCount(Company *c, TileIndex begin, TileIndex end)
+uint GetTunnelBridgeSignalSimulationSignalCount(TileIndex begin, TileIndex end)
{
- uint result = 2 + (GetTunnelBridgeLength(begin, end) / c->settings.simulated_wormhole_signals);
+ uint result = 2 + (GetTunnelBridgeLength(begin, end) / GetTunnelBridgeSignalSimulationSpacing(begin));
if (IsTunnelBridgeSignalSimulationBidirectional(begin)) result *= 2;
return result;
}
@@ -1254,7 +1274,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
Company *c = Company::Get(owner);
c->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
if (IsTunnelBridgeWithSignalSimulation(tile)) { // handle tunnel/bridge signals.
- c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(c, tile, endtile);
+ c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(tile, endtile);
}
DirtyCompanyInfrastructureWindows(owner);
}
@@ -2730,9 +2750,9 @@ static void UpdateRailTunnelBridgeInfrastructure(Company *c, TileIndex begin, Ti
if (IsTunnelBridgeWithSignalSimulation(begin)) {
if (add) {
- c->infrastructure.signal += GetTunnelBridgeSignalSimulationSignalCount(c, begin, end);
+ c->infrastructure.signal += GetTunnelBridgeSignalSimulationSignalCount(begin, end);
} else {
- c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(c, begin, end);
+ c->infrastructure.signal -= GetTunnelBridgeSignalSimulationSignalCount(begin, end);
}
}
}
diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h
index 123c4a0dcf..6e73e05f2b 100644
--- a/src/tunnelbridge_map.h
+++ b/src/tunnelbridge_map.h
@@ -530,6 +530,18 @@ static inline void SetTunnelBridgeExitSignalAspect(TileIndex t, uint8 aspect)
SB(_m[t].m3, 3, 3, aspect);
}
+static inline uint GetTunnelBridgeSignalSimulationSpacing(TileIndex t)
+{
+ assert_tile(IsRailTunnelBridgeTile(t), t);
+ return 1 + GB(_me[t].m8, 12, 4);
+}
+
+static inline void SetTunnelBridgeSignalSimulationSpacing(TileIndex t, uint spacing)
+{
+ assert_tile(IsRailTunnelBridgeTile(t), t);
+ SB(_me[t].m8, 12, 4, spacing - 1);
+}
+
static inline Trackdir GetTunnelBridgeExitTrackdir(TileIndex t, DiagDirection tunnel_bridge_dir)
{
return TrackEnterdirToTrackdir((Track)FIND_FIRST_BIT(GetAcrossTunnelBridgeTrackBits(t)), ReverseDiagDir(tunnel_bridge_dir));