mirror of https://github.com/OpenTTD/OpenTTD
Add: [NewGRF] Special value 0x7FFE for VarAction2 results specifying 'return calculated result'.
parent
9b2ee5bf5d
commit
4208f248d0
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue