Add implementation of multi-aspect signalling and GRF support

Requires realistic braking

See: #247
This commit is contained in:
Jonathan G Rennison
2021-08-28 12:51:06 +01:00
parent 6546f6deba
commit b2ef6c0de8
28 changed files with 917 additions and 35 deletions

View File

@@ -508,6 +508,8 @@
<li>m3 bits 7..4: bit set = signal 3..0 present</li> <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>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> </ul>
</li> </li>
<li>m2 bits 8..10: track reserved for pbs <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>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 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>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> </ul>
</li> </li>
<li>m5 bits 3..2: transport type <li>m5 bits 3..2: transport type

View File

@@ -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="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"><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" 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> <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>
<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 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="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="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>
<tr> <tr>
<td class="caption">depot</td> <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="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="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="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> <td class="bits"><span class="free">OOOO OOOO OO</span><span class="used" title="Railway type">XX XXXX</span></td>
</tr> </tr>
<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="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" 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"><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="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="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> <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>

View File

@@ -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. 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> </td>
</tr> </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> <tr><td>disable_realistic_braking</td><td>0 or 1</td>
<td> <td>
When this property is set realistic braking is disabled for trains of this railtype even when realistic braking is otherwise in effect. 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> </table>
</td> </td>
</tr> </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> </table>
<p> <p>
Custom signal sprites example: Custom signal sprites example:

View File

@@ -241,6 +241,22 @@
</table></p> </table></p>
<p>The property length is 1 byte. 0 is disabled (default). 1 is enabled.</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> <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> <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 /> <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. 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. The Action 0 Id field is not used, the value is ignored.
</p> </p>
<p>This is indicated by the feature name: <font face="monospace">action0_signals_recolour</font>, version 1</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 />
<br /> <br />
<h3 id="varaction2_station"><a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2/Stations">Variational Action 2 - Stations</a></h3> <h3 id="varaction2_station"><a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2/Stations">Variational Action 2 - Stations</a></h3>

View File

@@ -170,6 +170,7 @@ static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetyp
{ {
/* Backup bridge signal and custom bridgehead data. */ /* Backup bridge signal and custom bridgehead data. */
auto m2_backup = _m[t].m2; auto m2_backup = _m[t].m2;
auto m3_backup = _m[t].m3;
auto m4_backup = _m[t].m4; auto m4_backup = _m[t].m4;
auto m5_backup = _m[t].m5; auto m5_backup = _m[t].m5;
auto m6_backup = _me[t].m6; auto m6_backup = _me[t].m6;
@@ -182,6 +183,7 @@ static inline void MakeRailBridgeRamp(TileIndex t, Owner o, BridgeType bridgetyp
if (upgrade) { if (upgrade) {
/* Restore bridge signal and custom bridgehead data if we're upgrading an existing bridge. */ /* Restore bridge signal and custom bridgehead data if we're upgrading an existing bridge. */
_m[t].m2 = m2_backup; _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].m4, 0, 6, GB(m4_backup, 0, 6));
SB(_m[t].m5, 4, 3, GB(m5_backup, 4, 3)); SB(_m[t].m5, 4, 3, GB(m5_backup, 4, 3));
SB(_me[t].m6, 0, 2, GB(m6_backup, 0, 2)); SB(_me[t].m6, 0, 2, GB(m6_backup, 0, 2));

View File

@@ -29,6 +29,7 @@
#include "scope_info.h" #include "scope_info.h"
#include "core/random_func.hpp" #include "core/random_func.hpp"
#include "settings_func.h" #include "settings_func.h"
#include "signal_func.h"
#include <array> #include <array>
#include "table/strings.h" #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. /* 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 */ * So make sure the signal buffer is empty even in this case */
UpdateSignalsInBuffer(); UpdateSignalsInBuffer();
if (_extra_aspects > 0) FlushDeferredAspectUpdates();
SetDParam(0, _additional_cash_required); SetDParam(0, _additional_cash_required);
return_dcpi(CommandCost(STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY)); 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 */ /* update signals if needed */
UpdateSignalsInBuffer(); UpdateSignalsInBuffer();
if (_extra_aspects > 0) FlushDeferredAspectUpdates();
return_dcpi(res2); return_dcpi(res2);
} }

View File

@@ -350,8 +350,17 @@ void UpdateAllBlockSignals(Owner owner)
} while (bits != TRACK_BIT_NONE); } while (bits != TRACK_BIT_NONE);
} else if (IsLevelCrossingTile(tile) && (owner == INVALID_OWNER || GetTileOwner(tile) == owner)) { } else if (IsLevelCrossingTile(tile) && (owner == INVALID_OWNER || GetTileOwner(tile) == owner)) {
UpdateLevelCrossing(tile); 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()); } while (++tile != MapSize());
UpdateSignalsInBuffer(); UpdateSignalsInBuffer();
FlushDeferredAspectUpdates();
} }

View File

