From b2ef6c0de8455afdec9ec33b70e7ea14d2de2099 Mon Sep 17 00:00:00 2001
From: Jonathan G Rennison
Date: Sat, 28 Aug 2021 12:51:06 +0100
Subject: [PATCH] Add implementation of multi-aspect signalling and GRF support
Requires realistic braking
See: #247
---
docs/landscape.html | 4 +
docs/landscape_grid.html | 6 +-
docs/newgrf-additions-nml.html | 34 ++
docs/newgrf-additions.html | 32 ++
src/bridge_map.h | 2 +
src/command.cpp | 3 +
src/infrastructure.cpp | 9 +
src/misc.cpp | 1 +
src/newgrf.cpp | 16 +
src/newgrf.h | 7 +
src/newgrf_railtype.cpp | 18 +-
src/newgrf_railtype.h | 2 +-
src/openttd.cpp | 2 +
src/pbs.cpp | 5 +
src/rail.h | 5 +
src/rail_cmd.cpp | 33 +-
src/rail_map.h | 16 +-
src/saveload/afterload.cpp | 4 +
src/saveload/extended_ver_sl.cpp | 2 +-
src/saveload/misc_sl.cpp | 3 +
src/settings.cpp | 1 +
src/signal.cpp | 562 ++++++++++++++++++++++++++++++-
src/signal_func.h | 20 ++
src/table/newgrf_debug_data.h | 14 +
src/table/railtypes.h | 12 +
src/train_cmd.cpp | 71 +++-
src/tunnelbridge_cmd.cpp | 44 ++-
src/tunnelbridge_map.h | 24 ++
28 files changed, 917 insertions(+), 35 deletions(-)
diff --git a/docs/landscape.html b/docs/landscape.html
index 966c476bf8..308b55e7f5 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -508,6 +508,8 @@
m3 bits 7..4: bit set = signal 3..0 present
m4 bits 7..4: bit clear = signal 3..0 shows red
+ m7 bits 5..3: signal aspect for signal 0 or 1 (only valid if signal is present and not red, and multi-aspect signalling is in effect)
+ m7 bits 2..0: signal aspect for signal 2 or 3 (only valid if signal is present and not red, and multi-aspect signalling is in effect)
m2 bits 8..10: track reserved for pbs
@@ -1740,6 +1742,8 @@
m6 bit 0: set = entrance signal shows green, clear = entrance signal shows red
m2 bit 15: for bridge entrances only: storage for visual red/green state of signals starting from 15 is allocated outside the map array
m2 bits 14..4: for bridge entrances only: for signals 0..10 on bridge, signal is visually red if corresponding bit in 4..14 is set
+ m3 bits 5..3: entrance signal aspect (only valid if signal is present and not red, and multi-aspect signalling is in effect)
+ m3 bits 2..0: exit signal aspect (only valid if signal is present and not red, and multi-aspect signalling is in effect)
m5 bits 3..2: transport type
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html
index 02a4df7017..cf5000358e 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -104,7 +104,7 @@ the array so you can quickly see what is used and what is not.
OOOO XXXX |
OO XXXXXX |
OOOO OOOO |
- OOOO OOOO |
+ OOOO OOOO |
OOOO PPPP PPXX XXXX |
@@ -113,6 +113,7 @@ the array so you can quickly see what is used and what is not.
XXXX OOOO |
XXXX XXXX |
O1 XXXXXX |
+ OO PPPPPP |
depot |
@@ -120,6 +121,7 @@ the array so you can quickly see what is used and what is not.
OOOO OOOO |
OOOO XXXX |
11OX OOXX |
+ OOOO OOOO |
OOOO OOOO OOXX XXXX |
@@ -285,7 +287,7 @@ the array so you can quickly see what is used and what is not.
tunnel entrance |
OOO XXXXX |
PPPP PPPP PPPP PPPP |
- XXXX OOOO |
+ XXXX OOOO OO PPPPPP |
OOXX XXXX |
OPPX XX XX |
PPOO OOPP |
diff --git a/docs/newgrf-additions-nml.html b/docs/newgrf-additions-nml.html
index 05a6b06436..087cf68b38 100644
--- a/docs/newgrf-additions-nml.html
+++ b/docs/newgrf-additions-nml.html
@@ -66,6 +66,23 @@
If the OpenTTD version does not support this property/feature, then the property would ordinarily be ignored/skipped and no recolouring would be done.
+ extra_aspects | 0 - 6 |
+
+ The value is the number of additional signal aspects to use (e.g. 4-aspect signalling should use a value of 2).
+ When set, the lowest byte of extra_callback_info2 (signal state) may have the given number of additional values starting from 02:
+
+ Value | Meaning |
+ 00 | Red signal |
+ 01 | Green signal |
+ 02 | 1st extra aspect (e.g. yellow) |
+ 03 | 2nd extra aspect (e.g. double yellow) |
+ ... | Further extra aspects... |
+
+
+ The provided value is currently clamped to be within the range 0 - 6 (inclusive).
+ N.B. Realistic braking must be enabled for additional signal aspects to be used
+ |
+
disable_realistic_braking | 0 or 1 |
When this property is set realistic braking is disabled for trains of this railtype even when realistic braking is otherwise in effect.
@@ -161,6 +178,23 @@
|
+ extra_aspects | 0 - 6 |
+
+ The value is the number of additional signal aspects to use (e.g. 4-aspect signalling should use a value of 2).
+ When set, the lowest byte of extra_callback_info2 (signal state) may have the given number of additional values starting from 02:
+
+ Value | Meaning |
+ 00 | Red signal |
+ 01 | Green signal |
+ 02 | 1st extra aspect (e.g. yellow) |
+ 03 | 2nd extra aspect (e.g. double yellow) |
+ ... | Further extra aspects... |
+
+
+ The provided value is currently clamped to be within the range 0 - 6 (inclusive).
+ N.B. Realistic braking must be enabled for additional signal aspects to be used
+ |
+
Custom signal sprites example:
diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html
index 8b8c209e6c..8e9887f137 100644
--- a/docs/newgrf-additions.html
+++ b/docs/newgrf-additions.html
@@ -241,6 +241,22 @@
The property length is 1 byte. 0 is disabled (default). 1 is enabled.
This is indicated by the feature name: action0_railtype_recolour, version 1
+
+ This applies to Action 2/3 - Railtype custom signal sprites.
+ The value is the number of additional signal aspects to use (e.g. 4-aspect signalling should use a value of 2).
+ When set, the lowest byte of variable 0x18 (SS: signal state) may have the given number of additional values starting from 02:
+
+ Value | Meaning |
+ 00 | Red signal |
+ 01 | Green signal |
+ 02 | 1st extra aspect (e.g. yellow) |
+ 03 | 2nd extra aspect (e.g. double yellow) |
+ ... | Further extra aspects... |
+
+ The property length is 1 byte.
+ The provided value is currently clamped to be within the range 0 - 6 (inclusive).
+ N.B. Realistic braking must be enabled for additional signal aspects to be used.
+ This is indicated by the feature name: action0_railtype_extra_aspects, version 1
Disable use of realistic braking with this rail type (mappable property: railtype_disable_realistic_braking)
When this property is set realistic braking is disabled for trains of this railtype even when realistic braking is otherwise in effect.
The property length is 1 byte. 0 is realistic braking is not disabled for this railtype. 1 is disable realistic braking for this railtype.
@@ -319,6 +335,22 @@
The Action 0 Id field is not used, the value is ignored.
This is indicated by the feature name: action0_signals_recolour, version 1
+
+ This applies to Action 2/3 Signals (Feature 0E) custom signal sprites for this GRF.
+ The value is the number of additional signal aspects to use (e.g. 4-aspect signalling should use a value of 2).
+ When set, the lowest byte of variable 0x18 (SS: signal state) may have the given number of additional values starting from 02:
+
+ Value | Meaning |
+ 00 | Red signal |
+ 01 | Green signal |
+ 02 | 1st extra aspect (e.g. yellow) |
+ 03 | 2nd extra aspect (e.g. double yellow) |
+ ... | Further extra aspects... |
+
+ The property length is 1 byte.
+ The provided value is currently clamped to be within the range 0 - 6 (inclusive).
+ N.B. Realistic braking must be enabled for additional signal aspects to be used.
+ This is indicated by the feature name: action0_signals_extra_aspects, version 1
diff --git a/src/bridge_map.h b/src/bridge_map.h
index 2f67e2b37d..b6f37cb36b 100644
--- a/src/bridge_map.h
+++ b/src/bridge_map.h
@@ -170,6 +170,7 @@ static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetyp
{
/* Backup bridge signal and custom bridgehead data. */
auto m2_backup = _m[t].m2;
+ auto m3_backup = _m[t].m3;
auto m4_backup = _m[t].m4;
auto m5_backup = _m[t].m5;
auto m6_backup = _me[t].m6;
@@ -182,6 +183,7 @@ static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetyp
if (upgrade) {
/* Restore bridge signal and custom bridgehead data if we're upgrading an existing bridge. */
_m[t].m2 = m2_backup;
+ SB(_m[t].m3, 0, 6, GB(m3_backup, 0, 6));
SB(_m[t].m4, 0, 6, GB(m4_backup, 0, 6));
SB(_m[t].m5, 4, 3, GB(m5_backup, 4, 3));
SB(_me[t].m6, 0, 2, GB(m6_backup, 0, 2));
diff --git a/src/command.cpp b/src/command.cpp
index b89b996e73..e0c9cb0242 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -29,6 +29,7 @@
#include "scope_info.h"
#include "core/random_func.hpp"
#include "settings_func.h"
+#include "signal_func.h"
#include
#include "table/strings.h"
@@ -1087,6 +1088,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint64 p3,
/* It could happen we removed rail, thus gained money, and deleted something else.
* So make sure the signal buffer is empty even in this case */
UpdateSignalsInBuffer();
+ if (_extra_aspects > 0) FlushDeferredAspectUpdates();
SetDParam(0, _additional_cash_required);
return_dcpi(CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY));
}
@@ -1101,6 +1103,7 @@ CommandCost DoCommandPInternal(TileIndex tile, uint32 p1, uint32 p2, uint64 p3,
/* update signals if needed */
UpdateSignalsInBuffer();
+ if (_extra_aspects > 0) FlushDeferredAspectUpdates();
return_dcpi(res2);
}
diff --git a/src/infrastructure.cpp b/src/infrastructure.cpp
index f3948e54f0..f29c74880a 100644
--- a/src/infrastructure.cpp
+++ b/src/infrastructure.cpp
@@ -350,8 +350,17 @@ void UpdateAllBlockSignals(Owner owner)
} while (bits != TRACK_BIT_NONE);
} else if (IsLevelCrossingTile(tile) && (owner == INVALID_OWNER || GetTileOwner(tile) == owner)) {
UpdateLevelCrossing(tile);
+ } else if (IsTunnelBridgeWithSignalSimulation(tile)) {
+ if (IsTunnelBridgeSignalSimulationExit(tile)) {
+ AddSideToSignalBuffer(tile, INVALID_DIAGDIR, GetTileOwner(tile));
+ }
+ if (_extra_aspects > 0 && IsTunnelBridgeSignalSimulationEntrance(tile) && GetTunnelBridgeEntranceSignalState(tile) == SIGNAL_STATE_GREEN) {
+ SetTunnelBridgeEntranceSignalAspect(tile, 0);
+ UpdateAspectDeferred(tile, GetTunnelBridgeEntranceTrackdir(tile));
+ }
}
} while (++tile != MapSize());
UpdateSignalsInBuffer();
+ FlushDeferredAspectUpdates();
}
diff --git a/src/misc.cpp b/src/misc.cpp
index 249eaaad9e..58eb04cb0d 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -95,6 +95,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
_game_load_tick_skip_counter = 0;
_game_load_time = 0;
_extra_station_names_used = 0;
+ _extra_aspects = 0;
_loadgame_DBGL_data.clear();
if (reset_settings) MakeNewgameSettingsLive();
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index aba7b06539..ce5a55e54f 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -4088,6 +4088,11 @@ static ChangeInfoResult SignalsChangeInfo(uint id, int numinfo, int prop, const
SB(_cur.grffile->new_signal_ctrl_flags, NSCF_RECOLOUR_ENABLED, 1, (buf->ReadByte() != 0 ? 1 : 0));
break;
+ case A0RPI_SIGNALS_EXTRA_ASPECTS:
+ if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
+ _cur.grffile->new_signal_extra_aspects = std::min(buf->ReadByte(), NEW_SIGNALS_MAX_EXTRA_ASPECT);
+ break;
+
default:
ret = HandleAction0PropertyDefault(buf, prop);
break;
@@ -4426,6 +4431,11 @@ static ChangeInfoResult RailTypeChangeInfo(uint id, int numinfo, int prop, const
SB(rti->ctrl_flags, RTCF_RECOLOUR_ENABLED, 1, (buf->ReadByte() != 0 ? 1 : 0));
break;
+ case A0RPI_RAILTYPE_EXTRA_ASPECTS:
+ if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
+ rti->signal_extra_aspects = std::min(buf->ReadByte(), NEW_SIGNALS_MAX_EXTRA_ASPECT);
+ break;
+
default:
ret = HandleAction0PropertyDefault(buf, prop);
break;
@@ -4510,6 +4520,7 @@ static ChangeInfoResult RailTypeReserveInfo(uint id, int numinfo, int prop, cons
case A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS:
case A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING:
case A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR:
+ case A0RPI_RAILTYPE_EXTRA_ASPECTS:
buf->Skip(buf->ReadExtendedByte());
break;
@@ -8480,11 +8491,13 @@ static const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("action0_railtype_restricted_signals", 1),
GRFFeatureInfo("action0_railtype_disable_realistic_braking", 1),
GRFFeatureInfo("action0_railtype_recolour", 1),
+ GRFFeatureInfo("action0_railtype_extra_aspects", 1),
GRFFeatureInfo("action0_roadtype_extra_flags", 1),
GRFFeatureInfo("action0_global_extra_station_names", 1),
GRFFeatureInfo("action0_signals_programmable_signals", 1),
GRFFeatureInfo("action0_signals_restricted_signals", 1),
GRFFeatureInfo("action0_signals_recolour", 1),
+ GRFFeatureInfo("action0_signals_extra_aspects", 1),
GRFFeatureInfo("action3_signals_custom_signal_sprites", 1),
GRFFeatureInfo(),
};
@@ -8606,12 +8619,14 @@ static const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = {
GRFPropertyMapDefinition(GSF_RAILTYPES, A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS, "railtype_enable_restricted_signals"),
GRFPropertyMapDefinition(GSF_RAILTYPES, A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING, "railtype_disable_realistic_braking"),
GRFPropertyMapDefinition(GSF_RAILTYPES, A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR, "railtype_enable_signal_recolour"),
+ GRFPropertyMapDefinition(GSF_RAILTYPES, A0RPI_RAILTYPE_EXTRA_ASPECTS, "railtype_extra_aspects"),
GRFPropertyMapDefinition(GSF_ROADTYPES, A0RPI_ROADTYPE_EXTRA_FLAGS, "roadtype_extra_flags"),
GRFPropertyMapDefinition(GSF_TRAMTYPES, A0RPI_ROADTYPE_EXTRA_FLAGS, "roadtype_extra_flags"),
GRFPropertyMapDefinition(GSF_GLOBALVAR, A0RPI_GLOBALVAR_EXTRA_STATION_NAMES, "global_extra_station_names"),
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_ENABLE_PROGRAMMABLE_SIGNALS, "signals_enable_programmable_signals"),
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_ENABLE_RESTRICTED_SIGNALS, "signals_enable_restricted_signals"),
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR, "signals_enable_signal_recolour"),
+ GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_EXTRA_ASPECTS, "signals_extra_aspects"),
GRFPropertyMapDefinition(),
};
@@ -9409,6 +9424,7 @@ GRFFile::GRFFile(const GRFConfig *config)
this->new_signals_group = nullptr;
this->new_signal_ctrl_flags = 0;
+ this->new_signal_extra_aspects = 0;
/* Mark price_base_multipliers as 'not set' */
for (Price i = PR_BEGIN; i < PR_END; i++) {
diff --git a/src/newgrf.h b/src/newgrf.h
index f00696337e..1c1f26b6b9 100644
--- a/src/newgrf.h
+++ b/src/newgrf.h
@@ -119,11 +119,13 @@ enum Action0RemapPropertyIds {
A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS,
A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING,
A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR,
+ A0RPI_RAILTYPE_EXTRA_ASPECTS,
A0RPI_ROADTYPE_EXTRA_FLAGS,
A0RPI_GLOBALVAR_EXTRA_STATION_NAMES,
A0RPI_SIGNALS_ENABLE_PROGRAMMABLE_SIGNALS,
A0RPI_SIGNALS_ENABLE_RESTRICTED_SIGNALS,
A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR,
+ A0RPI_SIGNALS_EXTRA_ASPECTS,
};
enum GRFPropertyMapFallbackMode {
@@ -227,6 +229,10 @@ enum NewSignalCtrlFlags {
NSCF_RECOLOUR_ENABLED = 3, ///< Recolour sprites enabled
};
+enum {
+ NEW_SIGNALS_MAX_EXTRA_ASPECT = 6,
+};
+
/** New signal control flags. */
enum NewSignalAction3ID {
NSA3ID_CUSTOM_SIGNALS = 0, ///< Action 3 ID for custom signal sprites
@@ -285,6 +291,7 @@ struct GRFFile : ZeroedMemoryAllocator {
const SpriteGroup *new_signals_group; ///< New signals sprite group
byte new_signal_ctrl_flags; ///< Ctrl flags for new signals
+ byte new_signal_extra_aspects; ///< Number of extra aspects for new signals
GRFFile(const struct GRFConfig *config);
~GRFFile();
diff --git a/src/newgrf_railtype.cpp b/src/newgrf_railtype.cpp
index 71f07b739f..50593c6fea 100644
--- a/src/newgrf_railtype.cpp
+++ b/src/newgrf_railtype.cpp
@@ -115,13 +115,21 @@ SpriteID GetCustomRailSprite(const RailtypeInfo *rti, TileIndex tile, RailTypeSp
return group->GetResult();
}
-static PalSpriteID GetRailTypeCustomSignalSprite(const RailtypeInfo *rti, TileIndex tile, SignalType type, SignalVariant var, SignalState state, bool gui, bool restricted)
+inline uint8 RemapAspect(uint8 aspect, uint8 extra_aspects)
+{
+ if (likely(extra_aspects == 0 || _extra_aspects == 0)) return std::min(aspect, 1);
+ if (aspect == 0) return 0;
+ if (aspect >= extra_aspects + 1) return 1;
+ return aspect + 1;
+}
+
+static PalSpriteID GetRailTypeCustomSignalSprite(const RailtypeInfo *rti, TileIndex tile, SignalType type, SignalVariant var, uint8 aspect, bool gui, bool restricted)
{
if (rti->group[RTSG_SIGNALS] == nullptr) return { 0, PAL_NONE };
if (type == SIGTYPE_PROG && !HasBit(rti->ctrl_flags, RTCF_PROGSIG)) return { 0, PAL_NONE };
uint32 param1 = gui ? 0x10 : 0x00;
- uint32 param2 = (type << 16) | (var << 8) | state;
+ uint32 param2 = (type << 16) | (var << 8) | RemapAspect(aspect, rti->signal_extra_aspects);
if (restricted && HasBit(rti->ctrl_flags, RTCF_RESTRICTEDSIG)) SetBit(param2, 24);
RailTypeResolverObject object(rti, tile, TCX_NORMAL, RTSG_SIGNALS, param1, param2);
@@ -142,16 +150,16 @@ static PalSpriteID GetRailTypeCustomSignalSprite(const RailtypeInfo *rti, TileIn
* @param gui Is the sprite being used on the map or in the GUI?
* @return The sprite to draw.
*/
-CustomSignalSpriteResult GetCustomSignalSprite(const RailtypeInfo *rti, TileIndex tile, SignalType type, SignalVariant var, SignalState state, bool gui, bool restricted)
+CustomSignalSpriteResult GetCustomSignalSprite(const RailtypeInfo *rti, TileIndex tile, SignalType type, SignalVariant var, uint8 aspect, bool gui, bool restricted)
{
- PalSpriteID spr = GetRailTypeCustomSignalSprite(rti, tile, type, var, state, gui, restricted);
+ PalSpriteID spr = GetRailTypeCustomSignalSprite(rti, tile, type, var, aspect, gui, restricted);
if (spr.sprite != 0) return { spr, HasBit(rti->ctrl_flags, RTCF_PROGSIG) };
for (const GRFFile *grf : _new_signals_grfs) {
if (type == SIGTYPE_PROG && !HasBit(grf->new_signal_ctrl_flags, NSCF_PROGSIG)) continue;
uint32 param1 = gui ? 0x10 : 0x00;
- uint32 param2 = (type << 16) | (var << 8) | state;
+ uint32 param2 = (type << 16) | (var << 8) | RemapAspect(aspect, grf->new_signal_extra_aspects);
if (restricted && HasBit(grf->new_signal_ctrl_flags, NSCF_RESTRICTEDSIG)) SetBit(param2, 24);
NewSignalsResolverObject object(grf, tile, TCX_NORMAL, param1, param2);
diff --git a/src/newgrf_railtype.h b/src/newgrf_railtype.h
index 6c478c4590..8b8eb63e23 100644
--- a/src/newgrf_railtype.h
+++ b/src/newgrf_railtype.h
@@ -61,7 +61,7 @@ struct CustomSignalSpriteResult {
};
SpriteID GetCustomRailSprite(const RailtypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context = TCX_NORMAL, uint *num_results = nullptr);
-CustomSignalSpriteResult GetCustomSignalSprite(const RailtypeInfo *rti, TileIndex tile, SignalType type, SignalVariant var, SignalState state, bool gui = false, bool restricted = false);
+CustomSignalSpriteResult GetCustomSignalSprite(const RailtypeInfo *rti, TileIndex tile, SignalType type, SignalVariant var, uint8 aspect, bool gui = false, bool restricted = false);
RailType GetRailTypeTranslation(uint8 railtype, const GRFFile *grffile);
uint8 GetReverseRailTypeTranslation(RailType railtype, const GRFFile *grffile);
diff --git a/src/openttd.cpp b/src/openttd.cpp
index 87f1434f99..d9f3536a69 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -465,6 +465,7 @@ static void ShutdownGame()
_game_load_tick_skip_counter = 0;
_game_load_time = 0;
_extra_station_names_used = 0;
+ _extra_aspects = 0;
_loadgame_DBGL_data.clear();
_loadgame_DBGC_data.clear();
}
@@ -1860,6 +1861,7 @@ void StateGameLoop()
UpdateStateChecksum(c->money);
}
}
+ if (_extra_aspects > 0) FlushDeferredAspectUpdates();
assert(IsLocalCompany());
}
diff --git a/src/pbs.cpp b/src/pbs.cpp
index 9ba7ad2cbb..5c5cf70f61 100644
--- a/src/pbs.cpp
+++ b/src/pbs.cpp
@@ -85,6 +85,10 @@ bool TryReserveRailTrackdir(TileIndex tile, Trackdir td, bool trigger_stations)
if (success && HasPbsSignalOnTrackdir(tile, td)) {
SetSignalStateByTrackdir(tile, td, SIGNAL_STATE_GREEN);
MarkSingleSignalDirty(tile, td);
+ if (_extra_aspects > 0) {
+ SetSignalAspect(tile, TrackdirToTrack(td), 0);
+ UpdateAspectDeferred(tile, td);
+ }
}
return success;
}
@@ -240,6 +244,7 @@ void UnreserveRailTrack(TileIndex tile, Track t)
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgeEffectivelyPBS(tile) && IsTrackAcrossTunnelBridge(tile, t)) {
if (IsTunnelBridgePBS(tile)) {
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
+ if (_extra_aspects > 0) PropagateAspectChange(tile, GetTunnelBridgeExitTrackdir(tile), 0);
} else {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, GetTileOwner(tile));
}
diff --git a/src/rail.h b/src/rail.h
index 33a7c2feb5..b3689b37ff 100644
--- a/src/rail.h
+++ b/src/rail.h
@@ -224,6 +224,11 @@ public:
*/
byte ctrl_flags;
+ /**
+ * Signal extra aspects
+ */
+ uint8 signal_extra_aspects;
+
/**
* Cost multiplier for building this rail type
*/
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index a4a82c8bfd..640c4b59f9 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -70,7 +70,7 @@ void ResetRailTypes()
{0,0,0,0,0,0,0,0,{}},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0},
- 0, RAILTYPES_NONE, RAILTYPES_NONE, RAILTYPES_NONE, 0, 0, 0, RTFB_NONE, 0, 0, 0, 0, 0, 0,
+ 0, RAILTYPES_NONE, RAILTYPES_NONE, RAILTYPES_NONE, 0, 0, 0, RTFB_NONE, 0, 0, 0, 0, 0, 0, 0,
RailTypeLabelList(), 0, 0, RAILTYPES_NONE, RAILTYPES_NONE, 0,
{}, {} };
for (; i < lengthof(_railtypes); i++) _railtypes[i] = empty_railtype;
@@ -113,8 +113,8 @@ void ResolveRailTypeGUISprites(RailtypeInfo *rti)
for (SignalType type = SIGTYPE_NORMAL; type < SIGTYPE_END; type = (SignalType)(type + 1)) {
for (SignalVariant var = SIG_ELECTRIC; var <= SIG_SEMAPHORE; var = (SignalVariant)(var + 1)) {
- PalSpriteID red = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_RED, true).sprite;
- PalSpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_GREEN, true).sprite;
+ PalSpriteID red = GetCustomSignalSprite(rti, INVALID_TILE, type, var, 0, true).sprite;
+ PalSpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, 255, true).sprite;
if (red.sprite != 0) {
rti->gui_sprites.signals[type][var][0] = { red.sprite + SIGNAL_TO_SOUTH, red.pal };
} else {
@@ -1392,6 +1392,10 @@ static void SetupBridgeTunnelSignalSimulation(TileIndex entrance, TileIndex exit
SetTunnelBridgeSignalSimulationEntrance(entrance);
SetTunnelBridgeEntranceSignalState(entrance, SIGNAL_STATE_GREEN);
SetTunnelBridgeSignalSimulationExit(exit);
+ if (_extra_aspects > 0) {
+ SetTunnelBridgeEntranceSignalAspect(entrance, 0);
+ UpdateAspectDeferred(entrance, GetTunnelBridgeEntranceTrackdir(entrance));
+ }
}
static void ReReserveTrainPath(Train *v)
@@ -1504,6 +1508,10 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
SetTunnelBridgeSignalSimulationEntrance(t);
SetTunnelBridgeEntranceSignalState(t, SIGNAL_STATE_GREEN);
SetTunnelBridgeSignalSimulationExit(t);
+ if (_extra_aspects > 0) {
+ SetTunnelBridgeEntranceSignalAspect(t, 0);
+ UpdateAspectDeferred(t, GetTunnelBridgeEntranceTrackdir(t));
+ }
};
if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC) {
@@ -2673,12 +2681,27 @@ static const int SIGNAL_DIRTY_RIGHT = 14 * ZOOM_LVL_BASE;
static const int SIGNAL_DIRTY_TOP = 30 * ZOOM_LVL_BASE;
static const int SIGNAL_DIRTY_BOTTOM = 5 * ZOOM_LVL_BASE;
-void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos, SignalType type, SignalVariant variant, bool show_restricted)
+void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos, SignalType type,
+ SignalVariant variant, bool show_restricted, bool exit_signal = false)
{
uint x, y;
GetSignalXY(tile, pos, x, y);
- const CustomSignalSpriteResult result = GetCustomSignalSprite(rti, tile, type, variant, condition, false, show_restricted);
+ uint8 aspect;
+ if (condition == SIGNAL_STATE_GREEN) {
+ aspect = 1;
+ if (_extra_aspects > 0) {
+ if (IsPlainRailTile(tile)) {
+ aspect = GetSignalAspect(tile, track);
+ } else if (IsTunnelBridgeWithSignalSimulation(tile)) {
+ aspect = exit_signal? GetTunnelBridgeExitSignalAspect(tile) : GetTunnelBridgeEntranceSignalAspect(tile);
+ }
+ }
+ } else {
+ aspect = 0;
+ }
+
+ const CustomSignalSpriteResult result = GetCustomSignalSprite(rti, tile, type, variant, aspect, false, show_restricted);
SpriteID sprite = result.sprite.sprite;
PaletteID pal = PAL_NONE;
bool is_custom_sprite = (sprite != 0);
diff --git a/src/rail_map.h b/src/rail_map.h
index 83b427a42e..fd96e8d961 100644
--- a/src/rail_map.h
+++ b/src/rail_map.h
@@ -352,7 +352,7 @@ static inline bool IsPresignalProgrammable(TileIndex t, Track track)
/** One-way signals can't be passed the 'wrong' way. */
static inline bool IsOnewaySignal(TileIndex t, Track track)
{
- return GetSignalType(t, track) != SIGTYPE_PBS;
+ return IsOnewaySignal(GetSignalType(t, track));
}
static inline void CycleSignalSide(TileIndex t, Track track)
@@ -378,6 +378,20 @@ static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v)
if (track == INVALID_TRACK) SB(_m[t].m2, 7, 1, v);
}
+static inline uint8 GetSignalAspect(TileIndex t, Track track)
+{
+ assert_tile(GetRailTileType(t) == RAIL_TILE_SIGNALS, t);
+ byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 3 : 0;
+ return GB(_me[t].m7, pos, 3);
+}
+
+static inline void SetSignalAspect(TileIndex t, Track track, uint8 aspect)
+{
+ assert_tile(GetRailTileType(t) == RAIL_TILE_SIGNALS, t);
+ byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 3 : 0;
+ SB(_me[t].m7, pos, 3, aspect);
+}
+
/**
* Set the states of the signals (Along/AgainstTrackDir)
* @param tile the tile to set the states for
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index a78beefaf8..a10485da66 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -3976,6 +3976,8 @@ bool AfterLoadGame()
extern void YapfCheckRailSignalPenalties();
YapfCheckRailSignalPenalties();
+ UpdateExtraAspectsVariable();
+
if (_networking && !_network_server) {
SlProcessVENC();
}
@@ -4050,6 +4052,8 @@ void ReloadNewGRFData()
}
}
+ UpdateExtraAspectsVariable();
+
/* Update company statistics. */
AfterLoadCompanyStats();
/* Check and update house and town values */
diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index 5ade36cb14..8cfad01739 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -146,7 +146,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_ANIMATED_TILE_EXTRA, XSCF_NULL, 1, 1, "animated_tile_extra", nullptr, nullptr, nullptr },
{ XSLFI_NEWGRF_INFO_EXTRA, XSCF_NULL, 1, 1, "newgrf_info_extra", nullptr, nullptr, nullptr },
{ XSLFI_INDUSTRY_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 1, 1, "industry_cargo_adj", nullptr, nullptr, nullptr },
- { XSLFI_REALISTIC_TRAIN_BRAKING,XSCF_NULL, 3, 3, "realistic_train_braking", nullptr, nullptr, "VLKA" },
+ { XSLFI_REALISTIC_TRAIN_BRAKING,XSCF_NULL, 4, 4, "realistic_train_braking", nullptr, nullptr, "VLKA" },
{ XSLFI_INFLATION_FIXED_DATES, XSCF_IGNORABLE_ALL, 1, 1, "inflation_fixed_dates", nullptr, nullptr, nullptr },
{ XSLFI_WATER_FLOODING, XSCF_NULL, 2, 2, "water_flooding", nullptr, nullptr, nullptr },
{ XSLFI_MORE_HOUSES, XSCF_NULL, 2, 2, "more_houses", nullptr, nullptr, nullptr },
diff --git a/src/saveload/misc_sl.cpp b/src/saveload/misc_sl.cpp
index 29a5c80835..af7e8c4207 100644
--- a/src/saveload/misc_sl.cpp
+++ b/src/saveload/misc_sl.cpp
@@ -26,6 +26,7 @@
extern TileIndex _cur_tileloop_tile;
extern uint16 _disaster_delay;
extern byte _trees_tick_ctr;
+extern uint8 _extra_aspects;
/* Keep track of current game position */
int _saved_scrollpos_x;
@@ -95,6 +96,7 @@ static const SaveLoadGlobVarList _date_desc[] = {
SLEG_CONDVAR(_pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION),
SLEG_CONDVAR_X(_game_events_overall, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)),
SLEG_CONDVAR_X(_road_layout_change_counter, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)),
+ SLEG_CONDVAR_X(_extra_aspects, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 4)),
SLE_CONDNULL(4, SLV_11, SLV_120),
SLEG_END()
};
@@ -124,6 +126,7 @@ static const SaveLoadGlobVarList _date_check_desc[] = {
SLE_CONDNULL(1, SLV_4, SL_MAX_VERSION), // _pause_mode
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)), // _game_events_overall
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), // _road_layout_change_counter
+ SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 4)), // _extra_aspects
SLE_CONDNULL(4, SLV_11, SLV_120),
SLEG_END()
};
diff --git a/src/settings.cpp b/src/settings.cpp
index 6609d7679a..e9ce4ef8dd 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1199,6 +1199,7 @@ static bool TrainBrakingModelChanged(int32 p1)
}
}
+ UpdateExtraAspectsVariable();
UpdateAllBlockSignals();
InvalidateWindowData(WC_BUILD_SIGNAL, 0);
diff --git a/src/signal.cpp b/src/signal.cpp
index 34968059de..db3bb993f0 100644
--- a/src/signal.cpp
+++ b/src/signal.cpp
@@ -20,9 +20,14 @@
#include "programmable_signals.h"
#include "error.h"
#include "infrastructure_func.h"
+#include "tunnelbridge.h"
+#include "bridge_signal_map.h"
+#include "newgrf_newsignals.h"
#include "safeguards.h"
+uint8 _extra_aspects = 0;
+
/// List of signals dependent upon this one
typedef std::vector SignalDependencyList;
@@ -197,6 +202,7 @@ public:
};
static SmallSet _tbuset("_tbuset"); ///< set of signals that will be updated
+static SmallSet _tbpset("_tbpset"); ///< set of PBS signals to update the aspect of
static SmallSet _tbdset("_tbdset"); ///< set of open nodes in current signal block
static SmallSet _globset("_globset"); ///< set of places to be updated in following runs
@@ -285,10 +291,14 @@ struct SigInfo {
flags = SF_NONE;
num_exits = 0;
num_green = 0;
+ out_signal_tile = INVALID_TILE;
+ out_signal_trackdir = INVALID_TRACKDIR;
}
SigFlags flags;
uint num_exits;
uint num_green;
+ TileIndex out_signal_tile;
+ Trackdir out_signal_trackdir;
};
/**
@@ -354,18 +364,28 @@ static SigInfo ExploreSegment(Owner owner)
if (HasSignalOnTrackdir(tile, reversedir)) {
if (IsPbsSignalNonExtended(sig)) {
info.flags |= SF_PBS;
+ if (_extra_aspects > 0 && GetSignalStateByTrackdir(tile, reversedir) == SIGNAL_STATE_GREEN) {
+ _tbpset.Add(tile, reversedir);
+ }
} else if (!_tbuset.Add(tile, reversedir)) {
info.flags |= SF_FULL;
return info;
}
}
- if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) info.flags |= SF_PBS;
- /* if it is a presignal EXIT in OUR direction, count it */
- if (IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
- info.num_exits++;
- if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit
- info.num_green++;
+ if (HasSignalOnTrackdir(tile, trackdir)) {
+ if (!IsOnewaySignal(sig)) info.flags |= SF_PBS;
+ if (_extra_aspects > 0) {
+ info.out_signal_tile = tile;
+ info.out_signal_trackdir = trackdir;
+ }
+
+ /* if it is a presignal EXIT in OUR direction, count it */
+ if (IsExitSignal(sig)) { // found presignal exit
+ info.num_exits++;
+ if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit
+ info.num_green++;
+ }
}
}
@@ -455,11 +475,19 @@ static SigInfo ExploreSegment(Owner owner)
if (IsTunnelBridgeSignalSimulationExit(tile)) {
if (IsTunnelBridgePBS(tile)) {
info.flags |= SF_PBS;
+ if (_extra_aspects > 0 && GetTunnelBridgeExitSignalState(tile) == SIGNAL_STATE_GREEN) {
+ Trackdir exit_td = GetTunnelBridgeExitTrackdir(tile, tunnel_bridge_dir);
+ _tbpset.Add(tile, exit_td);
+ }
} else if (!_tbuset.Add(tile, INVALID_TRACKDIR)) {
info.flags |= SF_FULL;
return info;
}
}
+ if (_extra_aspects > 0 && IsTunnelBridgeSignalSimulationEntrance(tile)) {
+ info.out_signal_tile = tile;
+ info.out_signal_trackdir = GetTunnelBridgeEntranceTrackdir(tile, tunnel_bridge_dir);
+ }
if (!(info.flags & SF_TRAIN)) {
if (HasVehicleOnPos(tile, VEH_TRAIN, reinterpret_cast((uintptr_t)tile), &TrainInWormholeTileEnum)) info.flags |= SF_TRAIN;
if (!(info.flags & SF_TRAIN) && IsTunnelBridgeSignalSimulationExit(tile)) {
@@ -510,6 +538,162 @@ static SigInfo ExploreSegment(Owner owner)
return info;
}
+static uint8 GetSignalledTunnelBridgeEntranceForwardAspect(TileIndex tile, TileIndex tile_exit)
+{
+ if (!IsTunnelBridgeSignalSimulationEntrance(tile)) return 0;
+ const uint spacing = GetTunnelBridgeSignalSimulationSpacing(tile);
+ const uint signal_count = GetTunnelBridgeLength(tile, tile_exit) / spacing;
+ if (IsBridge(tile)) {
+ uint8 aspect = 0;
+ for (uint i = 0; i < signal_count; i++) {
+ if (GetBridgeEntranceSimulatedSignalState(tile, i) == SIGNAL_STATE_GREEN) {
+ aspect++;
+ } else {
+ return std::min(aspect, _extra_aspects + 1);
+ }
+ }
+ if (GetTunnelBridgeExitSignalState(tile_exit) == SIGNAL_STATE_GREEN) aspect += GetTunnelBridgeExitSignalAspect(tile_exit);
+ return std::min(aspect, _extra_aspects + 1);
+ } else {
+ int free_tiles = GetAvailableFreeTilesInSignalledTunnelBridge(tile, tile_exit, tile);
+ if (free_tiles == INT_MAX) {
+ uint aspect = signal_count;
+ if (GetTunnelBridgeExitSignalState(tile_exit) == SIGNAL_STATE_GREEN) aspect += GetTunnelBridgeExitSignalAspect(tile_exit);
+ return std::min(aspect, _extra_aspects + 1);
+ } else {
+ if (free_tiles < (int)spacing) return 0;
+ return std::min((free_tiles / spacing) - 1, _extra_aspects + 1);
+ }
+ }
+}
+
+uint8 GetForwardAspectFollowingTrack(TileIndex tile, Trackdir trackdir)
+{
+ Owner owner = GetTileOwner(tile);
+ DiagDirection exitdir = TrackdirToExitdir(trackdir);
+ DiagDirection enterdir = ReverseDiagDir(exitdir);
+ if (IsTileType(tile, MP_TUNNELBRIDGE) && TrackdirEntersTunnelBridge(tile, trackdir)) {
+ TileIndex other = GetOtherTunnelBridgeEnd(tile);
+ if (IsTunnelBridgeWithSignalSimulation(tile)) {
+ return GetSignalledTunnelBridgeEntranceForwardAspect(tile, other);
+ }
+ tile = other;
+ } else {
+ tile += TileOffsByDiagDir(exitdir);
+ }
+ while (true) {
+ switch (GetTileType(tile)) {
+ case MP_RAILWAY: {
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) return 0;
+
+ if (IsRailDepot(tile)) {
+ return 0;
+ }
+
+ TrackBits tracks = GetTrackBits(tile); // trackbits of tile
+ TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]); // only incidating trackbits
+
+ if (tracks_masked == TRACK_BIT_NONE) return 0; // no incidating track
+ if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) tracks = tracks_masked;
+
+ if (!HasAtMostOneBit(tracks)) {
+ TrackBits reserved_bits = GetRailReservationTrackBits(tile) & tracks_masked;
+ if (reserved_bits == TRACK_BIT_NONE) return 0; // no reservation on junction
+ tracks = reserved_bits;
+ }
+
+ Track track = (Track)FIND_FIRST_BIT(tracks);
+ trackdir = TrackEnterdirToTrackdir(track, ReverseDiagDir(enterdir));
+
+ if (HasSignals(tile)) {
+ if (HasSignalOnTrack(tile, track)) { // now check whole track, not trackdir
+ if (HasSignalOnTrackdir(tile, trackdir)) {
+ if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_RED) return 0;
+ return GetSignalAspect(tile, track);
+ } else if (IsOnewaySignal(tile, track)) {
+ return 0; // one-way signal facing the wrong way
+ }
+ }
+ }
+
+ exitdir = TrackdirToExitdir(trackdir);
+ enterdir = ReverseDiagDir(exitdir);
+ tile += TileOffsByDiagDir(exitdir);
+
+ break;
+ }
+
+ case MP_STATION:
+ if (!HasStationRail(tile)) return 0;
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) return 0;
+ if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) return 0; // different axis
+ if (IsStationTileBlocked(tile)) return 0; // 'eye-candy' station tile
+
+ tile += TileOffsByDiagDir(exitdir);
+ break;
+
+ case MP_ROAD:
+ if (!IsLevelCrossing(tile)) return 0;
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) return 0;
+ if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) return 0; // different axis
+
+ tile += TileOffsByDiagDir(exitdir);
+ break;
+
+ case MP_TUNNELBRIDGE: {
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) return 0;
+ if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return 0;
+
+ TrackBits tracks = GetTunnelBridgeTrackBits(tile); // trackbits of tile
+ TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]); // only incidating trackbits
+
+ if (tracks_masked == TRACK_BIT_NONE) return 0; // no incidating track
+ if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) tracks = tracks_masked;
+
+ if (!HasAtMostOneBit(tracks)) {
+ TrackBits reserved_bits = GetTunnelBridgeReservationTrackBits(tile) & tracks_masked;
+ if (reserved_bits == TRACK_BIT_NONE) return 0; // no reservation on junction
+ tracks = reserved_bits;
+ }
+
+ Track track = (Track)FIND_FIRST_BIT(tracks);
+ trackdir = TrackEnterdirToTrackdir(track, ReverseDiagDir(enterdir));
+
+ if (IsTunnelBridgeWithSignalSimulation(tile) && HasTrack(GetAcrossTunnelBridgeTrackBits(tile), track)) {
+ return GetSignalAspectGeneric(tile, trackdir);
+ }
+
+ if (TrackdirEntersTunnelBridge(tile, trackdir)) {
+ tile = GetOtherTunnelBridgeEnd(tile);
+ enterdir = GetTunnelBridgeDirection(tile);
+ exitdir = ReverseDiagDir(enterdir);
+ } else {
+ exitdir = TrackdirToExitdir(trackdir);
+ enterdir = ReverseDiagDir(exitdir);
+ tile += TileOffsByDiagDir(exitdir);
+ }
+ break;
+ }
+
+ default:
+ return 0;
+ }
+ }
+}
+
+static uint8 GetForwardAspect(const SigInfo &info, TileIndex tile, Trackdir trackdir)
+{
+ if (info.flags & SF_JUNCTION) {
+ return GetForwardAspectFollowingTrack(tile, trackdir);
+ } else {
+ return (info.out_signal_tile != INVALID_TILE) ? GetSignalAspectGeneric(info.out_signal_tile, info.out_signal_trackdir) : 0;
+ }
+}
+
+static uint8 GetForwardAspectAndIncrement(const SigInfo &info, TileIndex tile, Trackdir trackdir)
+{
+ return std::min(GetForwardAspect(info, tile, trackdir) + 1, _extra_aspects + 1);
+}
/**
* Update signals around segment in _tbuset
@@ -529,13 +713,42 @@ static void UpdateSignalsAroundSegment(SigInfo info)
while (_tbuset.Get(&tile, &trackdir)) {
if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExit(tile)) {
- if (IsTunnelBridgePBS(tile) || (_settings_game.vehicle.train_braking_model == TBM_REALISTIC && HasAcrossTunnelBridgeReservation(tile))) continue;
+ if (IsTunnelBridgePBS(tile) || (_settings_game.vehicle.train_braking_model == TBM_REALISTIC && HasAcrossTunnelBridgeReservation(tile))) {
+ if (_extra_aspects > 0 && GetTunnelBridgeExitSignalState(tile) == SIGNAL_STATE_GREEN) {
+ Trackdir exit_td = GetTunnelBridgeExitTrackdir(tile);
+ uint8 aspect = GetForwardAspectAndIncrement(info, tile, exit_td);
+ if (aspect != GetTunnelBridgeExitSignalAspect(tile)) {
+ SetTunnelBridgeExitSignalAspect(tile, aspect);
+ MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ PropagateAspectChange(tile, exit_td, aspect);
+ }
+ }
+ continue;
+ }
SignalState old_state = GetTunnelBridgeExitSignalState(tile);
SignalState new_state = (info.flags & SF_TRAIN) ? SIGNAL_STATE_RED : SIGNAL_STATE_GREEN;
+ bool refresh = false;
if (old_state != new_state) {
SetTunnelBridgeExitSignalState(tile, new_state);
- MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ refresh = true;
+ }
+ if (_extra_aspects > 0) {
+ const uint8 current_aspect = (old_state == SIGNAL_STATE_GREEN) ? GetTunnelBridgeExitSignalAspect(tile) : 0;
+ uint8 aspect;
+ if (new_state == SIGNAL_STATE_GREEN) {
+ aspect = GetForwardAspectAndIncrement(info, tile, trackdir);
+ } else {
+ aspect = 0;
+ }
+ if (aspect != current_aspect || old_state != new_state) {
+ if (new_state == SIGNAL_STATE_GREEN) SetTunnelBridgeExitSignalAspect(tile, aspect);
+ refresh = true;
+ Trackdir exit_td = GetTunnelBridgeExitTrackdir(tile);
+ PropagateAspectChange(tile, exit_td, aspect);
+ }
}
+ if (refresh) MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+
continue;
}
@@ -546,7 +759,18 @@ static void UpdateSignalsAroundSegment(SigInfo info)
SignalState newstate = SIGNAL_STATE_GREEN;
/* don't change signal state if tile is reserved in realistic braking mode */
- if ((_settings_game.vehicle.train_braking_model == TBM_REALISTIC && HasBit(GetRailReservationTrackBits(tile), track))) continue;
+ if ((_settings_game.vehicle.train_braking_model == TBM_REALISTIC && HasBit(GetRailReservationTrackBits(tile), track))) {
+ if (_extra_aspects > 0 && GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) {
+ uint8 aspect = GetForwardAspectAndIncrement(info, tile, trackdir);
+ uint8 old_aspect = GetSignalAspect(tile, track);
+ if (aspect != old_aspect) {
+ SetSignalAspect(tile, track, aspect);
+ if (old_aspect != 0) MarkSingleSignalDirty(tile, trackdir);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ }
+ continue;
+ }
/* determine whether the new state is red */
if (info.flags & SF_TRAIN) {
@@ -587,8 +811,29 @@ static void UpdateSignalsAroundSegment(SigInfo info)
}
}
+ bool refresh = false;
+ const SignalState current_state = GetSignalStateByTrackdir(tile, trackdir);
+
+ if (_extra_aspects > 0) {
+ const uint8 current_aspect = (current_state == SIGNAL_STATE_GREEN) ? GetSignalAspect(tile, track) : 0;
+ uint8 aspect;
+ if (newstate == SIGNAL_STATE_GREEN) {
+ aspect = 1;
+ if (info.out_signal_tile != INVALID_TILE) {
+ aspect = std::min(GetSignalAspectGeneric(info.out_signal_tile, info.out_signal_trackdir) + 1, _extra_aspects + 1);
+ }
+ } else {
+ aspect = 0;
+ }
+ if (aspect != current_aspect || newstate != current_state) {
+ SetSignalAspect(tile, track, aspect);
+ refresh = true;
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ }
+
/* only when the state changes */
- if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
+ if (newstate != current_state) {
if (IsExitSignal(sig)) {
/* for pre-signal exits, add block to the global set */
DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
@@ -598,10 +843,32 @@ static void UpdateSignalsAroundSegment(SigInfo info)
MarkDependencidesForUpdate(SignalReference(tile, track));
}
SetSignalStateByTrackdir(tile, trackdir, newstate);
+ refresh = true;
+ }
+ if (refresh) {
MarkSingleSignalDirty(tile, trackdir);
}
}
+ while (_tbpset.Get(&tile, &trackdir)) {
+ uint8 aspect = GetForwardAspectAndIncrement(info, tile, trackdir);
+ if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+ uint8 old_aspect = GetTunnelBridgeExitSignalAspect(tile);
+ if (aspect != old_aspect) {
+ SetTunnelBridgeExitSignalAspect(tile, aspect);
+ if (old_aspect != 0) MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ } else {
+ uint8 old_aspect = GetSignalAspect(tile, track);
+ Track track = TrackdirToTrack(trackdir);
+ if (aspect != old_aspect) {
+ SetSignalAspect(tile, track, aspect);
+ if (old_aspect != 0) MarkSingleSignalDirty(tile, trackdir);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ }
+ }
}
@@ -609,6 +876,7 @@ static void UpdateSignalsAroundSegment(SigInfo info)
static inline void ResetSets()
{
_tbuset.Reset();
+ _tbpset.Reset();
_tbdset.Reset();
_globset.Reset();
}
@@ -943,3 +1211,277 @@ void CheckRemoveSignal(TileIndex tile, Track track)
_signal_dependencies.erase(i);
}
}
+
+uint8 GetSignalAspectGeneric(TileIndex tile, Trackdir trackdir)
+{
+ switch (GetTileType(tile)) {
+ case MP_RAILWAY:
+ if (HasSignalOnTrackdir(tile, trackdir) && GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) {
+ return GetSignalAspect(tile, TrackdirToTrack(trackdir));
+ }
+ break;
+
+ case MP_TUNNELBRIDGE:
+ if (IsTunnelBridgeSignalSimulationEntrance(tile) && TrackdirEntersTunnelBridge(tile, trackdir)) {
+ return (GetTunnelBridgeEntranceSignalState(tile) == SIGNAL_STATE_GREEN) ? GetTunnelBridgeEntranceSignalAspect(tile) : 0;
+ }
+ if (IsTunnelBridgeSignalSimulationExit(tile) && TrackdirExitsTunnelBridge(tile, trackdir)) {
+ return (GetTunnelBridgeExitSignalState(tile) == SIGNAL_STATE_GREEN) ? GetTunnelBridgeExitSignalAspect(tile) : 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void RefreshBridgeOnExitAspectChange(TileIndex entrance, TileIndex exit)
+{
+ const uint simulated_wormhole_signals = GetTunnelBridgeSignalSimulationSpacing(entrance);
+ const uint bridge_length = GetTunnelBridgeLength(entrance, exit);
+ const TileIndexDiffC offset = TileIndexDiffCByDiagDir(GetTunnelBridgeDirection(entrance));
+ const TileIndexDiff diff = TileDiffXY(offset.x * simulated_wormhole_signals, offset.y * simulated_wormhole_signals);
+ const uint signal_count = bridge_length / simulated_wormhole_signals;
+ TileIndex tile = entrance;
+ for (uint i = signal_count; i > 0; i--) {
+ tile += diff;
+ if (i <= 2) MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ }
+}
+
+void PropagateAspectChange(TileIndex tile, Trackdir trackdir, uint8 aspect)
+{
+ aspect = std::min(aspect + 1, _extra_aspects + 1);
+ Owner owner = GetTileOwner(tile);
+ DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
+ DiagDirection enterdir = ReverseDiagDir(exitdir);
+ if (IsTileType(tile, MP_TUNNELBRIDGE) && TrackdirExitsTunnelBridge(tile, trackdir)) {
+ TileIndex other = GetOtherTunnelBridgeEnd(tile);
+ if (IsBridge(tile)) RefreshBridgeOnExitAspectChange(other, tile);
+ aspect = std::min(GetSignalledTunnelBridgeEntranceForwardAspect(other, tile) + 1, _extra_aspects + 1);
+ tile = other;
+ } else {
+ tile += TileOffsByDiagDir(exitdir);
+ }
+ while (true) {
+ switch (GetTileType(tile)) {
+ case MP_RAILWAY: {
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) return;
+
+ if (IsRailDepot(tile)) {
+ return;
+ }
+
+ TrackBits tracks = GetTrackBits(tile); // trackbits of tile
+ TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]); // only incidating trackbits
+
+ if (tracks_masked == TRACK_BIT_NONE) return; // no incidating track
+ if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) tracks = tracks_masked;
+
+ if (!HasAtMostOneBit(tracks)) {
+ TrackBits reserved_bits = GetRailReservationTrackBits(tile) & tracks_masked;
+ if (reserved_bits == TRACK_BIT_NONE) return; // no reservation on junction
+ tracks = reserved_bits;
+ }
+
+ Track track = (Track)FIND_FIRST_BIT(tracks);
+ trackdir = TrackEnterdirToTrackdir(track, ReverseDiagDir(enterdir));
+
+ if (HasSignals(tile)) {
+ if (HasSignalOnTrack(tile, track)) { // now check whole track, not trackdir
+ Trackdir reversedir = ReverseTrackdir(trackdir);
+
+ if (HasSignalOnTrackdir(tile, reversedir)) {
+ if (GetSignalStateByTrackdir(tile, reversedir) == SIGNAL_STATE_RED) return;
+ if (GetSignalAspect(tile, track) == aspect) return; // aspect already correct
+ SetSignalAspect(tile, track, aspect);
+ MarkSingleSignalDirty(tile, reversedir);
+ aspect = std::min(aspect + 1, _extra_aspects + 1);
+ } else if (IsOnewaySignal(tile, track)) {
+ return; // one-way signal facing the wrong way
+ }
+ }
+ }
+
+ exitdir = TrackdirToExitdir(trackdir);
+ enterdir = ReverseDiagDir(exitdir);
+ tile += TileOffsByDiagDir(exitdir);
+
+ break;
+ }
+
+ case MP_STATION:
+ if (!HasStationRail(tile)) return;
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) return;
+ if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) return; // different axis
+ if (IsStationTileBlocked(tile)) return; // 'eye-candy' station tile
+
+ tile += TileOffsByDiagDir(exitdir);
+ break;
+
+ case MP_ROAD:
+ if (!IsLevelCrossing(tile)) return;
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) return;
+ if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) return; // different axis
+
+ tile += TileOffsByDiagDir(exitdir);
+ break;
+
+ case MP_TUNNELBRIDGE: {
+ if (!IsOneSignalBlock(owner, GetTileOwner(tile))) return;
+ if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return;
+
+ TrackBits tracks = GetTunnelBridgeTrackBits(tile); // trackbits of tile
+ TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]); // only incidating trackbits
+
+ if (tracks_masked == TRACK_BIT_NONE) return; // no incidating track
+ if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) tracks = tracks_masked;
+
+ if (!HasAtMostOneBit(tracks)) {
+ TrackBits reserved_bits = GetTunnelBridgeReservationTrackBits(tile) & tracks_masked;
+ if (reserved_bits == TRACK_BIT_NONE) return; // no reservation on junction
+ tracks = reserved_bits;
+ }
+
+ Track track = (Track)FIND_FIRST_BIT(tracks);
+ trackdir = TrackEnterdirToTrackdir(track, ReverseDiagDir(enterdir));
+
+ if (TrackdirEntersTunnelBridge(tile, trackdir)) {
+ TileIndex other = GetOtherTunnelBridgeEnd(tile);
+ if (IsTunnelBridgeWithSignalSimulation(tile)) {
+ /* exit signal */
+ if (!IsTunnelBridgeSignalSimulationExit(tile) || GetTunnelBridgeExitSignalState(tile) != SIGNAL_STATE_GREEN) return;
+ if (GetTunnelBridgeExitSignalAspect(tile) == aspect) return;
+ SetTunnelBridgeExitSignalAspect(tile, aspect);
+ MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ if (IsBridge(tile)) RefreshBridgeOnExitAspectChange(other, tile);
+ aspect = std::min(GetSignalledTunnelBridgeEntranceForwardAspect(other, tile) + 1, _extra_aspects + 1);
+ }
+ enterdir = GetTunnelBridgeDirection(other);
+ exitdir = ReverseDiagDir(enterdir);
+ tile = other;
+ } else {
+ if (TrackdirEntersTunnelBridge(tile, ReverseTrackdir(trackdir))) {
+ if (IsTunnelBridgeWithSignalSimulation(tile)) {
+ /* entrance signal */
+ if (!IsTunnelBridgeSignalSimulationEntrance(tile) || GetTunnelBridgeEntranceSignalState(tile) != SIGNAL_STATE_GREEN) return;
+ if (GetTunnelBridgeEntranceSignalAspect(tile) == aspect) return;
+ SetTunnelBridgeEntranceSignalAspect(tile, aspect);
+ MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ aspect = std::min(aspect + 1, _extra_aspects + 1);
+ }
+ }
+ exitdir = TrackdirToExitdir(trackdir);
+ enterdir = ReverseDiagDir(exitdir);
+ tile += TileOffsByDiagDir(exitdir);
+ }
+ break;
+ }
+
+ default:
+ return;
+ }
+ }
+}
+
+static std::vector> _deferred_aspect_updates;
+
+void UpdateAspectDeferred(TileIndex tile, Trackdir trackdir)
+{
+ _deferred_aspect_updates.push_back({ tile, trackdir });
+}
+
+void FlushDeferredAspectUpdates()
+{
+ /* Iterate in reverse order to reduce backtracking when updating the aspects of a new reservation */
+ for (auto iter = _deferred_aspect_updates.rbegin(); iter != _deferred_aspect_updates.rend(); ++iter) {
+ TileIndex tile = iter->first;
+ Trackdir trackdir = iter->second;
+ switch (GetTileType(tile)) {
+ case MP_RAILWAY:
+ if (HasSignalOnTrackdir(tile, trackdir) && GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN && GetSignalAspect(tile, TrackdirToTrack(trackdir)) == 0) {
+ uint8 aspect = GetForwardAspectFollowingTrackAndIncrement(tile, trackdir);
+ SetSignalAspect(tile, TrackdirToTrack(trackdir), aspect);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ break;
+
+ case MP_TUNNELBRIDGE:
+ if (IsTunnelBridgeSignalSimulationEntrance(tile) && TrackdirEntersTunnelBridge(tile, trackdir) &&
+ GetTunnelBridgeEntranceSignalState(tile) == SIGNAL_STATE_GREEN && GetTunnelBridgeEntranceSignalAspect(tile) == 0) {
+ uint8 aspect = GetForwardAspectFollowingTrackAndIncrement(tile, trackdir);
+ SetTunnelBridgeEntranceSignalAspect(tile, aspect);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ if (IsTunnelBridgeSignalSimulationExit(tile) && TrackdirExitsTunnelBridge(tile, trackdir) &&
+ GetTunnelBridgeExitSignalState(tile) == SIGNAL_STATE_GREEN && GetTunnelBridgeExitSignalAspect(tile) == 0) {
+ uint8 aspect = GetForwardAspectFollowingTrackAndIncrement(tile, trackdir);
+ SetTunnelBridgeExitSignalAspect(tile, aspect);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ _deferred_aspect_updates.clear();
+}
+
+void UpdateAllSignalAspects()
+{
+ for (TileIndex tile = 0; tile != MapSize(); ++tile) {
+ if (IsTileType(tile, MP_RAILWAY) && HasSignals(tile)) {
+ TrackBits bits = GetTrackBits(tile);
+ do {
+ Track track = RemoveFirstTrack(&bits);
+ if (HasSignalOnTrack(tile, track)) {
+ Trackdir trackdir = TrackToTrackdir(track);
+ if (!HasSignalOnTrackdir(tile, trackdir)) trackdir = ReverseTrackdir(trackdir);
+ if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) {
+ uint8 aspect = GetForwardAspectFollowingTrackAndIncrement(tile, trackdir);
+ SetSignalAspect(tile, track, aspect);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ }
+ } while (bits != TRACK_BIT_NONE);
+ } else if (IsTunnelBridgeWithSignalSimulation(tile)) {
+ if (IsTunnelBridgeSignalSimulationEntrance(tile) && GetTunnelBridgeEntranceSignalState(tile) == SIGNAL_STATE_GREEN) {
+ Trackdir trackdir = GetTunnelBridgeEntranceTrackdir(tile);
+ uint8 aspect = GetForwardAspectFollowingTrackAndIncrement(tile, trackdir);
+ SetTunnelBridgeEntranceSignalAspect(tile, aspect);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ if (IsTunnelBridgeSignalSimulationExit(tile) && GetTunnelBridgeExitSignalState(tile) == SIGNAL_STATE_GREEN) {
+ Trackdir trackdir = GetTunnelBridgeExitTrackdir(tile);
+ uint8 aspect = GetForwardAspectFollowingTrackAndIncrement(tile, trackdir);
+ SetTunnelBridgeExitSignalAspect(tile, aspect);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+ }
+ }
+}
+
+void UpdateExtraAspectsVariable()
+{
+ uint8 new_extra_aspects = 0;
+
+ if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC) {
+ for (RailType r = RAILTYPE_BEGIN; r != RAILTYPE_END; r++) {
+ const RailtypeInfo *rti = GetRailTypeInfo(r);
+ new_extra_aspects = std::max(new_extra_aspects, rti->signal_extra_aspects);
+ }
+ for (const GRFFile *grf : _new_signals_grfs) {
+ new_extra_aspects = std::max(new_extra_aspects, grf->new_signal_extra_aspects);
+ }
+ }
+
+
+ if (new_extra_aspects != _extra_aspects) {
+ _extra_aspects = new_extra_aspects;
+ if (_extra_aspects > 0) UpdateAllSignalAspects();
+ MarkWholeScreenDirty();
+ }
+}
diff --git a/src/signal_func.h b/src/signal_func.h
index 4655f8aa5f..368dd00ffc 100644
--- a/src/signal_func.h
+++ b/src/signal_func.h
@@ -19,6 +19,8 @@
#include "settings_type.h"
#include "vehicle_type.h"
+extern uint8 _extra_aspects;
+
/**
* Maps a trackdir to the bit that stores its status in the map arrays, in the
* direction along with the trackdir.
@@ -85,6 +87,12 @@ static inline bool IsProgrammableSignal(SignalType type)
return type == SIGTYPE_PROG;
}
+/** One-way signals can't be passed the 'wrong' way. */
+static inline bool IsOnewaySignal(SignalType type)
+{
+ return type != SIGTYPE_PBS;
+}
+
/// Is this signal type unsuitable for realistic braking?
static inline bool IsSignalTypeUnsuitableForRealisticBraking(SignalType type)
{
@@ -153,5 +161,17 @@ void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner);
void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner);
void UpdateSignalsInBuffer();
void UpdateSignalsInBufferIfOwnerNotAddable(Owner owner);
+uint8 GetForwardAspectFollowingTrack(TileIndex tile, Trackdir trackdir);
+uint8 GetSignalAspectGeneric(TileIndex tile, Trackdir trackdir);
+void PropagateAspectChange(TileIndex tile, Trackdir trackdir, uint8 aspect);
+void UpdateAspectDeferred(TileIndex tile, Trackdir trackdir);
+void FlushDeferredAspectUpdates();
+void UpdateAllSignalAspects();
+void UpdateExtraAspectsVariable();
+
+inline uint8 GetForwardAspectFollowingTrackAndIncrement(TileIndex tile, Trackdir trackdir)
+{
+ return std::min(GetForwardAspectFollowingTrack(tile, trackdir) + 1, _extra_aspects + 1);
+}
#endif /* SIGNAL_FUNC_H */
diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h
index d27392adaa..cc5534113e 100644
--- a/src/table/newgrf_debug_data.h
+++ b/src/table/newgrf_debug_data.h
@@ -909,6 +909,20 @@ class NIHRailType : public NIHelper {
if (secondary != INVALID_RAILTYPE) {
writeRailType(secondary);
}
+
+ if (IsTileType(index, MP_RAILWAY) && HasSignals(index)) {
+ print("Signals:");
+ for (Trackdir td = TRACKDIR_BEGIN; td < TRACKDIR_END; td = (Trackdir)(td + 1)) {
+ if (!IsValidTrackdir(td)) continue;
+ if (HasTrack(index, TrackdirToTrack(td)) && HasSignalOnTrackdir(index, td)) {
+ char *b = buffer;
+ const SignalState state = GetSignalStateByTrackdir(index, td);
+ b += seprintf(b, lastof(buffer), " trackdir: %d, state: %d", td, state);
+ if (_extra_aspects > 0 && state == SIGNAL_STATE_GREEN) seprintf(b, lastof(buffer), ", aspect: %d", GetSignalAspect(index, TrackdirToTrack(td)));
+ print(buffer);
+ }
+ }
+ }
}
};
diff --git a/src/table/railtypes.h b/src/table/railtypes.h
index 7ff8776fb1..e558926cd6 100644
--- a/src/table/railtypes.h
+++ b/src/table/railtypes.h
@@ -83,6 +83,9 @@ static const RailtypeInfo _original_railtypes[] = {
/* control flags */
0,
+ /* signal extra aspects */
+ 0,
+
/* cost multiplier */
8,
@@ -190,6 +193,9 @@ static const RailtypeInfo _original_railtypes[] = {
/* control flags */
0,
+ /* signal extra aspects */
+ 0,
+
/* cost multiplier */
12,
@@ -293,6 +299,9 @@ static const RailtypeInfo _original_railtypes[] = {
/* control flags */
0,
+ /* signal extra aspects */
+ 0,
+
/* cost multiplier */
16,
@@ -396,6 +405,9 @@ static const RailtypeInfo _original_railtypes[] = {
/* control flags */
0,
+ /* signal extra aspects */
+ 0,
+
/* cost multiplier */
24,
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index 2f0eab374f..2d78f89ffc 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -2903,6 +2903,9 @@ void ReverseTrainDirection(Train *v)
if (IsTunnelBridgeWithSignalSimulation(v->tile) && IsTunnelBridgeSignalSimulationEntrance(v->tile)) {
/* Flip signal on tunnel entrance tile red. */
SetTunnelBridgeEntranceSignalState(v->tile, SIGNAL_STATE_RED);
+ if (_extra_aspects > 0) {
+ PropagateAspectChange(v->tile, GetTunnelBridgeEntranceTrackdir(v->tile), 0);
+ }
MarkTileDirtyByTile(v->tile, VMDF_NOT_MAP_MODE);
update_check_tunnel_bridge_signal_counters(v);
ClrBit(v->flags, VRF_TRAIN_STUCK);
@@ -3346,11 +3349,46 @@ static int GetAndClearLastBridgeEntranceSetSignalIndex(TileIndex bridge_entrance
return 0;
}
+static void UpdateTunnelBridgeEntranceSignalAspect(TileIndex tile)
+{
+ Trackdir trackdir = GetTunnelBridgeEntranceTrackdir(tile);
+ uint8 aspect = GetForwardAspectFollowingTrackAndIncrement(tile, trackdir);
+ uint8 old_aspect = GetTunnelBridgeEntranceSignalAspect(tile);
+ if (aspect != old_aspect) {
+ SetTunnelBridgeEntranceSignalAspect(tile, aspect);
+ MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ PropagateAspectChange(tile, trackdir, aspect);
+ }
+}
+
static void SetTunnelBridgeEntranceSignalGreen(TileIndex tile)
{
if (GetTunnelBridgeEntranceSignalState(tile) == SIGNAL_STATE_RED) {
SetTunnelBridgeEntranceSignalState(tile, SIGNAL_STATE_GREEN);
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ if (_extra_aspects > 0) {
+ SetTunnelBridgeEntranceSignalAspect(tile, 0);
+ UpdateAspectDeferred(tile, GetTunnelBridgeEntranceTrackdir(tile));
+ }
+ } else if (_extra_aspects > 0) {
+ UpdateTunnelBridgeEntranceSignalAspect(tile);
+ }
+}
+
+static void UpdateEntranceAspectFromMiddleSignalChange(TileIndex entrance, int signal_number)
+{
+ if (signal_number < _extra_aspects && GetTunnelBridgeEntranceSignalState(entrance) == SIGNAL_STATE_GREEN) {
+ UpdateTunnelBridgeEntranceSignalAspect(entrance);
+ }
+}
+
+static void UpdateAspectFromBridgeMiddleSignalChange(TileIndex entrance, TileIndexDiff diff, int signal_number)
+{
+ UpdateEntranceAspectFromMiddleSignalChange(entrance, signal_number);
+ if (signal_number > 0) {
+ for (int i = std::max(0, signal_number - _extra_aspects); i < signal_number; i++) {
+ MarkTileDirtyByTile(entrance + (diff * (i + 1)), VMDF_NOT_MAP_MODE);
+ }
}
}
@@ -3360,8 +3398,10 @@ static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDir
/* Clearing last bridge signal. */
int signal_offset = GetAndClearLastBridgeEntranceSetSignalIndex(end);
if (signal_offset) {
- TileIndex last_signal_tile = end + (TileOffsByDiagDir(dir) * GetTunnelBridgeSignalSimulationSpacing(tile) * signal_offset);
+ TileIndexDiff diff = TileOffsByDiagDir(dir) * GetTunnelBridgeSignalSimulationSpacing(tile);
+ TileIndex last_signal_tile = end + (diff * signal_offset);
MarkTileDirtyByTile(last_signal_tile, VMDF_NOT_MAP_MODE);
+ if (_extra_aspects > 0) UpdateAspectFromBridgeMiddleSignalChange(end, diff, signal_offset - 1);
}
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
}
@@ -3380,6 +3420,9 @@ static void HandleLastTunnelBridgeSignals(TileIndex tile, TileIndex end, DiagDir
if (IsTunnelBridgeSignalSimulationEntrance(end)) SetTunnelBridgeEntranceSignalGreen(end);
if (IsTunnelBridgeSignalSimulationEntrance(tile)) SetTunnelBridgeEntranceSignalGreen(tile);
+ } else if (IsTunnel(end) && _extra_aspects > 0) {
+ uint signal_count = GetTunnelBridgeLength(tile, end) / GetTunnelBridgeSignalSimulationSpacing(end);
+ if (signal_count > 0) UpdateEntranceAspectFromMiddleSignalChange(end, signal_count - 1);
}
}
@@ -3389,6 +3432,7 @@ static void UnreserveBridgeTunnelTile(TileIndex tile)
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgeEffectivelyPBS(tile)) {
if (IsTunnelBridgePBS(tile)) {
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
+ if (_extra_aspects > 0) PropagateAspectChange(tile, GetTunnelBridgeExitTrackdir(tile), 0);
} else {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, GetTileOwner(tile));
}
@@ -3985,6 +4029,10 @@ static void TryLongReserveChooseTrainTrack(Train *v, TileIndex tile, Trackdir td
SetTunnelReservation(exit_tile, true);
}
SetTunnelBridgeExitSignalState(exit_tile, SIGNAL_STATE_GREEN);
+ if (_extra_aspects > 0) {
+ SetTunnelBridgeExitSignalAspect(exit_tile, 0);
+ UpdateAspectDeferred(exit_tile, GetTunnelBridgeExitTrackdir(exit_tile));
+ }
ChooseTrainTrack(v, ft.m_new_tile, ft.m_exitdir, TrackdirBitsToTrackBits(ft.m_new_td_bits), CTTF_NO_LOOKAHEAD_VALIDATE | (force_res ? CTTF_FORCE_RES : CTTF_NONE), nullptr, lookahead_state);
@@ -4087,6 +4135,10 @@ static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir,
do_track_reservation = true;
changed_signal = TrackEnterdirToTrackdir(track, enterdir);
SetSignalStateByTrackdir(tile, changed_signal, SIGNAL_STATE_GREEN);
+ if (_extra_aspects > 0) {
+ SetSignalAspect(tile, track, 0);
+ UpdateAspectDeferred(tile, changed_signal);
+ }
} else if (!do_track_reservation) {
return track;
}
@@ -4927,6 +4979,10 @@ static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
if (t->lookahead->reservation_end_tile == t->tile && t->lookahead->reservation_end_position - t->lookahead->current_position <= (int)TILE_SIZE && !HasBit(t->lookahead->flags, TRLF_TB_EXIT_FREE)) return false;
SignalState exit_state = GetTunnelBridgeExitSignalState(tile);
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN);
+ if (_extra_aspects > 0) {
+ SetTunnelBridgeExitSignalAspect(tile, 0);
+ UpdateAspectDeferred(tile, GetTunnelBridgeExitTrackdir(tile));
+ }
TileIndex veh_orig_tile = t->tile;
TrackBits veh_orig_track = t->track;
Direction veh_orig_direction = t->direction;
@@ -4978,6 +5034,10 @@ static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
t->direction = veh_orig_direction;
if (ok && IsTunnelBridgeEffectivelyPBS(tile)) {
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN);
+ if (_extra_aspects > 0) {
+ SetTunnelBridgeExitSignalAspect(tile, 0);
+ UpdateAspectDeferred(tile, GetTunnelBridgeExitTrackdir(tile));
+ }
mark_dirty = true;
}
return ok;
@@ -5033,6 +5093,9 @@ static void HandleSignalBehindTrain(Train *v, int signal_number)
} else if (IsBridge(v->tile) && signal_number >= 0) {
SetBridgeEntranceSimulatedSignalState(v->tile, signal_number, SIGNAL_STATE_GREEN);
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
+ if (_extra_aspects > 0) UpdateAspectFromBridgeMiddleSignalChange(v->tile, TileOffsByDiagDir(GetTunnelBridgeDirection(v->tile)) * simulated_wormhole_signals, signal_number);
+ } else if (IsTunnel(v->tile) && signal_number >= 0 && _extra_aspects > 0) {
+ UpdateEntranceAspectFromMiddleSignalChange(v->tile, signal_number);
}
}
@@ -5335,11 +5398,17 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
}
/* Flip signal on tunnel entrance tile red. */
SetTunnelBridgeEntranceSignalState(gp.new_tile, SIGNAL_STATE_RED);
+ if (_extra_aspects > 0) {
+ PropagateAspectChange(gp.new_tile, GetTunnelBridgeEntranceTrackdir(gp.new_tile), 0);
+ }
MarkTileDirtyByTile(gp.new_tile, VMDF_NOT_MAP_MODE);
if (IsTunnelBridgeSignalSimulationBidirectional(gp.new_tile)) {
/* Set incoming signal in other direction to red as well */
TileIndex other_end = GetOtherTunnelBridgeEnd(gp.new_tile);
SetTunnelBridgeEntranceSignalState(other_end, SIGNAL_STATE_RED);
+ if (_extra_aspects > 0) {
+ PropagateAspectChange(other_end, GetTunnelBridgeEntranceTrackdir(other_end), 0);
+ }
MarkTileDirtyByTile(other_end, VMDF_NOT_MAP_MODE);
}
}
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index 93ed5ec684..49e92930b1 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -1719,7 +1719,15 @@ static void DrawTunnelBridgeRampSingleSignal(const TileInfo *ti, bool is_green,
SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
- PalSpriteID sprite = GetCustomSignalSprite(rti, ti->tile, type, variant, is_green ? SIGNAL_STATE_GREEN : SIGNAL_STATE_RED).sprite;
+ uint8 aspect = 0;
+ if (is_green) {
+ if (_extra_aspects > 0) {
+ aspect = show_exit ? GetTunnelBridgeExitSignalAspect(ti->tile) : GetTunnelBridgeEntranceSignalAspect(ti->tile);
+ } else {
+ aspect = 1;
+ }
+ }
+ PalSpriteID sprite = GetCustomSignalSprite(rti, ti->tile, type, variant, aspect).sprite;
bool is_custom_sprite = (sprite.sprite != 0);
if (is_custom_sprite) {
@@ -1764,9 +1772,8 @@ static void DrawTunnelBridgeRampSignal(const TileInfo *ti)
}
/* Draws a signal on tunnel / bridge entrance tile. */
-static void DrawBridgeSignalOnMiddlePart(const TileInfo *ti, TileIndex bridge_start_tile, uint z)
+static void DrawBridgeSignalOnMiddlePart(const TileInfo *ti, TileIndex bridge_start_tile, TileIndex bridge_end_tile, uint z)
{
-
uint bridge_signal_position = 0;
int m2_position = 0;
@@ -1802,9 +1809,27 @@ static void DrawBridgeSignalOnMiddlePart(const TileInfo *ti, TileIndex bridge_st
SignalVariant variant = IsTunnelBridgeSemaphore(bridge_start_tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
SignalState state = GetBridgeEntranceSimulatedSignalState(bridge_start_tile, m2_position);
+ uint8 aspect = 0;
+ if (state == SIGNAL_STATE_GREEN) {
+ aspect = 1;
+ if (_extra_aspects > 0) {
+ const uint bridge_length = GetTunnelBridgeLength(bridge_start_tile, bridge_end_tile) + 1;
+ while (true) {
+ bridge_signal_position += simulated_wormhole_signals;
+ if (bridge_signal_position >= bridge_length) {
+ if (GetTunnelBridgeExitSignalState(bridge_end_tile) == SIGNAL_STATE_GREEN) aspect += GetTunnelBridgeExitSignalAspect(bridge_end_tile);
+ break;
+ }
+ m2_position++;
+ if (GetBridgeEntranceSimulatedSignalState(bridge_start_tile, m2_position) != SIGNAL_STATE_GREEN) break;
+ aspect++;
+ if (aspect >= _extra_aspects + 1) break;
+ }
+ }
+ }
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(bridge_start_tile));
- PalSpriteID sprite = GetCustomSignalSprite(rti, bridge_start_tile, SIGTYPE_NORMAL, variant, state).sprite;
+ PalSpriteID sprite = GetCustomSignalSprite(rti, bridge_start_tile, SIGTYPE_NORMAL, variant, aspect).sprite;
if (sprite.sprite != 0) {
sprite.sprite += position;
@@ -1820,6 +1845,7 @@ static void DrawBridgeSignalOnMiddlePart(const TileInfo *ti, TileIndex bridge_st
}
AddSortableSpriteToDraw(sprite.sprite, sprite.pal, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR);
+ break;
}
m2_position++;
}
@@ -1979,7 +2005,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti, DrawTileProcParams params)
if (IsTunnelBridgeWithSignalSimulation(ti->tile)) {
extern void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition,
- SignalOffsets image, uint pos, SignalType type, SignalVariant variant, bool show_restricted);
+ SignalOffsets image, uint pos, SignalType type, SignalVariant variant, bool show_restricted, bool exit_signal = false);
DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
@@ -1992,14 +2018,14 @@ static void DrawTile_TunnelBridge(TileInfo *ti, DrawTileProcParams params)
image = (SignalOffsets)(image ^ 1);
}
if (IsTunnelBridgeSignalSimulationEntrance(ti->tile)) {
- DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeEntranceSignalState(ti->tile), image, position, SIGTYPE_NORMAL, variant, false);
+ DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeEntranceSignalState(ti->tile), image, position, SIGTYPE_NORMAL, variant, false, false);
}
if (IsTunnelBridgeSignalSimulationExit(ti->tile)) {
SignalType type = SIGTYPE_NORMAL;
if (IsTunnelBridgePBS(ti->tile)) {
type = IsTunnelBridgeSignalSimulationEntrance(ti->tile) ? SIGTYPE_PBS : SIGTYPE_PBS_ONEWAY;
}
- DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeExitSignalState(ti->tile), (SignalOffsets)(image ^ 1), position ^ 1, type, variant, false);
+ DrawSingleSignal(ti->tile, rti, t, GetTunnelBridgeExitSignalState(ti->tile), (SignalOffsets)(image ^ 1), position ^ 1, type, variant, false, true);
}
};
switch (t) {
@@ -2308,8 +2334,8 @@ void DrawBridgeMiddle(const TileInfo *ti)
if (HasRailCatenaryDrawn(GetRailType(rampsouth))) {
DrawRailCatenaryOnBridge(ti);
}
- if (IsTunnelBridgeSignalSimulationEntrance(rampsouth)) DrawBridgeSignalOnMiddlePart(ti, rampsouth, z);
- if (IsTunnelBridgeSignalSimulationEntrance(rampnorth)) DrawBridgeSignalOnMiddlePart(ti, rampnorth, z);
+ if (IsTunnelBridgeSignalSimulationEntrance(rampsouth)) DrawBridgeSignalOnMiddlePart(ti, rampsouth, rampnorth, z);
+ if (IsTunnelBridgeSignalSimulationEntrance(rampnorth)) DrawBridgeSignalOnMiddlePart(ti, rampnorth, rampsouth, z);
}
/* draw roof, the component of the bridge which is logically between the vehicle and the camera */
diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h
index f6e5782e8f..123c4a0dcf 100644
--- a/src/tunnelbridge_map.h
+++ b/src/tunnelbridge_map.h
@@ -506,6 +506,30 @@ static inline void SetTunnelBridgePBS(TileIndex t, bool is_pbs)
SB(_me[t].m6, 6, 1, is_pbs ? 1 : 0);
}
+static inline uint8 GetTunnelBridgeEntranceSignalAspect(TileIndex t)
+{
+ assert_tile(IsTunnelBridgeWithSignalSimulation(t), t);
+ return GB(_m[t].m3, 0, 3);
+}
+
+static inline void SetTunnelBridgeEntranceSignalAspect(TileIndex t, uint8 aspect)
+{
+ assert_tile(IsTunnelBridgeWithSignalSimulation(t), t);
+ SB(_m[t].m3, 0, 3, aspect);
+}
+
+static inline uint8 GetTunnelBridgeExitSignalAspect(TileIndex t)
+{
+ assert_tile(IsTunnelBridgeWithSignalSimulation(t), t);
+ return GB(_m[t].m3, 3, 3);
+}
+
+static inline void SetTunnelBridgeExitSignalAspect(TileIndex t, uint8 aspect)
+{
+ assert_tile(IsTunnelBridgeWithSignalSimulation(t), t);
+ SB(_m[t].m3, 3, 3, aspect);
+}
+
static inline Trackdir GetTunnelBridgeExitTrackdir(TileIndex t, DiagDirection tunnel_bridge_dir)
{
return TrackEnterdirToTrackdir((Track)FIND_FIRST_BIT(GetAcrossTunnelBridgeTrackBits(t)), ReverseDiagDir(tunnel_bridge_dir));