2026-02-11 21:12:05 +00:00
# FXID
2026-02-19 04:46:23 +04:00
`FXID` — бинарный формат эффекта в движке Parkan: Iron Strategy.
Эта страница задаёт контракт формата и исполнения на уровне, достаточном для 1:1 порта рендера/симуляции эффектов и для lossless-инструментов.
2026-02-11 21:50:33 +00:00
Связанный контейнер: [NRes / RsLi ](nres.md ).
2026-02-19 04:46:23 +04:00
## 1. Контейнер
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
- Тип р е с у р с а в `NRes` : `0x44495846` (`FXID` ).
- Значения `attr1/attr2/attr3` в типовых игровых данных стабильны, но при редактуре их нужно сохранять как есть.
2026-02-11 21:12:05 +00:00
2026-02-19 04:46:23 +04:00
## 2. Бинарный формат
2026-02-11 21:50:33 +00:00
2026-02-11 22:06:56 +00:00
В с е значения little-endian.
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
### 2.1. Заголовок (60 байт)
2026-02-11 21:12:05 +00:00
```c
struct FxHeader60 {
2026-02-11 22:06:56 +00:00
uint32_t cmd_count; // 0x00
uint32_t time_mode; // 0x04
float duration_sec; // 0x08
float phase_jitter; // 0x0C
uint32_t flags; // 0x10
uint32_t settings_id; // 0x14
float rand_shift_x; // 0x18
float rand_shift_y; // 0x1C
float rand_shift_z; // 0x20
float pivot_x; // 0x24
float pivot_y; // 0x28
float pivot_z; // 0x2C
float scale_x; // 0x30
float scale_y; // 0x34
float scale_z; // 0x38
2026-02-11 21:12:05 +00:00
};
```
2026-02-19 04:46:23 +04:00
Поток команд начинается строго с `offset = 0x3C` .
### 2.2. Команда
2026-02-11 22:06:56 +00:00
Каждая команда:
2026-02-11 21:12:05 +00:00
2026-02-19 04:46:23 +04:00
1. `uint32 cmd_word`
2. body фиксированного размера, зависящего от `opcode`
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
Поля `cmd_word` :
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
- `opcode = cmd_word & 0xFF`
- `enabled = (cmd_word >> 8) & 1`
- `bits 9..31` нужно сохранять 1:1
2026-02-11 21:50:33 +00:00
2026-02-11 22:06:56 +00:00
Выравнивания между командами нет.
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
### 2.3. Размеры команд
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
| Opcode | Размер |
2026-02-11 22:06:56 +00:00
|---:|---:|
| 1 | 224 |
| 2 | 148 |
| 3 | 200 |
| 4 | 204 |
| 5 | 112 |
| 6 | 4 |
| 7 | 208 |
| 8 | 248 |
| 9 | 208 |
| 10 | 208 |
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
## 3. Смысл заголовка
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
- `cmd_count` : число команд в потоке.
- `time_mode` : способ вычисления текущего коэффициента эффекта.
- `duration_sec` : длительность (в рантайме переводится в миллисекунды).
- `phase_jitter` : амплитуда случайного фазового сдвига.
- `flags` : флаги поведения (видимость, альфа-модификаторы, режимы гейтинга).
- `settings_id` : индекс профиля/настроек эффекта.
- `rand_shift_*` : случайный пространственный сдвиг.
- `pivot_*` : локальная опора.
- `scale_*` : базовый масштаб инстанса эффекта.
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
## 4. Флаги заголовка
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
Практически важные биты:
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
- `0x0001` : случайный сдвиг фазы
- `0x0008` : случайный пространственный сдвиг (`rand_shift_*` )
- `0x0010` : ветки видимости/окклюзии
- `0x0020` : треугольный ремап альфы
- `0x0040` : инверсия исходного active-state
- `0x0080` , `0x0100` : фильтрация по времени суток
- `0x0200` : умножение альфы на нормализованное время жизни
- `0x0400` , `0x1000` : дополнительные биты состояния менеджера эффекта
- `0x0800` : дополнительный гейтинг
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
Неизвестные биты должны сохраняться без изменений.
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
## 5. `time_mode` (0..17)
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
База:
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
- `tn = (now - start) / (end - start)`
- `prev = предыдущая вычисленная альфа`
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
Поддерживаемые семейства режимов:
2026-02-11 21:12:05 +00:00
2026-02-19 04:46:23 +04:00
- константный режим;
- линейный (`tn` ), обратный (`1-tn` ), циклический (`fract(tn)` );
- режимы от внешних параметров мира/очереди;
- режимы на основе норм векторов состояния;
- режимы с ограничением вниз/вверх относительно `prev` .
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
После вычисления:
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
- при `flags & 0x0200` применяется `alpha *= tn` ;
- при `flags & 0x0020` применяется triangular remap.
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
## 6. Resource-ссылки внутри команд
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
Для opcode `2/3/4/5/7/8/9/10` используется ссылка:
2026-02-11 21:50:33 +00:00
2026-02-11 22:06:56 +00:00
```c
struct ResourceRef64 {
2026-02-19 04:46:23 +04:00
char archive[32];
char name[32];
2026-02-11 22:06:56 +00:00
};
```
2026-02-19 04:46:23 +04:00
Контракт:
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
- строки ASCII, нуль-терминированные;
- сравнение имён регистронезависимое;
- обычно:
- `opcode 2` : `sounds.lib` + `*.wav`
- остальные: `material.lib` + имя материала/эффекта.
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
## 7. Runtime-контракт исполнения
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
Н а создании инстанса:
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
1. Заголовок копируется в runtime-состояние.
2. Вычисляется `end_time` .
3. Для каждой команды создаётся runtime-объект по `opcode` .
4. В объект копируется `enabled` .
5. Объект инициализируется контекстом эффекта.
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
Н а каждом кадре:
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
1. Вычисляется текущий коэффициент/альфа по `time_mode` и `flags` .
2. Выполняется update каждой команды.
3. Выполняется emit/render часть активных команд.
4. Применяются события Start/Stop/Restart.
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
## 8. Строгий парсер (рекомендуемый)
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
1. Проверить `len(payload) >= 60` .
2. Прочитать `cmd_count` .
3. Идти от `ptr = 0x3C` .
4. Для каждой команды:
- проверить `ptr + 4 <= len` ;
- прочитать `opcode` ;
- проверить, что `opcode` поддержан;
- проверить `ptr + size(opcode) <= len` ;
- сдвинуть `ptr += size(opcode)` .
5. Проверить `ptr == len(payload)` .
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
## 9. Writer и редактор
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
Для lossless-совместимости:
2026-02-11 22:06:56 +00:00
2026-02-19 04:46:23 +04:00
- сохранять все неизвестные поля/биты;
- не менять фиксированные размеры команд;
2026-02-11 22:06:56 +00:00
- не добавлять padding;
2026-02-19 04:46:23 +04:00
- пересчитывать только `cmd_count` и размеры контейнера;
- сохранять порядок команд.
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
## 10. Что требуется для 1:1 переноса
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
1. Полная поддержка opcode `1..10` .
2. Точный контракт вычисления `time_mode` и `flags` .
3. Точное поведение `ResourceRef64` .
4. Повторяемый RNG и одинаковая политика плавающей точки.
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
## 11. Статус валидации
2026-02-11 21:50:33 +00:00
2026-02-19 04:46:23 +04:00
- Формальные инварианты FXID зафиксированы в `tools/msh_doc_validator.py` и `tools/fxid_abs100_audit.py` .
- В текущем рабочем окружении нет полного набора игровых архивов (`testdata` без payload), поэтому массовая повторная проверка корпуса здесь не выполнялась.