Платформа NyxtaleЯзык Nyx
Синтаксис
Полная грамматика .nsx: заголовок, реплики, выборы, метки, ветвления, директивы.
.nsx — построчный формат. Одна строка — одна мысль; вложенность задаётся отступом. Пустые строки
игнорируются.
Строк всего четыре вида, и каждый узнаётся по первому символу:
| Сигил | Что это | Пример |
|---|---|---|
@ |
инструкция движку | @bg forest_night |
| (нет) | реплика — то, что читают | Марк: Кто здесь? |
* |
опция выбора | * choice "Войти" |
-> |
переход | -> inside |
Сигилов четыре, а не один, чтобы сцена листалась как рукопись, а не разбиралась как конфиг. Реплика — самая частая строка в истории — не носит никакого сигила вообще: проза выглядит прозой.
Заголовок
@nsx_version 1
@scene doorway
@render horror + creepypasta
@nsx_version <целое>— обязательна, идёт первой.@scene <идентификатор>— имя сцены: латиница, цифры, подчёркивание.@render <тема> [+ <примесь> …]— оформление. Без неё движок берётdefaultи предупреждает.
Реплики
narrator: Тишина стояла такая, что было слышно лампу.
Марк: Кто здесь?
{hero}: Я вернулся.
Говорящий — это отображаемый текст, а не идентификатор: Марк, Смотритель, любые буквы любого
алфавита. narrator — обычное имя говорящего, просто читатель рисует его без плашки.
{hero} — подстановка флага в имя: имя персонажа подставится на лету. То же работает и в тексте
реплики.
Эмоцию можно указать в скобках — читатель получает её отдельным полем:
mark (fear): Здесь кто-то был.
Идентификатор эмоции — только латиницей.
Марк (страх):сейчас не разбирается, хотя имя говорящего кириллицей работает. Это ограничение парсера, а не решение формата.
Комментарии
// в начале строки или после директивы — комментарий до конца строки; /* … */ — блочный, можно на
несколько строк. Внутри реплики оба знака литеральны: проза есть проза, и движок не имеет права
съесть половину предложения за то, что автор поставил в нём слэш.
// заметка себе: здесь потом добавить звук
@bg hall_night // фон выбран нарочно тусклым
/* этот кусок пока отложен —
вернуться к нему после главы 3 */
narrator: Он ушёл; она осталась стоять.
Точка с запятой комментарием не является — это была ранняя версия языка, и её сняли ровно потому,
что ; слишком часто встречается в живой фразе.
Выбор
* choice "Войти" @if has_key == 1
@set trespassed = 1
narrator: Дверь поддалась.
-> inside
* choice "Постучать" @timer 8
-> knock
* choice "Заплатить сторожу" @cost 5 candles
-> bribe
Слово choice обязательно, текст — в кавычках. Идущие подряд * choice на одном отступе — один
узел выбора с несколькими опциями. Тело опции — её отступ.
Модификаторы в шапке опции:
| Модификатор | Что делает |
|---|---|
@if <выражение> |
опция видна, только если выражение истинно |
@cost <N> <валюта> |
опция платная |
@timer <секунды> |
на выбор ставится таймер |
@timerсчитает секунды, не миллисекунды.
Выборы вкладываются: тело опции может содержать свой * choice.
Переходы и метки
@label inside
narrator: Внутри было теплее, чем снаружи. Это и пугало.
-> hall
@label <имя> — адрес. -> <цель> или @jump <цель> — переход к нему. Метки не видны читателю.
Условия
Строкой — когда нужно просто увести:
@if fear > 5 -> panic
@unless has_key == 1 -> locked_out
Блоком — когда под условием несколько действий:
@if fear > 5
@heartbeat 110
narrator: Руки не слушались.
-> panic
@unless — то же самое с отрицанием.
Флаги
@set fear = 0
@set fear += 2
@set fear -= 1
@set name = 0
Операторы — =, +=, -=. Флаг нужно задать до того, как его прочитают: если найдётся путь, на
котором чтение случается раньше записи, валидатор скажет об этом кодом NSX_READ_BEFORE_WRITE.
Выражения
Приоритет от низкого к высокому:
or < and < not < сравнения (== != < <= > >=) < + - < * / % < унарный - < primaryand, or, not пишутся и как &&, ||, ! — одно и то же.
Primary — целое число, true / false, флаг, скобки или функция: rand, min, max, clamp,
abs.
@set roll = 0
@set roll = rand(1, 6)
@set roll = clamp(roll, 1, 5)
@if roll > 3 and fear < 10 -> steady
Арифметика только целочисленная. Деление отбрасывает дробь в сторону нуля, деление на ноль даёт
0. Это не упрощение: превью в браузере, движок на телефоне и повтор по сохранению обязаны сойтись
бит в бит, иначе превью ведёт автора в одну концовку, а игрока — в другую.
rand() детерминирован: он ходит от rng_seed, который лежит в состоянии и в сохранении. Одно и то
же прохождение всегда даёт один и тот же бросок.
Директивы
Всё, что начинается с @ и не перечислено выше, — директива-эффект:
@bg forest_night
@ambient wind_howl loop
@jumpscare shadow_figure @sound stinger_01
@sanity_show
Аргументы позиционные; именованные пишутся как @подфлаг значение. Ассеты — по идентификатору.
Все 94 директивы с типами аргументов и границами — в справочнике. Он собран из реестра движка, поэтому не может отстать от него.
Концовки
@end trespass
@end [имя] завершает сцену. Имя необязательно, но именованная концовка — это то, что читатель
«нашёл», и то, что движок считает при обходе.
Что проверяется
Компилятор ловит синтаксис, пока вы печатаете. Валидатор ловит целостность: битые ссылки, недостижимые узлы, тупики, ловушки-циклы, необъявленные флаги, чтение до записи, отсутствующие ассеты, лицензию без атрибуции.
Коды и починки — в справочнике диагностик.
Директивы этой темы
Собрано из реестра движка: каждая директива этих категорий ведёт сюда из справочника.
@autoadvanceАвто-адванс vs тап — темп чтения@fontСмена гарнитуры (serif/mono/sans)@instantМгновенный вывод реплики@lineheightМежстрочный ритм (дыхание строк)@redactРедакция: [ДАННЫЕ УДАЛЕНЫ]@shoutКрик — крупный резкий текст@staggerСтаггер: слова проступают по одному@text_corruptПорча текста — буквы искажаются@text_eraseСамостирающийся текст@text_fadeПроявление/растворение строки@text_shakeДрожь текста — нестабильная реплика@textsizeРазмер текста — уровень 1–6@textspeedСкорость печати текста@thoughtМысль персонажа — бабл рядом с ним@unredactСнять редакцию — раскрыть скрытый текст@whisperШёпот — мелкий тусклый текст