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 />
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 />
If this field is omitted, no bit is set or cleared, and the test is not observable.
</p>
If this field is omitted, no bit is set or cleared, and the test is not observable (unless the SVAL chunk is used).
</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 />
<h4 id="FTST-example">Example NFO:</h4>
<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 />
If this field is omitted, no bit is set or cleared.
</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>
<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:
@ -656,6 +672,9 @@
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>
<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>
<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 />
@ -805,6 +824,9 @@
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>
<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>
<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>
@ -879,6 +901,9 @@
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>
<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>
<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>

@ -7643,6 +7643,13 @@ static void SkipIf(ByteReader *buf)
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 {
/* Tests that use 'param' and are not GRF ID checks. */
uint32 param_val = GetParamVal(param, &cond_val); // cond_val is modified for param == 0x85
@ -9212,6 +9219,7 @@ struct GRFFeatureTest {
uint16 min_version;
uint16 max_version;
uint8 platform_var_bit;
uint32 test_91_value;
void Reset()
{
@ -9219,6 +9227,7 @@ struct GRFFeatureTest {
this->min_version = 1;
this->max_version = UINT16_MAX;
this->platform_var_bit = 0;
this->test_91_value = 0;
}
void ExecuteTest()
@ -9228,7 +9237,16 @@ struct GRFFeatureTest {
if (this->platform_var_bit > 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);
} 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);
}
if (this->feature != nullptr && this->feature->observation_flag != GFTOF_INVALID) {
@ -9296,12 +9314,25 @@ static bool ChangeGRFFeatureSetPlatformVarBit(size_t len, ByteReader *buf)
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 */
AllowedSubtags _tags_ftst[] = {
AllowedSubtags('NAME', ChangeGRFFeatureTestName),
AllowedSubtags('MINV', ChangeGRFFeatureMinVersion),
AllowedSubtags('MAXV', ChangeGRFFeatureMaxVersion),
AllowedSubtags('SETP', ChangeGRFFeatureSetPlatformVarBit),
AllowedSubtags('SVAL', ChangeGRFFeatureTestSuccessResultValue),
AllowedSubtags()
};
@ -9326,6 +9357,7 @@ struct GRFPropertyMapAction {
std::string name;
GRFPropertyMapFallbackMode fallback_mode;
uint8 ttd_ver_var_bit;
uint32 test_91_value;
uint8 input_shift;
uint8 output_shift;
uint input_mask;
@ -9342,6 +9374,7 @@ struct GRFPropertyMapAction {
this->name.clear();
this->fallback_mode = GPMFM_IGNORE;
this->ttd_ver_var_bit = 0;
this->test_91_value = 0;
this->input_shift = 0;
this->output_shift = 0;
this->input_mask = 0;
@ -9376,6 +9409,9 @@ struct GRFPropertyMapAction {
if (this->ttd_ver_var_bit > 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 (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
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) {
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 (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);
@ -9471,6 +9510,9 @@ struct GRFPropertyMapAction {
if (this->ttd_ver_var_bit > 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) {
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) {
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 (this->fallback_mode == GPMFM_ERROR_ON_DEFINITION) {
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;
}
/** 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. */
static bool ChangePropertyRemapSetInputShift(size_t len, ByteReader *buf)
{
@ -9706,6 +9764,7 @@ AllowedSubtags _tags_fidm[] = {
AllowedSubtags('FTID', ChangePropertyRemapFeatureId),
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
AllowedSubtags()
};
@ -9727,6 +9786,7 @@ AllowedSubtags _tags_a0pm[] = {
AllowedSubtags('PROP', ChangePropertyRemapPropertyId),
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
AllowedSubtags()
};
@ -9751,6 +9811,7 @@ AllowedSubtags _tags_a2vm[] = {
AllowedSubtags('VMSK', ChangePropertyRemapSetOutputMask),
AllowedSubtags('VPRM', ChangePropertyRemapSetOutputParam),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
AllowedSubtags()
};
@ -9771,6 +9832,7 @@ AllowedSubtags _tags_a5tm[] = {
AllowedSubtags('TYPE', ChangePropertyRemapTypeId),
AllowedSubtags('FLBK', ChangePropertyRemapSetFallbackMode),
AllowedSubtags('SETT', ChangePropertyRemapSetTTDVerVarBit),
AllowedSubtags('SVAL', ChangePropertyRemapSuccessResultValue),
AllowedSubtags()
};

@ -357,6 +357,7 @@ struct GRFFile : ZeroedMemoryAllocator {
uint32 var8D_overlay; ///< Overlay for global variable 8D (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)

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

Loading…
Cancel
Save