diff --git a/docs/newgrf-additions-nml.html b/docs/newgrf-additions-nml.html
index 7599bae8ed..8853be091f 100644
--- a/docs/newgrf-additions-nml.html
+++ b/docs/newgrf-additions-nml.html
@@ -512,6 +512,11 @@ item (FEAT_GLOBALVARS) {
The value is clamped to be less than or equal to the value set in the extra_aspects property.
+
no_default_style | 0 or 1 |
When enabled, custom signal graphics from this GRF are only used for custom signal styles, not the default style
diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html
index 5a76eaad01..5ccd66cd32 100644
--- a/docs/newgrf-additions.html
+++ b/docs/newgrf-additions.html
@@ -480,6 +480,13 @@
The Action 0 Id field is not used, the value is ignored.
This is indicated by the feature name: action0_signals_style, version 1
+ Set custom signal style signal drawn on opposite side (mappable property: signals_style_opposite_side)
+ This applies to the most recent custom signal style defined using the signals_define_style property.
+ When enabled, signals using this style are drawn on the opposite side of the track.
+ The property length is 1 byte. 0 is disabled (default). 1 is enabled.
+ The Action 0 Id field is not used, the value is ignored.
+
+ This is indicated by the feature name: action0_signals_style, version 1
Set whether custom signal sprites should not be used for the default signal style (mappable property: signals_no_default_style)
This applies to Action 2/3 Signals (Feature 0E) custom signal sprites for this GRF.
When enabled, this GRF is not used for the default signal style, it is only used for custom signal styles defined with signals_define_style.
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index 839a296964..799af476f8 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -4271,6 +4271,15 @@ static ChangeInfoResult SignalsChangeInfo(uint id, int numinfo, int prop, const
break;
}
+ case A0RPI_SIGNALS_STYLE_OPPOSITE_SIDE: {
+ if (MappedPropertyLengthMismatch(buf, 1, mapping_entry)) break;
+ uint8 value = buf->ReadByte();
+ if (_cur.grffile->current_new_signal_style != nullptr) {
+ SB(_cur.grffile->current_new_signal_style->style_flags, NSSF_OPPOSITE_SIDE, 1, (value != 0 ? 1 : 0));
+ }
+ break;
+ }
+
default:
ret = HandleAction0PropertyDefault(buf, prop);
break;
diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp
index 50fb098228..6733165d1a 100644
--- a/src/newgrf_extension.cpp
+++ b/src/newgrf_extension.cpp
@@ -99,6 +99,7 @@ extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = {
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_STYLE_LOOKAHEAD_EXTRA_ASPECTS, "signals_style_lookahead_extra_aspects"),
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_STYLE_SEMAPHORE_ENABLED, "signals_style_semaphore_enabled"),
GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_STYLE_ELECTRIC_ENABLED, "signals_style_electric_enabled"),
+ GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_STYLE_OPPOSITE_SIDE, "signals_style_opposite_side"),
GRFPropertyMapDefinition(GSF_OBJECTS, A0RPI_OBJECT_USE_LAND_GROUND, "object_use_land_ground"),
GRFPropertyMapDefinition(GSF_OBJECTS, A0RPI_OBJECT_EDGE_FOUNDATION_MODE, "object_edge_foundation_mode"),
GRFPropertyMapDefinition(GSF_OBJECTS, A0RPI_OBJECT_FLOOD_RESISTANT, "object_flood_resistant"),
diff --git a/src/newgrf_extension.h b/src/newgrf_extension.h
index 7674af06ae..f504352708 100644
--- a/src/newgrf_extension.h
+++ b/src/newgrf_extension.h
@@ -43,6 +43,7 @@ enum Action0RemapPropertyIds {
A0RPI_SIGNALS_STYLE_LOOKAHEAD_EXTRA_ASPECTS,
A0RPI_SIGNALS_STYLE_SEMAPHORE_ENABLED,
A0RPI_SIGNALS_STYLE_ELECTRIC_ENABLED,
+ A0RPI_SIGNALS_STYLE_OPPOSITE_SIDE,
A0RPI_OBJECT_USE_LAND_GROUND,
A0RPI_OBJECT_EDGE_FOUNDATION_MODE,
A0RPI_OBJECT_FLOOD_RESISTANT,
diff --git a/src/newgrf_newsignals.h b/src/newgrf_newsignals.h
index cac4f26934..1329780672 100644
--- a/src/newgrf_newsignals.h
+++ b/src/newgrf_newsignals.h
@@ -28,6 +28,7 @@ enum NewSignalStyleFlags {
NSSF_NO_ASPECT_INC = 0,
NSSF_ALWAYS_RESERVE_THROUGH = 1,
NSSF_LOOKAHEAD_ASPECTS_SET = 2,
+ NSSF_OPPOSITE_SIDE = 3,
};
struct NewSignalStyle {
diff --git a/src/rail.h b/src/rail.h
index e645184fea..268e49e149 100644
--- a/src/rail.h
+++ b/src/rail.h
@@ -479,7 +479,7 @@ static inline Money SignalMaintenanceCost(uint32 num)
}
void MarkSingleSignalDirty(TileIndex tile, Trackdir td);
-void MarkSingleSignalDirtyAtZ(TileIndex tile, Trackdir td, uint z);
+void MarkSingleSignalDirtyAtZ(TileIndex tile, Trackdir td, bool opposite_side, uint z);
void DrawTrainDepotSprite(int x, int y, int image, RailType railtype);
int TicksToLeaveDepot(const Train *v);
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 7a2f8e33d6..c3a89444e5 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -2763,7 +2763,7 @@ static uint GetSaveSlopeZ(uint x, uint y, Track track)
return GetSlopePixelZ(x, y);
}
-static void GetSignalXY(TileIndex tile, uint pos, uint &x, uint &y)
+static void GetSignalXY(TileIndex tile, uint pos, bool opposite, uint &x, uint &y)
{
bool side;
switch (_settings_game.construction.train_signal_side) {
@@ -2771,6 +2771,7 @@ static void GetSignalXY(TileIndex tile, uint pos, uint &x, uint &y)
case 2: side = true; break; // right
default: side = _settings_game.vehicle.road_side != 0; break; // driving side
}
+ side ^= opposite;
static const Point SignalPositions[2][12] = {
{ // Signals on the left side
/* LEFT LEFT RIGHT RIGHT UPPER UPPER */
@@ -2796,8 +2797,25 @@ void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, Sign
if (type == SIGTYPE_NO_ENTRY) pos ^= 1;
+ uint8 style = 0;
+ if (_num_new_signal_styles > 0) {
+ switch (context) {
+ case CSSC_TRACK:
+ style = GetSignalStyle(tile, track);
+ break;
+
+ case CSSC_TUNNEL_BRIDGE_ENTRANCE:
+ case CSSC_TUNNEL_BRIDGE_EXIT:
+ style = GetTunnelBridgeSignalStyle(tile);
+ break;
+
+ default:
+ break;
+ }
+ }
+
uint x, y;
- GetSignalXY(tile, pos, x, y);
+ GetSignalXY(tile, pos, HasBit(_signal_opposite_side_style_mask, style), x, y);
uint8 aspect;
if (condition == SIGNAL_STATE_GREEN) {
@@ -2824,23 +2842,6 @@ void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, Sign
aspect = 0;
}
- uint8 style = 0;
- if (_num_new_signal_styles > 0) {
- switch (context) {
- case CSSC_TRACK:
- style = GetSignalStyle(tile, track);
- break;
-
- case CSSC_TUNNEL_BRIDGE_ENTRANCE:
- case CSSC_TUNNEL_BRIDGE_EXIT:
- style = GetTunnelBridgeSignalStyle(tile);
- break;
-
- default:
- break;
- }
- }
-
const CustomSignalSpriteResult result = GetCustomSignalSprite(rti, tile, type, variant, aspect, context, style, prog);
SpriteID sprite = result.sprite.sprite;
PaletteID pal = PAL_NONE;
@@ -2926,7 +2927,7 @@ static void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track trac
}
template
-void MarkSingleSignalDirtyIntl(TileIndex tile, Trackdir td, F get_z)
+void MarkSingleSignalDirtyIntl(TileIndex tile, Trackdir td, bool opposite, F get_z)
{
static const uint8 trackdir_to_pos[TRACKDIR_END] = {
8, // TRACKDIR_X_NE
@@ -2948,7 +2949,7 @@ void MarkSingleSignalDirtyIntl(TileIndex tile, Trackdir td, F get_z)
};
uint x, y;
- GetSignalXY(tile, trackdir_to_pos[td], x, y);
+ GetSignalXY(tile, trackdir_to_pos[td], opposite, x, y);
Point pt = RemapCoords(x, y, get_z(x, y));
MarkAllViewportsDirty(
pt.x - SIGNAL_DIRTY_LEFT,
@@ -2965,15 +2966,18 @@ void MarkSingleSignalDirty(TileIndex tile, Trackdir td)
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
return;
}
-
- MarkSingleSignalDirtyIntl(tile, td, [td](uint x, uint y) -> uint {
+ bool opposite = false;
+ if (_signal_opposite_side_style_mask != 0) {
+ opposite = HasBit(_signal_opposite_side_style_mask, GetSignalStyleGeneric(tile, TrackdirToTrack(td)));
+ }
+ MarkSingleSignalDirtyIntl(tile, td, opposite, [td](uint x, uint y) -> uint {
return GetSaveSlopeZ(x, y, TrackdirToTrack(td));
});
}
-void MarkSingleSignalDirtyAtZ(TileIndex tile, Trackdir td, uint z)
+void MarkSingleSignalDirtyAtZ(TileIndex tile, Trackdir td, bool opposite_side, uint z)
{
- MarkSingleSignalDirtyIntl(tile, td, [z](uint x, uint y) -> uint {
+ MarkSingleSignalDirtyIntl(tile, td, opposite_side, [z](uint x, uint y) -> uint {
return z;
});
}
diff --git a/src/rail_map.h b/src/rail_map.h
index 97db9f6f33..c2edc84fc9 100644
--- a/src/rail_map.h
+++ b/src/rail_map.h
@@ -409,6 +409,18 @@ static inline uint8 GetSignalStyle(TileIndex t, Track track)
return GB(_me[t].m6, pos, 4);
}
+static inline uint8 GetSignalStyleGeneric(TileIndex t, Track track)
+{
+ switch (GetTileType(t)) {
+ case MP_RAILWAY:
+ return GetSignalStyle(t, track);
+ case MP_TUNNELBRIDGE:
+ return GetTunnelBridgeSignalStyle(t);
+ default:
+ return 0;
+ }
+}
+
static inline void SetSignalStyle(TileIndex t, Track track, uint8 style)
{
assert_tile(GetRailTileType(t) == RAIL_TILE_SIGNALS, t);
diff --git a/src/signal.cpp b/src/signal.cpp
index 11affb0b01..04f33831e7 100644
--- a/src/signal.cpp
+++ b/src/signal.cpp
@@ -33,6 +33,7 @@ uint64 _aspect_cfg_hash = 0;
uint16 _non_aspect_inc_style_mask = 0;
uint16 _always_reserve_through_style_mask = 0;
uint16 _no_tunnel_bridge_style_mask = 0;
+uint16 _signal_opposite_side_style_mask = 0;
bool _signal_sprite_oversized = false;
/// List of signals dependent upon this one
@@ -1513,6 +1514,7 @@ static bool DetermineExtraAspectsVariable()
_non_aspect_inc_style_mask = 0;
_no_tunnel_bridge_style_mask = 0;
_always_reserve_through_style_mask = 0;
+ _signal_opposite_side_style_mask = 0;
if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC) {
for (RailType r = RAILTYPE_BEGIN; r != RAILTYPE_END; r++) {
@@ -1536,6 +1538,9 @@ static bool DetermineExtraAspectsVariable()
} else {
_new_signal_styles[i].lookahead_extra_aspects = _new_signal_styles[i].grffile->new_signal_extra_aspects;
}
+ if (HasBit(_new_signal_styles[i].style_flags, NSSF_OPPOSITE_SIDE)) {
+ SetBit(_signal_opposite_side_style_mask, i + 1);
+ }
}
for (uint i = _num_new_signal_styles; i < MAX_NEW_SIGNAL_STYLES; i++) {
_new_signal_styles[i].lookahead_extra_aspects = new_extra_aspects;
diff --git a/src/signal_func.h b/src/signal_func.h
index 9e3ea80972..126fd4b506 100644
--- a/src/signal_func.h
+++ b/src/signal_func.h
@@ -24,6 +24,7 @@ extern uint64 _aspect_cfg_hash;
extern uint16 _non_aspect_inc_style_mask;
extern uint16 _always_reserve_through_style_mask;
extern uint16 _no_tunnel_bridge_style_mask;
+extern uint16 _signal_opposite_side_style_mask;
extern bool _signal_sprite_oversized;
/**
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index c60cc11020..2eecbb7ed2 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -1729,9 +1729,12 @@ static void DrawBridgeRoadBits(TileIndex head_tile, int x, int y, int z, int off
static void DrawTunnelBridgeRampSingleSignal(const TileInfo *ti, bool is_green, uint position, SignalType type, bool show_exit)
{
- bool side = (_settings_game.vehicle.road_side != 0) &&_settings_game.construction.train_signal_side;
+ bool side = (_settings_game.vehicle.road_side != 0) && _settings_game.construction.train_signal_side;
DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
+ uint8 style = GetTunnelBridgeSignalStyle(ti->tile);
+ side ^= HasBit(_signal_opposite_side_style_mask, style);
+
static const Point SignalPositions[2][4] = {
{ /* X X Y Y Signals on the left side */
{13, 3}, { 2, 13}, { 3, 4}, {13, 14}
@@ -1761,7 +1764,7 @@ static void DrawTunnelBridgeRampSingleSignal(const TileInfo *ti, bool is_green,
}
bool show_restricted = IsTunnelBridgeRestrictedSignal(ti->tile);
const TraceRestrictProgram *prog = show_restricted ? GetExistingTraceRestrictProgram(ti->tile, FindFirstTrack(GetAcrossTunnelBridgeTrackBits(ti->tile))) : nullptr;
- const CustomSignalSpriteResult result = GetCustomSignalSprite(rti, ti->tile, type, variant, aspect, show_exit ? CSSC_TUNNEL_BRIDGE_EXIT : CSSC_TUNNEL_BRIDGE_ENTRANCE, GetTunnelBridgeSignalStyle(ti->tile), prog);
+ const CustomSignalSpriteResult result = GetCustomSignalSprite(rti, ti->tile, type, variant, aspect, show_exit ? CSSC_TUNNEL_BRIDGE_EXIT : CSSC_TUNNEL_BRIDGE_ENTRANCE, style, prog);
PalSpriteID sprite = result.sprite;
bool is_custom_sprite = (sprite.sprite != 0);
@@ -1827,9 +1830,10 @@ static void DrawTunnelBridgeRampSignal(const TileInfo *ti)
}
}
-static void GetBridgeSignalXY(TileIndex tile, DiagDirection bridge_direction, uint &position, uint &x, uint &y)
+static void GetBridgeSignalXY(TileIndex tile, DiagDirection bridge_direction, bool opposite_side, uint &position, uint &x, uint &y)
{
bool side = (_settings_game.vehicle.road_side != 0) && _settings_game.construction.train_signal_side;
+ side ^= opposite_side;
static const Point SignalPositions[2][4] = {
{ /* X X Y Y Signals on the left side */
@@ -1863,9 +1867,10 @@ static void DrawBridgeSignalOnMiddlePart(const TileInfo *ti, TileIndex bridge_st
while (bridge_signal_position <= bridge_section) {
bridge_signal_position += simulated_wormhole_signals;
if (bridge_signal_position == bridge_section) {
+ uint8 style = GetBridgeSignalStyle(bridge_start_tile);
uint position, x, y;
- GetBridgeSignalXY(ti->tile, GetTunnelBridgeDirection(bridge_start_tile), position, x, y);
+ GetBridgeSignalXY(ti->tile, GetTunnelBridgeDirection(bridge_start_tile), HasBit(_signal_opposite_side_style_mask, style), position, x, y);
SignalVariant variant = IsTunnelBridgeSemaphore(bridge_start_tile) ? SIG_SEMAPHORE : SIG_ELECTRIC;
SignalState state = GetBridgeEntranceSimulatedSignalState(bridge_start_tile, m2_position);
@@ -1889,7 +1894,7 @@ static void DrawBridgeSignalOnMiddlePart(const TileInfo *ti, TileIndex bridge_st
}
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(bridge_start_tile));
- PalSpriteID sprite = GetCustomSignalSprite(rti, bridge_start_tile, SIGTYPE_NORMAL, variant, aspect, CSSC_BRIDGE_MIDDLE, GetBridgeSignalStyle(bridge_start_tile)).sprite;
+ PalSpriteID sprite = GetCustomSignalSprite(rti, bridge_start_tile, SIGTYPE_NORMAL, variant, aspect, CSSC_BRIDGE_MIDDLE, style).sprite;
if (sprite.sprite != 0) {
sprite.sprite += position;
@@ -1920,8 +1925,13 @@ void MarkSingleBridgeSignalDirty(TileIndex tile, TileIndex bridge_start_tile)
return;
}
+ bool opposite_side = false;
+ if (_signal_opposite_side_style_mask != 0) {
+ opposite_side = HasBit(_signal_opposite_side_style_mask, GetTunnelBridgeSignalStyle(bridge_start_tile));
+ }
+
uint position, x, y;
- GetBridgeSignalXY(tile, GetTunnelBridgeDirection(bridge_start_tile), position, x, y);
+ GetBridgeSignalXY(tile, GetTunnelBridgeDirection(bridge_start_tile), opposite_side, position, x, y);
Point pt = RemapCoords(x, y, GetBridgePixelHeight(bridge_start_tile) + 5 - BRIDGE_Z_START);
MarkAllViewportsDirty(
pt.x - SIGNAL_DIRTY_LEFT,
@@ -1939,15 +1949,22 @@ void MarkTunnelBridgeSignalDirty(TileIndex tile, bool exit)
return;
}
+ bool opposite_side = false;
+ if (_signal_opposite_side_style_mask != 0) {
+ opposite_side = HasBit(_signal_opposite_side_style_mask, GetTunnelBridgeSignalStyle(tile));
+ }
+
if (IsRailCustomBridgeHeadTile(tile)) {
Trackdir td = exit ? GetTunnelBridgeExitTrackdir(tile) : GetTunnelBridgeEntranceTrackdir(tile);
- MarkSingleSignalDirtyAtZ(tile, td, GetTileMaxPixelZ(tile));
+ MarkSingleSignalDirtyAtZ(tile, td, opposite_side, GetTileMaxPixelZ(tile));
return;
}
bool side = (_settings_game.vehicle.road_side != 0) && _settings_game.construction.train_signal_side;
DiagDirection dir = GetTunnelBridgeDirection(tile);
+ side ^= opposite_side;
+
uint position;
switch (dir) {
default: NOT_REACHED();
|