@@ -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_tick_skip_counter = 0;
_game_load_time = 0; _game_load_time = 0;
_extra_station_names_used = 0; _extra_station_names_used = 0;
_extra_aspects = 0;
_loadgame_DBGL_data.clear(); _loadgame_DBGL_data.clear();
if (reset_settings) MakeNewgameSettingsLive(); if (reset_settings) MakeNewgameSettingsLive();

View File

@@ -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)); SB(_cur.grffile->new_signal_ctrl_flags, NSCF_RECOLOUR_ENABLED, 1, (buf->ReadByte() != 0 ? 1 : 0));
break; 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: default:
ret = HandleAction0PropertyDefault(buf, prop); ret = HandleAction0PropertyDefault(buf, prop);
break; 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)); SB(rti->ctrl_flags, RTCF_RECOLOUR_ENABLED, 1, (buf->ReadByte() != 0 ? 1 : 0));
break; 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: default:
ret = HandleAction0PropertyDefault(buf, prop); ret = HandleAction0PropertyDefault(buf, prop);
break; break;
@@ -4510,6 +4520,7 @@ static ChangeInfoResult RailTypeReserveInfo(uint id, int numinfo, int prop, cons
case A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS: case A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS:
case A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING: case A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING:
case A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR: case A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR:
case A0RPI_RAILTYPE_EXTRA_ASPECTS:
buf->Skip(buf->ReadExtendedByte()); buf->Skip(buf->ReadExtendedByte());
break; break;
@@ -8480,11 +8491,13 @@ static const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("action0_railtype_restricted_signals", 1), GRFFeatureInfo("action0_railtype_restricted_signals", 1),
GRFFeatureInfo("action0_railtype_disable_realistic_braking", 1), GRFFeatureInfo("action0_railtype_disable_realistic_braking", 1),
GRFFeatureInfo("action0_railtype_recolour", 1), GRFFeatureInfo("action0_railtype_recolour", 1),
GRFFeatureInfo("action0_railtype_extra_aspects", 1),
GRFFeatureInfo("action0_roadtype_extra_flags", 1), GRFFeatureInfo("action0_roadtype_extra_flags", 1),
GRFFeatureInfo("action0_global_extra_station_names", 1), GRFFeatureInfo("action0_global_extra_station_names", 1),
GRFFeatureInfo("action0_signals_programmable_signals", 1), GRFFeatureInfo("action0_signals_programmable_signals", 1),
GRFFeatureInfo("action0_signals_restricted_signals", 1), GRFFeatureInfo("action0_signals_restricted_signals", 1),
GRFFeatureInfo("action0_signals_recolour", 1), GRFFeatureInfo("action0_signals_recolour", 1),
GRFFeatureInfo("action0_signals_extra_aspects", 1),
GRFFeatureInfo("action3_signals_custom_signal_sprites", 1), GRFFeatureInfo("action3_signals_custom_signal_sprites", 1),
GRFFeatureInfo(), 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_ENABLE_RESTRICTED_SIGNALS, "railtype_enable_restricted_signals"),
GRFPropertyMapDefinition(GSF_RAILTYPES, A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING, "railtype_disable_realistic_braking"), 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_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_ROADTYPES, A0RPI_ROADTYPE_EXTRA_FLAGS, "roadtype_extra_flags"),
GRFPropertyMapDefinition(GSF_TRAMTYPES, 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_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_PROGRAMMABLE_SIGNALS, "signals_enable_programmable_signals"),
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_ENABLE_RESTRICTED_SIGNALS, "signals_enable_restricted_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_ENABLE_SIGNAL_RECOLOUR, "signals_enable_signal_recolour"),
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_EXTRA_ASPECTS, "signals_extra_aspects"),
GRFPropertyMapDefinition(), GRFPropertyMapDefinition(),
}; };
@@ -9409,6 +9424,7 @@ GRFFile::GRFFile(const GRFConfig *config)
this->new_signals_group = nullptr; this->new_signals_group = nullptr;
this->new_signal_ctrl_flags = 0; this->new_signal_ctrl_flags = 0;
this->new_signal_extra_aspects = 0;
/* Mark price_base_multipliers as 'not set' */ /* Mark price_base_multipliers as 'not set' */
for (Price i = PR_BEGIN; i < PR_END; i++) { for (Price i = PR_BEGIN; i < PR_END; i++) {

View File

@@ -119,11 +119,13 @@ enum Action0RemapPropertyIds {
A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS, A0RPI_RAILTYPE_ENABLE_RESTRICTED_SIGNALS,
A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING, A0RPI_RAILTYPE_DISABLE_REALISTIC_BRAKING,
A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR, A0RPI_RAILTYPE_ENABLE_SIGNAL_RECOLOUR,
A0RPI_RAILTYPE_EXTRA_ASPECTS,
A0RPI_ROADTYPE_EXTRA_FLAGS, A0RPI_ROADTYPE_EXTRA_FLAGS,
A0RPI_GLOBALVAR_EXTRA_STATION_NAMES, A0RPI_GLOBALVAR_EXTRA_STATION_NAMES,
A0RPI_SIGNALS_ENABLE_PROGRAMMABLE_SIGNALS, A0RPI_SIGNALS_ENABLE_PROGRAMMABLE_SIGNALS,
A0RPI_SIGNALS_ENABLE_RESTRICTED_SIGNALS, A0RPI_SIGNALS_ENABLE_RESTRICTED_SIGNALS,
A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR, A0RPI_SIGNALS_ENABLE_SIGNAL_RECOLOUR,
A0RPI_SIGNALS_EXTRA_ASPECTS,
}; };
enum GRFPropertyMapFallbackMode { enum GRFPropertyMapFallbackMode {
@@ -227,6 +229,10 @@ enum NewSignalCtrlFlags {
NSCF_RECOLOUR_ENABLED = 3, ///< Recolour sprites enabled NSCF_RECOLOUR_ENABLED = 3, ///< Recolour sprites enabled
}; };
enum {
NEW_SIGNALS_MAX_EXTRA_ASPECT = 6,
};
/** New signal control flags. */ /** New signal control flags. */
enum NewSignalAction3ID { enum NewSignalAction3ID {
NSA3ID_CUSTOM_SIGNALS = 0, ///< Action 3 ID for custom signal sprites 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 const SpriteGroup *new_signals_group; ///< New signals sprite group
byte new_signal_ctrl_flags; ///< Ctrl flags for new signals 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(const struct GRFConfig *config);
~GRFFile(); ~GRFFile();

View File

@@ -115,13 +115,21 @@ SpriteID GetCustomRailSprite(const RailtypeInfo *rti, TileIndex tile, RailTypeSp
return group->GetResult(); 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 (rti->group[RTSG_SIGNALS] == nullptr) return { 0, PAL_NONE };
if (type == SIGTYPE_PROG && !HasBit(rti->ctrl_flags, RTCF_PROGSIG)) return { 0, PAL_NONE }; if (type == SIGTYPE_PROG && !HasBit(rti->ctrl_flags, RTCF_PROGSIG)) return { 0, PAL_NONE };
uint32 param1 = gui ? 0x10 : 0x00; 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); if (restricted && HasBit(rti->ctrl_flags, RTCF_RESTRICTEDSIG)) SetBit(param2, 24);
RailTypeResolverObject object(rti, tile, TCX_NORMAL, RTSG_SIGNALS, param1, param2); 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? * @param gui Is the sprite being used on the map or in the GUI?
* @return The sprite to draw. * @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) }; if (spr.sprite != 0) return { spr, HasBit(rti->ctrl_flags, RTCF_PROGSIG) };
for (const GRFFile *grf : _new_signals_grfs) { for (const GRFFile *grf : _new_signals_grfs) {
if (type == SIGTYPE_PROG && !HasBit(grf->new_signal_ctrl_flags, NSCF_PROGSIG)) continue; if (type == SIGTYPE_PROG && !HasBit(grf->new_signal_ctrl_flags, NSCF_PROGSIG)) continue;
uint32 param1 = gui ? 0x10 : 0x00; 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); if (restricted && HasBit(grf->new_signal_ctrl_flags, NSCF_RESTRICTEDSIG)) SetBit(param2, 24);
NewSignalsResolverObject object(grf, tile, TCX_NORMAL, param1, param2); NewSignalsResolverObject object(grf, tile, TCX_NORMAL, param1, param2);

View File

@@ -61,7 +61,7 @@ struct CustomSignalSpriteResult {
}; };
SpriteID GetCustomRailSprite(const RailtypeInfo *rti, TileIndex tile, RailTypeSpriteGroup rtsg, TileContext context = TCX_NORMAL, uint *num_results = nullptr); 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); RailType GetRailTypeTranslation(uint8 railtype, const GRFFile *grffile);
uint8 GetReverseRailTypeTranslation(RailType railtype, const GRFFile *grffile); uint8 GetReverseRailTypeTranslation(RailType railtype, const GRFFile *grffile);

View File

@@ -465,6 +465,7 @@ static void ShutdownGame()
_game_load_tick_skip_counter = 0; _game_load_tick_skip_counter = 0;
_game_load_time = 0; _game_load_time = 0;
_extra_station_names_used = 0; _extra_station_names_used = 0;
_extra_aspects = 0;
_loadgame_DBGL_data.clear(); _loadgame_DBGL_data.clear();
_loadgame_DBGC_data.clear(); _loadgame_DBGC_data.clear();
} }
@@ -1860,6 +1861,7 @@ void StateGameLoop()
UpdateStateChecksum(c->money); UpdateStateChecksum(c->money);
} }
} }
if (_extra_aspects > 0) FlushDeferredAspectUpdates();
assert(IsLocalCompany()); assert(IsLocalCompany());
} }

