GRF: Add second success indicator mechanism for feature tests and ID mappings

pull/491/head
Jonathan G Rennison 1 year ago
parent 85de07dee1
commit c756b8de8e

@ -81,8 +81,21 @@
If the test is successful, the bit is set (to 1), otherwise the bit is cleared (to 0).<br /> If the test is successful, the bit is set (to 1), otherwise the bit is cleared (to 0).<br />
The bit number MUST be in the range: 4 &le; bit number &le; 31. These bits can be assumed to be 0 on implementations which do not support this feature test mechanism.<br /> The bit number MUST be in the range: 4 &le; bit number &le; 31. These bits can be assumed to be 0 on implementations which do not support this feature test mechanism.<br />
Global variable 0x9D can then be tested by using a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/Action7">Action 7 or 9</a>, or a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2">Variational Action 2</a>.<br /> Global variable 0x9D can then be tested by using a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/Action7">Action 7 or 9</a>, or a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2">Variational Action 2</a>.<br />
If this field is omitted, no bit is set or cleared, and the test is not observable. If this field is omitted, no bit is set or cleared, and the test is not observable (unless the SVAL chunk is used).
</p> </p>
<h4 id="FTST-SVAL">Feature Set Global Variable 0x91 Success Test Value: C "FTST" -> B "SVAL"</h4>
<p>Within an FTST chunk, the SVAL binary (type B) field contains a 32 bit value which can be tested using the mechanism below. This is 4 bytes.<br />
If the test is successful, the value is added to a list of successful result values which can be checked using the mechanism below.<br />
If the test is unsuccessful, no action is taken.<br />
The value must be &gt; 0, a value of 0 has no effect.</p>
<p>To check if the test succeeded (the value has been added to the list of successful result values), use <a href="https://newgrf-specs.tt-wiki.net/wiki/Action7">Action 7 or 9</a>.<br />
The parameter to test is global variable 0x91 (this always has a value of 0, equivalent to the case where the test has failed, if this feature test mechanism is not present).<br />
The condition-type to use is: 02 (\7=, equality) to jump if the value is in the list (the test succeeded), or 03 (\7!, inequality) to jump if the value is not in the list (the test did not succeed).<br />
The value in the Action 7 or 9 must match the value specified in this SVAL chunk.
</p>
<p>If this field is omitted, the test is not observable (unless the SETP chunk is used).</p>
<p>This chunk and the associated test mechanism requires the feature name: <font face="monospace">feature_test</font>, version 2.<br />
Using this chunk instead or as well as SETP allows observing the result of more than 28 feature tests.</p>
<br /> <br />
<h4 id="FTST-example">Example NFO:</h4> <h4 id="FTST-example">Example NFO:</h4>
<pre> <pre>
@ -134,6 +147,9 @@
Global variable 0x8D can then be tested by using a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/Action7">Action 7 or 9</a>, or a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2">Variational Action 2</a>.<br /> Global variable 0x8D can then be tested by using a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/Action7">Action 7 or 9</a>, or a standard <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2">Variational Action 2</a>.<br />
If this field is omitted, no bit is set or cleared. If this field is omitted, no bit is set or cleared.
</p> </p>
<h4 id="A0PM-SVAL">Success Indicator Global Variable 0x91 Test Value: C "A0PM" -> B "SVAL"</h4>
<p>This behaves identically to the <a href="#FTST-SVAL">C "FTST" -> B "SVAL"</a> case, above</p>
<p>This chunk and the associated test mechanism requires the feature name: <font face="monospace">property_mapping</font>, version 2.</p>
<h4 id="A0PM-FLBK">Fallback Mode: C "A0PM" -> B "FLBK"</h4> <h4 id="A0PM-FLBK">Fallback Mode: C "A0PM" -> B "FLBK"</h4>
<p>Within an A0PM chunk, the FLBK binary (type B) field contains the fallback mode. This is 1 byte.<br /> <p>Within an A0PM chunk, the FLBK binary (type B) field contains the fallback mode. This is 1 byte.<br />
The fallback mode may take the following values: The fallback mode may take the following values:
@ -656,6 +672,9 @@
If this is not specified, a value of 0 is assumed.</p> If this is not specified, a value of 0 is assumed.</p>
<h4 id="A2VM-SETT">Success Indicator Global Variable 0x8D Bit: C "A2VM" -> B "SETT"</h4> <h4 id="A2VM-SETT">Success Indicator Global Variable 0x8D Bit: C "A2VM" -> B "SETT"</h4>
<p>This behaves identically to the <a href="#A0PM-SETT">C "A0PM" -> B "SETT"</a> case, above</p> <p>This behaves identically to the <a href="#A0PM-SETT">C "A0PM" -> B "SETT"</a> case, above</p>
<h4 id="A2VM-SVAL">Success Indicator Global Variable 0x91 Test Value: C "A2VM" -> B "SVAL"</h4>
<p>This behaves identically to the <a href="#FTST-SVAL">C "FTST" -> B "SVAL"</a> case, above</p>
<p>This chunk and the associated test mechanism requires the feature name: <font face="monospace">variable_mapping</font>, version 3.</p>
<h4 id="A2VM-VPRM">Replacement parameter: C "A2VM" -> B "VPRM"</h4> <h4 id="A2VM-VPRM">Replacement parameter: C "A2VM" -> B "VPRM"</h4>
<p>Within an A2VM chunk, the VPRM binary (type B) field contains the Variational Action 2 parameter value (as in 60+X variables) to use on the mapped variable. This is 4 bytes.<br /> <p>Within an A2VM chunk, the VPRM binary (type B) field contains the Variational Action 2 parameter value (as in 60+X variables) to use on the mapped variable. This is 4 bytes.<br />
If this is not specified, a value of 0 is assumed.<br /> If this is not specified, a value of 0 is assumed.<br />
@ -805,6 +824,9 @@
It is possible to override existing type IDs, however this use is not recommended.</p> It is possible to override existing type IDs, however this use is not recommended.</p>
<h4 id="A5TM-SETT">Success Indicator Global Variable 0x8D Bit: C "A5TM" -> B "SETT"</h4> <h4 id="A5TM-SETT">Success Indicator Global Variable 0x8D Bit: C "A5TM" -> B "SETT"</h4>
<p>This behaves identically to the <a href="#A0PM-SETT">C "A0PM" -> B "SETT"</a> case, above</p> <p>This behaves identically to the <a href="#A0PM-SETT">C "A0PM" -> B "SETT"</a> case, above</p>
<h4 id="A5TM-SVAL">Success Indicator Global Variable 0x91 Test Value: C "A5TM" -> B "SVAL"</h4>
<p>This behaves identically to the <a href="#FTST-SVAL">C "FTST" -> B "SVAL"</a> case, above</p>
<p>This chunk and the associated test mechanism requires the feature name: <font face="monospace">action5_type_id_mapping</font>, version 2.</p>
<h4 id="A5TM-FLBK">Fallback Mode: C "A5TM" -> B "FLBK"</h4> <h4 id="A5TM-FLBK">Fallback Mode: C "A5TM" -> B "FLBK"</h4>
<p>This behaves identically to the <a href="#A0PM-FLBK">C "A0PM" -> B "FLBK"</a> case, above</p> <p>This behaves identically to the <a href="#A0PM-FLBK">C "A0PM" -> B "FLBK"</a> case, above</p>
<h4 id="A5TM-example">Example NFO:</h4> <h4 id="A5TM-example">Example NFO:</h4>
@ -879,6 +901,9 @@
At the time of writing known existing feature IDs include the values: 0x00 - 0x13 (inclusive) and 0x48.</p> At the time of writing known existing feature IDs include the values: 0x00 - 0x13 (inclusive) and 0x48.</p>
<h4 id="FIDM-SETT">Success Indicator Global Variable 0x8D Bit: C "FIDM" -> B "SETT"</h4> <h4 id="FIDM-SETT">Success Indicator Global Variable 0x8D Bit: C "FIDM" -> B "SETT"</h4>
<p>This behaves identically to the <a href="#A0PM-SETT">C "A0PM" -> B "SETT"</a> case, above</p> <p>This behaves identically to the <a href="#A0PM-SETT">C "A0PM" -> B "SETT"</a> case, above</p>
<h4 id="FIDM-SVAL">Success Indicator Global Variable 0x91 Test Value: C "FIDM" -> B "SVAL"</h4>
<p>This behaves identically to the <a href="#FTST-SVAL">C "FTST" -> B "SVAL"</a> case, above</p>
<p>This chunk and the associated test mechanism requires the feature name: <font face="monospace">feature_id_mapping</font>, version 2.</p>
<h4 id="FIDM-FLBK">Fallback Mode: C "FIDM" -> B "FLBK"</h4> <h4 id="FIDM-FLBK">Fallback Mode: C "FIDM" -> B "FLBK"</h4>
<p>This behaves identically to the <a href="#A0PM-FLBK">C "A0PM" -> B "FLBK"</a> case, above</p> <p>This behaves identically to the <a href="#A0PM-FLBK">C "A0PM" -> B "FLBK"</a> case, above</p>
<h4 id="FIDM-example">Example NFO:</h4> <h4 id="FIDM-example">Example NFO:</h4>

