Датасеты
Если вы делаете что-то поверх наших датасетов — редактор, импортёр, конвертер из своего формата, ИИ-генератор контента — начните отсюда. Страница самодостаточна: всё, что нужно, либо здесь, либо по публичному URL.
Модель за минуту
Заголовок раздела «Модель за минуту»Датасет — именованный контейнер с механикой: классы, подклассы, виды, линии, предыстории, черты. Концептуально сорсбук: одна группа контента, один источник, одна лицензия. Игрок подключает наборы к себе, визард мержит подключённые и строит по ним персонажа.
Внутри всё устроено на грантах: сущность не имеет полей «навыки» и «бонусы», она имеет массив типизированных грантов — «дай владение навыком», «дай +1 к Ловкости», «предложи выбрать один из…». Словарь грантов закрыт — 28 типов. Вы не пишете DSL и не исполняете чужой код, вы наполняете типизированный JSON.
Что вы получаете
Заголовок раздела «Что вы получаете»Статические файлы, забираются curl’ом без ключей и регистрации.
| Артефакт | URL |
|---|---|
| Схема всего датасета | https://longstoryshort.app/schema/v1/wizard-dataset.json |
| Схемы по сущностям | …/schema/v1/dataset-{class,subclass,race,subrace,background,feat}.json |
| Схема гранта | …/schema/v1/grant.json |
| SRD 5.1 | https://longstoryshort.app/datasets/srd-2014.json (~209 KB) |
| SRD 5.2 | https://longstoryshort.app/datasets/srd-2024.json (~274 KB) |
| Слаги заклинаний SRD 5.1 | https://longstoryshort.app/datasets/srd-spell-slugs-2014.json (318 записей) |
| Слаги заклинаний SRD 5.2 | https://longstoryshort.app/datasets/srd-spell-slugs-2024.json (339 записей) |
JSON Schema — draft-07. Схема генерируется из тех же исходников, по которым
работает приложение, и включает description на каждом типе гранта: она же и
есть справочник по полям, отдельной таблицы полей нет и не нужно.
SRD-наборы — не игрушечные примеры: это ровно те данные, на которых работает продакшен. Вместе — 24 класса и 24 подкласса на 20 уровней, 18 видов с линиями, черты, счётчики, развилки снаряжения.
Первые пять минут
Заголовок раздела «Первые пять минут»Правите JSON руками — одна строка даёт автокомплит и подсветку ошибок в VS Code, JetBrains и любом редакторе с поддержкой JSON Schema:
{ "$schema": "https://longstoryshort.app/schema/v1/wizard-dataset.json", "id": "my-homebrew", "name": "Мой хоумбрю", "system": "dnd_5", "edition": "2024", "author": "Вы", "license": "CC-BY-4.0", "races": []}Пишете код — валидируйте чем угодно:
const validate = new Ajv().compile(await (await fetch(SCHEMA_URL)).json());if (!validate(dataset)) console.error(validate.errors);Хотите понять формат — откройте srd-2024.json и найдите варвара.
Словарь грантов
Заголовок раздела «Словарь грантов»28 типов, дискриминация по полю type. Точные поля каждого — в grant.json.
| Канал | Типы |
|---|---|
| Владения | skill-fixed, skill-choice, expertise-choice, tool-fixed, tool-choice, language-fixed, language-choice, armor-prof, weapon-prof, saving-throw |
| Числа | bonus, asi-fixed, asi-flexible, asi-pool, hp-die, speed, size |
| Контент | trait, resource, feat |
| Заклинания | spellcasting, spell-fixed, spell-choice |
| Снаряжение | equipment-fixed, equipment-choice, gold, gold-dice |
| Развилка | pick-one |
Заклинания — три разных вопроса, три гранта. spellcasting объявляет, что сущность
кастует, и как (характеристика, прогрессия, из чьего списка — spellList, когда он
отличается от id класса, как у гибридов вроде Мистического рыцаря). spell-fixed выдаёт
одно именное заклинание по слагу — всегда подготовленное, вне лимита. spell-choice
предлагает игроку выбрать count заклинаний заданного круга (0 — заговоры) из списка
spellList. У spell-fixed/spell-choice общие поля выдачи: ability (характеристика,
пусто — наследует листовую), uses (count/countExpr + per: 'long-rest' | 'short-rest'
shortRestRegain— свой счётчик заклинания, а не ячейка),withSlots(можно ли поверх счётчика заплатить ещё и ячейкой),alwaysPrepared(сегодня выразим только какtrue).
Счётчик заклинания — это Ресурс, просто заведённый другим каналом, и поля восстановления
у него те же. per говорит, какой отдых его восстанавливает; shortRestRegain — сколько
возвращает КОРОТКИЙ отдых, формулой над теми же переменными (1, [PROF], ceil([LVL]/2)).
Без него короткий отдых наполняет счётчик целиком, и шаблон «одно применение на коротком
отдыхе, все на длинном» приходилось врать в одну из сторон. Поле осмысленно только при
per: 'short-rest' — при 'long-rest' его никто не читает, и линт об этом предупредит.
Ячейки заклинаний растут из progression. Это не справочное поле: персонаж,
собранный визардом, получает ячейки сам — по классу, уровню и семейству таблиц.
Их четыре: full (бард, жрец, друид, чародей, волшебник), half (паладин,
следопыт), third (Мистический рыцарь, Арканный ловкач) и pact (колдун — свой
пул на листе). Переписывать числа в персонажа не надо: игрок может поправить
любое, но хранится разница с вашей таблицей, а не итог, и левелап её не затирает.
Если прогрессия вашего класса не совпадает ни с одной из четырёх — объявите
собственную таблицу, slotsByLevel. Двадцать одна строка (индекс = уровень
персонажа, нулевая — заглушка), в строке девять чисел по кругам 1…9; строка
короче девяти дополняется нулями. Её числа побеждают встроенные, но progression
рядом не отменяется: тип продолжает отвечать за то, в какой пул едут ячейки и
с каким весом класс сложится в мультиклассе. Уровень выше последней строки значит
«ячеек нет», а не «как на последней», — поэтому короткая таблица это ошибка, а не
предупреждение. Классу, чья таблица совпадает со встроенной, поле заводить не
надо: скопированные 20×9 чисел — ровно то место, где ваши данные разойдутся с
правилами при следующей правке.
Ячейки к тому же стали целями системы бонусов — spellSlot.1…spellSlot.9 и
pactSlot.1…pactSlot.9 (полный список таргетов, как всегда, в grant.json).
Предмет или черта, дающие лишнюю ячейку, выражаются обычным грантом bonus.
Свой класс-заклинатель: spellList обязателен. Список заклинаний резолвится
по id класса, а собственного списка заклинаний у вашего класса в базе нет — без
этого поля игрок получит ячейки и пустой список, из которого нечего выбрать, без
единой ошибки. Укажите, чей список класс использует: "cleric", "wizard", любой
id класса. То же и у подкласса-гибрида вроде Мистического рыцаря: у него
classId: "fighter", а spellList: "wizard".
«Сколько можно взять» — отдельные поля, и без них счётчика не будет.
cantripsByLevel и knownByLevel — таблицы по уровням (индекс = уровень
персонажа, нулевой элемент — заглушка); preparedFormula — там, где
подготовленные считаются формулой, а не таблицей; bookAtFirst/bookPerLevel —
книга волшебника, отдельный счёт от подготовленных. Отсутствие числа значит «не
показываем», а не «ноль»: лист не станет угадывать лимит за вас.
У формулы три значения, и половина уровня в них округляется по-разному:
| Значение | Кто так считает | На 1 уровне при модификаторе +3 |
|---|---|---|
mod+level |
жрец, друид, волшебник | 4 |
mod+half-level |
паладин, следопыт — половина вниз | 3 |
mod+half-level-up |
половина вверх | 4 |
Разница не косметическая: на каждом нечётном уровне это целое заклинание. Общее у всех трёх — минимум одно, иначе кастер с отрицательным модификатором не смог бы подготовить ничего.
Слаг — не то, что вы придумаете. spell-fixed/выбранное игроком заклинание
резолвятся по slug — слагифицированному английскому имени (hellish-rebuke), которое
должно совпасть с тем, что реально лежит в базе заклинаний Long Story Short. Слаг ссылается
на заклинание вне самого датасета — свой корпус заклинаний вы поставляете отдельным
файлом (импорт заклинаний — отдельный путь от импорта датасета), а датасет лишь называет
слаг. Ссылка на SRD-заклинание резолвится всегда — файлы слагов из таблицы выше говорят,
какие это. Ссылка на НЕ-SRD заклинание резолвится только у игрока, который уже загрузил
заклинание с этим слагом (свой хоумбрю или файл заклинаний, который вы выложили вместе с
датасетом) — это не ошибка датасета, а нормальное «пока нет».
Сам линт про это не знает — у него нет контекста игрока, и он остаётся чистой функцией без сети (это же используется как гейт при сборке бандла и в тестах). Но при подключении датасета через визард на нашем сайте предупреждение о неизвестном слаге автоматически перепроверяется против вашей собственной коллекции заклинаний и пропадает, если слаг у вас уже есть — типичный сценарий при тестировании своего датасета вместе со своим файлом заклинаний.
pick-one вкладывает гранты в свои options — обходя датасет, рекурсируйте
внутрь, иначе половина контента пройдёт мимо вашей валидации.
bonus бьёт по таргету. Схема содержит закрытый enum допустимых таргетов —
в нём только те, которые лист реально читает. Всё остальное отклоняется наравне с
опечаткой, и это то поведение, которое вам нужно: бонус на неподдержанный таргет
так же невидим, как prof.skill.stelth. Открытые семейства, где ключ приходит из
данных персонажа (weapon.<любое>.attack), выражены через pattern и проходят.
Граница: что схема ловит, а что нет
Заголовок раздела «Граница: что схема ловит, а что нет»Схема проверяет форму, а не смысл. Валидный по схеме датасет всё ещё может молча ничего не делать — потому что оставшиеся правила межполевые и межсущностные, а JSON Schema так не умеет.
| Схема поймает | Схема НЕ поймает |
|---|---|
| Опечатку в имени поля или лишнее поле | Всё из таблицы ниже |
| Неизвестный тип гранта | |
| Опечатку в таргете бонуса или ключе навыка | |
| Таргет, который лист не читает | |
| Отсутствие обязательного поля, неверный тип значения |
Проверки, которые надо сделать самим. Ровно этот список гоняет наш импортёр;
error блокирует импорт, warning пускает — но почти каждый warning означает
«грант записан и не сделает ничего»:
| Проверка | Что ломается |
|---|---|
bonus несёт ровно одно из value | expr |
error |
expr-формула вычисляется |
error |
| Ключи навыков, характеристик, брони и оружия — из известных | error |
Грант выбора предлагает хотя бы один слот (count > 0) |
error |
id уникален внутри коллекции |
error |
classId / raceId / featId ведёт в существующую сущность |
error |
Именованный pick-one лежит там, где его вообще раскрывают |
error |
resource несёт ровно одно из max | maxExpr; формулы вычисляются |
error |
| Лицензия — из белого списка (см. «Жёсткие правила») | error |
trait несёт params и прозу — текст потеряется, канал не тот |
warning |
Формула броска у trait парсится |
warning |
pairId счётчика указывает на существующую черту (или явный null) |
warning |
| Счётчик стоит там, где его никто не читает | warning |
| Тип гранта реален, но канала-потребителя у него нет | warning |
| Открытый фит-слот, под который нет ни одного кандидата | warning |
spell-choice предлагает хотя бы одно заклинание (count > 0), круг задан, spellList не пуст |
error |
Два spell-choice одного круга на одной сущности — пики склеятся в один слот |
error |
spell-fixed/spell-choice: uses без count и без countExpr, или сразу оба |
error |
uses.shortRestRegain не вычисляется в число на каком-то уровне — короткий отдых молча восстановит счётчик целиком |
error |
uses.shortRestRegain при per: 'long-rest' — поле не сработает |
warning |
spell-fixed: слаг не входит в наш публичный список SRD-слагов (см. таблицу выше) |
warning |
spellcasting: slotsByLevel — не массив строк по уровням, или короче 21 записи, или в строке не целое неотрицательное число |
error |
spellcasting: slotsByLevel пуста, или строка длиннее девяти кругов |
warning |
Ссылки между наборами легальны. Хоумбрю-книга подклассов вправе ссылаться
classId на SRD-класс, которого в ней самой нет. Ваш валидатор должен резолвить
ссылки не только внутри проверяемого файла, но и по всем подключённым наборам,
иначе пометит битой каждую такую ссылку.
Готового пакета с этими проверками мы пока не отдаём — реализуйте у себя.
Жёсткие правила
Заголовок раздела «Жёсткие правила»id сущности — внешний ключ, а не название. По нему резолвятся ссылки и
привязываются выборы игрока. Переименовали id в опубликованном наборе — сломали
чужие листы. Слаг английского названия, один раз и навсегда.
Лицензия обязательна и проверяется. Пустое поле отклоняет импорт так же, как
неподходящее. Проходят только CC0, CC-BY и CC-BY-SA с версией (CC-BY-4.0,
не CC-BY). OGL, MIT, свободный текст, любые NC/ND-варианты — нет. Это касается
и вашего собственного оригинального контента.
Правка датасета ретроактивно меняет уже собранных персонажей. Поле version
есть, но импорт его не читает, и защиты с нашей стороны пока нет. Ваше разделение
«черновик / опубликовано» — единственный барьер. v1 в URL схемы — про версию
формата, не про версию контента.
Публиковать датасет пока некуда. Реестра сообщества нет; подключение идёт через импорт JSON-файла.
Что учесть при чтении SRD-json
Заголовок раздела «Что учесть при чтении SRD-json»Тексты черт лежат в description у trait-гранта. Это единственный канал;
файлы самодостаточные, скачали — и у вас полный набор с текстами. Авторьте так же.
Всё описано. SRD 5.1 — 183 черты, SRD 5.2 — 232 — все с текстом.
Перед публикацией оба набора прогоняются всеми проверками из раздела выше, и при любой находке файл не выкладывается — так что как эталон им можно доверять.
Что на этом строят
Заголовок раздела «Что на этом строят»Валидатор в своём пайплайне. Самое дешёвое и самое полезное: схема — в CI, проверки из раздела выше — рядом.
Визуальный редактор. Схема даёт формы и типы полей почти даром. Но
enum таргетов — не украшение: никогда не давайте автору вводить таргет
строкой, стройте пикер из списка в схеме.
Генерация датасетов моделью. Артефакты складываются в связку:
- По-сущностные схемы — не для удобства, а по необходимости. Полная схема —
union из 28 вариантов с вложенностью в несколько уровней; для structured output
это плохо. Генерируйте один класс за вызов против
dataset-class.json— дешевле и заметно надёжнее. - SRD как few-shot. Модель учится формату на примерах существенно лучше, чем на схеме, а у вас их 24 класса и 18 видов — эталонных, не выдуманных.
- Схема закрывает пространство выбора. Закрытый enum таргетов против
свободной строки — это разница между «модель попадает» и «модель придумывает
prof.skill.stelth». - Проверки замыкают цикл. Сгенерировали → провалидировали → прогнали проверки → вернули сообщение в модель → перегенерировали. Без четвёртого шага вы получите датасеты, которые проходят валидацию и молча не работают.
Конвертер из своего формата. SRD-наборы — ваш golden file: гоняйте конвертацию в обратную сторону и сверяйтесь.
