# Каталог татуировок

В каталоге собраны татуировки CXG Base и установленных наборов. Внешние наборы необязательны: можно не включать ни одного, включить один или несколько. Если набор отсутствует или остановлен, его татуировки скрываются в магазине, а каталог CXG Base остаётся доступен.

См. также [Введение](https://docs.cxgstudios.com/docs/ru/01-cxg-tattoos/01-introduccion.md), [Установка](https://docs.cxgstudios.com/docs/ru/01-cxg-tattoos/02-instalacion.md), [Настройка](https://docs.cxgstudios.com/docs/ru/01-cxg-tattoos/03-configuracion.md), [Интеграции](https://docs.cxgstudios.com/docs/ru/01-cxg-tattoos/05-integraciones.md) и [Интерфейс](https://docs.cxgstudios.com/docs/ru/01-cxg-tattoos/06-interfaz.md).

## Дополнительные наборы

В начальную конфигурацию входят следующие необязательные источники:

| ID | Ресурс | Файл каталога |
| --- | --- | --- |
| `xgc-classic` | `xgc_TattooClasic` | `tatoo_clasic_full_packconfigdump.json` |
| `xgc-gangs` | `xgc_TattooGangs` | `gangs_full_packconfigdump.json` |
| `xgc-japanese-mafia` | `xgc_TattooJapaneseMafia` | `japanese_mafia_full_packconfigdump.json` |
| `xgc-police` | `xgc_TattooPolice` | `police_full_packconfigdump.json` |
| `xgc-world-countries` | `xgc_TattooWorldCountries` | `world_country_full_packconfigdump.json` |
| `cxg-blackout` | `CXGtatoo_balckout` | `blackout_full_packconfigdump.json` |

Устанавливайте только принадлежащие вам наборы и запускайте их перед `cxg-tattoos`. Сохраняйте имена ресурсов и пути в точности такими, как на сервере, учитывая регистр букв. Чтобы временно скрыть источник, измените его `enabled` на `false`; чтобы снова показать — измените на `true` и убедитесь, что ресурс запущен.

CXG Base использует файл `shared/tattoos.json` внутри ресурса. Источник `native-game` получает дизайны из встроенных игровых татуировок и изначально отключён. Чтобы использовать его, настройте также источники проверки `native.serverMetas`, указав фактические ресурсы и файлы сервера. В примере заданы `tatto` и `shop_tattoo.meta`; наличие такого ресурса не предполагается. Одного включения `enabled` недостаточно, чтобы эти дизайны можно было покупать.

## Добавление источника JSON

Каждый объект в `Config.TattooCatalog.sources` задаёт источник. Пример повторяет структуру ресурсов наборов; его можно адаптировать, указав фактическое имя ресурса и его файл:

```lua
{
    id = 'mi-pack',
    label = 'Mi Pack',
    color = '#62b6cb',
    enabled = true,
    resource = 'mi_pack',
    path = 'tattoos.json',
    thumbnailPattern = 'miniatures/{Name}.webp',
    root = { mode = 'auto', field = 'Overlays' },
    defaults = {
        collection = 'mi_pack_overlays',
        zone = 'ZONE_TORSO',
        price = 5000,
        requiredLevel = 1,
        overlayTarget = 'male',
    },
}
```

`id` должен оставаться постоянным уникальным идентификатором источника; `label` и `color` помогают распознавать его в интерфейсе. `resource` и `path` указывают на JSON. Файл должен находиться в доступном ресурсе. Не добавляйте один дизайн в два источника, если не хотите показывать его дважды; параметр `deduplicateAcrossSources` позволяет удалять повторы между источниками.

Общие параметры находятся в `Config.TattooCatalog`:

| Поле | Начальное значение | Назначение |
| --- | --- | --- |
| `deduplicate` | `true` | Удаляет повторы дизайнов внутри одного источника. |
| `deduplicateAcrossSources` | `false` | Позволяет не объединять одинаковые дизайны из разных источников. |
| `serverFallback` | `true` | Запрашивает источник у сервера, если клиент не может его прочитать. |
| `serverFallbackTimeoutMs` | `7000` | Максимальное время ожидания такого запроса в миллисекундах. |

Резервное чтение не делает остановленный набор доступным.

## Формат записи татуировки

Файл может содержать JSON-массив или объект со свойством, в котором находится массив. При `root.mode = 'auto'` считыватель принимает оба формата; при `root.mode = 'field'` поле `root.field` указывает на список. `root.mode = 'array'` требует массив в корне документа.

В этом массиве показан формат JSON. Название коллекции и хеши в примере вымышлены: замените их именами из установленного набора. Добавление записи в JSON не устанавливает графические overlays.

```json
[
  {
    "Collection": "mi_pack_overlays",
    "Name": "Rosa del desierto",
    "HashNameMale": "MP_MI_PACK_ROSE_M",
    "HashNameFemale": "MP_MI_PACK_ROSE_F",
    "Zone": "ZONE_TORSO",
    "Price": 5000,
    "Xp": 15,
    "RequiredLevel": 2
  }
]
```

Для идентификации дизайна нужны `Collection` и хотя бы один overlay hash. Укажите `HashNameMale` и `HashNameFemale`, если для татуировки есть вариант для каждого персонажа. Используйте одну из зон: `ZONE_HEAD`, `ZONE_TORSO`, `ZONE_LEFT_ARM`, `ZONE_RIGHT_ARM`, `ZONE_LEFT_LEG` или `ZONE_RIGHT_LEG`. `Name` — название, которое увидит покупатель. Если не указать `Price`, используется цена источника по умолчанию; если не указать `RequiredLevel`, берётся `defaults.requiredLevel`, начальное значение которого — 1. XP может задаваться в самой татуировке или в настройках уровней.

Если в файлах используются другие названия полей, настройте `fields` в `Config.TattooCatalog`. Каждая запись этой таблицы содержит список псевдонимов, по которым ищется соответствующее значение. Например, файл с полями `OverlayHash`, `Cost` и `BodyZone` можно сопоставить так:

```lua
fields = {
    collection = { 'Collection', 'CollectionName' },
    name = { 'Name', 'DisplayName' },
    overlay = { 'OverlayHash' },
    hashNameMale = { 'MaleOverlay' },
    hashNameFemale = { 'FemaleOverlay' },
    zone = { 'BodyZone' },
    price = { 'Cost' },
    xp = { 'Xp', 'Experience' },
    requiredLevel = { 'RequiredLevel' },
}
```

Общие значения, например имя коллекции или зона, заданные один раз в корневом объекте, можно назначить через `rootFields`. `defaults` каждого источника заполняет отсутствующие значения. Если в JSON есть одно общее поле, например `OverlayHash`, параметр `defaults.overlayTarget` указывает, считать ли татуировку мужской (`male`) или женской (`female`).

Форматы, сильно отличающиеся от ожидаемого, можно преобразовать с помощью необязательных функций `converters.decode(decoded, settings)` или `converters.entry(entry, context)` в конфигурации. Первая преобразует документ целиком, вторая — каждую запись дизайна. Возвращайте таблицу с полями, подготовленными для обычного сопоставления.

## Миниатюры

Чтобы использовать изображения из того же набора, сохраните в его ресурсе папку `miniatures/` и задайте шаблон, например:

```lua
thumbnailPattern = 'miniatures/{Name}.webp'
```

Подстановки `{Name}`, `{HashNameMale}`, `{HashNameFemale}` и `{HashName}` заменяются данными татуировки. Итоговый файл должен существовать в ресурсе, например `miniatures/Rosa del desierto.webp`, а сам ресурс должен быть запущен. Интерфейс запрашивает миниатюру по мере необходимости; копировать изображения в пакет CXG не нужно.

Для отдельного изображения также можно указать `Thumbnail` или `ThumbnailUrl` в записи. URL, начинающиеся с `http://`, `https://` и `data:`, используются как есть. Локальный путь к миниатюре из набора должен соответствовать его `thumbnailPattern`; файл должен существовать, а ресурс — передавать его в NUI. Внешние ресурсы должны хранить свои файлы внутри набора.

## Изменения в конфигурации и панели управления

Редактируйте конфигурацию, чтобы добавлять источники, менять способ их чтения, включать и отключать наборы и настраивать значения по умолчанию. В административном разделе каталога можно менять названия, цены, уровни и доступность татуировок без правки JSON. Административные изменения сохраняются и переживают синхронизацию каталога. Восстановление возвращает для выбранной татуировки значения из её источника.

Уже купленные игроком татуировки остаются привязаны к его персонажу, даже если временно отключить набор, из которого они поступили. Набор снова появится в магазине, когда станет доступен.