View File

@@ -85,6 +85,10 @@ bool TryReserveRailTrackdir(TileIndex tile, Trackdir td, bool trigger_stations)
if (success && HasPbsSignalOnTrackdir(tile, td)) { if (success && HasPbsSignalOnTrackdir(tile, td)) {
SetSignalStateByTrackdir(tile, td, SIGNAL_STATE_GREEN); SetSignalStateByTrackdir(tile, td, SIGNAL_STATE_GREEN);
MarkSingleSignalDirty(tile, td); MarkSingleSignalDirty(tile, td);
if (_extra_aspects > 0) {
SetSignalAspect(tile, TrackdirToTrack(td), 0);
UpdateAspectDeferred(tile, td);
}
} }
return success; return success;
} }
@@ -240,6 +244,7 @@ void UnreserveRailTrack(TileIndex tile, Track t)
if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgeEffectivelyPBS(tile) && IsTrackAcrossTunnelBridge(tile, t)) { if (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgeEffectivelyPBS(tile) && IsTrackAcrossTunnelBridge(tile, t)) {
if (IsTunnelBridgePBS(tile)) { if (IsTunnelBridgePBS(tile)) {
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED); SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
if (_extra_aspects > 0) PropagateAspectChange(tile, GetTunnelBridgeExitTrackdir(tile), 0);
} else { } else {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, GetTileOwner(tile)); UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, GetTileOwner(tile));
} }