@ -7643,6 +7643,13 @@ static void SkipIf(ByteReader *buf)
default: grfmsg(1, "SkipIf: Unsupported GRF condition type %02X. Ignoring", condtype); return; default: grfmsg(1, "SkipIf: Unsupported GRF condition type %02X. Ignoring", condtype); return;
} }
} else if (param == 0x91 && (condtype == 0x02 || condtype == 0x03) && cond_val > 0) {
const std::vector<uint32> &values = _cur.grffile->var91_values;
/* condtype 0x02: skip if test result found
* condtype 0x03: skip if test result not found
*/
bool found = std::find(values.begin(), values.end(), cond_val) != values.end();
result = (found == (condtype == 0x02));
} else { } else {
/* Tests that use 'param' and are not GRF ID checks. */ /* Tests that use 'param' and are not GRF ID checks. */
uint32 param_val = GetParamVal(param, &cond_val); // cond_val is modified for param == 0x85 uint32 param_val = GetParamVal(param, &cond_val); // cond_val is modified for param == 0x85
@ -9212,6 +9219,7 @@ struct GRFFeatureTest {
uint16 min_version; uint16 min_version;
uint16 max_version; uint16 max_version;
uint8 platform_var_bit; uint8 platform_var_bit;
uint32 test_91_value;
void Reset() void Reset()
{ {
@ -9219,6 +9227,7 @@ struct GRFFeatureTest {
this->min_version = 1; this->min_version = 1;
this->max_version = UINT16_MAX; this->max_version = UINT16_MAX;
this->platform_var_bit = 0; this->platform_var_bit = 0;
this->test_91_value = 0;
} }
void ExecuteTest() void ExecuteTest()
@ -9228,7 +9237,16 @@ struct GRFFeatureTest {
if (this->platform_var_bit > 0) { if (this->platform_var_bit > 0) {
SB(_cur.grffile->var9D_overlay, this->platform_var_bit, 1, has_feature ? 1 : 0); SB(_cur.grffile->var9D_overlay, this->platform_var_bit, 1, has_feature ? 1 : 0);
grfmsg(2, "Action 14 feature test: feature test: setting bit %u of var 0x9D to %u, %u", platform_var_bit, has_feature ? 1 : 0, _cur.grffile->var9D_overlay); grfmsg(2, "Action 14 feature test: feature test: setting bit %u of var 0x9D to %u, %u", platform_var_bit, has_feature ? 1 : 0, _cur.grffile->var9D_overlay);
} else { }
if (this->test_91_value > 0) {
if (has_feature) {
grfmsg(2, "Action 14 feature test: feature test: adding test value 0x%X to var 0x91", this->test_91_value);
include(_cur.grffile->var91_values, this->test_91_value);
} else {
grfmsg(2, "Action 14 feature test: feature test: not adding test value 0x%X to var 0x91", this->test_91_value);
}
}
if (this->platform_var_bit == 0 && this->test_91_value == 0) {
grfmsg(2, "Action 14 feature test: feature test: doing nothing: %u", has_feature ? 1 : 0); grfmsg(2, "Action 14 feature test: feature test: doing nothing: %u", has_feature ? 1 : 0);
} }
if (this->feature != nullptr && this->feature->observation_flag != GFTOF_INVALID) { if (this->feature != nullptr && this->feature->observation_flag != GFTOF_INVALID) {
@ -9296,12 +9314,25 @@ static bool ChangeGRFFeatureSetPlatformVarBit(size_t len, ByteReader *buf)
return true; return true;
} }
/** Callback function for 'FTST'->'SVAL' to add a test success result value for checking using global variable 91. */
static bool ChangeGRFFeatureTestSuccessResultValue(size_t len, ByteReader *buf)
{
if (len != 4) {
grfmsg(2, "Action 14 feature test: expected 4 bytes for 'FTST'->'SVAL' but got " PRINTF_SIZE ", ignoring this field", len);
buf->Skip(len);
} else {
_current_grf_feature_test.test_91_value = buf->ReadDWord();
}
return true;
}
/** Action14 tags for the FTST node */ /** Action14 tags for the FTST node */
AllowedSubtags _tags_ftst[] = { AllowedSubtags _tags_ftst[] = {
AllowedSubtags('NAME', ChangeGRFFeatureTestName), AllowedSubtags('NAME', ChangeGRFFeatureTestName),
AllowedSubtags('MINV', ChangeGRFFeatureMinVersion), AllowedSubtags('MINV', ChangeGRFFeatureMinVersion),
AllowedSubtags('MAXV', ChangeGRFFeatureMaxVersion), AllowedSubtags('MAXV', ChangeGRFFeatureMaxVersion),
AllowedSubtags('SETP', ChangeGRFFeatureSetPlatformVarBit), AllowedSubtags('SETP', ChangeGRFFeatureSetPlatformVarBit),
AllowedSubtags('SVAL', ChangeGRFFeatureTestSuccessResultValue),
AllowedSubtags() AllowedSubtags()
}; };
@ -9326,6 +9357,7 @@ struct GRFPropertyMapAction {
std::string name; std::string name;
GRFPropertyMapFallbackMode fallback_mode; GRFPropertyMapFallbackMode fallback_mode;
uint8 ttd_ver_var_bit; uint8 ttd_ver_var_bit;
uint32 test_91_value;
uint8 input_shift; uint8 input_shift;
uint8 output_shift; uint8 output_shift;
uint input_mask; uint input_mask;
@ -9342,6 +9374,7 @@ struct GRFPropertyMapAction {
this->name.clear(); this->name.clear();
this->fallback_mode = GPMFM_IGNORE; this->fallback_mode = GPMFM_IGNORE;
this->ttd_ver_var_bit = 0; this->ttd_ver_var_bit = 0;
this->test_91_value = 0;
this->input_shift = 0; this->input_shift = 0;
this->output_shift = 0; this->output_shift = 0;
this->input_mask = 0; this->input_mask = 0;
@ -9376,6 +9409,9 @@ struct GRFPropertyMapAction {
if (this->ttd_ver_var_bit > 0) { if (this->ttd_ver_var_bit > 0) {
SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0); SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0);
} }
if (this->test_91_value > 0 && success) {
include(_cur.grffile->var91_values, this->test_91_value);
}
if (!success) { if (!success) {
if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) { if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
grfmsg(0, "Error: Unimplemented mapped %s: %s, mapped to: 0x%02X", this->descriptor, str, this->prop_id); grfmsg(0, "Error: Unimplemented mapped %s: %s, mapped to: 0x%02X", this->descriptor, str, this->prop_id);
@ -9427,6 +9463,9 @@ struct GRFPropertyMapAction {
if (this->ttd_ver_var_bit > 0) { if (this->ttd_ver_var_bit > 0) {
SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0); SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0);
} }
if (this->test_91_value > 0 && success) {
include(_cur.grffile->var91_values, this->test_91_value);
}
if (!success) { if (!success) {
if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) { if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
grfmsg(0, "Error: Unimplemented mapped %s: %s, feature: %s, mapped to: %X", this->descriptor, str, GetFeatureString(this->feature), this->prop_id); grfmsg(0, "Error: Unimplemented mapped %s: %s, feature: %s, mapped to: %X", this->descriptor, str, GetFeatureString(this->feature), this->prop_id);
@ -9471,6 +9510,9 @@ struct GRFPropertyMapAction {
if (this->ttd_ver_var_bit > 0) { if (this->ttd_ver_var_bit > 0) {
SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0); SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0);
} }
if (this->test_91_value > 0 && success) {
include(_cur.grffile->var91_values, this->test_91_value);
}
if (!success) { if (!success) {
grfmsg(2, "Unimplemented mapped %s: %s, feature: %s, mapped to 0", this->descriptor, str, GetFeatureString(this->feature)); grfmsg(2, "Unimplemented mapped %s: %s, feature: %s, mapped to 0", this->descriptor, str, GetFeatureString(this->feature));
} }
@ -9502,6 +9544,9 @@ struct GRFPropertyMapAction {
if (this->ttd_ver_var_bit > 0) { if (this->ttd_ver_var_bit > 0) {
SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0); SB(_cur.grffile->var8D_overlay, this->ttd_ver_var_bit, 1, success ? 1 : 0);
} }
if (this->test_91_value > 0 && success) {
include(_cur.grffile->var91_values, this->test_91_value);
}
if (!success) { if (!success) {
if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) { if (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
grfmsg(0, "Error: Unimplemented mapped %s: %s, mapped to: %X", this->descriptor, str, this->prop_id); grfmsg(0, "Error: Unimplemented mapped %s: %s, mapped to: %X", this->descriptor, str, this->prop_id);
@ -9625,6 +9670,19 @@ static bool ChangePropertyRemapSetTTDVerVarBit(size_t len, ByteReader *buf)
return true; return true;
} }
/** Callback function for >'SVAL' to add a success result value for checking using global variable 91. */
static bool ChangePropertyRemapSuccessResultValue(size_t len, ByteReader *buf)
{
GRFPropertyMapAction &action = _current_grf_property_map_action;
if (len != 4) {
grfmsg(2, "Action 14 %s mapping: expected 4 bytes for '%s'->'SVAL' but got " PRINTF_SIZE ", ignoring this field", action.descriptor, action.tag_name, len);
buf->Skip(len);
} else {
action.test_91_value = buf->ReadDWord();
}
return true;
}
/** Callback function for ->'RSFT' to set the input shift value for variable remapping. */ /** Callback function for ->'RSFT' to set the input shift value for variable remapping. */
static bool ChangePropertyRemapSetInputShift(size_t len, ByteReader *buf) static bool ChangePropertyRemapSetInputShift(size_t len, ByteReader *buf)
{ {
@ -9706,6 +9764,7 @@ AllowedSubtags _tags_fidm[] = {
AllowedSubtags('FTID', ChangePropertyRemapFeatureId), AllowedSubtags('FTID', ChangePropertyRemapFeatureId),
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode), AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit), AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
AllowedSubtags() AllowedSubtags()
}; };
@ -9727,6 +9786,7 @@ AllowedSubtags _tags_a0pm[] = {
AllowedSubtags('PROP', ChangePropertyRemapPropertyId), AllowedSubtags('PROP', ChangePropertyRemapPropertyId),
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode), AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit), AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
AllowedSubtags() AllowedSubtags()
}; };
@ -9751,6 +9811,7 @@ AllowedSubtags _tags_a2vm[] = {
AllowedSubtags('VMSK', ChangePropertyRemapSetOutputMask), AllowedSubtags('VMSK', ChangePropertyRemapSetOutputMask),
AllowedSubtags('VPRM', ChangePropertyRemapSetOutputParam), AllowedSubtags('VPRM', ChangePropertyRemapSetOutputParam),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit), AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
AllowedSubtags() AllowedSubtags()
}; };
@ -9771,6 +9832,7 @@ AllowedSubtags _tags_a5tm[] = {
AllowedSubtags('TYPE', ChangePropertyRemapTypeId), AllowedSubtags('TYPE', ChangePropertyRemapTypeId),
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode), AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit), AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
AllowedSubtags() AllowedSubtags()
}; };

