diff --git a/src/newgrf/newgrf_act2.cpp b/src/newgrf/newgrf_act2.cpp index 89625f4062..e8f62d8fd6 100644 --- a/src/newgrf/newgrf_act2.cpp +++ b/src/newgrf/newgrf_act2.cpp @@ -23,6 +23,7 @@ #include "../safeguards.h" constexpr uint16_t GROUPID_CALLBACK_FAILED = 0x7FFF; ///< Explicit "failure" result. +constexpr uint16_t GROUPID_CALCULATED_RESULT = 0x7FFE; ///< Return calculated result from VarAction2. /** * Map the colour modifiers of TTDPatch to those that Open is using. @@ -420,15 +421,31 @@ static void NewSpriteGroup(ByteReader &buf) std::vector ranges; ranges.resize(buf.ReadByte()); for (auto &range : ranges) { - range.group = GetGroupFromGroupID(setid, type, buf.ReadWord()); + auto groupid = buf.ReadWord(); + if (groupid == GROUPID_CALCULATED_RESULT) { + range.result.calculated_result = true; + } else { + range.result.group = GetGroupFromGroupID(setid, type, groupid); + } range.low = buf.ReadVarSize(varsize); range.high = buf.ReadVarSize(varsize); } - group->default_group = GetGroupFromGroupID(setid, type, buf.ReadWord()); - group->error_group = ranges.empty() ? group->default_group : ranges[0].group; - /* nvar == 0 is a special case -- we turn our value into a callback result */ - group->calculated_result = ranges.empty(); + auto defgroupid = buf.ReadWord(); + if (defgroupid == GROUPID_CALCULATED_RESULT) { + group->default_result.calculated_result = true; + } else { + group->default_result.group = GetGroupFromGroupID(setid, type, defgroupid); + } + /* 'calculated_result' makes no sense for the 'error' case. Use callback failure (nullptr) instead */ + group->error_group = ranges.empty() ? group->default_result.group : ranges[0].result.group; + /* nvar == 0 is a special case: + * - set "default_result" to "calculated_result". + * - the old value specifies the "error_group". */ + if (ranges.empty()) { + group->default_result.calculated_result = true; + group->default_result.group = nullptr; + } /* Sort ranges ascending. When ranges overlap, this may required clamping or splitting them */ std::vector bounds; @@ -440,13 +457,13 @@ static void NewSpriteGroup(ByteReader &buf) std::sort(bounds.begin(), bounds.end()); bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end()); - std::vector target; + std::vector target; target.reserve(bounds.size()); for (const auto &bound : bounds) { - const SpriteGroup *t = group->default_group; + auto t = group->default_result; for (const auto &range : ranges) { if (range.low <= bound && bound <= range.high) { - t = range.group; + t = range.result; break; } } @@ -455,11 +472,11 @@ static void NewSpriteGroup(ByteReader &buf) assert(target.size() == bounds.size()); for (uint j = 0; j < bounds.size(); ) { - if (target[j] != group->default_group) { + if (target[j] != group->default_result) { DeterministicSpriteGroupRange &r = group->ranges.emplace_back(); - r.group = target[j]; + r.result = target[j]; r.low = bounds[j]; - while (j < bounds.size() && target[j] == r.group) { + while (j < bounds.size() && target[j] == r.result) { j++; } r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX; diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 63347e84c8..d8bcbe59de 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -224,26 +224,27 @@ static bool RangeHighComparator(const DeterministicSpriteGroupRange &range, uint object.last_value = last_value; - if (this->calculated_result) { - /* nvar == 0 is a special case -- we turn our value into a callback result */ - return static_cast(GB(value, 0, 15)); - } + auto result = this->default_result; if (this->ranges.size() > 4) { const auto &lower = std::lower_bound(this->ranges.begin(), this->ranges.end(), value, RangeHighComparator); if (lower != this->ranges.end() && lower->low <= value) { assert(lower->low <= value && value <= lower->high); - return SpriteGroup::Resolve(lower->group, object, false); + result = lower->result; } } else { for (const auto &range : this->ranges) { if (range.low <= value && value <= range.high) { - return SpriteGroup::Resolve(range.group, object, false); + result = range.result; + break; } } } - return SpriteGroup::Resolve(this->default_group, object, false); + if (result.calculated_result) { + return static_cast(GB(value, 0, 15)); + } + return SpriteGroup::Resolve(result.group, object, false); } diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index b8121dcef9..5abc45dbd7 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -163,8 +163,15 @@ struct DeterministicSpriteGroupAdjust { }; -struct DeterministicSpriteGroupRange { +struct DeterministicSpriteGroupResult { + bool calculated_result = false; const SpriteGroup *group = nullptr; + + bool operator==(const DeterministicSpriteGroupResult &) const = default; +}; + +struct DeterministicSpriteGroupRange { + DeterministicSpriteGroupResult result; uint32_t low = 0; uint32_t high = 0; }; @@ -175,12 +182,11 @@ struct DeterministicSpriteGroup : SpriteGroup { VarSpriteGroupScope var_scope{}; DeterministicSpriteGroupSize size{}; - bool calculated_result = false; std::vector adjusts{}; std::vector ranges{}; // Dynamically allocated /* Dynamically allocated, this is the sole owner */ - const SpriteGroup *default_group = nullptr; + DeterministicSpriteGroupResult default_result; const SpriteGroup *error_group = nullptr; // was first range, before sorting ranges