View File

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

View File

@@ -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,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, RailTypeLabelList(), 0, 0, RAILTYPES_NONE, RAILTYPES_NONE, 0,
{}, {} }; {}, {} };
for (; i < lengthof(_railtypes); i++) _railtypes[i] = empty_railtype; 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 (SignalType type = SIGTYPE_NORMAL; type < SIGTYPE_END; type = (SignalType)(type + 1)) {
for (SignalVariant var = SIG_ELECTRIC; var <= SIG_SEMAPHORE; var = (SignalVariant)(var + 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 red = GetCustomSignalSprite(rti, INVALID_TILE, type, var, 0, true).sprite;
PalSpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_GREEN, true).sprite; PalSpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, 255, true).sprite;
if (red.sprite != 0) { if (red.sprite != 0) {
rti->gui_sprites.signals[type][var][0] = { red.sprite + SIGNAL_TO_SOUTH, red.pal }; rti->gui_sprites.signals[type][var][0] = { red.sprite + SIGNAL_TO_SOUTH, red.pal };
} else { } else {
@@ -1392,6 +1392,10 @@ static void SetupBridgeTunnelSignalSimulation(TileIndex entrance, TileIndex exit
SetTunnelBridgeSignalSimulationEntrance(entrance); SetTunnelBridgeSignalSimulationEntrance(entrance);
SetTunnelBridgeEntranceSignalState(entrance, SIGNAL_STATE_GREEN); SetTunnelBridgeEntranceSignalState(entrance, SIGNAL_STATE_GREEN);
SetTunnelBridgeSignalSimulationExit(exit); SetTunnelBridgeSignalSimulationExit(exit);
if (_extra_aspects > 0) {
SetTunnelBridgeEntranceSignalAspect(entrance, 0);
UpdateAspectDeferred(entrance, GetTunnelBridgeEntranceTrackdir(entrance));
}
} }
static void ReReserveTrainPath(Train *v) static void ReReserveTrainPath(Train *v)
@@ -1504,6 +1508,10 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
SetTunnelBridgeSignalSimulationEntrance(t); SetTunnelBridgeSignalSimulationEntrance(t);
SetTunnelBridgeEntranceSignalState(t, SIGNAL_STATE_GREEN); SetTunnelBridgeEntranceSignalState(t, SIGNAL_STATE_GREEN);
SetTunnelBridgeSignalSimulationExit(t); SetTunnelBridgeSignalSimulationExit(t);
if (_extra_aspects > 0) {
SetTunnelBridgeEntranceSignalAspect(t, 0);
UpdateAspectDeferred(t, GetTunnelBridgeEntranceTrackdir(t));
}
}; };
if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC) { 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_TOP = 30 * ZOOM_LVL_BASE;
static const int SIGNAL_DIRTY_BOTTOM = 5 * 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; uint x, y;
GetSignalXY(tile, pos, 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; SpriteID sprite = result.sprite.sprite;
PaletteID pal = PAL_NONE; PaletteID pal = PAL_NONE;
bool is_custom_sprite = (sprite != 0); bool is_custom_sprite = (sprite != 0);

View File

@@ -352,7 +352,7 @@ static inline bool IsPresignalProgrammable(TileIndex t, Track track)
/** One-way signals can't be passed the 'wrong' way. */ /** One-way signals can't be passed the 'wrong' way. */
static inline bool IsOnewaySignal(TileIndex t, Track track) 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) 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); 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) * Set the states of the signals (Along/AgainstTrackDir)
* @param tile the tile to set the states for * @param tile the tile to set the states for

View File

@@ -3976,6 +3976,8 @@ bool AfterLoadGame()
extern void YapfCheckRailSignalPenalties(); extern void YapfCheckRailSignalPenalties();
YapfCheckRailSignalPenalties(); YapfCheckRailSignalPenalties();
UpdateExtraAspectsVariable();
if (_networking && !_network_server) { if (_networking && !_network_server) {
SlProcessVENC(); SlProcessVENC();
} }
@@ -4050,6 +4052,8 @@ void ReloadNewGRFData()
} }
} }
UpdateExtraAspectsVariable();
/* Update company statistics. */ /* Update company statistics. */
AfterLoadCompanyStats(); AfterLoadCompanyStats();
/* Check and update house and town values */ /* Check and update house and town values */

View File

@@ -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_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_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_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_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_WATER_FLOODING, XSCF_NULL, 2, 2, "water_flooding", nullptr, nullptr, nullptr },
{ XSLFI_MORE_HOUSES, XSCF_NULL, 2, 2, "more_houses", nullptr, nullptr, nullptr }, { XSLFI_MORE_HOUSES, XSCF_NULL, 2, 2, "more_houses", nullptr, nullptr, nullptr },

View File

@@ -26,6 +26,7 @@
extern TileIndex _cur_tileloop_tile; extern TileIndex _cur_tileloop_tile;
extern uint16 _disaster_delay; extern uint16 _disaster_delay;
extern byte _trees_tick_ctr; extern byte _trees_tick_ctr;
extern uint8 _extra_aspects;
/* Keep track of current game position */ /* Keep track of current game position */
int _saved_scrollpos_x; 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(_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(_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(_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), SLE_CONDNULL(4, SLV_11, SLV_120),
SLEG_END() SLEG_END()
}; };
@@ -124,6 +126,7 @@ static const SaveLoadGlobVarList _date_check_desc[] = {
SLE_CONDNULL(1, SLV_4, SL_MAX_VERSION), // _pause_mode 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_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(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), SLE_CONDNULL(4, SLV_11, SLV_120),
SLEG_END() SLEG_END()
}; };

View File

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

View File