@ -357,6 +357,7 @@ struct GRFFile : ZeroedMemoryAllocator {
uint32 var8D_overlay; ///< Overlay for global variable 8D (action 0x14) uint32 var8D_overlay; ///< Overlay for global variable 8D (action 0x14)
uint32 var9D_overlay; ///< Overlay for global variable 9D (action 0x14) uint32 var9D_overlay; ///< Overlay for global variable 9D (action 0x14)
std::vector<uint32> var91_values; ///< Test result values for global variable 91 (action 0x14, only testable using action 7/9)
uint32 observed_feature_tests; ///< Observed feature test bits (see: GRFFeatureTestObservationFlag) uint32 observed_feature_tests; ///< Observed feature test bits (see: GRFFeatureTestObservationFlag)

@ -16,11 +16,11 @@
/** Action14 feature list */ /** Action14 feature list */
extern const GRFFeatureInfo _grf_feature_list[] = { extern const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("feature_test", 1), GRFFeatureInfo("feature_test", 2),
GRFFeatureInfo("property_mapping", 1), GRFFeatureInfo("property_mapping", 2),
GRFFeatureInfo("variable_mapping", 2), GRFFeatureInfo("variable_mapping", 3),
GRFFeatureInfo("feature_id_mapping", 1), GRFFeatureInfo("feature_id_mapping", 2),
GRFFeatureInfo("action5_type_id_mapping", 1), GRFFeatureInfo("action5_type_id_mapping", 2),
GRFFeatureInfo("action0_station_prop1B", 1), GRFFeatureInfo("action0_station_prop1B", 1),
GRFFeatureInfo("action0_station_disallowed_bridge_pillars", 1), GRFFeatureInfo("action0_station_disallowed_bridge_pillars", 1),
GRFFeatureInfo("varaction2_station_var42", 1), GRFFeatureInfo("varaction2_station_var42", 1),

Loading…
Cancel
Save