Add implementation of multi-aspect signalling and GRF support

Requires realistic braking

See: #247
pull/306/head
Jonathan G Rennison 3 years ago
parent 6546f6deba
commit b2ef6c0de8

@ -508,6 +508,8 @@
<li>m3 bits 7..4: bit set = signal 3..0 present</li>
<li>m4 bits 7..4: bit clear = signal 3..0 shows red</li>
<li style="color: blue">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)</li>
<li style="color: blue">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)</li>
</ul>
</li>
<li>m2 bits 8..10: track reserved for pbs
@ -1740,6 +1742,8 @@
<li>m6 bit 0: set = entrance signal shows green, clear = entrance signal shows red</li>
<li>m2 bit 15: for bridge entrances only: storage for visual red/green state of signals starting from 15 is allocated outside the map array</li>
<li>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</li>
<li>m3 bits 5..3: entrance signal aspect (only valid if signal is present and not red, and multi-aspect signalling is in effect)</li>
<li>m3 bits 2..0: exit signal aspect (only valid if signal is present and not red, and multi-aspect signalling is in effect)</li>
</ul>
</li>
<li>m5 bits 3..2: transport type

@ -104,7 +104,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Ground type: fences, snow, desert">XXXX</span></td>
<td class="bits"><span class="used" title="Rail tile type: rail, rail with signals, depot">OO</span> <span class="used" title="Track pieces">XXXXXX</span></td>
<td class="bits" rowspan=3><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=3><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=2><span class="free">OOOO</span> <span class="patch" title="Secondary railway type (used for lower or right track when two parallel tracks on tile)">PPPP PP</span><span class="used" title="Railway type">XX XXXX</span></td>
</tr>
<tr>
@ -113,6 +113,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="used" title="Signals present">XXXX</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="used" title="Signals colors">XXXX</span> <span class="used" title="Ground type: fences, snow, desert">XXXX</span></td>
<td class="bits"><span class="used" title="Rail tile type: rail, rail with signals, depot">O1</span> <span class="used" title="Track pieces">XXXXXX</span></td>
<td class="bits"><span class="free">OO</span> <span class="patch" title="Signal aspects">PPPPPP</span></td>
</tr>
<tr>
<td class="caption">depot</td>
@ -120,6 +121,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits"><span class="free">OOOO</span> <span class="free">OOOO</span></td>
<td class="bits"><span class="free">OOOO</span> <span class="used" title="Ground type: fences, snow, desert (fences on depot are not valid)">XXXX</span></td>
<td class="bits"><span class="used" title="Rail tile type: rail, rail with signals, depot">11</span><span class="free">O</span><span class="used" title="PBS reservation">X</span> <span class="free">OO</span><span class="used" title="Depot exit direction">XX</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO OO</span><span class="used" title="Railway type">XX XXXX</span></td>
</tr>
<tr>
@ -285,7 +287,7 @@ the array so you can quickly see what is used and what is not.
<td class="caption">tunnel entrance</td>
<td class="bits" rowspan=3><span class="free">OOO</span> <span class="used" title="Owner">XXXXX</span></td>
<td class="bits"><span class="patch-pool" title="Tunnel index on pool (or overflow sentinel)">PPPP PPPP PPPP PPPP</span></td>
<td class="bits" rowspan=4><span class="rearrange" title="Owner of tram (road only; a rearrangement can free some of these bits)">XXXX</span> <span class="free">OOOO</span></td>
<td class="bits" rowspan=4><span class="rearrange" title="Owner of tram (road only; a rearrangement can free some of these bits)">XXXX</span> <span class="free">OOOO</span><br /><span class="free">OO</span> <span class="patch" title="Entrance/exit signal aspects (rail only)">PPPPPP</span></td>
<td class="bits"><span class="free">OO</span><span class="used" title="Road type">XX XXXX</span></td>
<td class="bits"><span class="used" title="Bridge or tunnel bit">O</span><span class="patch" title="Signal simulation mode (rail only)">PP</span><span class="rearrange" title="PBS reservation (rail; a rearrangement can free some of these bits)">X</span> <span class="used" title="Transport type">XX</span> <span class="used" title="Direction of the tunnel/bridge">XX</span></td>
<td class="bits"><span class="patch" title="PBS mode, exit signal state">PP</span><span class="free">OO OO</span><span class="patch" title="Semaphore/light mode, entrance signal state">PP</span></td>