@@ -20,9 +20,14 @@
#include "programmable_signals.h" #include "programmable_signals.h"
#include "error.h" #include "error.h"
#include "infrastructure_func.h" #include "infrastructure_func.h"
#include "tunnelbridge.h"
#include "bridge_signal_map.h"
#include "newgrf_newsignals.h"
#include "safeguards.h" #include "safeguards.h"
uint8 _extra_aspects = 0;
/// List of signals dependent upon this one /// List of signals dependent upon this one
typedef std::vector<SignalReference> SignalDependencyList; 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> _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_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 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; flags = SF_NONE;
num_exits = 0; num_exits = 0;
num_green = 0; num_green = 0;
out_signal_tile = INVALID_TILE;
out_signal_trackdir = INVALID_TRACKDIR;
} }
SigFlags flags; SigFlags flags;
uint num_exits; uint num_exits;
uint num_green; 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 (HasSignalOnTrackdir(tile, reversedir)) {
if (IsPbsSignalNonExtended(sig)) { if (IsPbsSignalNonExtended(sig)) {
info.flags |= SF_PBS; info.flags |= SF_PBS;
if (_extra_aspects > 0 && GetSignalStateByTrackdir(tile, reversedir) == SIGNAL_STATE_GREEN) {
_tbpset.Add(tile, reversedir);
}
} else if (!_tbuset.Add(tile, reversedir)) { } else if (!_tbuset.Add(tile, reversedir)) {
info.flags |= SF_FULL; info.flags |= SF_FULL;
return info; 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 (HasSignalOnTrackdir(tile, trackdir)) {
if (IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit if (!IsOnewaySignal(sig)) info.flags |= SF_PBS;
info.num_exits++; if (_extra_aspects > 0) {
if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit info.out_signal_tile = tile;
info.num_green++; 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 (IsTunnelBridgeSignalSimulationExit(tile)) {
if (IsTunnelBridgePBS(tile)) { if (IsTunnelBridgePBS(tile)) {
info.flags |= SF_PBS; 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)) { } else if (!_tbuset.Add(tile, INVALID_TRACKDIR)) {
info.flags |= SF_FULL; info.flags |= SF_FULL;
return info; 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 (!(info.flags & SF_TRAIN)) {
if (HasVehicleOnPos(tile, VEH_TRAIN, reinterpret_cast<void *>((uintptr_t)tile), &TrainInWormholeTileEnum)) 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)) { if (!(info.flags & SF_TRAIN) && IsTunnelBridgeSignalSimulationExit(tile)) {
@@ -510,6 +538,162 @@ static SigInfo ExploreSegment(Owner owner)
return info; 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 * Update signals around segment in _tbuset
@@ -529,13 +713,42 @@ static void UpdateSignalsAroundSegment(SigInfo info)
while (_tbuset.Get(&tile, &trackdir)) { while (_tbuset.Get(&tile, &trackdir)) {
if (IsTileType(tile, MP_TUNNELBRIDGE) && IsTunnelBridgeSignalSimulationExit(tile)) { 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 old_state = GetTunnelBridgeExitSignalState(tile);
SignalState new_state = (info.flags & SF_TRAIN) ? SIGNAL_STATE_RED : SIGNAL_STATE_GREEN; SignalState new_state = (info.flags & SF_TRAIN) ? SIGNAL_STATE_RED : SIGNAL_STATE_GREEN;
bool refresh = false;
if (old_state != new_state) { if (old_state != new_state) {
SetTunnelBridgeExitSignalState(tile, 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; continue;
} }
@@ -546,7 +759,18 @@ static void UpdateSignalsAroundSegment(SigInfo info)
SignalState newstate = SIGNAL_STATE_GREEN; SignalState newstate = SIGNAL_STATE_GREEN;
/* don't change signal state if tile is reserved in realistic braking mode */ /* 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 */ /* determine whether the new state is red */
if (info.flags & SF_TRAIN) { 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 */ /* only when the state changes */
if (newstate != GetSignalStateByTrackdir(tile, trackdir)) { if (newstate != current_state) {
if (IsExitSignal(sig)) { if (IsExitSignal(sig)) {
/* for pre-signal exits, add block to the global set */ /* for pre-signal exits, add block to the global set */
DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir)); DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
@@ -598,10 +843,32 @@ static void UpdateSignalsAroundSegment(SigInfo info)
MarkDependencidesForUpdate(SignalReference(tile, track)); MarkDependencidesForUpdate(SignalReference(tile, track));
} }
SetSignalStateByTrackdir(tile, trackdir, newstate); SetSignalStateByTrackdir(tile, trackdir, newstate);
refresh = true;
}
if (refresh) {
MarkSingleSignalDirty(tile, trackdir); 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() static inline void ResetSets()
{ {
_tbuset.Reset(); _tbuset.Reset();
_tbpset.Reset();
_tbdset.Reset(); _tbdset.Reset();
_globset.Reset(); _globset.Reset();
} }
@@ -943,3 +1211,277 @@ void CheckRemoveSignal(TileIndex tile, Track track)
_signal_dependencies.erase(i); _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();
}
}

View File

@@ -19,6 +19,8 @@
#include "settings_type.h" #include "settings_type.h"
#include "vehicle_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 * Maps a trackdir to the bit that stores its status in the map arrays, in the
* direction along with the trackdir. * direction along with the trackdir.
@@ -85,6 +87,12 @@ static inline bool IsProgrammableSignal(SignalType type)
return type == SIGTYPE_PROG; 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? /// Is this signal type unsuitable for realistic braking?
static inline bool IsSignalTypeUnsuitableForRealisticBraking(SignalType type) 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 AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner);
void UpdateSignalsInBuffer(); void UpdateSignalsInBuffer();
void UpdateSignalsInBufferIfOwnerNotAddable(Owner owner); 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 */ #endif /* SIGNAL_FUNC_H */

View File

@@ -909,6 +909,20 @@ class NIHRailType : public NIHelper {
if (secondary != INVALID_RAILTYPE) { if (secondary != INVALID_RAILTYPE) {
writeRailType(secondary); 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);
}
}
}
} }
}; };

View File

@@ -83,6 +83,9 @@ static const RailtypeInfo _original_railtypes[] = {
/* control flags */ /* control flags */
0, 0,
/* signal extra aspects */
0,
/* cost multiplier */ /* cost multiplier */
8, 8,
@@ -190,6 +193,9 @@ static const RailtypeInfo _original_railtypes[] = {
/* control flags */ /* control flags */
0, 0,
/* signal extra aspects */
0,
/* cost multiplier */ /* cost multiplier */
12, 12,
@@ -293,6 +299,9 @@ static const RailtypeInfo _original_railtypes[] = {
/* control flags */ /* control flags */
0, 0,
/* signal extra aspects */
0,
/* cost multiplier */ /* cost multiplier */
16, 16,
@@ -396,6 +405,9 @@ static const RailtypeInfo _original_railtypes[] = {
/* control flags */ /* control flags */
0, 0,
/* signal extra aspects */
0,
/* cost multiplier */ /* cost multiplier */
24, 24,

View File

@@ -2903,6 +2903,9 @@ void ReverseTrainDirection(Train *v)
if (IsTunnelBridgeWithSignalSimulation(v->tile) && IsTunnelBridgeSignalSimulationEntrance(v->tile)) { if (IsTunnelBridgeWithSignalSimulation(v->tile) && IsTunnelBridgeSignalSimulationEntrance(v->tile)) {
/* Flip signal on tunnel entrance tile red. */ /* Flip signal on tunnel entrance tile red. */
SetTunnelBridgeEntranceSignalState(v->tile, SIGNAL_STATE_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); MarkTileDirtyByTile(v->tile, VMDF_NOT_MAP_MODE);
update_check_tunnel_bridge_signal_counters(v); update_check_tunnel_bridge_signal_counters(v);
ClrBit(v->flags, VRF_TRAIN_STUCK); ClrBit(v->flags, VRF_TRAIN_STUCK);
@@ -3346,11 +3349,46 @@ static int GetAndClearLastBridgeEntranceSetSignalIndex(TileIndex bridge_entrance
return 0; 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) static void SetTunnelBridgeEntranceSignalGreen(TileIndex tile)
{ {
if (GetTunnelBridgeEntranceSignalState(tile) == SIGNAL_STATE_RED) { if (GetTunnelBridgeEntranceSignalState(tile) == SIGNAL_STATE_RED) {
SetTunnelBridgeEntranceSignalState(tile, SIGNAL_STATE_GREEN); SetTunnelBridgeEntranceSignalState(tile, SIGNAL_STATE_GREEN);
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE); 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. */ /* Clearing last bridge signal. */
int signal_offset = GetAndClearLastBridgeEntranceSetSignalIndex(end); int signal_offset = GetAndClearLastBridgeEntranceSetSignalIndex(end);
if (signal_offset) { 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); MarkTileDirtyByTile(last_signal_tile, VMDF_NOT_MAP_MODE);
if (_extra_aspects > 0) UpdateAspectFromBridgeMiddleSignalChange(end, diff, signal_offset - 1);
} }
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE); 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(end)) SetTunnelBridgeEntranceSignalGreen(end);
if (IsTunnelBridgeSignalSimulationEntrance(tile)) SetTunnelBridgeEntranceSignalGreen(tile); 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 (IsTunnelBridgeSignalSimulationExit(tile) && IsTunnelBridgeEffectivelyPBS(tile)) {
if (IsTunnelBridgePBS(tile)) { if (IsTunnelBridgePBS(tile)) {
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED); SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_RED);
if (_extra_aspects > 0) PropagateAspectChange(tile, GetTunnelBridgeExitTrackdir(tile), 0);
} else { } else {
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, GetTileOwner(tile)); UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, GetTileOwner(tile));
} }
@@ -3985,6 +4029,10 @@ static void TryLongReserveChooseTrainTrack(Train *v, TileIndex tile, Trackdir td
SetTunnelReservation(exit_tile, true); SetTunnelReservation(exit_tile, true);
} }
SetTunnelBridgeExitSignalState(exit_tile, SIGNAL_STATE_GREEN); 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); 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; do_track_reservation = true;
changed_signal = TrackEnterdirToTrackdir(track, enterdir); changed_signal = TrackEnterdirToTrackdir(track, enterdir);
SetSignalStateByTrackdir(tile, changed_signal, SIGNAL_STATE_GREEN); SetSignalStateByTrackdir(tile, changed_signal, SIGNAL_STATE_GREEN);
if (_extra_aspects > 0) {
SetSignalAspect(tile, track, 0);
UpdateAspectDeferred(tile, changed_signal);
}
} else if (!do_track_reservation) { } else if (!do_track_reservation) {
return track; 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; 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); SignalState exit_state = GetTunnelBridgeExitSignalState(tile);
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN); SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN);
if (_extra_aspects > 0) {
SetTunnelBridgeExitSignalAspect(tile, 0);
UpdateAspectDeferred(tile, GetTunnelBridgeExitTrackdir(tile));
}
TileIndex veh_orig_tile = t->tile; TileIndex veh_orig_tile = t->tile;
TrackBits veh_orig_track = t->track; TrackBits veh_orig_track = t->track;
Direction veh_orig_direction = t->direction; Direction veh_orig_direction = t->direction;
@@ -4978,6 +5034,10 @@ static bool CheckTrainStayInWormHolePathReserve(Train *t, TileIndex tile)
t->direction = veh_orig_direction; t->direction = veh_orig_direction;
if (ok && IsTunnelBridgeEffectivelyPBS(tile)) { if (ok && IsTunnelBridgeEffectivelyPBS(tile)) {
SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN); SetTunnelBridgeExitSignalState(tile, SIGNAL_STATE_GREEN);
if (_extra_aspects > 0) {
SetTunnelBridgeExitSignalAspect(tile, 0);
UpdateAspectDeferred(tile, GetTunnelBridgeExitTrackdir(tile));
}
mark_dirty = true; mark_dirty = true;
} }
return ok; return ok;
@@ -5033,6 +5093,9 @@ static void HandleSignalBehindTrain(Train *v, int signal_number)
} else if (IsBridge(v->tile) && signal_number >= 0) { } else if (IsBridge(v->tile) && signal_number >= 0) {
SetBridgeEntranceSimulatedSignalState(v->tile, signal_number, SIGNAL_STATE_GREEN); SetBridgeEntranceSimulatedSignalState(v->tile, signal_number, SIGNAL_STATE_GREEN);
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE); 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. */ /* Flip signal on tunnel entrance tile red. */
SetTunnelBridgeEntranceSignalState(gp.new_tile, SIGNAL_STATE_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); MarkTileDirtyByTile(gp.new_tile, VMDF_NOT_MAP_MODE);
if (IsTunnelBridgeSignalSimulationBidirectional(gp.new_tile)) { if (IsTunnelBridgeSignalSimulationBidirectional(gp.new_tile)) {
/* Set incoming signal in other direction to red as well */ /* Set incoming signal in other direction to red as well */
TileIndex other_end = GetOtherTunnelBridgeEnd(gp.new_tile); TileIndex other_end = GetOtherTunnelBridgeEnd(gp.new_tile);
SetTunnelBridgeEntranceSignalState(other_end, SIGNAL_STATE_RED); SetTunnelBridgeEntranceSignalState(other_end, SIGNAL_STATE_RED);
if (_extra_aspects > 0) {
PropagateAspectChange(other_end, GetTunnelBridgeEntranceTrackdir(other_end), 0);
}
MarkTileDirtyByTile(other_end, VMDF_NOT_MAP_MODE); MarkTileDirtyByTile(other_end, VMDF_NOT_MAP_MODE);
} }
} }

