Merge pull request #262 from Kalmarv/pr/tod-v3

Time of day: the new one loads only after the old one has unloaded
This commit is contained in:
zeex64 authored and GitHub committed 2026-10-16 20:35:04 -05:00
commit 20d58a4b73
2 files changed
+36 -3

No files matched your search

+23
View File
@@ -169,6 +169,29 @@ inline WorldLayerMapSelection select_world_layer_map(
return result;
}
// A TOD_<n>_<name> layer (or a lighting tier's <tier>_TOD_<n>_<name>): one time of day among its siblings.
inline bool world_layer_time_of_day(std::size_t slot) {
const auto& bundle = world_layer_nodes()[slot].bundle;
auto leaf = bundle.substr(bundle.find_last_of('/') + 1);
for (auto& c : leaf) if (c >= 'A' && c <= 'Z') c = static_cast<char>(c + ('a' - 'A'));
const auto mid = leaf.find("_tod_");
const auto at = leaf.starts_with("tod_") ? 4 : mid == std::string::npos ? leaf.size() : mid + 5;
return at < leaf.size() && leaf[at] >= '1' && leaf[at] <= '9';
}
// A sibling time of day that is to be off but not fully unloaded yet, which `slot` must wait for;
// world_layer_nodes().size() when there is none.
template <class FullyOff>
std::size_t world_layer_old_time_of_day(std::size_t slot, const std::vector<std::optional<bool>>& desired, FullyOff&& fully_off) {
const auto& nodes = world_layer_nodes();
if (!world_layer_time_of_day(slot)) return nodes.size();
for (std::size_t at = 0; at < nodes.size(); ++at)
if (at != slot && nodes[at].parent == nodes[slot].parent && nodes[at].map == nodes[slot].map &&
desired[at] == false && world_layer_time_of_day(at) && !fully_off(at))
return at;
return nodes.size();
}
inline bool world_layer_supported(
std::size_t choice,
const std::vector<bool>& present) noexcept {
+13 -3
View File
@@ -171,8 +171,9 @@ bool ensure_seasonal_state(std::uintptr_t entity, bool wanted, std::uint64_t now
}
if (reached(wanted)) return true;
// An engine-initiated auto-load is still in flight. Wait for it rather than
// claiming success from a non-null request handle or repeatedly reloading.
if (wanted && state.requested) { status = "Loading..."; return false; }
// claiming success from a non-null request handle, repeatedly reloading, or
// unloading it midway (the engine ignores that unload and finishes the load).
if (state.requested && !state.loaded) { status = "Loading..."; return false; }
node.pending = wanted; node.requested_at = now;
const auto slot = node.slot;
SeasonalEvent event{local_runtime().base + addr::world_layers::subworld_event_vtable, wanted ? 0xd27bc17eu : 0x21f3f677u};
@@ -309,6 +310,10 @@ void update_world_layers(std::uintptr_t manager, std::uint64_t now) {
std::vector<bool> done(targets.size()), inactive(targets.size());
std::vector<std::string> status(targets.size(), "Waiting for layer");
bool issued{};
const auto fully_off = [&](std::size_t at) {
SeasonalState live;
return !targets[at] || (seasonal_state(r.nodes.at(targets[at]), live) && !live.requested && !live.loaded);
};
for (unsigned slot = 0; slot < targets.size(); ++slot) {
if (world_layer_nodes()[slot].map != map) continue;
const auto parent = world_parent(static_cast<int>(slot));
@@ -323,7 +328,12 @@ void update_world_layers(std::uintptr_t manager, std::uint64_t now) {
if (!targets[slot]) continue;
SeasonalState state;
if (!seasonal_state(r.nodes.at(targets[slot]), state)) continue;
if (!desired[slot]) {
// Two times of day loaded at once can overflow the engine's light tables: the old one goes first.
const auto old = desired[slot] == true && !state.requested ? world_layer_old_time_of_day(slot, desired, fully_off) : targets.size();
if (old < targets.size()) {
status[slot] = "Waiting for the old time of day";
(void)ensure_seasonal_state(targets[old], false, now, issued, status[old]);
} else if (!desired[slot]) {
done[slot] = state.requested == state.loaded;
inactive[slot] = done[slot] && !state.loaded;
status[slot] = done[slot] ? "Default" : "Loading...";