aboutsummaryrefslogtreecommitdiff
path: root/docs/tomes/06-behavior.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tomes/06-behavior.md')
-rw-r--r--docs/tomes/06-behavior.md120
1 files changed, 111 insertions, 9 deletions
diff --git a/docs/tomes/06-behavior.md b/docs/tomes/06-behavior.md
index bcb7320..0ebbbf7 100644
--- a/docs/tomes/06-behavior.md
+++ b/docs/tomes/06-behavior.md
@@ -326,8 +326,48 @@ Resource Distributor работает со списком зарегистрир
записях при игровом x87 control word `0x007f`: проверены добавление, повторный
ключ, закреплённая запись, нулевая сумма и округление. Это позволяет Rust
сохранять начальный список, но пока не подтверждает веса реальных объектов в
-живой миссии. Для регулярного шага по-прежнему не хватает интерфейсов ArealMap.
-Остальные ограничения собраны в [границах знания](../appendices/knowledge-boundaries.md).
+живой миссии.
+
+Узкая часть регулярного шага уже связана с загруженным состоянием движка.
+`advance_loaded_mission_resources(engine, queue_time_ms)` принимает явную
+метку времени общей 32-битной очереди. Для каждого видимого Behavior clan он
+проверяет срок запуска, выполняет два прохода по списку и сохраняет пять
+итогов: доступную энергию и руду, буфер руды, потребление энергии и потребление
+руды. В обоих проходах объекты разрешаются заново по исходному TMA ID. Первый
+проход измеряет поставки и потребность, второй повторно читает представленные
+профильные и Control-значения перед применением выделения. Нативный код также
+запрашивает интерфейсы во втором проходе, но динамические getter-callbacks в
+этом снимке Rust не воспроизводятся.
+
+Планировщик использует беззнаковое сравнение времени: равенство ненулевому
+сроку ещё не запускает шаг, а нулевой срок не задерживает его. Переполнение
+вычитания даёт 32-битную длительность. Затем
+длительность переводится в секунды с округлением, подтверждённым x87-путём
+оригинальной DLL. Нативный oracle проверяет его на длинных интервалах, включая
+значения около границы точного целого `f32`, а также на равенстве и строгом
+проходе срока. Формула запуска, native vectors и команда воспроизведения
+описаны в технической главе [script VM](../appendices/script-vm.md).
+
+Шаг работает только с уже созданными полными объектами миссии и снимками тех
+профилей и Control-таблиц, чьи запросы и setters представлены в runtime. Он
+обновляет загруженные `CurrentOre`, поля строк распределителя и поддержанный
+Control selector `1`; неподдержанное свойство, запрос Control или невозможное
+арифметическое значение возвращает ошибку. Обновления нескольких кланов, их
+расписания и RNG фиксируются одной транзакцией: если обязательный запрос
+позднего клана не разрешён, ранее подготовленные изменения остаются
+неприменёнными. Приложение пока не вызывает
+этот API из игрового цикла, а динамические callbacks, которые переносят
+количество между Control provider-строками, ещё не моделируются. Поэтому это
+исполняемый загруженный resource step, а не полный симулятор миссии.
+
+Для настоящих ресурсов AutoDemo тест проходит несколько временных точек:
+генераторы `ObjectId 6` и `ObjectId 7` читают `Transfer_Power_Out = 10` из
+`prof_generator.var`; после шага в 500 мс каждый сообщает 5 единиц доступной
+энергии. Это подтверждает связь загруженного профиля с runtime-расчётом, но не
+сравнение полной миссии с оригинальной игрой. Native oracle сверяет исходные
+планировщик и распределитель GOG `Behavior.dll`; техническая процедура и
+проверяемые векторы описаны в [script VM](../appendices/script-vm.md). Остальные ограничения собраны в
+[границах знания](../appendices/knowledge-boundaries.md).
Первый часто встречающийся table entry с side effect — `Handler(2)` (176
records в corpus). Он берёт active instruction, разрешает семь slots через
@@ -819,13 +859,75 @@ tag-26 ряда получается `1.0` provider × `1.0` coefficient × `1.0
`1.0`. Эти запросы используют начальные provider-значения после конструкторов,
а не результаты живого игрового тика.
-Поведение provider-строк меняется и после загрузки: например,
-`Control.dll!FUN_10010f30` (RVA `0x10f30`) обновляет и ограничивает текущее
-значение, а соседние процедуры распространяют его между связанными строками.
-Эти изменения пока не отражены в Rust-снимке. Поэтому запрос Control `0xe00` и
-Behavior selector `0x1002`, который его суммирует, используют исходное состояние
-полной загрузки. Selector `0x1005` прямо возвращает загруженную ставку профиля.
-Ни один из них пока не исполняет Control tick или последующие изменения профиля.
+У Control есть и начальные сведения о модели узла, но их таблица не совпадает
+с NDP. Sidecar задаёт состав и порядок provider-строк, а поле type-1 `+0x14`
+получает начальное значение `1.0` от нативного конструктора. AniMesh отдельно
+собирает глобальный список MSH-узлов. При создании строки Control берёт
+mesh-состояние по её порядковому номеру в общей таблице provider, а не пытается
+угадать пару по локальному номеру строки sidecar-а. Например, после 22
+корневых строк номер 22 указывает на первый глобальный узел присоединённой
+`EXTO`-модели, который
+происходит из её локального узла `1`.
+
+В снимке это необязательное поле: `None` означает, что для данного номера не
+удалось получить MSH-узел; исходные запросы к NDP от этого не исчезают. Поэтому
+начальное значение и поза модели остаются отдельными частями состояния:
+
+```rust
+initial_mesh_state: Option<MissionControlNodeInitialMeshState>
+```
+
+Внутри снимка хранятся родительский индекс, переведённые флаги Control и число
+начальных стадий. Узлы корневой модели добавляются все, а присоединённая `EXTO`
+начинает с локального узла `1`. Это описывает исходную настройку: код пока не
+переключает стадии в ходе игры и не исполняет полный Control tick.
+
+На той же начальной стадии Control вычисляет количество строки по NDP
+`max_volume`. Для загрузки Unit DAT проверенные множители равны `1.0`, поэтому
+количество совпадает с исходным значением NDP. Оригинал начинает с одинакового
+текущего и последнего обработанного количества; Rust сохраняет его один раз как
+`initial_amount_bits`. Это исходный снимок, а не неизменяемая вместимость:
+позднее Control меняет оба количества. В лицензированном `min_l_n1.dat`
+проверены, например, root row `0` со значением `1.0` и строка `EXTO` с local
+row `1` в общей позиции `22` со значением `6000.0`.
+
+После загрузки количество в строках может меняться. Контроллер распределяет
+добавку либо пропорционально свободному месту, либо по очереди: при максимумах
+`[30, 60, 50]`, текущих значениях `[10, 20, 0]` и добавке `22` первый способ
+даёт `[14, 28, 10]`. При вычитании последовательный способ начинает с последней
+строки. Так одна таблица описывает и текущее значение, и то, как оно меняется.
+
+У строк есть и связи с родительскими узлами. Когда стадия родителя меняется,
+Control пересчитывает дочерние строки в глубину: ребёнка вместе со всем его
+поддеревом, затем следующего ребёнка. Для числа вариантов `count` внутренняя
+стадия вычисляется как `count - ceil(f32(count * ratio))`, а видимое значение
+ограничивается диапазоном модели. Например, при трёх стадиях доли
+`[0.1, 0.4, 0.5, 1, 0]` дали `[2, 1, 1, 0, 3]`; последнее значение — sentinel
+истощения, а в модель передаётся индекс не выше `count - 1`. Нативные проверки
+подтвердили порядок обхода и передачу состояния в AniMesh.
+
+Выбранная стадия также задаёт, какие данные MSH попадают в пересчёт видимости.
+У одного синтетического узла первый слот давал `[42, 330]`, а слот другой стадии —
+`[102, 570]`. В проверенных векторах одинаковые данные при скрытии и повторном
+включении восстановили суммы; переходы с разными слотами — нет. Эти проверки
+исполняют исходные DLL на тестовых строках, но не являются запуском Control на
+полной миссии; Rust пока сохраняет только начальное состояние provider-строк.
+Подробные границы приведены в [техническом приложении](../appendices/script-vm.md#родители-стадии-и-видимость-provider-узлов).
+
+Для точного повтора важен порядок округления. При исходных значениях `[2^24, 2]`
+и запросе `-1` оригинал сначала складывает общий итог с изменением, затем
+вычитает прежний итог. Из-за округления фактическое изменение получается `-2`.
+Нативная проверка подтвердила это поведение; Rust пока использует начальный
+снимок provider-строк и не исполняет их дальнейшее распределение.
+
+Отдельный путь объекта класса `4` умножает интервал на контактный коэффициент и
+передаёт изменение строке. В проверенном примере коэффициент `1`, интервал `2`,
+и значение строки уменьшается на `2`; этот коэффициент не является ставкой
+производства из профиля. Проверка времени охватывает только начальную ветвь:
+активную ветвь не запускали, а источник времени и полный порядок вызовов в игре
+пока не установлены. Поэтому запросы Rust `0xe00` и `0x1002` используют
+начальные снимки provider-строк, а `0x1005` — загруженную ставку профиля.
+Точные нативные векторы и открытые границы описаны в [приложении](../appendices/script-vm.md#изменение-provider-состояния-control).
На x87 control word `0x007f` каждое умножение округляет мантиссу до 24 бит,
но промежуточная степень может выйти за диапазон `f32` и вернуться в него при