View File

@@ -1719,7 +1719,15 @@ static void DrawTunnelBridgeRampSingleSignal(const TileInfo *ti, bool is_green,
SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC; SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile)); 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); bool is_custom_sprite = (sprite.sprite != 0);
if (is_custom_sprite) { if (is_custom_sprite) {
@@ -1764,9 +1772,8 @@ static void DrawTunnelBridgeRampSignal(const TileInfo *ti)
} }
/* Draws a signal on tunnel / bridge entrance tile. */ /* 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; uint bridge_signal_position = 0;
int m2_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; SignalVariant variant = IsTunnelBridgeSemaphore(bridge_start_tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
SignalState state = GetBridgeEntranceSimulatedSignalState(bridge_start_tile, m2_position); 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)); 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) { if (sprite.sprite != 0) {
sprite.sprite += position; 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); AddSortableSpriteToDraw(sprite.sprite, sprite.pal, x, y, 1, 1, TILE_HEIGHT, z, false, 0, 0, BB_Z_SEPARATOR);
break;
} }
m2_position++; m2_position++;
} }
@@ -1979,7 +2005,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti, DrawTileProcParams params)
if (IsTunnelBridgeWithSignalSimulation(ti->tile)) { if (IsTunnelBridgeWithSignalSimulation(ti->tile)) {
extern void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition, 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); DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
SignalVariant variant = IsTunnelBridgeSemaphore(ti->tile) ? SIG_SEMAPHORE : SIG_ELECTRIC; 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); image = (SignalOffsets)(image ^ 1);
} }
if (IsTunnelBridgeSignalSimulationEntrance(ti->tile)) { 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)) { if (IsTunnelBridgeSignalSimulationExit(ti->tile)) {
SignalType type = SIGTYPE_NORMAL; SignalType type = SIGTYPE_NORMAL;
if (IsTunnelBridgePBS(ti->tile)) { if (IsTunnelBridgePBS(ti->tile)) {
type = IsTunnelBridgeSignalSimulationEntrance(ti->tile) ? SIGTYPE_PBS : SIGTYPE_PBS_ONEWAY; 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) { switch (t) {
@@ -2308,8 +2334,8 @@ void DrawBridgeMiddle(const TileInfo *ti)
if (HasRailCatenaryDrawn(GetRailType(rampsouth))) { if (HasRailCatenaryDrawn(GetRailType(rampsouth))) {
DrawRailCatenaryOnBridge(ti); DrawRailCatenaryOnBridge(ti);
} }
if (IsTunnelBridgeSignalSimulationEntrance(rampsouth)) DrawBridgeSignalOnMiddlePart(ti, rampsouth, z); if (IsTunnelBridgeSignalSimulationEntrance(rampsouth)) DrawBridgeSignalOnMiddlePart(ti, rampsouth, rampnorth, z);
if (IsTunnelBridgeSignalSimulationEntrance(rampnorth)) DrawBridgeSignalOnMiddlePart(ti, 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 */ /* draw roof, the component of the bridge which is logically between the vehicle and the camera */

View File

@@ -506,6 +506,30 @@ static inline void SetTunnelBridgePBS(TileIndex t, bool is_pbs)
SB(_me[t].m6, 6, 1, is_pbs ? 1 : 0); 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) static inline Trackdir GetTunnelBridgeExitTrackdir(TileIndex t, DiagDirection tunnel_bridge_dir)
{ {
return TrackEnterdirToTrackdir((Track)FIND_FIRST_BIT(GetAcrossTunnelBridgeTrackBits(t)), ReverseDiagDir(tunnel_bridge_dir)); return TrackEnterdirToTrackdir((Track)FIND_FIRST_BIT(GetAcrossTunnelBridgeTrackBits(t)), ReverseDiagDir(tunnel_bridge_dir));