Слои, переводы и миграции наборов правил

Эта страница объясняет, как предложить варианты, которые игрок включает для одной игры, перевести текст набора правил и перенести сохраненные значения на новые id при изменении листа персонажа. Она предназначена для авторов наборов правил. Перед началом прочитайте руководство Создание наборов правил Game Mode.

Слои: варианты вашего набора правил

Слой – именованный вариант набора правил, который игрок включает при создании игры: мало магии, суровая зима, более жесткая сложность. Слои живут в файле набора правил в необязательном массиве layers, поэтому путешествуют с ним и никогда не пропадают из использовавшей их игры. Мастер настройки показывает их переключателями под набором правил; выбор закреплен на весь срок игры, как и сам набор.

"layers": [
  {
    "id": "hard_winter",
    "label": "Hard winter",
    "summary": "Cold, hunger and short days. Everything is harder.",
    "conflicts": ["mud_season"],
    "gm": {
      "guidance": "Hard winter is on. Let a failed check cost warmth, food or daylight as well as progress.",
      "worldGuidance": "Hard winter is on. Build a world of closed roads, thin stores and rationed settlements."
    },
    "fields": [{ "id": "calling", "removeValues": ["Sailor"], "default": "Hauler" }],
    "difficultyLadder": [{ "label": "Easy", "dc": 7 }],
    "catalogs": [{ "id": "knacks", "hide": { "filter": "grit", "above": 0 } }]
  },
  {
    "id": "mud_season",
    "label": "Mud season",
    "summary": "Thaw, flooded roads and slow going."
  }
]

Что может слой. Список закрыт; каждый эффект что-то сужает либо добавляет текст:

"items": {
  "market": { "removeSellers": ["magic_dealer", "scribe"], "unsold": [{ "tag": "magical" }] },
  "lootTables": [{ "id": "treasure_low", "label": "Pocket treasure", "entries": [{ "coins": "sp", "weight": 1, "count": "4d6" }] }]
}

Чего слой не может. Он не добавляет значение перечисления, поле, навык, запас или отдых, продавца или таблицу добычи, не меняет вид разрешения, текущее состояние или боевые числа, не добавляет вызов модели. Значение, _добавленное_ слоем, было бы неизвестно остальным читателям листа, поэтому значения могут только убираться. Все вне списка – изменение самого набора правил или второй набор.

Конфликты. conflicts именует слои, которые нельзя включать вместе. Достаточно указать одну сторону пары. Мастер настройки блокирует другой переключатель; если сохраненный выбор все же содержит оба, объявленный позже отбрасывается, поэтому те же два выбора всегда дают те же правила.

Лист, уже хранящий удаленное значение, сохраняет его. Ничто не переписывает персонажа. Редактор перестает предлагать значение; персонаж, уже имевший его, показывает его как есть. Выключите слой в новой игре – значение снова предлагается. То же со скрытой записью каталога: она отсутствует в окне выбора, но уже выбранная игроком строка остается в листе. Монеты, которые убирает слой и которые у кого-то уже есть, остаются в его сумке.

Ограничения. 12 слоев на набор правил и 4000 символов указаний на слой, считая обе строки вместе. Пакетный набор с layers или базовым gm.worldGuidance требует Capability API 1.25, набор, чьи слои убирают монеты, – 1.64, а набор, чьи слои меняют его items, – 1.813. Импортируемый набор проверяет читающий его Engine, поэтому ему ничего такого не нужно.

Слои другого автора (слой малой магии для чужого набора правил, поставляемый своим файлом) – будущее дополнение. Сегодня слой поставляется внутри набора, которому принадлежит.

Переводы

Переводы (Capability API 1.811). Отображаемые строки набора правил на другом языке составляют отдельный перевод для каждого языка, названный по коду языка, как и собственные языковые пакеты Engine (ru, pt-BR, zh-Hans). В официальном каталоге агентов каждый перевод представлен языковым пакетом рядом с файлом набора правил (ниже). Файл пакета набора правил, которым вы делитесь, может вместо этого содержать их внутри, по одному файлу на язык рядом с ruleset.json: locales/<language>.json, перечисленные в манифесте, как и остальные файлы. В любом случае каждый из них – каталог локализации пакета с разделом ruleset: каждый ключ – отображаемая строка набора правил или его каталогов, дословно на английском языке, а его значение – эта строка на языке файла.

