VarAction2: Add support for more varaction2 types

Add CB failure and deterministic relative types
pull/507/head
Jonathan G Rennison 1 year ago
parent 48d602962f
commit 5718730d68

@ -36,6 +36,7 @@
<h3 id="sections">Sections</h3>
<ul>
<li><a href="#builtin-functions">Builtin functions</a></li>
<li><a href="#switch-types">Additional switch types</a></li>
<li><a href="#railtype-properties">Railtype properties</a></li>
<li><a href="#railtype-variables">Railtype variables</a></li>
<li><a href="#roadtype-properties">Roadtype properties</a></li>
@ -58,6 +59,21 @@
In most cases it is not necessary to use this function, as extended properties (listed below) which are not supported are simply skipped/ignored.
</p>
<h3 id="switch-types"><a href="https://newgrf-specs.tt-wiki.net/wiki/NML:Switch">Additional switch types</a></h3>
<p>
In addition to SELF and PARENT, switches for vehicle features may use one of the following types below.<br />
(These are mostly the same as in <a href="https://newgrf-specs.tt-wiki.net/wiki/NML:Random_switch">random_switch</a>).<br />
The 'x' parameter (count x vehicles in given direction) is currently required to be a compile-time constant between 0 and 255.
<table>
<tr><th>Type</th><th>Meaning</th></tr>
<tr><td>BACKWARD_SELF(x)</td><td>Count x vehicles backward (away from the engine), starting at the vehicle itself</td></tr>
<tr><td>FORWARD_SELF(x)</td><td>Count x vehicles forward (towards the engine), starting at the vehicle itself</td></tr>
<tr><td>BACKWARD_ENGINE(x)</td><td>Count x vehicles backward, starting at the leading engine</td></tr>
<tr><td>BACKWARD_SAMEID(x)</td><td>Count x vehicles backward, starting at the first vehicle in the chain with the same ID</td></tr>
</table>
<p>These require the <span class="code">more_varaction2_types</span> feature. If this feature is not present, switches of these types will produce a CB_FAILED result.</p>
</p>
<h3 id="railtype-properties"><a href="https://newgrf-specs.tt-wiki.net/wiki/NML:Railtypes#Railtype_properties">Railtype properties</a></h3>
<table>
<tr><th>Property</th><th>Value range</th><th>Comment</th></tr>

