Add custom signal style properties to set electric/semaphore enabled types

pull/400/head
Jonathan G Rennison 2 years ago
parent 175c5d4d50
commit 191f710c40

@ -456,24 +456,56 @@ item (FEAT_GLOBALVARS) {
</tr>
<tr><td>style_name</td><td>string</td>
<td>
Set the name of the most recently defined style (defined using the define_style property).<br />
Set the name of the most recently defined style (defined using the <span class="code">define_style</span> property).<br />
This property should be used if using the define_style property, as otherwise the style will have no name.
</td>
</tr>
<tr><td>style_electric_enabled</td><td>bitmask(SIGNAL_TYPE_XXX, ...)</td>
<td>
Set which electric signal types may be built using this signal style for the most recently defined style (defined using the <span class="code">define_style</span> property).<br />
At least one of this property and <span class="code">style_semaphore_enabled</span> should be set to a non-zero value, as otherwise
no signal types will be enabled for this custom signal style.<br />
If PROG or NO_ENTRY are set, it is not necessary to also set <span class="code">enable_programmable_pre_signals</span> or <span class="code">enable_no_entry_signals</span>.
<dl>
<dt>NORMAL</dt>
<dd>Normal/block signal</dd>
<dt>ENTRY</dt>
<dd>Pre-signal entry</dd>
<dt>EXIT</dt>
<dd>Pre-signal exit</dd>
<dt>COMBO</dt>
<dd>Pre-signal combo</dd>
<dt>PBS</dt>
<dd>Two-way PBS</dd>
<dt>PBS_ONEWAY</dt>
<dd>One-way PBS</dd>
<dt>PROG</dt>
<dd>Programmable pre-signal</dd>
<dt>NO_ENTRY</dt>
<dd>No-entry</dd>
</dl>
</td>
</tr>
<tr><td>style_semaphore_enabled</td><td>bitmask(SIGNAL_TYPE_XXX, ...)</td>
<td>
Set which semaphore signal types may be built using this signal style for the most recently defined style (defined using the <span class="code">define_style</span> property).<br />
See <span class="code">style_electric_enabled</span>, above.
</td>
</tr>
<tr><td>style_no_aspect_increase</td><td>0 or 1</td>
<td>
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 <span class="code">define_style</span> property) does not increase
the signal aspect with respect to the signals either side (i.e. function like a banner repeater).
</td>
</tr>
<tr><td>style_always_reserve_through</td><td>0 or 1</td>
<td>
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 <span class="code">define_style</span> property).
</td>
</tr>
<tr><td>style_lookahead_extra_aspects</td><td>0 - 6</td>
<td>
Set the look-ahead extra aspects for the most recently defined style (defined using the define_style property).<br />
Set the look-ahead extra aspects for the most recently defined style (defined using the <span class="code">define_style</span> property).<br />
This property only makes a difference when the "limit train lookahead to signal aspect" game setting is enabled.<br />
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 <span class="code">extra_callback_info2</span>.<br />
Example values could include: 1 for traditional banner repeater signals, or 0 for shunt signals.<br />

@ -431,9 +431,35 @@
The Action 0 Id field is not used, the value is ignored.
</p>
<p>This is indicated by the feature name: <font face="monospace">action0_signals_style</font>, version 1</p>
<h4 id="signals_style_electric_enabled">Set custom signal style train electric signal types enabled (mappable property: signals_style_electric_enabled)</h4>
<p>This applies to the most recent custom signal style defined using the <a href="#signals_define_style">signals_define_style</a> property.<br />
This sets which electric signal types may be built using this signal style.</p>
<p>At least one of this property and <a href="#signals_style_semaphore_enabled">signals_style_semaphore_enabled</a> should be set to a non-zero value, as otherwise
no signal types will be enabled for this custom signal style.</p>
<p>The property length is 4 bytes:
<table>
<tr><th>Bit</th><th>Value</th><th>Meaning</th></tr>
<tr><td>0</td><td>1</td><td>Normal/block</td></tr>
<tr><td>1</td><td>2</td><td>Pre-signal entry</td></tr>
<tr><td>2</td><td>4</td><td>Pre-signal exit</td></tr>
<tr><td>3</td><td>8</td><td>Pre-signal combo</td></tr>
<tr><td>4</td><td>10</td><td>Two-way PBS</td></tr>
<tr><td>5</td><td>20</td><td>One-way PBS</td></tr>
<tr><td>6</td><td>40</td><td>Programmable pre-signal</td></tr>
<tr><td>7</td><td>80</td><td>No-entry</td></tr>
</table>
If the programmable pre-signal and/or no-entry bits are set, it is not necessary to also set <a href="#signals_enable_programmable_signals">signals_enable_programmable_signals</a>
or <a href="#signals_enable_no_entry_signals">signals_enable_no_entry_signals</a>.
</p>
<p>This is indicated by the feature name: <font face="monospace">action0_signals_style</font>, version 1</p>
<h4 id="signals_style_semaphore_enabled">Set custom signal style train semaphore signal types enabled (mappable property: signals_style_semaphore_enabled)</h4>
<p>This applies to the most recent custom signal style defined using the <a href="#signals_define_style">signals_define_style</a> property.<br />
This sets which semaphore signal types may be built using this signal style.</p>
<p>The property length is 4 bytes and has an identical format to <a href="#signals_style_electric_enabled">signals_style_electric_enabled</a>, above.</p>
<p>This is indicated by the feature name: <font face="monospace">action0_signals_style</font>, version 1</p>
<h4 id="signals_style_no_aspect_increase">Set custom signal style no aspect increase mode (mappable property: signals_style_no_aspect_increase)</h4>
<p>This applies to the most recent custom signal style defined using the <a href="#signals_define_style">signals_define_style</a> property.<br />
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).</p>
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).</p>
<p>The property length is 1 byte. 0 is disabled (default). 1 is enabled.<br />
The Action 0 Id field is not used, the value is ignored.
</p>

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

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

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

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

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

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

@ -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<NWidgetStacked>(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<uint>(index, _num_new_signal_styles);
this->SetDisableStates();
this->SetDirty();
break;

Loading…
Cancel
Save