1
0
Fork 0

Add: [NewGRF] Special value 0x7FFE for VarAction2 results specifying 'return calculated result'.

pull/14149/head
frosch 2025-04-28 21:44:20 +02:00
parent 9b2ee5bf5d
commit 4208f248d0
3 changed files with 45 additions and 21 deletions

View File

@ -23,6 +23,7 @@
#include "../safeguards.h" #include "../safeguards.h"
constexpr uint16_t GROUPID_CALLBACK_FAILED = 0x7FFF; ///< Explicit "failure" result. 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. * Map the colour modifiers of TTDPatch to those that Open is using.
@ -420,15 +421,31 @@ static void NewSpriteGroup(ByteReader &buf)
std::vector<DeterministicSpriteGroupRange> ranges; std::vector<DeterministicSpriteGroupRange> ranges;
ranges.resize(buf.ReadByte()); ranges.resize(buf.ReadByte());
for (auto &range : ranges) { 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.low = buf.ReadVarSize(varsize);
range.high = buf.ReadVarSize(varsize); range.high = buf.ReadVarSize(varsize);
} }
group->default_group = GetGroupFromGroupID(setid, type, buf.ReadWord()); auto defgroupid = buf.ReadWord();
group->error_group = ranges.empty() ? group->default_group : ranges[0].group; if (defgroupid == GROUPID_CALCULATED_RESULT) {
/* nvar == 0 is a special case -- we turn our value into a callback result */ group->default_result.calculated_result = true;
group->calculated_result = ranges.empty(); } 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 */ /* Sort ranges ascending. When ranges overlap, this may required clamping or splitting them */
std::vector<uint32_t> bounds; std::vector<uint32_t> bounds;
@ -440,13 +457,13 @@ static void NewSpriteGroup(ByteReader &buf)
std::sort(bounds.begin(), bounds.end()); std::sort(bounds.begin(), bounds.end());
bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end()); bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end());
std::vector<const SpriteGroup *> target; std::vector<DeterministicSpriteGroupResult> target;
target.reserve(bounds.size()); target.reserve(bounds.size());
for (const auto &bound : bounds) { for (const auto &bound : bounds) {
const SpriteGroup *t = group->default_group; auto t = group->default_result;
for (const auto &range : ranges) { for (const auto &range : ranges) {
if (range.low <= bound && bound <= range.high) { if (range.low <= bound && bound <= range.high) {
t = range.group; t = range.result;
break; break;
} }
} }
@ -455,11 +472,11 @@ static void NewSpriteGroup(ByteReader &buf)
assert(target.size() == bounds.size()); assert(target.size() == bounds.size());
for (uint j = 0; j < 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(); DeterministicSpriteGroupRange &r = group->ranges.emplace_back();
r.group = target[j]; r.result = target[j];
r.low = bounds[j]; r.low = bounds[j];
while (j < bounds.size() && target[j] == r.group) { while (j < bounds.size() && target[j] == r.result) {
j++; j++;
} }
r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX; r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX;

View File

@ -224,26 +224,27 @@ static bool RangeHighComparator(const DeterministicSpriteGroupRange &range, uint
object.last_value = last_value; object.last_value = last_value;
if (this->calculated_result) { auto result = this->default_result;
/* nvar == 0 is a special case -- we turn our value into a callback result */
return static_cast<CallbackResult>(GB(value, 0, 15));
}
if (this->ranges.size() > 4) { if (this->ranges.size() > 4) {
const auto &lower = std::lower_bound(this->ranges.begin(), this->ranges.end(), value, RangeHighComparator); const auto &lower = std::lower_bound(this->ranges.begin(), this->ranges.end(), value, RangeHighComparator);
if (lower != this->ranges.end() && lower->low <= value) { if (lower != this->ranges.end() && lower->low <= value) {
assert(lower->low <= value && value <= lower->high); assert(lower->low <= value && value <= lower->high);
return SpriteGroup::Resolve(lower->group, object, false); result = lower->result;
} }
} else { } else {
for (const auto &range : this->ranges) { for (const auto &range : this->ranges) {
if (range.low <= value && value <= range.high) { 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<CallbackResult>(GB(value, 0, 15));
}
return SpriteGroup::Resolve(result.group, object, false);
} }

View File

@ -163,8 +163,15 @@ struct DeterministicSpriteGroupAdjust {
}; };
struct DeterministicSpriteGroupRange { struct DeterministicSpriteGroupResult {
bool calculated_result = false;
const SpriteGroup *group = nullptr; const SpriteGroup *group = nullptr;
bool operator==(const DeterministicSpriteGroupResult &) const = default;
};
struct DeterministicSpriteGroupRange {
DeterministicSpriteGroupResult result;
uint32_t low = 0; uint32_t low = 0;
uint32_t high = 0; uint32_t high = 0;
}; };
@ -175,12 +182,11 @@ struct DeterministicSpriteGroup : SpriteGroup {
VarSpriteGroupScope var_scope{}; VarSpriteGroupScope var_scope{};
DeterministicSpriteGroupSize size{}; DeterministicSpriteGroupSize size{};
bool calculated_result = false;
std::vector<DeterministicSpriteGroupAdjust> adjusts{}; std::vector<DeterministicSpriteGroupAdjust> adjusts{};
std::vector<DeterministicSpriteGroupRange> ranges{}; // Dynamically allocated std::vector<DeterministicSpriteGroupRange> ranges{}; // Dynamically allocated
/* Dynamically allocated, this is the sole owner */ /* 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 const SpriteGroup *error_group = nullptr; // was first range, before sorting ranges