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"
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<DeterministicSpriteGroupRange> 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<uint32_t> 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<const SpriteGroup *> target;
std::vector<DeterministicSpriteGroupResult> 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;

View File

@ -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<CallbackResult>(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<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;
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<DeterministicSpriteGroupAdjust> adjusts{};
std::vector<DeterministicSpriteGroupRange> 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