From 191f710c404cba1312d51c389181944164641765 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 19 Jun 2022 17:43:46 +0100 Subject: [PATCH] Add custom signal style properties to set electric/semaphore enabled types --- docs/newgrf-additions-nml.html | 40 +++++++++++++++++++++++++++---- docs/newgrf-additions.html | 28 +++++++++++++++++++++- src/newgrf.cpp | 18 ++++++++++++++ src/newgrf_extension.cpp | 2 ++ src/newgrf_extension.h | 2 ++ src/newgrf_newsignals.h | 2 ++ src/newgrf_railtype.cpp | 6 +++-- src/rail_cmd.cpp | 35 +++++++++++++++++++++++---- src/rail_gui.cpp | 44 ++++++++++++++++++++++++++++++++-- 9 files changed, 164 insertions(+), 13 deletions(-) diff --git a/docs/newgrf-additions-nml.html b/docs/newgrf-additions-nml.html index e658cc1a77..7599bae8ed 100644 --- a/docs/newgrf-additions-nml.html +++ b/docs/newgrf-additions-nml.html @@ -456,24 +456,56 @@ item (FEAT_GLOBALVARS) { style_namestring - Set the name of the most recently defined style (defined using the define_style property).
+ Set the name of the most recently defined style (defined using the define_style property).
This property should be used if using the define_style property, as otherwise the style will have no name. + style_electric_enabledbitmask(SIGNAL_TYPE_XXX, ...) + + Set which electric signal types may be built using this signal style for the most recently defined style (defined using the define_style property).
+ At least one of this property and style_semaphore_enabled should be set to a non-zero value, as otherwise + no signal types will be enabled for this custom signal style.
+ If PROG or NO_ENTRY are set, it is not necessary to also set enable_programmable_pre_signals or enable_no_entry_signals. +
+
NORMAL
+
Normal/block signal
+
ENTRY
+
Pre-signal entry
+
EXIT
+
Pre-signal exit
+
COMBO
+
Pre-signal combo
+
PBS
+
Two-way PBS
+
PBS_ONEWAY
+
One-way PBS
+
PROG
+
Programmable pre-signal
+
NO_ENTRY
+
No-entry
+
+ + + style_semaphore_enabledbitmask(SIGNAL_TYPE_XXX, ...) + + Set which semaphore signal types may be built using this signal style for the most recently defined style (defined using the define_style property).
+ See style_electric_enabled, above. + + style_no_aspect_increase0 or 1 - Set whether the most recently defined style (defined using the define_style property) does not increase + Set whether the most recently defined style (defined using the define_style property) does not increase the signal aspect with respect to the signals either side (i.e. function like a banner repeater). style_always_reserve_through0 or 1 - Set whether reserve through is unconditionally enabled for the most recently defined style (defined using the define_style property). + Set whether reserve through is unconditionally enabled for the most recently defined style (defined using the define_style property). style_lookahead_extra_aspects0 - 6 - Set the look-ahead extra aspects for the most recently defined style (defined using the define_style property).
+ Set the look-ahead extra aspects for the most recently defined style (defined using the define_style property).
This property only makes a difference when the "limit train lookahead to signal aspect" game setting is enabled.
This limits the signal aspect which the hypothetical train driver can "read" from the signal without affecting signal aspect propagation to other signals, or variable extra_callback_info2.
Example values could include: 1 for traditional banner repeater signals, or 0 for shunt signals.
diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html index 52c2178171..5a76eaad01 100644 --- a/docs/newgrf-additions.html +++ b/docs/newgrf-additions.html @@ -431,9 +431,35 @@ 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 train electric signal types enabled (mappable property: signals_style_electric_enabled)

+

This applies to the most recent custom signal style defined using the signals_define_style property.
+ This sets which electric signal types may be built using this signal style.

+

At least one of this property and signals_style_semaphore_enabled should be set to a non-zero value, as otherwise + no signal types will be enabled for this custom signal style.

+

The property length is 4 bytes: + + + + + + + + + + +
BitValueMeaning
01Normal/block
12Pre-signal entry
24Pre-signal exit
38Pre-signal combo
410Two-way PBS
520One-way PBS
640Programmable pre-signal
780No-entry
+ If the programmable pre-signal and/or no-entry bits are set, it is not necessary to also set signals_enable_programmable_signals + or signals_enable_no_entry_signals. +

+

This is indicated by the feature name: action0_signals_style, version 1

+

Set custom signal style train semaphore signal types enabled (mappable property: signals_style_semaphore_enabled)

+

This applies to the most recent custom signal style defined using the signals_define_style property.
+ This sets which semaphore signal types may be built using this signal style.

+

The property length is 4 bytes and has an identical format to signals_style_electric_enabled, above.

+

This is indicated by the feature name: action0_signals_style, version 1

Set custom signal style no aspect increase mode (mappable property: signals_style_no_aspect_increase)

This applies to the most recent custom signal style defined using the signals_define_style property.
- When enabled, signals using this style do not increase the signal aspect with respect to the signals either side (i.e. function like a banner repeater).

+ When enabled, signals using this style do not increase the signal aspect with respect to the signals either side (i.e. the behaviour is like a banner repeater).

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.

diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 245a0a3133..70e1086da8 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4253,6 +4253,24 @@ static ChangeInfoResult SignalsChangeInfo(uint id, int numinfo, int prop, const break; } + case A0RPI_SIGNALS_STYLE_SEMAPHORE_ENABLED: { + if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break; + uint32 mask = buf->ReadDWord(); + if (_cur.grffile->current_new_signal_style != nullptr) { + _cur.grffile->current_new_signal_style->semaphore_mask = (uint8)mask; + } + break; + } + + case A0RPI_SIGNALS_STYLE_ELECTRIC_ENABLED: { + if (MappedPropertyLengthMismatch(buf, 4, mapping_entry)) break; + uint32 mask = buf->ReadDWord(); + if (_cur.grffile->current_new_signal_style != nullptr) { + _cur.grffile->current_new_signal_style->electric_mask = (uint8)mask; + } + break; + } + default: ret = HandleAction0PropertyDefault(buf, prop); break; diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp index 7510543658..50fb098228 100644 --- a/src/newgrf_extension.cpp +++ b/src/newgrf_extension.cpp @@ -97,6 +97,8 @@ extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = { GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_STYLE_NO_ASPECT_INCREASE, "signals_style_no_aspect_increase"), GRFPropertyMapDefinition(GSF_SIGNALS, A0RPI_SIGNALS_STYLE_ALWAYS_RESERVE_THROUGH, "signals_style_always_reserve_through"), 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_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 64910f179b..7674af06ae 100644 --- a/src/newgrf_extension.h +++ b/src/newgrf_extension.h @@ -41,6 +41,8 @@ enum Action0RemapPropertyIds { A0RPI_SIGNALS_STYLE_NO_ASPECT_INCREASE, A0RPI_SIGNALS_STYLE_ALWAYS_RESERVE_THROUGH, A0RPI_SIGNALS_STYLE_LOOKAHEAD_EXTRA_ASPECTS, + A0RPI_SIGNALS_STYLE_SEMAPHORE_ENABLED, + A0RPI_SIGNALS_STYLE_ELECTRIC_ENABLED, 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 dd59678f20..cac4f26934 100644 --- a/src/newgrf_newsignals.h +++ b/src/newgrf_newsignals.h @@ -36,6 +36,8 @@ struct NewSignalStyle { uint8 grf_local_id; uint8 style_flags; uint8 lookahead_extra_aspects; + uint8 semaphore_mask; + uint8 electric_mask; PalSpriteID signals[SIGTYPE_END][2][2]; }; diff --git a/src/newgrf_railtype.cpp b/src/newgrf_railtype.cpp index 8547918cee..350f93566e 100644 --- a/src/newgrf_railtype.cpp +++ b/src/newgrf_railtype.cpp @@ -163,8 +163,10 @@ CustomSignalSpriteResult GetCustomSignalSprite(const RailtypeInfo *rti, TileInde } for (const GRFFile *grf : _new_signals_grfs) { - if (type == SIGTYPE_PROG && !HasBit(grf->new_signal_ctrl_flags, NSCF_PROGSIG)) continue; - if (type == SIGTYPE_NO_ENTRY && !HasBit(grf->new_signal_ctrl_flags, NSCF_NOENTRYSIG)) continue; + if (style == 0) { + if (type == SIGTYPE_PROG && !HasBit(grf->new_signal_ctrl_flags, NSCF_PROGSIG)) continue; + if (type == SIGTYPE_NO_ENTRY && !HasBit(grf->new_signal_ctrl_flags, NSCF_NOENTRYSIG)) continue; + } if (!HasBit(grf->new_signal_style_mask, style)) continue; uint32 param1 = (context == CSSC_GUI) ? 0x10 : 0x00; diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index c82be6877d..4bfa85b882 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1501,6 +1501,13 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, CommandCost ret = CheckTileOwnership(tile); if (ret.Failed()) return ret; + auto is_style_usable = [](SignalVariant sigvar, uint8 style_id, uint8 mask) { + if (style_id == 0) return true; + + const NewSignalStyle &style = _new_signal_styles[style_id - 1]; + return ((sigvar == SIG_SEMAPHORE ? style.semaphore_mask : style.electric_mask) & mask) == mask; + }; + CommandCost cost; /* handle signals simulation on tunnel/bridge. */ if (IsTileType(tile, MP_TUNNELBRIDGE)) { @@ -1520,25 +1527,36 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, if (!(p2_signal_in && p2_signal_out)) { cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] * ((GetTunnelBridgeLength(tile, tile_exit) + 4) >> 2) * (bidirectional ? 2 : 1)); // minimal 1 if (HasBit(_no_tunnel_bridge_style_mask, signal_style)) return_cmd_error(STR_ERROR_UNSUITABLE_SIGNAL_TYPE); + if (!is_style_usable(sigvar, signal_style, bidirectional ? 0x11 : (is_pbs ? 0x21 : 0x1))) return_cmd_error(STR_ERROR_UNSUITABLE_SIGNAL_TYPE); } } else { if (HasBit(p1, 17)) return CommandCost(); - bool is_bidi = IsTunnelBridgeSignalSimulationBidirectional(tile); + const bool is_bidi = IsTunnelBridgeSignalSimulationBidirectional(tile); bool will_be_bidi = is_bidi; + const bool is_semaphore = IsTunnelBridgeSemaphore(tile); + bool will_be_semaphore = is_semaphore; + bool will_be_pbs = IsTunnelBridgePBS(tile); + const uint8 is_style = GetTunnelBridgeSignalStyle(tile); + uint8 will_be_style = is_style; if (!p2_active) { if (convert_signal) { will_be_bidi = bidirectional && !ctrl_pressed; - change_style = (signal_style != GetTunnelBridgeSignalStyle(tile)); + change_style = (signal_style != is_style); + will_be_style = signal_style; + will_be_pbs = is_pbs; + will_be_semaphore = (sigvar == SIG_SEMAPHORE); if (HasBit(_no_tunnel_bridge_style_mask, signal_style)) return_cmd_error(STR_ERROR_UNSUITABLE_SIGNAL_TYPE); } else if (ctrl_pressed) { will_be_bidi = false; + will_be_pbs = !will_be_pbs; } } else if (!is_pbs) { will_be_bidi = false; } - if ((p2_active && (sigvar == SIG_SEMAPHORE) != IsTunnelBridgeSemaphore(tile)) || - (convert_signal && (ctrl_pressed || (sigvar == SIG_SEMAPHORE) != IsTunnelBridgeSemaphore(tile)))) { + if ((p2_active && (sigvar == SIG_SEMAPHORE) != is_semaphore) || + (convert_signal && (ctrl_pressed || (sigvar == SIG_SEMAPHORE) != is_semaphore))) { flip_variant = true; + will_be_semaphore = !is_semaphore; } if (flip_variant || change_style) { cost = CommandCost(EXPENSES_CONSTRUCTION, ((_price[PR_BUILD_SIGNALS] * (will_be_bidi ? 2 : 1)) + (_price[PR_CLEAR_SIGNALS] * (is_bidi ? 2 : 1))) * @@ -1546,6 +1564,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, } else if (is_bidi != will_be_bidi) { cost = CommandCost(EXPENSES_CONSTRUCTION, _price[will_be_bidi ? PR_BUILD_SIGNALS : PR_CLEAR_SIGNALS] * ((GetTunnelBridgeLength(tile, tile_exit) + 4) >> 2)); // minimal 1 } + if (!is_style_usable(will_be_semaphore ? SIG_SEMAPHORE : SIG_ELECTRIC, will_be_style, will_be_bidi ? 0x11 : (will_be_pbs ? 0x21 : 0x1))) return_cmd_error(STR_ERROR_UNSUITABLE_SIGNAL_TYPE); } auto remove_pbs_bidi = [&]() { if (IsTunnelBridgeSignalSimulationBidirectional(tile)) { @@ -1677,10 +1696,12 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, if (!HasSignalOnTrack(tile, track)) { /* build new signals */ cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); + if (!is_style_usable(sigvar, signal_style, 1 << sigtype)) return_cmd_error(STR_ERROR_UNSUITABLE_SIGNAL_TYPE); } else { if (p2 != 0 && (sigvar != GetSignalVariant(tile, track) || signal_style != GetSignalStyle(tile, track))) { /* convert signals <-> semaphores and/or change style */ cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]); + if (!is_style_usable(sigvar, signal_style, 1 << sigtype)) return_cmd_error(STR_ERROR_UNSUITABLE_SIGNAL_TYPE); } else if (convert_signal) { /* convert button pressed */ @@ -1692,6 +1713,12 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, cost = CommandCost(); } + if (ctrl_pressed) { + if (!is_style_usable((GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC, GetSignalStyle(tile, track), 1 << GetSignalType(tile, track))) return_cmd_error(STR_ERROR_UNSUITABLE_SIGNAL_TYPE); + } else { + if (!is_style_usable(sigvar, signal_style, 1 << sigtype)) return_cmd_error(STR_ERROR_UNSUITABLE_SIGNAL_TYPE); + } + } else { /* it is free to change orientation/pre-exit-combo signals */ cost = CommandCost(); diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 31f9d95192..078fb0e663 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1835,7 +1835,42 @@ private: y + this->IsWidgetLowered(widget_index)); } - void SetSignalUIMode() { + void SetDisableStates() + { + for (int widget = WID_BS_SEMAPHORE_NORM; widget <= WID_BS_SEMAPHORE_NO_ENTRY; widget++) { + this->SetWidgetDisabledState(widget, _cur_signal_style > 0 && !HasBit(_new_signal_styles[_cur_signal_style - 1].semaphore_mask, TypeForClick(widget - WID_BS_SEMAPHORE_NORM))); + } + for (int widget = WID_BS_ELECTRIC_NORM; widget <= WID_BS_ELECTRIC_NO_ENTRY; widget++) { + this->SetWidgetDisabledState(widget, _cur_signal_style > 0 && !HasBit(_new_signal_styles[_cur_signal_style - 1].electric_mask, TypeForClick(widget - WID_BS_ELECTRIC_NORM))); + } + if (_cur_signal_style > 0) { + const NewSignalStyle &style = _new_signal_styles[_cur_signal_style - 1]; + if (!HasBit(_cur_signal_variant == SIG_SEMAPHORE ? style.semaphore_mask : style.electric_mask, _cur_signal_type)) { + /* Currently selected signal type isn't allowed, pick another */ + this->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_button); + + _cur_signal_variant = SIG_ELECTRIC; + _cur_signal_button = 0; + + const uint type_count = (WID_BS_SEMAPHORE_NO_ENTRY + 1 - WID_BS_SEMAPHORE_NORM); + for (uint i = 0; i < type_count * 2; i++) { + SignalVariant var = (i < type_count) ? SIG_ELECTRIC : SIG_SEMAPHORE; + uint button = i % type_count; + if (HasBit(var == SIG_SEMAPHORE ? style.semaphore_mask : style.electric_mask, TypeForClick(button))) { + _cur_signal_variant = var; + _cur_signal_button = button; + break; + } + } + + _cur_signal_type = TypeForClick(_cur_signal_button); + this->LowerWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_button); + } + } + } + + void SetSignalUIMode() + { this->all_signal_mode = (_settings_client.gui.signal_gui_mode == SIGNAL_GUI_ALL); this->realistic_braking_mode = (_settings_game.vehicle.train_braking_model == TBM_REALISTIC); this->progsig_ui_shown = _settings_client.gui.show_progsig_ui; @@ -1870,6 +1905,8 @@ private: this->SetWidgetDisabledState(WID_BS_TOGGLE_SIZE, this->realistic_braking_mode); this->GetWidget(WID_BS_STYLE_SEL)->SetDisplayedPlane(this->style_selector_shown ? 0 : SZSP_NONE); + + this->SetDisableStates(); } void ClearRemoveState() @@ -1958,7 +1995,9 @@ public: int var = SIG_SEMAPHORE - (widget - WID_BS_SEMAPHORE_NORM) / SIGTYPE_END; // SignalVariant order is reversed compared to the widgets. PalSpriteID sprite = { 0, 0 }; if (_cur_signal_style > 0) { - sprite = _new_signal_styles[_cur_signal_style - 1].signals[type][var][this->IsWidgetLowered(widget)]; + const NewSignalStyle &style = _new_signal_styles[_cur_signal_style - 1]; + if (!HasBit(var == SIG_SEMAPHORE ? style.semaphore_mask : style.electric_mask, type)) return; + sprite = style.signals[type][var][this->IsWidgetLowered(widget)]; } if (sprite.sprite == 0) { sprite = GetRailTypeInfo(_cur_railtype)->gui_sprites.signals[type][var][this->IsWidgetLowered(widget)]; @@ -2083,6 +2122,7 @@ public: switch (widget) { case WID_BS_STYLE: _cur_signal_style = std::min(index, _num_new_signal_styles); + this->SetDisableStates(); this->SetDirty(); break;