@ -47,6 +47,7 @@
<li><a href="#a0signals">Action 0 - Signals (Feature 0E)</a></li>
<li><a href="#a0objects">Action 0 - Objects</a></li>
<li><a href="#variable-mapping">Action 14 - Variable Mapping for Variational Action 2</a></li>
<li><a href="#varaction2_additional_types">Variational Action 2 - Additional Types</a></li>
<li><a href="#varaction2_station">Variational Action 2 - Stations</a></li>
<li><a href="#varaction2_railtypes">Variational Action 2 - Railtypes</a></li>
<li><a href="#varaction2_object">Variational Action 2 - Objects</a></li>
@ -724,6 +725,59 @@
01 00 // default: link to action2ID 01
</pre>
<br />
<h3 id="varaction2_additional_types">Variational Action 2 - Additional Types</h3>
<p>Additional variational action 2 types are indicated by the feature name: <font face="monospace">more_varaction2_types</font>, this document describes version 1.<br />
This feature name must be tested for to enable this feature.</p>
<p>See the <a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2#Type">Variational Action 2 Specification (types)</a> for background information.</p>
<p>This feature adds the type value 87, for additional variation action 2 modes/scopes. (This is used instead of the standard values: 81, 82, 85, 86, 89 or 8A).<br />
An additional byte follows to specify which sub-type.</p>
<p>
<pre>&lt;Sprite-number&gt; * &lt;Length&gt; 02 &lt;feature&gt; &lt;set-id&gt; 87 &lt;sub-type&gt; ...</pre>
The following sub-types are defined:
<table>
<tr><th>Sub-type</th><th>Meaning</th></tr>
<tr><td>00</td><td>Callback failure group</td></tr>
<tr><td>01</td><td>Relative scope for vehicles (same syntax as <a href="https://newgrf-specs.tt-wiki.net/wiki/RandomAction2#80.2F83.2F84">random action 2 type 84</a>)</td></tr>
<tr><td>02</td><td>Relative scope for vehicles (longer syntax)</td></tr>
</table>
<p>If an unknown sub-type is used, or if subsequent sub-type bytes are invalid, a callback failure group is created, the same as sub-type 00.</p>
<h4 id="varaction2_additional_type_00">Sub-type 00: Callback failure</h4>
<pre>&lt;Sprite-number&gt; * &lt;Length&gt; 02 &lt;feature&gt; &lt;set-id&gt; <b>87 00</b></pre>
This produces a callback failed result in all cases (appropriate whether or not variable 0C is currently 0).<br />
This can be used to avoiding needing to branch on variable 0C or create empty (feature-dependant) real sprite/sprite layout/industry production groups.
</p>
<h4 id="varaction2_additional_type_01">Sub-type 01: Relative scope for vehicles (random action 2 type 84 syntax)</h4>
<pre>&lt;Sprite-number&gt; * &lt;Length&gt; 02 &lt;feature&gt; &lt;set-id&gt; <b>87 01 &lt;count&gt;</b> &lt;variable&gt; &lt;varadjust&gt; [&lt;operator&gt; &lt;variable&gt; &lt;varadjust&gt;]... &lt;nvar&gt; (&lt;set-id&gt; &lt;low-range&gt; &lt;high-range&gt;){n} &lt;default&gt;</pre>
<p>This sub-type is only valid for vehicles.</p>
<p>The vehicle which is accessed can be any in the vehicle consist. The <font face="monospace">count</font> byte selects which, this has the same syntax and functionality
as the <a href="https://newgrf-specs.tt-wiki.net/wiki/RandomAction2#count"><font face="monospace">count</font> byte in random action 2 type 84</a>.</p>
<p>Note: The count byte mode where temporary variable 0x100 is used should be used with caution.</p>
<h4 id="varaction2_additional_type_02">Sub-type 02: Relative scope for vehicles (longer syntax)</h4>
<pre>&lt;Sprite-number&gt; * &lt;Length&gt; 02 &lt;feature&gt; &lt;set-id&gt; <b>87 02 &lt;mode&gt; &lt;offset&gt;</b> &lt;variable&gt; &lt;varadjust&gt; [&lt;operator&gt; &lt;variable&gt; &lt;varadjust&gt;]... &lt;nvar&gt; (&lt;set-id&gt; &lt;low-range&gt; &lt;high-range&gt;){n} &lt;default&gt;</pre>
<p>This sub-type is only valid for vehicles.</p>
<p>The vehicle which is accessed can be any in the vehicle consist. The <font face="monospace">mode</font> and <font face="monospace">offset</font> bytes select which.</p>
<p>The <font face="monospace">mode</font> byte has the following syntax:
<table>
<tr><th>Bits</th><th>Meaning</th></tr>
<tr><td>0 - 1</td><td>
Selection mode:<br/>
These values are the same as bits 6 - 7 of the <a href="https://newgrf-specs.tt-wiki.net/wiki/RandomAction2#count"><font face="monospace">count</font> byte in random action 2 type 84</a>.
<table>
<tr><th>Value</th><th>Meaning</th></tr>
<tr><td>0</td><td>Count back (away from the engine), starting at this vehicle</td></tr>
<tr><td>1</td><td>Count forward (toward the engine), starting at this vehicle</td></tr>
<tr><td>2</td><td>Count back, starting at the engine</td></tr>
<tr><td>3</td><td>Count back, starting at the first vehicle in this chain of vehicles with the same ID, as for vehicle variable 41</td></tr>
</table>
</td></tr>
<tr><td>7</td><td>If set, use variable 0x100 for the offset. The offset byte must be 0.</td></tr>
</table>
The remaining bits are reserved for future used and must be set to 0.
</p>
<p>The <font face="monospace">offset</font> byte specifies how far to count from the starting vehicle. A value of 0 is not special.<br />
Bit 7 of the <font face="monospace">mode</font> byte must be set to use temporary variable 0x100 instead.</p>
<p>Note: Using temporary variable 0x100 as the offset should be used with caution.</p>
<br />
<h3 id="varaction2_station"><a href="https://newgrf-specs.tt-wiki.net/wiki/VariationalAction2/Stations">Variational Action 2 - Stations</a></h3>
<h4 id="varaction2_station_var42">Track type in purchase list (42)</h4>
<p>This is indicated by the feature name: <font face="monospace">varaction2_station_var42</font>, version 1</p>

