feat(runtime): propagate Control node stage changes
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s

This commit is contained in:
Valentin Popov committed 2026-10-29 17:55:55 +04:00
1 parent 0c31c34b57
commit be4031cb54
5 files changed
+410 -47

No files matched your search

+29 -8
View File
@@ -997,22 +997,43 @@ clock Control.
даёт `[14, 28, 10]`. При вычитании последовательный способ начинает с последней
строки. Так одна таблица описывает и текущее значение, и то, как оно меняется.
У строк есть и связи с родительскими узлами. Когда стадия родителя меняется,
Control пересчитывает дочерние строки в глубину: ребёнка вместе со всем его
поддеревом, затем следующего ребёнка. Для числа вариантов `count` внутренняя
стадия вычисляется как `count - ceil(f32(count * ratio))`, а видимое значение
ограничивается диапазоном модели. Например, при трёх стадиях доли
У строк есть и связи с родительскими узлами. Нативный `Control.dll` сначала
обновляет запрошенную строку через `0x11220`, затем `0x11130` ищет её детей
среди строк с большим индексом. Каждый найденный ребёнок обновляется вместе со
своим поддеревом, и только потом обход продолжается со следующим соседом.
Например, для родителей `[-1, 0, 0, 1, 3]` порядок строк становится
`1 → 3 → 4 → 2`: сначала весь первый участок дерева, затем его сосед.
Стадия увеличивается, когда доля ресурса уменьшается. Для числа вариантов
`count` внутренняя стадия вычисляется как `count - ceil(f32(count * ratio))`,
а видимое значение ограничивается диапазоном модели. Например, при трёх стадиях доли
`[0.1, 0.4, 0.5, 1, 0]` дали `[2, 1, 1, 0, 3]`; последнее значение — sentinel
истощения, а в модель передаётся индекс не выше `count - 1`. Нативные проверки
подтвердили порядок обхода и передачу состояния в AniMesh.
подтвердили арифметику и порядок обхода.
После обновления родителя Control сравнивает его старую стадию с новой и
проверяет флаг истощения `0x10`. Если стадия выросла либо строка истощена,
количество каждого ребёнка полностью уменьшается тем же путём, что и обычная
добавка ресурса. Для ребёнка без защитной маски родительские флаги `0xc0`
задают флаг `0x80`; в обычной ветви переход ставит `0x100` при ухудшении и
очищает у ребёнка `0x80`. Маска `0x140` запрещает только изменение этих флагов:
уменьшение количества и рекурсивный вызов всё равно выполняются. Дочерние
стадии пересчитываются и тогда, когда родитель не ухудшился.
Выбранная стадия также задаёт, какие данные MSH попадают в пересчёт видимости.
У одного синтетического узла первый слот давал `[42, 330]`, а слот другой стадии —
`[102, 570]`. В проверенных векторах одинаковые данные при скрытии и повторном
включении восстановили суммы; переходы с разными слотами — нет. Эти проверки
исполняют исходные DLL на тестовых строках, но не являются запуском Control на
полной миссии. Rust выполняет отдельный стадийный шаг в подтверждённой области,
но обход потомков, пересчёт видимости и остальные callbacks ещё не подключены.
полной миссии.
В Rust `update_loaded_mission_control_node_stage` сохраняет отдельный шаг одной
строки, а `propagate_loaded_mission_control_node_stage` обновляет строку и её
поддерево в том же порядке, что native `0x11130`. Весь снимок provider-таблицы
публикуется только после успешной обработки всех потомков. Если позже по обходу
понадобится неизвестная связь узлов, lifecycle, FX или пересчёт видимости,
операция сообщает об этом и оставляет снимок без изменений. Эта CPU-проекция
ещё не подключена к игровому Control clock или выводу модели в окно.
Прямой путь изменения количества остаётся отдельной операцией.
Подробные границы приведены в [техническом приложении](../appendices/script-vm.md#родители-стадии-и-видимость-provider-узлов).