@ -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.
</td>
</tr>
<tr><td>extra_aspects</td><td>0 - 6</td>
<td>
The value is the number of additional signal aspects to use (e.g. 4-aspect signalling should use a value of 2).<br />
When set, the lowest byte of <span class="code">extra_callback_info2</span> (signal state) may have the given number of additional values starting from 02:
<table>
<tr><th>Value</th><th>Meaning</th></tr>
<tr><td>00</td><td>Red signal</td></tr>
<tr><td>01</td><td>Green signal</td></tr>
<tr><td>02</td><td>1st extra aspect (e.g. yellow)</td></tr>
<tr><td>03</td><td>2nd extra aspect (e.g. double yellow)</td></tr>
<tr><td>...</td><td>Further extra aspects...</td></tr>
</table>
<br />
The provided value is currently clamped to be within the range 0 - 6 (inclusive).<br />
N.B. Realistic braking must be enabled for additional signal aspects to be used
</td>
</tr>
<tr><td>disable_realistic_braking</td><td>0 or 1</td>
<td>
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 @@
</table>
</td>
</tr>
<tr><td>extra_aspects</td><td>0 - 6</td>
<td>
The value is the number of additional signal aspects to use (e.g. 4-aspect signalling should use a value of 2).<br />
When set, the lowest byte of <span class="code">extra_callback_info2</span> (signal state) may have the given number of additional values starting from 02:
<table>
<tr><th>Value</th><th>Meaning</th></tr>
<tr><td>00</td><td>Red signal</td></tr>
<tr><td>01</td><td>Green signal</td></tr>
<tr><td>02</td><td>1st extra aspect (e.g. yellow)</td></tr>
<tr><td>03</td><td>2nd extra aspect (e.g. double yellow)</td></tr>
<tr><td>...</td><td>Further extra aspects...</td></tr>
</table>
<br />
The provided value is currently clamped to be within the range 0 - 6 (inclusive).<br />
N.B. Realistic braking must be enabled for additional signal aspects to be used
</td>
</tr>
</table>
<p>
Custom signal sprites example:

@ -241,6 +241,22 @@
</table></p>
<p>The property length is 1 byte. 0 is disabled (default). 1 is enabled.</p>
<p>This is indicated by the feature name: <font face="monospace">action0_railtype_recolour</font>, version 1</p>
<h4 id="railtype_extra_aspects">Set number of additional signal aspects (mappable property: railtype_extra_aspects)</h4>
<p>This applies to <a href="https://newgrf-specs.tt-wiki.net/wiki/Action3/Railtypes#Signal_sprites_.280B.29">Action 2/3 - Railtype custom signal sprites</a>.<br />
The value is the number of additional signal aspects to use (e.g. 4-aspect signalling should use a value of 2).<br />
When set, the lowest byte of variable 0x18 (SS: signal state) may have the given number of additional values starting from 02:
<table>
<tr><th>Value</th><th>Meaning</th></tr>
<tr><td>00</td><td>Red signal</td></tr>
<tr><td>01</td><td>Green signal</td></tr>
<tr><td>02</td><td>1st extra aspect (e.g. yellow)</td></tr>
<tr><td>03</td><td>2nd extra aspect (e.g. double yellow)</td></tr>
<tr><td>...</td><td>Further extra aspects...</td></tr>
</table></p>
<p>The property length is 1 byte.<br />
The provided value is currently clamped to be within the range 0 - 6 (inclusive).</p>
<p>N.B. Realistic braking must be enabled for additional signal aspects to be used.</p>
<p>This is indicated by the feature name: <font face="monospace">action0_railtype_extra_aspects</font>, version 1</p>
<h4 id="railtype_disable_realistic_braking">Disable use of realistic braking with this rail type (mappable property: railtype_disable_realistic_braking)</h4>
<p>When this property is set realistic braking is disabled for trains of this railtype even when realistic braking is otherwise in effect.<br />
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.
</p>
<p>This is indicated by the feature name: <font face="monospace">action0_signals_recolour</font>, version 1</p>
<h4 id="signals_extra_aspects">Set number of additional signal aspects (mappable property: signals_extra_aspects)</h4>
<p>This applies to <a href="#a3signals_custom_signal_sprites">Action 2/3 Signals (Feature 0E) custom signal sprites</a> for this GRF.<br />
The value is the number of additional signal aspects to use (e.g. 4-aspect signalling should use a value of 2).<br />
When set, the lowest byte of variable 0x18 (SS: signal state) may have the given number of additional values starting from 02:
<table>
<tr><th>Value</th><th>Meaning</th></tr>
<tr><td>00</td><td>Red signal</td></tr>
<tr><td>01</td><td>Green signal</td></tr>
<tr><td>02</td><td>1st extra aspect (e.g. yellow)</td></tr>
<tr><td>03</td><td>2nd extra aspect (e.g. double yellow)</td></tr>
<tr><td>...</td><td>Further extra aspects...</td></tr>
</table></p>
<p>The property length is 1 byte.<br />
The provided value is currently clamped to be within the range 0 - 6 (inclusive).</p>
<p>N.B. Realistic braking must be enabled for additional signal aspects to be used.</p>
<p>This is indicated by the feature name: <font face="monospace">action0_signals_extra_aspects</font>, version 1</p>
<br />
<br />
<h3 id="varaction2_station"><a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2/Stations">Variational Action 2 - Stations</a></h3>

