# Catálogo de tatuajes

El catálogo reúne tatuajes de CXG Base y de los packs que hayas instalado. Los packs externos son opcionales: puedes activar ninguno, uno o varios. Si un pack falta o está parado, sus tatuajes se ocultan de la tienda y el catálogo CXG Base sigue disponible.

Consulta también [Introducción](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/01-introduccion.md), [Instalación](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/02-instalacion.md), [Configuración](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/03-configuracion.md), [Integraciones](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/05-integraciones.md) e [Interfaz](https://docs.cxgstudios.com/docs/es/01-cxg-tattoos/06-interfaz.md).

## Packs opcionales

La configuración inicial incluye estas fuentes opcionales:

| ID | Recurso | Archivo del catálogo |
| --- | --- | --- |
| `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` |

Instala solo los packs que poseas y arráncalos antes de `cxg-tattoos`. Mantén el nombre de recurso y la ruta tal como existen en tu servidor, respetando mayúsculas y minúsculas. Para ocultar temporalmente una fuente, cambia su `enabled` a `false`; para volver a mostrarla, ponlo en `true` y asegúrate de que el recurso esté iniciado.

CXG Base usa `shared/tattoos.json` dentro del recurso. La fuente `native-game` obtiene diseños de los tatuajes nativos del juego y viene desactivada. Para utilizarla, configura también las fuentes de validación de `native.serverMetas` con los recursos y archivos reales de tu servidor. La entrada de ejemplo apunta a `tatto` y `shop_tattoo.meta`; no presupone que ese recurso esté instalado. Activar `enabled` por sí solo no garantiza que esos diseños se puedan comprar.

## Añadir una fuente JSON

Cada objeto de `Config.TattooCatalog.sources` configura una fuente. Este ejemplo sigue la forma de los recursos de pack y se puede adaptar con el nombre real del recurso y su archivo:

```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` debe identificar la fuente de forma estable; `label` y `color` se usan para reconocerla en la interfaz. `resource` y `path` apuntan al JSON. El archivo debe estar dentro de un recurso disponible. No añadas el mismo diseño en dos fuentes salvo que quieras que aparezca dos veces; `deduplicateAcrossSources` permite eliminar duplicados entre ellas.

Estos ajustes generales están en `Config.TattooCatalog`:

| Campo | Valor inicial | Uso |
| --- | --- | --- |
| `deduplicate` | `true` | Elimina diseños duplicados dentro de una fuente. |
| `deduplicateAcrossSources` | `false` | Permite mantener diseños de distintas fuentes separados. |
| `serverFallback` | `true` | Solicita al servidor una fuente que el cliente no pueda leer. |
| `serverFallbackTimeoutMs` | `7000` | Tiempo máximo de espera de esa solicitud, en milisegundos. |

El fallback de lectura no convierte un pack detenido en un pack disponible.

## Formato de cada tatuaje

El archivo puede ser un array JSON o un objeto cuya propiedad contenga el array. Con `root.mode = 'auto'`, el lector acepta ambos formatos; con `root.mode = 'field'`, `root.field` indica dónde está la lista. `root.mode = 'array'` fuerza un array en la raíz.

Este array muestra el formato JSON. La colección y los hashes del ejemplo son ficticios: sustitúyelos por los nombres reales del pack instalado. Añadir una entrada JSON no instala los overlays gráficos.

```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` y al menos un hash de overlay son necesarios para identificar el diseño. Incluye `HashNameMale` y `HashNameFemale` si el tatuaje tiene una variante para cada personaje. Usa una de estas zonas: `ZONE_HEAD`, `ZONE_TORSO`, `ZONE_LEFT_ARM`, `ZONE_RIGHT_ARM`, `ZONE_LEFT_LEG` o `ZONE_RIGHT_LEG`. `Name` es el nombre que verá el comprador. Si omites `Price`, se usa el precio predeterminado de la fuente; si omites `RequiredLevel`, se utiliza `defaults.requiredLevel`, cuyo valor inicial es 1. El XP puede venir del tatuaje o de la configuración de niveles.

Para archivos con otros nombres de campos, ajusta `fields` en `Config.TattooCatalog`. Cada entrada de esa tabla es una lista de alias que se consultan para encontrar el valor correspondiente. Por ejemplo, un archivo que usa `OverlayHash`, `Cost` y `BodyZone` puede mapearse así:

```lua
fields = {
    collection = { 'Collection', 'CollectionName' },
    name = { 'Name', 'DisplayName' },
    overlay = { 'OverlayHash' },
    hashNameMale = { 'MaleOverlay' },
    hashNameFemale = { 'FemaleOverlay' },
    zone = { 'BodyZone' },
    price = { 'Cost' },
    xp = { 'Xp', 'Experience' },
    requiredLevel = { 'RequiredLevel' },
}
```

También puedes asignar valores comunes desde `rootFields`, por ejemplo un nombre de colección o una zona definidos una sola vez en el objeto raíz. `defaults` de cada fuente completa los valores que falten. Si tu JSON contiene un solo campo genérico, como `OverlayHash`, `defaults.overlayTarget` indica si debe tratarse como tatuaje masculino (`male`) o femenino (`female`).

Los formatos muy distintos se pueden transformar con las funciones opcionales `converters.decode(decoded, settings)` o `converters.entry(entry, context)` en la configuración. La primera adapta el documento entero; la segunda adapta cada diseño. Devuelve una tabla con los campos ya preparados para el mapeo normal.

## Miniaturas

Para usar imágenes que vienen dentro del mismo pack, conserva la carpeta `miniatures/` en ese recurso y define un patrón como:

```lua
thumbnailPattern = 'miniatures/{Name}.webp'
```

`{Name}`, `{HashNameMale}`, `{HashNameFemale}` y `{HashName}` se sustituyen con datos del tatuaje. El archivo resultante debe existir dentro del recurso, por ejemplo `miniatures/Rosa del desierto.webp`, y el recurso debe estar iniciado. La interfaz solicita cada miniatura cuando la necesita; no copies imágenes al paquete de CXG.

También puedes poner `Thumbnail` o `ThumbnailUrl` en una entrada para indicar una imagen concreta. Las URL `http://`, `https://` y `data:` se usan tal cual. La ruta de una miniatura local de un pack debe ajustarse a su `thumbnailPattern`; el archivo debe existir y el recurso debe poder servirlo a la NUI. Los recursos externos deben conservar su archivo dentro del pack.

## Cambios desde la configuración y desde administración

Edita la configuración para añadir fuentes, cambiar su lectura, activar o desactivar packs y ajustar sus valores por defecto. Usa la administración del catálogo para cambiar nombres, precio, nivel y disponibilidad de tatuajes sin editar el JSON. Los cambios administrativos se guardan y sobreviven a las sincronizaciones del catálogo. La acción de restaurar valores devuelve ese tatuaje a los valores de su fuente.

Los tatuajes que un jugador ya compró siguen vinculados a su personaje aunque se apague temporalmente el pack que los proporcionó. El pack no aparece en la tienda hasta que vuelva a estar disponible.