{
  "_meta": { "locale": "ru", "direction": "ltr" },
  "package": { "name": "5e (SRD 5.1)" },
  "ruleset": { "Prone": "Лежащий", "Fire Bolt": "Огненный снаряд", "Goblin": "Гоблин" }
}

_meta.locale должен обозначать язык, по которому назван файл, а файл содержит только $schema, _meta, package и ruleset, и каждое значение – текст. Пустая строка еще не переведена и отображается на английском; файл без раздела ruleset (собственное английское описание пакета, locales/en.json) ничего не переводит. Переводы влияют только на отображение: правила, команды листа персонажа и все, что сообщается Game Master, по-прежнему используют английский текст. Экран боя (имена, состояния, действия, заклинания, журнал и порядок ходов) показывает каждую строку на языке игрока, если в переводе набора правил она есть, сначала из файла самого языка, затем из файла его базового языка (pt для pt-BR), а иначе – на английском; "Goblin 2" использует перевод "Goblin" с его номером. Файлу пакета с переводами нужен Capability API 1.811. При импорте сохраняется только перевод на язык, выбранный в Engine (или на его базовый язык), а остальные отбрасываются; чтобы читать набор правил на другом языке, смените язык Engine и импортируйте файл снова, заменив сохраненный перевод. Переводы не входят в версию, поэтому повторный импорт той же версии с исправленным переводом тоже заменяет его, а файл той же версии без переводов оставляет сохраненный перевод.

Набор правил из каталога никогда не содержит locales/<language>.json внутри своего файла (такой файл отклоняется еще до скачивания). Его переводы – языковые пакеты (Capability API 1.811): по одному файлу JSON на язык, <id>-<version>.<language>.json, опубликованному рядом с <id>-<version>.ruleset.json и содержащему ровно то же, что и locales/<language>.json (_meta.locale обозначает язык, по которому назван файл, и есть раздел ruleset). Запись каталога перечисляет их рядом со своим artifact:

"locales": {
  "ru": {
    "url": "https://raw.githubusercontent.com/Pasta-Devs/Marinara-Agents/main/artifacts/ember-roads-1.0.0.ru.json",
    "sha256": "<64 hex>",
    "bytes": 812345,
    "name": "Угольные дороги",
    "description": "<the store card's description in Russian>"
  }
}

Каждый пакет – это URL с https той же формы, что и URL артефакта, с именем, соответствующим его версии и языку, с его sha256 и размером (не более 8 MB); набор правил перечисляет не более 40 пакетов и никогда не включает en (английский текст – это сам набор правил), а его манифест объявляет Capability API 1.811. name и description – название и описание карточки магазина на этом языке, поэтому карточка отображается на языке Engine без загрузки пакета. При установке загружается файл набора правил, а затем только пакет на языке, выбранном в Engine (или на его базовом языке), проверяются его размер, контрольная сумма и язык, после чего он сохраняется вместе с установленной версией. При смене языка Engine (и при каждом открытии страницы в нем) каждый установленный набор правил из каталога, в котором указан пакет на новом языке, загружает его, а пакет на старом языке удаляется. Если пакет получить не удалось (нет подключения к сети, пакет не указан или файл не соответствует указанному в каталоге), набор правил остается на английском; следующая установка или смена языка повторяет попытку. Обновление сохраняет предыдущую версию для отката без ее пакета: после отката следующая смена языка снова загружает пакет той версии, если он все еще указан в каталоге.

Перенос значений на новые id: sheet.migrations

Добавление нового ничего больше не требует. Когда новая структура переносит значение в другое место (переименованное поле, один уровень, который становится уровнем для каждого класса, один запас костей хитов, который становится запасом для каждой кости), опишите это в sheet.migrations (Capability API 1.77), чтобы каждый персонаж, созданный по старой структуре, сохранил свои числа: в библиотеке, в играх, в старых сохранениях, ветках и контрольных точках и в импортированных карточках.

"migrations": [
  {
    "from": 3,
    "steps": [
      { "rename": "field", "from": "xp", "to": "experience" },
      {
        "route": "level",
        "by": "class",
        "cases": [
          { "words": ["fighter"], "to": "level_fighter" },
          { "words": ["wizard"], "to": "level_wizard" }
        ],
        "else": "level_other",
        "default": 1
      },
      {
        "routeLive": "pool",
        "from": "hit_dice",
        "cases": ["hit_dice_d6", "hit_dice_d8", "hit_dice_d10", "hit_dice_d12"],
        "else": "hit_dice_d8"
      }
    ]
  }
]