@ -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));

@ -29,6 +29,7 @@
#include "scope_info.h"
#include "core/random_func.hpp"
#include "settings_func.h"
#include "signal_func.h"
#include <array>
#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);
}

@ -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();
}

@ -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();

@ -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<byte>(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<byte>(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++) {

@ -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();

@ -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<uint8>(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);

@ -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);

@ -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());
}

@ -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));
}

@ -224,6 +224,11 @@ public:
*/
byte ctrl_flags;
/**
* Signal extra aspects
*/
uint8 signal_extra_aspects;
/**
* Cost multiplier for building this rail type
*/

@ -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);

@ -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

@ -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 */

@ -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 },

@ -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()
};

@ -1199,6 +1199,7 @@ static bool TrainBrakingModelChanged(int32 p1)
}
}
UpdateExtraAspectsVariable();
UpdateAllBlockSignals();
InvalidateWindowData(WC_BUILD_SIGNAL, 0);

@ -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<SignalReference> SignalDependencyList;
@ -197,6 +202,7 @@ public:
};
static SmallSet<Trackdir, SIG_TBU_SIZE> _tbuset("_tbuset"); ///< set of signals that will be updated
static SmallSet<Trackdir, SIG_TBU_SIZE> _tbpset("_tbpset"); ///< set of PBS signals to update the aspect of
static SmallSet<DiagDirection, SIG_TBD_SIZE> _tbdset("_tbdset"); ///< set of open nodes in current signal block
static SmallSet<DiagDirection, SIG_GLOB_SIZE> _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<void *>((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<uint>(aspect, _extra_aspects + 1);
}
}
if (GetTunnelBridgeExitSignalState(tile_exit) == SIGNAL_STATE_GREEN) aspect += GetTunnelBridgeExitSignalAspect(tile_exit);
return std::min<uint>(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<uint>(aspect, _extra_aspects + 1);
} else {
if (free_tiles < (int)spacing) return 0;
return std::min<uint>((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<uint8>(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<uint8>(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<uint8>(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<uint>(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<uint8>(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<uint>(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<uint>(aspect + 1, _extra_aspects + 1);
}
}
exitdir = TrackdirToExitdir(trackdir);
enterdir = ReverseDiagDir(exitdir);
tile += TileOffsByDiagDir(exitdir);
}
break;
}
default:
return;
}
}
}
static std::vector<std::pair<TileIndex, Trackdir>> _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<uint8>(new_extra_aspects, rti->signal_extra_aspects);
}
for (const GRFFile *grf : _new_signals_grfs) {
new_extra_aspects = std::max<uint8>(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();
}
}

@ -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<uint8>(GetForwardAspectFollowingTrack(tile, trackdir) + 1, _extra_aspects + 1);
}
#endif /* SIGNAL_FUNC_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);
}
}
}
}
};

@ -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,

@ -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<int>(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);
}
}

@ -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 */

@ -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));

Loading…
Cancel
Save