From 5718730d685c3057afba777521b8953c5c3fc528 Mon Sep 17 00:00:00 2001
From: Jonathan G Rennison
Date: Tue, 14 Mar 2023 17:57:30 +0000
Subject: [PATCH] VarAction2: Add support for more varaction2 types
Add CB failure and deterministic relative types
---
docs/newgrf-additions-nml.html | 16 +++++
docs/newgrf-additions.html | 54 +++++++++++++++
src/newgrf.cpp | 120 ++++++++++++++++++++++++++++++---
src/newgrf_airport.cpp | 2 +-
src/newgrf_airporttiles.h | 2 +-
src/newgrf_canal.cpp | 2 +-
src/newgrf_engine.cpp | 19 +++---
src/newgrf_engine.h | 4 +-
src/newgrf_extension.cpp | 1 +
src/newgrf_extension.h | 1 +
src/newgrf_generic.cpp | 2 +-
src/newgrf_house.h | 4 +-
src/newgrf_industries.h | 2 +-
src/newgrf_industrytiles.h | 2 +-
src/newgrf_newlandscape.h | 2 +-
src/newgrf_newsignals.h | 2 +-
src/newgrf_object.h | 2 +-
src/newgrf_railtype.h | 2 +-
src/newgrf_roadstop.h | 2 +-
src/newgrf_roadtype.h | 2 +-
src/newgrf_spritegroup.cpp | 64 +++++++++++++++---
src/newgrf_spritegroup.h | 21 +++++-
src/newgrf_station.h | 2 +-
src/newgrf_town.h | 2 +-
24 files changed, 282 insertions(+), 50 deletions(-)
diff --git a/docs/newgrf-additions-nml.html b/docs/newgrf-additions-nml.html
index 581990b10e..93f5e4d315 100644
--- a/docs/newgrf-additions-nml.html
+++ b/docs/newgrf-additions-nml.html
@@ -36,6 +36,7 @@
Sections
+
+ In addition to SELF and PARENT, switches for vehicle features may use one of the following types below.
+ (These are mostly the same as in random_switch).
+ The 'x' parameter (count x vehicles in given direction) is currently required to be a compile-time constant between 0 and 255.
+
Property | Value range | Comment |
diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html
index eaf354397f..f029e4c0d6 100644
--- a/docs/newgrf-additions.html
+++ b/docs/newgrf-additions.html
@@ -47,6 +47,7 @@
Action 0 - Signals (Feature 0E)
Action 0 - Objects
Action 14 - Variable Mapping for Variational Action 2
+ Variational Action 2 - Additional Types
Variational Action 2 - Stations
Variational Action 2 - Railtypes
Variational Action 2 - Objects
@@ -724,6 +725,59 @@
01 00 // default: link to action2ID 01
+ Variational Action 2 - Additional Types
+ Additional variational action 2 types are indicated by the feature name: more_varaction2_types, this document describes version 1.
+ This feature name must be tested for to enable this feature.
+ See the Variational Action 2 Specification (types) for background information.
+ 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).
+ An additional byte follows to specify which sub-type.
+
+
<Sprite-number> * <Length> 02 <feature> <set-id> 87 <sub-type> ...
+ The following sub-types are defined:
+
+ Sub-type | Meaning |
+ 00 | Callback failure group |
+ 01 | Relative scope for vehicles (same syntax as random action 2 type 84) |
+ 02 | Relative scope for vehicles (longer syntax) |
+
+ 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.
+ Sub-type 00: Callback failure
+ <Sprite-number> * <Length> 02 <feature> <set-id> 87 00
+ This produces a callback failed result in all cases (appropriate whether or not variable 0C is currently 0).
+ This can be used to avoiding needing to branch on variable 0C or create empty (feature-dependant) real sprite/sprite layout/industry production groups.
+
+ Sub-type 01: Relative scope for vehicles (random action 2 type 84 syntax)
+ <Sprite-number> * <Length> 02 <feature> <set-id> 87 01 <count> <variable> <varadjust> [<operator> <variable> <varadjust>]... <nvar> (<set-id> <low-range> <high-range>){n} <default>
+ This sub-type is only valid for vehicles.
+ The vehicle which is accessed can be any in the vehicle consist. The count byte selects which, this has the same syntax and functionality
+ as the count byte in random action 2 type 84.
+ Note: The count byte mode where temporary variable 0x100 is used should be used with caution.
+ Sub-type 02: Relative scope for vehicles (longer syntax)
+ <Sprite-number> * <Length> 02 <feature> <set-id> 87 02 <mode> <offset> <variable> <varadjust> [<operator> <variable> <varadjust>]... <nvar> (<set-id> <low-range> <high-range>){n} <default>
+ This sub-type is only valid for vehicles.
+ The vehicle which is accessed can be any in the vehicle consist. The mode and offset bytes select which.
+ The mode byte has the following syntax:
+
+ Bits | Meaning |
+ 0 - 1 |
+ Selection mode:
+ These values are the same as bits 6 - 7 of the count byte in random action 2 type 84.
+
+ Value | Meaning |
+ 0 | Count back (away from the engine), starting at this vehicle |
+ 1 | Count forward (toward the engine), starting at this vehicle |
+ 2 | Count back, starting at the engine |
+ 3 | Count back, starting at the first vehicle in this chain of vehicles with the same ID, as for vehicle variable 41 |
+
+ |
+ 7 | If set, use variable 0x100 for the offset. The offset byte must be 0. |
+
+ The remaining bits are reserved for future used and must be set to 0.
+
+ The offset byte specifies how far to count from the starting vehicle. A value of 0 is not special.
+ Bit 7 of the mode byte must be set to use temporary variable 0x100 instead.
+ Note: Using temporary variable 0x100 as the offset should be used with caution.
+
Track type in purchase list (42)
This is indicated by the feature name: varaction2_station_var42, version 1
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index f927760b09..ffd06b55d0 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -5789,6 +5789,17 @@ static void ProcessDeterministicSpriteGroupRanges(const std::vectorobserved_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:
diff --git a/src/newgrf_airport.cpp b/src/newgrf_airport.cpp
index 7e11b9be99..1e3031601e 100644
--- a/src/newgrf_airport.cpp
+++ b/src/newgrf_airport.cpp
@@ -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;
diff --git a/src/newgrf_airporttiles.h b/src/newgrf_airporttiles.h
index f95a88023b..dff59e1375 100644
--- a/src/newgrf_airporttiles.h
+++ b/src/newgrf_airporttiles.h
@@ -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;
diff --git a/src/newgrf_canal.cpp b/src/newgrf_canal.cpp
index 2ebecf59fd..71668b67d4 100644
--- a/src/newgrf_canal.cpp
+++ b/src/newgrf_canal.cpp
@@ -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;
diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp
index b587ae2d33..f3687c303a 100644
--- a/src/newgrf_engine.cpp
+++ b/src/newgrf_engine.cpp
@@ -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) {
diff --git a/src/newgrf_engine.h b/src/newgrf_engine.h
index 7364b58d58..dfe2d2ffc8 100644
--- a/src/newgrf_engine.h
+++ b/src/newgrf_engine.h
@@ -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;
diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp
index 6be3e8570d..93a5ef2da9 100644
--- a/src/newgrf_extension.cpp
+++ b/src/newgrf_extension.cpp
@@ -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(),
};
diff --git a/src/newgrf_extension.h b/src/newgrf_extension.h
index a1e94a3d0e..d3e06548cb 100644
--- a/src/newgrf_extension.h
+++ b/src/newgrf_extension.h
@@ -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,
};
diff --git a/src/newgrf_generic.cpp b/src/newgrf_generic.cpp
index 8dd96cbc6f..54ec4067ba 100644
--- a/src/newgrf_generic.cpp
+++ b/src/newgrf_generic.cpp
@@ -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;
diff --git a/src/newgrf_house.h b/src/newgrf_house.h
index 6f01a8d373..7adea9ca6c 100644
--- a/src/newgrf_house.h
+++ b/src/newgrf_house.h
@@ -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;
diff --git a/src/newgrf_industries.h b/src/newgrf_industries.h
index 6fc0c3f770..6c0cd4ba2b 100644
--- a/src/newgrf_industries.h
+++ b/src/newgrf_industries.h
@@ -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;
diff --git a/src/newgrf_industrytiles.h b/src/newgrf_industrytiles.h
index ba5752cd13..68a1fd9ad6 100644
--- a/src/newgrf_industrytiles.h
+++ b/src/newgrf_industrytiles.h
@@ -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;
diff --git a/src/newgrf_newlandscape.h b/src/newgrf_newlandscape.h
index 307b88986e..105e6ce1a8 100644
--- a/src/newgrf_newlandscape.h
+++ b/src/newgrf_newlandscape.h
@@ -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;
diff --git a/src/newgrf_newsignals.h b/src/newgrf_newsignals.h
index d32e5850e4..1c72edd875 100644
--- a/src/newgrf_newsignals.h
+++ b/src/newgrf_newsignals.h
@@ -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;
diff --git a/src/newgrf_object.h b/src/newgrf_object.h
index 5b6da81c19..ef6b3763ea 100644
--- a/src/newgrf_object.h
+++ b/src/newgrf_object.h
@@ -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:
diff --git a/src/newgrf_railtype.h b/src/newgrf_railtype.h
index 8764efcef4..a4dc0f43e0 100644
--- a/src/newgrf_railtype.h
+++ b/src/newgrf_railtype.h
@@ -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;
diff --git a/src/newgrf_roadstop.h b/src/newgrf_roadstop.h
index 1adc150fd1..90bb1bc15d 100644
--- a/src/newgrf_roadstop.h
+++ b/src/newgrf_roadstop.h
@@ -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: {
diff --git a/src/newgrf_roadtype.h b/src/newgrf_roadtype.h
index 4c29d47178..b18c243a33 100644
--- a/src/newgrf_roadtype.h
+++ b/src/newgrf_roadtype.h
@@ -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;
diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp
index 62ac6a0f55..1868d3d35b 100644
--- a/src/newgrf_spritegroup.cpp
+++ b/src/newgrf_spritegroup.cpp
@@ -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(&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(&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);
diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h
index 6ff3b399a4..adc7a57bb5 100644
--- a/src/newgrf_spritegroup.h
+++ b/src/newgrf_spritegroup.h
@@ -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.
diff --git a/src/newgrf_station.h b/src/newgrf_station.h
index e4f6e79235..cff5d68db4 100644
--- a/src/newgrf_station.h
+++ b/src/newgrf_station.h
@@ -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:
diff --git a/src/newgrf_town.h b/src/newgrf_town.h
index b08232a766..d3313336df 100644
--- a/src/newgrf_town.h
+++ b/src/newgrf_town.h
@@ -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;