@ -5789,6 +5789,17 @@ static void ProcessDeterministicSpriteGroupRanges(const std::vector<Deterministi
}
}
static VarSpriteGroupScopeOffset ParseRelativeScopeByte(byte relative)
{
VarSpriteGroupScopeOffset var_scope_count = (GB(relative, 6, 2) << 8);
if ((relative & 0xF) == 0) {
SetBit(var_scope_count, 15);
} else {
var_scope_count |= (relative & 0xF);
}
return var_scope_count;
}
/* Action 0x02 */
static void NewSpriteGroup(ByteReader *buf)
{
@ -5819,6 +5830,16 @@ static void NewSpriteGroup(ByteReader *buf)
* we do not need to delete anything if there is an exception from the
* ByteReader. */
/* Decoded sprite type */
enum SpriteType {
STYPE_NORMAL,
STYPE_DETERMINISTIC,
STYPE_DETERMINISTIC_RELATIVE,
STYPE_DETERMINISTIC_RELATIVE_2,
STYPE_RANDOMIZED,
STYPE_CB_FAILURE,
};
SpriteType stype = STYPE_NORMAL;
switch (type) {
/* Deterministic Sprite Group */
case 0x81: // Self scope, byte
@ -5827,7 +5848,73 @@ static void NewSpriteGroup(ByteReader *buf)
case 0x86: // Parent scope, word
case 0x89: // Self scope, dword
case 0x8A: // Parent scope, dword
stype = STYPE_DETERMINISTIC;
break;
/* Randomized Sprite Group */
case 0x80: // Self scope
case 0x83: // Parent scope
case 0x84: // Relative scope
stype = STYPE_RANDOMIZED;
break;
/* Extension type */
case 0x87:
if (HasBit(_cur.grffile->observed_feature_tests, GFTOF_MORE_VARACTION2_TYPES)) {
byte subtype = buf->ReadByte();
switch (subtype) {
case 0:
stype = STYPE_CB_FAILURE;
break;
case 1:
stype = STYPE_DETERMINISTIC_RELATIVE;
break;
case 2:
stype = STYPE_DETERMINISTIC_RELATIVE_2;
break;
default:
grfmsg(1, "NewSpriteGroup: Unknown 0x87 extension subtype %02X for feature %s, handling as CB failure", subtype, GetFeatureString(feature));
stype = STYPE_CB_FAILURE;
break;
}
}
break;
default:
break;
}
switch (stype) {
/* Deterministic Sprite Group */
case STYPE_DETERMINISTIC:
case STYPE_DETERMINISTIC_RELATIVE:
case STYPE_DETERMINISTIC_RELATIVE_2:
{
VarSpriteGroupScopeOffset var_scope_count = 0;
if (stype == STYPE_DETERMINISTIC_RELATIVE) {
var_scope_count = ParseRelativeScopeByte(buf->ReadByte());
} else if (stype == STYPE_DETERMINISTIC_RELATIVE_2) {
uint8 mode = buf->ReadByte();
uint8 offset = buf->ReadByte();
bool invalid = false;
if ((mode & 0x7F) >= VSGSRM_END) {
invalid = true;
}
if (HasBit(mode, 7)) {
/* Use variable 0x100 */
if (offset != 0) invalid = true;
}
if (invalid) {
grfmsg(1, "NewSpriteGroup: Unknown 0x87 extension subtype 2 relative mode: %02X %02X for feature %s, handling as CB failure", mode, offset, GetFeatureString(feature));
act_group = NewCallbackResultSpriteGroupNoTransform(CALLBACK_FAILED);
break;
}
var_scope_count = (mode << 8) | offset;
}
byte varadjust;
byte varsize;
@ -5839,13 +5926,22 @@ static void NewSpriteGroup(ByteReader *buf)
group->feature = feature;
if (_action6_override_active) group->sg_flags |= SGF_ACTION6;
act_group = group;
group->var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
switch (GB(type, 2, 2)) {
default: NOT_REACHED();
case 0: group->size = DSG_SIZE_BYTE; varsize = 1; break;
case 1: group->size = DSG_SIZE_WORD; varsize = 2; break;
case 2: group->size = DSG_SIZE_DWORD; varsize = 4; break;
if (stype == STYPE_DETERMINISTIC_RELATIVE || stype == STYPE_DETERMINISTIC_RELATIVE_2) {
group->var_scope = (feature <= GSF_AIRCRAFT) ? VSG_SCOPE_RELATIVE : VSG_SCOPE_SELF;
group->var_scope_count = var_scope_count;
group->size = DSG_SIZE_DWORD;
varsize = 4;
} else {
group->var_scope = HasBit(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
switch (GB(type, 2, 2)) {
default: NOT_REACHED();
case 0: group->size = DSG_SIZE_BYTE; varsize = 1; break;
case 1: group->size = DSG_SIZE_WORD; varsize = 2; break;
case 2: group->size = DSG_SIZE_DWORD; varsize = 4; break;
}
}
const VarAction2AdjustInfo info = { feature, GetGrfSpecFeatureForScope(feature, group->var_scope), varsize };
@ -5962,9 +6058,7 @@ static void NewSpriteGroup(ByteReader *buf)
}
/* Randomized Sprite Group */
case 0x80: // Self scope
case 0x83: // Parent scope
case 0x84: // Relative scope
case STYPE_RANDOMIZED:
{
assert(RandomizedSpriteGroup::CanAllocateItem());
RandomizedSpriteGroup *group = new RandomizedSpriteGroup();
@ -5975,7 +6069,7 @@ static void NewSpriteGroup(ByteReader *buf)
if (HasBit(type, 2)) {
if (feature <= GSF_AIRCRAFT) group->var_scope = VSG_SCOPE_RELATIVE;
group->count = buf->ReadByte();
group->var_scope_count = ParseRelativeScopeByte(buf->ReadByte());
}
uint8 triggers = buf->ReadByte();
@ -6005,8 +6099,12 @@ static void NewSpriteGroup(ByteReader *buf)
break;
}
case STYPE_CB_FAILURE:
act_group = NewCallbackResultSpriteGroupNoTransform(CALLBACK_FAILED);
break;
/* Neither a variable or randomized sprite group... must be a real group */
default:
case STYPE_NORMAL:
{
switch (feature) {
case GSF_TRAINS:

@ -49,7 +49,7 @@ struct AirportResolverObject : public ResolverObject {
AirportResolverObject(TileIndex tile, Station *st, byte airport_id, byte layout,
CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->airport_scope;

@ -48,7 +48,7 @@ struct AirportTileResolverObject : public ResolverObject {
AirportTileResolverObject(const AirportTileSpec *ats, TileIndex tile, Station *st,
CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &tiles_scope;

@ -41,7 +41,7 @@ struct CanalResolverObject : public ResolverObject {
CanalResolverObject(CanalFeature feature, TileIndex tile,
CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->canal_scope;

@ -329,32 +329,33 @@ static byte MapAircraftMovementAction(const Aircraft *v)
}
/* virtual */ ScopeResolver *VehicleResolverObject::GetScope(VarSpriteGroupScope scope, byte relative)
/* virtual */ ScopeResolver *VehicleResolverObject::GetScope(VarSpriteGroupScope scope, VarSpriteGroupScopeOffset relative)
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->self_scope;
case VSG_SCOPE_PARENT: return &this->parent_scope;
case VSG_SCOPE_RELATIVE: {
int32 count = GB(relative, 0, 4);
if (this->self_scope.v != nullptr && (relative != this->cached_relative_count || count == 0)) {
int32 count = GB(relative, 0, 8);
if (this->self_scope.v != nullptr && (relative != this->cached_relative_count || HasBit(relative, 15))) {
/* Note: This caching only works as long as the VSG_SCOPE_RELATIVE cannot be used in
* VarAct2 with procedure calls. */
if (count == 0) count = GetRegister(0x100);
/* Therefore procedure calls made from within a relative scope must save and restore the cached relative scope */
if (HasBit(relative, 15)) count = GetRegister(0x100);
const Vehicle *v = nullptr;
switch (GB(relative, 6, 2)) {
switch (GB(relative, 8, 2)) {
default: NOT_REACHED();
case 0x00: // count back (away from the engine), starting at this vehicle
case VSGSRM_BACKWARD_SELF: // count back (away from the engine), starting at this vehicle
v = this->self_scope.v;
break;
case 0x01: // count forward (toward the engine), starting at this vehicle
case VSGSRM_FORWARD_SELF: // count forward (toward the engine), starting at this vehicle
v = this->self_scope.v;
count = -count;
break;
case 0x02: // count back, starting at the engine
case VSGSRM_BACKWARD_ENGINE: // count back, starting at the engine
v = this->parent_scope.v;
break;
case 0x03: { // count back, starting at the first vehicle in this chain of vehicles with the same ID, as for vehicle variable 41
case VSGSRM_BACKWARD_SAMEID: { // count back, starting at the first vehicle in this chain of vehicles with the same ID, as for vehicle variable 41
const Vehicle *self = this->self_scope.v;
for (const Vehicle *u = self->First(); u != self; u = u->Next()) {
if (u->engine_type != self->engine_type) {

@ -57,12 +57,12 @@ struct VehicleResolverObject : public ResolverObject {
VehicleScopeResolver parent_scope; ///< Scope resolver for its parent vehicle.
VehicleScopeResolver relative_scope; ///< Scope resolver for an other vehicle in the chain.
byte cached_relative_count; ///< Relative position of the other vehicle.
VarSpriteGroupScopeOffset cached_relative_count; ///< Relative position of the other vehicle.
VehicleResolverObject(EngineID engine_type, const Vehicle *v, WagonOverride wagon_override, bool rotor_in_gui = false,
CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override;
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override;
const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const override;

@ -65,6 +65,7 @@ extern const GRFFeatureInfo _grf_feature_list[] = {
GRFFeatureInfo("town_feature", 1),
GRFFeatureInfo("town_uncapped_variables", 1),
GRFFeatureInfo("town_zone_callback", 1, GFTOF_TOWN_ZONE_CALLBACK),
GRFFeatureInfo("more_varaction2_types", 1, GFTOF_MORE_VARACTION2_TYPES),
GRFFeatureInfo(),
};

@ -100,6 +100,7 @@ enum GRFFeatureTestObservationFlag : uint8 {
GFTOF_MORE_OBJECTS_PER_GRF = 0,
GFTOF_MORE_ACTION2_IDS,
GFTOF_TOWN_ZONE_CALLBACK,
GFTOF_MORE_VARACTION2_TYPES,
GFTOF_INVALID = 0xFF,
};

@ -58,7 +58,7 @@ struct GenericResolverObject : public ResolverObject {
GenericResolverObject(bool ai_callback, CallbackID callback = CBID_NO_CALLBACK);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->generic_scope;

@ -82,7 +82,7 @@ struct HouseResolverObject : public ResolverObject {
CallbackID callback = CBID_NO_CALLBACK, uint32 param1 = 0, uint32 param2 = 0,
bool not_yet_constructed = false, uint8 initial_random_bits = 0, CargoTypes watched_cargo_triggers = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->house_scope;
@ -103,7 +103,7 @@ struct FakeHouseResolverObject : public ResolverObject {
FakeHouseResolverObject(HouseID house_id,
CallbackID callback = CBID_NO_CALLBACK, uint32 param1 = 0, uint32 param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->house_scope;

@ -49,7 +49,7 @@ struct IndustriesResolverObject : public ResolverObject {
TownScopeResolver *GetTown();
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &industries_scope;

@ -44,7 +44,7 @@ struct IndustryTileResolverObject : public ResolverObject {
IndustryTileResolverObject(IndustryGfx gfx, TileIndex tile, Industry *indus,
CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &indtile_scope;

@ -39,7 +39,7 @@ struct NewLandscapeResolverObject : public ResolverObject {
NewLandscapeResolverObject(const GRFFile *grffile, const TileInfo *ti, NewLandscapeType landscape_type, uint32 param1 = 0, uint32 param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->newlandscape_scope;

@ -89,7 +89,7 @@ struct NewSignalsResolverObject : public ResolverObject {
NewSignalsResolverObject(const GRFFile *grffile, TileIndex tile, TileContext context, uint32 param1, uint32 param2, CustomSignalSpriteContext signal_context, uint8 signal_style, const TraceRestrictProgram *prog = nullptr);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->newsignals_scope;

@ -174,7 +174,7 @@ struct ObjectResolverObject : public ResolverObject {
CallbackID callback = CBID_NO_CALLBACK, uint32 param1 = 0, uint32 param2 = 0);
~ObjectResolverObject();
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF:

@ -46,7 +46,7 @@ struct RailTypeResolverObject : public ResolverObject {
RailTypeResolverObject(const RailtypeInfo *rti, TileIndex tile, TileContext context, RailTypeSpriteGroup rtsg, uint32 param1 = 0, uint32 param2 = 0, CustomSignalSpriteContext signal_context = CSSC_GUI, const TraceRestrictProgram *prog = nullptr);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->railtype_scope;

@ -111,7 +111,7 @@ struct RoadStopResolverObject : public ResolverObject {
RoadStopResolverObject(const RoadStopSpec* roadstopspec, BaseStation* st, TileIndex tile, RoadType roadtype, StationType type, uint8 view, CallbackID callback = CBID_NO_CALLBACK, uint32 param1 = 0, uint32 param2 = 0);
~RoadStopResolverObject();
ScopeResolver* GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override {
ScopeResolver* GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override {
switch (scope) {
case VSG_SCOPE_SELF: return &this->roadstop_scope;
case VSG_SCOPE_PARENT: {

@ -42,7 +42,7 @@ struct RoadTypeResolverObject : public ResolverObject {
RoadTypeResolverObject(const RoadTypeInfo *rti, TileIndex tile, TileContext context, RoadTypeSpriteGroup rtsg, uint32 param1 = 0, uint32 param2 = 0);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &this->roadtype_scope;

@ -18,6 +18,7 @@
#include "newgrf_extension.h"
#include "scope.h"
#include "debug_settings.h"
#include "newgrf_engine.h"
#include "safeguards.h"
@ -165,7 +166,7 @@ static inline uint32 GetVariable(const ResolverObject &object, ScopeResolver *sc
* @param relative Additional parameter for #VSG_SCOPE_RELATIVE.
* @return The resolver for the requested scope.
*/
/* virtual */ ScopeResolver *ResolverObject::GetScope(VarSpriteGroupScope scope, byte relative)
/* virtual */ ScopeResolver *ResolverObject::GetScope(VarSpriteGroupScope scope, VarSpriteGroupScopeOffset relative)
{
return &this->default_scope;
}
@ -259,7 +260,7 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con
uint32 last_value = 0;
uint32 value = 0;
ScopeResolver *scope = object.GetScope(this->var_scope);
ScopeResolver *scope = object.GetScope(this->var_scope, this->var_scope_count);
const DeterministicSpriteGroupAdjust *end = this->adjusts.data() + this->adjusts.size();
for (const DeterministicSpriteGroupAdjust *iter = this->adjusts.data(); iter != end; ++iter) {
@ -271,6 +272,17 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con
/* Try to get the variable. We shall assume it is available, unless told otherwise. */
GetVariableExtra extra(adjust.and_mask << adjust.shift_num);
if (adjust.variable == 0x7E) {
const Vehicle *relative_scope_vehicle = nullptr;
VarSpriteGroupScopeOffset relative_scope_cached_count = 0;
if (this->var_scope == VSG_SCOPE_RELATIVE) {
/* Save relative scope vehicle in case it will be changed during the procedure */
VehicleResolverObject *veh_object = dynamic_cast<VehicleResolverObject *>(&object);
if (veh_object != nullptr) {
relative_scope_vehicle = veh_object->relative_scope.v;
relative_scope_cached_count = veh_object->cached_relative_count;
}
}
const SpriteGroup *subgroup = SpriteGroup::Resolve(adjust.subroutine, object, false);
if (subgroup == nullptr) {
value = CALLBACK_FAILED;
@ -278,6 +290,13 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con
value = subgroup->GetCallbackResult();
}
if (relative_scope_vehicle != nullptr) {
/* Reset relative scope vehicle in case it was changed during the procedure */
VehicleResolverObject *veh_object = static_cast<VehicleResolverObject *>(&object);
veh_object->relative_scope.v = relative_scope_vehicle;
veh_object->cached_relative_count = relative_scope_cached_count;
}
/* Note: 'last_value' and 'reseed' are shared between the main chain and the procedure */
} else if (adjust.variable == 0x7B) {
_sprite_group_resolve_check_veh_check = false;
@ -338,7 +357,7 @@ bool DeterministicSpriteGroup::GroupMayBeBypassed() const
const SpriteGroup *RandomizedSpriteGroup::Resolve(ResolverObject &object) const
{
ScopeResolver *scope = object.GetScope(this->var_scope, this->count);
ScopeResolver *scope = object.GetScope(this->var_scope, this->var_scope_count);
if (object.callback == CBID_RANDOM_TRIGGER) {
/* Handle triggers */
byte match = this->triggers & object.waiting_triggers;
@ -445,6 +464,14 @@ static const char *_sg_size_names[] {
"DWORD",
};
static const char *_sg_relative_scope_modes[] {
"BACKWARD_SELF",
"FORWARD_SELF",
"BACKWARD_ENGINE",
"BACKWARD_SAMEID",
};
static_assert(lengthof(_sg_relative_scope_modes) == VSGSRM_END);
static char *GetAdjustOperationName(char *str, const char *last, DeterministicSpriteGroupAdjustOperation operation)
{
if (operation < DSGA_OP_END) return strecat(str, _dsg_op_names[operation], last);
@ -601,6 +628,23 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
if (sg->sg_flags & SGF_INLINING) strecat(extra_info, " (inlining)", lastof(extra_info));
}
char scope_buffer[64] = "";
auto get_scope_name = [&](VarSpriteGroupScope var_scope, VarSpriteGroupScopeOffset var_scope_count) -> const char * {
if (var_scope == VSG_SCOPE_RELATIVE) {
char *b = scope_buffer;
b += seprintf(b, lastof(scope_buffer), "%s[%s, ", _sg_scope_names[var_scope], _sg_relative_scope_modes[GB(var_scope_count, 8, 2)]);
byte offset = GB(var_scope_count, 0, 8);
if (HasBit(var_scope_count, 15)) {
b += seprintf(b, lastof(scope_buffer), "var 0x100]");
} else {
b += seprintf(b, lastof(scope_buffer), "%u]", offset);
}
return scope_buffer;
} else {
return _sg_scope_names[var_scope];
}
};
switch (sg->type) {
case SGT_REAL: {
const RealSpriteGroup *rsg = (const RealSpriteGroup*)sg;
@ -660,26 +704,26 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
}
if (dsg == this->top_default_group && !((flags & SGDF_DEFAULT) && strlen(padding) == 2)) {
seprintf(this->buffer, lastof(this->buffer), "%sTOP LEVEL DEFAULT GROUP: Deterministic (%s, %s), [%u]",
padding, _sg_scope_names[dsg->var_scope], _sg_size_names[dsg->size], dsg->nfo_line);
padding, get_scope_name(dsg->var_scope, dsg->var_scope_count), _sg_size_names[dsg->size], dsg->nfo_line);
print();
return;
}
if (dsg == this->top_graphics_group && !((flags & SGDF_RANGE) && strlen(padding) == 2)) {
seprintf(this->buffer, lastof(this->buffer), "%sTOP LEVEL GRAPHICS GROUP: Deterministic (%s, %s), [%u]",
padding, _sg_scope_names[dsg->var_scope], _sg_size_names[dsg->size], dsg->nfo_line);
padding, get_scope_name(dsg->var_scope, dsg->var_scope_count), _sg_size_names[dsg->size], dsg->nfo_line);
print();
return;
}
auto res = this->seen_dsgs.insert(dsg);
if (!res.second) {
seprintf(this->buffer, lastof(this->buffer), "%sGROUP SEEN ABOVE: Deterministic (%s, %s), [%u]",
padding, _sg_scope_names[dsg->var_scope], _sg_size_names[dsg->size], dsg->nfo_line);
padding, get_scope_name(dsg->var_scope, dsg->var_scope_count), _sg_size_names[dsg->size], dsg->nfo_line);
print();
return;
}
char *p = this->buffer;
p += seprintf(p, lastof(this->buffer), "%sDeterministic (%s, %s)%s [%u]",
padding, _sg_scope_names[dsg->var_scope], _sg_size_names[dsg->size], extra_info, dsg->nfo_line);
padding, get_scope_name(dsg->var_scope, dsg->var_scope_count), _sg_size_names[dsg->size], extra_info, dsg->nfo_line);
if (HasBit(_misc_debug_flags, MDF_NEWGRF_SG_DUMP_MORE_DETAIL)) {
if (dsg->dsg_flags & DSGF_NO_DSE) p += seprintf(p, lastof(this->buffer), ", NO_DSE");
if (dsg->dsg_flags & DSGF_VAR_TRACKING_PENDING) p += seprintf(p, lastof(this->buffer), ", VAR_PENDING");
@ -752,9 +796,9 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, const char *paddi
}
}
seprintf(this->buffer, lastof(this->buffer), "%sRandom (%s, %s, triggers: %X, count: %X, lowest_randbit: %X, groups: %u)%s [%u]",
padding, _sg_scope_names[rsg->var_scope], rsg->cmp_mode == RSG_CMP_ANY ? "ANY" : "ALL",
rsg->triggers, rsg->count, rsg->lowest_randbit, (uint)rsg->groups.size(), extra_info, rsg->nfo_line);
seprintf(this->buffer, lastof(this->buffer), "%sRandom (%s, %s, triggers: %X, lowest_randbit: %X, groups: %u)%s [%u]",
padding, get_scope_name(rsg->var_scope, rsg->var_scope_count), rsg->cmp_mode == RSG_CMP_ANY ? "ANY" : "ALL",
rsg->triggers, rsg->lowest_randbit, (uint)rsg->groups.size(), extra_info, rsg->nfo_line);
print();
emit_start();
std::string sub_padding(padding);

@ -120,6 +120,22 @@ enum VarSpriteGroupScope : uint8 {
};
DECLARE_POSTFIX_INCREMENT(VarSpriteGroupScope)
enum VarSpriteGroupScopeRelativeMode : uint8 {
VSGSRM_BACKWARD_SELF = 0,
VSGSRM_FORWARD_SELF = 1,
VSGSRM_BACKWARD_ENGINE = 2,
VSGSRM_BACKWARD_SAMEID = 3,
VSGSRM_END,
};
/*
* Decoded relative scope offset:
* Bits 0..7: offset
* Bits 8..9: mode (VarSpriteGroupScopeRelativeMode)
* Bit 15: use var 0x100
*/
typedef uint16 VarSpriteGroupScopeOffset;
GrfSpecFeature GetGrfSpecFeatureForParentScope(GrfSpecFeature feature);
inline GrfSpecFeature GetGrfSpecFeatureForScope(GrfSpecFeature feature, VarSpriteGroupScope scope)
@ -474,6 +490,7 @@ struct DeterministicSpriteGroup : SpriteGroup {
DeterministicSpriteGroup() : SpriteGroup(SGT_DETERMINISTIC) {}
VarSpriteGroupScope var_scope;
VarSpriteGroupScopeOffset var_scope_count;
DeterministicSpriteGroupSize size;
bool calculated_result;
DeterministicSpriteGroupFlags dsg_flags = DSGF_NONE;
@ -505,10 +522,10 @@ struct RandomizedSpriteGroup : SpriteGroup {
RandomizedSpriteGroup() : SpriteGroup(SGT_RANDOMIZED) {}
VarSpriteGroupScope var_scope; ///< Take this object:
VarSpriteGroupScopeOffset var_scope_count;
RandomizedSpriteGroupCompareMode cmp_mode; ///< Check for these triggers:
byte triggers;
byte count;
byte lowest_randbit; ///< Look for this in the per-object randomized bitmask:
@ -690,7 +707,7 @@ struct ResolverObject {
virtual const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const;
virtual ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0);
virtual ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0);
/**
* Returns the waiting triggers that did not trigger any rerandomisation.

@ -59,7 +59,7 @@ struct StationResolverObject : public ResolverObject {
TownScopeResolver *GetTown();
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF:

@ -62,7 +62,7 @@ struct TownResolverObject : public ResolverObject {
TownResolverObject(const struct GRFFile *grffile, Town *t, bool readonly);
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, VarSpriteGroupScopeOffset relative = 0) override
{
switch (scope) {
case VSG_SCOPE_SELF: return &town_scope;

Loading…
Cancel
Save