# Developer Platform

Welcome to your team’s developer platform

<h2 align="center">Documentación RealCity</h2>

<p align="center">Revisa toda nuesta documentación de RealCityDevelopments </p>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><h4><i class="fa-leaf">:leaf:</i></h4></td><td><strong>Scripts Docs</strong></td><td>Revisa todas las documentaciones de nuestros scripts</td><td><a href="/spaces/6l7GQMHsynYeBIx56VgF">/spaces/6l7GQMHsynYeBIx56VgF</a></td><td><a href="/files/LHIOZGoI6dPF36TuRaxH">/files/LHIOZGoI6dPF36TuRaxH</a></td></tr><tr><td><h4><i class="fa-server">:server:</i></h4></td><td><strong>Server Docs</strong></td><td>Revisa todas las documentaciones de nuestras bases de servidore</td><td><a href="/spaces/6l7GQMHsynYeBIx56VgF">/spaces/6l7GQMHsynYeBIx56VgF</a></td><td><a href="/files/IaUMODRBBSBL8eUaPX09">/files/IaUMODRBBSBL8eUaPX09</a></td></tr></tbody></table>

<h2 align="center">Unete a nuestra comunidad</h2>

<p align="center">Si quieres mejorar tu servidor de FiveM y quieres tener la oportunidad de formar parte de algo grande y único te invitamos a entrar al servidor de Discord y Github.</p>

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th><th></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><h4><i class="fa-discord">:discord:</i></h4></td><td><strong>Comunidad Discord</strong></td><td>Unete a nuestra comunidad para estar atento siempre a las últimas noticias.</td><td><a href="https://discord.realcity.dev/" class="button secondary">Entrar a Discord</a></td><td></td></tr><tr><td><h4><i class="fa-github">:github:</i></h4></td><td><strong>GitHub</strong></td><td>Visita nuestro perfil de Github de nuestro fundador para ver nuestras creaciones de codigo abierto!</td><td><a href="https://github.com/xbymarcos" class="button secondary">Ir a Github</a></td><td></td></tr></tbody></table>


# ¡Bienvenido/a!

Te damos la bienvenida a la documentación oficial de los script de RealCity Developments. Si estás aquí es porque has comprado o estás pensando en comprar uno de nuestros scripts para FiveM. Y solo por estar aquí te vamos a dar las gracias de todo corazón ❤️

### ¿Qué script quieres ver?

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-bolt">:bolt:</i></h4></td><td><strong>Quickstart</strong></td><td>Create your first site</td><td></td><td></td><td><a href="/pages/7FvWQMF0kTK7HGhlQfmo">/pages/7FvWQMF0kTK7HGhlQfmo</a></td></tr><tr><td><h4><i class="fa-leaf">:leaf:</i></h4></td><td><strong>Editor basics</strong></td><td>Learn the basics of GitBook</td><td></td><td></td><td><a href="https://github.com/GitbookIO/gitbook-templates/blob/main/product-docs/broken-reference/README.md">https://github.com/GitbookIO/gitbook-templates/blob/main/product-docs/broken-reference/README.md</a></td></tr><tr><td><h4><i class="fa-globe-pointer">:globe-pointer:</i></h4></td><td><strong>Publish your docs</strong></td><td>Share your docs online</td><td></td><td></td><td><a href="/pages/QPzbTvC6XsT5gERiU43E">/pages/QPzbTvC6XsT5gERiU43E</a></td></tr></tbody></table>


# Preview

{% embed url="<https://www.youtube.com/watch?ab_channel=RealCityDevelopments&v=GXPgONjzxGI>" %}

## Tienda Tebex

{% embed url="<https://shop.realcity.dev/es/package/6917960>" %}


# Instalación

## Guía de instalación

Esta guía describe una instalación nueva, una actualización y las verificaciones mínimas antes de abrir el servidor a jugadores.

### 1. Requisitos

#### Obligatorios

| Componente         | Uso                                          |
| ------------------ | -------------------------------------------- |
| FiveM server       | Ejecución del recurso Lua y de la NUI.       |
| ESX, QBCore o Qbox | Datos de personaje, trabajo y dinero.        |
| `ox_lib`           | Localización y utilidades compartidas.       |
| `oxmysql`          | Consultas y transacciones con MySQL/MariaDB. |
| MySQL/MariaDB      | Persistencia de facturas, pagos y auditoría. |

#### Solo para facturas de empresa

| Framework | Recurso esperado                                       |
| --------- | ------------------------------------------------------ |
| ESX       | `esx_addonaccount`                                     |
| QBCore    | `qb-management` o `qb-banking`                         |
| Qbox      | Una capa compatible con `qb-management` o `qb-banking` |

El terminal POS y las facturas personales pueden operar sin sociedad. Una factura empresarial requiere que el bridge pueda ingresar y, en caso de rollback, retirar fondos de la cuenta indicada.

### 2. Preparar la carpeta

La carpeta debe llamarse exactamente `rc-billing`:

```
resources/
└── [realcitydev]/
    └── rc-billing/
        ├── fxmanifest.lua
        ├── config.lua
        ├── bridge/
        ├── client/
        ├── server/
        ├── locales/
        ├── sql/
        └── web/public/
```

Evita nombres como `rc-billing-main`, `rc-billing-v1` o una carpeta duplicada `rc-billing/rc-billing`. El nombre se utiliza en exports, callbacks NUI y ejemplos de integración.

### 3. Instalar la base de datos

La base de datos se instala sola por defecto, pero en caso de que sea necesario puede instalarla ejecutando estos dos archivos:

Haz una copia de seguridad antes de importar SQL en una base existente.

Ejecuta los archivos en este orden:

1. `sql/invoices.sql`
2. `sql/payment_requests.sql`

### 4. Orden de inicio

Las dependencias y el framework deben estar iniciados antes de `rc-billing`.

#### QBCore

```cfg
ensure oxmysql
ensure ox_lib
ensure qb-core
ensure qb-management
ensure rc-billing
```

Si utilizas `qb-banking`, reemplaza `qb-management` por el nombre correspondiente y confirma que expone `AddMoney` y `RemoveMoney`.

#### ESX

```cfg
ensure oxmysql
ensure ox_lib
ensure es_extended
ensure esx_addonaccount
ensure rc-billing
```

Las cuentas compartidas configuradas, por ejemplo `society_police`, deben existir antes del primer pago empresarial.

#### Qbox

```cfg
ensure oxmysql
ensure ox_lib
ensure qbx_core

# Inicia aquí la capa bancaria compatible de tu servidor.
ensure rc-billing
```

La detección utiliza el nombre oficial `qbx_core`. El bridge consume la capa de compatibilidad QB que Qbox publica mediante el export `qb-core`.

### 5. Configuración inicial

Abre `config.lua` y revisa obligatoriamente:

1. `Config.Locale`.
2. `Config.AllowPersonalInvoices` y `Config.RequireAceForPersonalInvoices`.
3. `Config.CompanyJobs`.
4. `Config.PaymentAccount`.
5. `Config.MaxInvoiceAmount` y límites por empresa.
6. `Config.DefaultCommission` y comisiones específicas.
7. `Config.DiscordWebhook`.
8. Comandos y teclas predeterminadas.

No publiques un webhook real dentro de un repositorio público o paquete de demostración.

### 6. Permisos ACE

La creación personal puede quedar abierta o restringida:

```lua
Config.AllowPersonalInvoices = true
Config.RequireAceForPersonalInvoices = true
Config.CreateInvoicePermission = 'rc-billing.create'
```

Ejemplo de permisos:

```cfg
add_ace group.admin rc-billing.admin allow
add_ace group.admin rc-billing.create allow
add_ace group.moderator rc-billing.create allow
```

Si `RequireAceForPersonalInvoices` es `false`, el ACE de creación no se consulta. Si `AllowPersonalInvoices` es `false`, ninguna persona puede emitir facturas personales aunque tenga ACE.

### 7. Primera prueba

Reinicia el recurso desde consola:

```
restart rc-billing
```

Conecta dos personajes y ejecuta esta prueba completa:

1. Abre `/openinvoice` con el emisor.
2. Crea una factura personal de importe pequeño.
3. Confirma que el receptor ve la solicitud.
4. Recházala y comprueba que queda cancelada.
5. Crea otra factura y acéptala.
6. Realiza un pago parcial.
7. Completa el importe y verifica el saldo de ambos.
8. Abre `/openpos`, crea una solicitud y acéptala.
9. Repite con una factura empresarial y verifica la comisión y la sociedad.
10. Revisa `invoice_payments` e `invoice_audit_logs`.

### 8. Actualizar una instalación

1. Detén `rc-billing`.
2. Copia `config.lua` y exporta las tablas del recurso.
3. Lee `CHANGELOG.md` y compara las nuevas opciones de configuración.
4. Sustituye los archivos del recurso.
5. Fusiona manualmente tu configuración anterior con la nueva.
6. Ejecuta ambos archivos SQL. Sus bloques de migración comprueban columnas e índices existentes.
7. Inicia el recurso.
8. Repite la prueba funcional básica.

No elimines las tablas para actualizar: perderías historial, pagos y auditoría.

### 9. Desinstalación

1. Elimina o comenta `ensure rc-billing`.
2. Reinicia el servidor.
3. Conserva las tablas si necesitas histórico.
4. Si quieres borrar todos los datos de manera irreversible, hazlo manualmente después de una copia de seguridad.

El recurso no elimina datos automáticamente al detenerse o desinstalarse.

### 10. Instalación validada

La instalación está lista cuando se cumplen todos estos puntos:

* No aparece `No supported framework detected`.
* No hay errores de columnas o tablas en `oxmysql`.
* `/openinvoice` y `/openpos` abren y cierran correctamente.
* Un destinatario solo puede pagar sus propias facturas.
* Una empresa recibe el importe esperado.
* Una comisión llega al empleado configurado.
* Los estados cambian en el orden documentado.
* El build NUI servido coincide con el código de la versión.

Continúa con Configuración y Solución de problemas.


# Configuración

## Referencia de configuración

Toda la configuración pública está en `config.lua`. El archivo se comparte entre cliente y servidor, por lo que no debes guardar secretos en él. Un webhook configurado aquí puede formar parte de los datos enviados al cliente por el sistema de recursos; utiliza una configuración privada de servidor si amplías el producto para manejar secretos.

### Resumen

| Opción                                 | Tipo           | Valor incluido        | Finalidad                   |
| -------------------------------------- | -------------- | --------------------- | --------------------------- |
| `Config.Locale`                        | `string`       | `'es'`                | Idioma predeterminado.      |
| `Config.AllowPersonalInvoices`         | `boolean`      | `true`                | Activa facturas personales. |
| `Config.CreateInvoicePermission`       | `string`       | `'rc-billing.create'` | ACE de creación personal.   |
| `Config.RequireAceForPersonalInvoices` | `boolean`      | `false`               | Obliga a comprobar el ACE.  |
| `Config.AdminAcePermission`            | `string`       | `'rc-billing.admin'`  | ACE administrativo.         |
| `Config.CompanyJobs`                   | `table`        | Ejemplos incluidos    | Empresas autorizadas.       |
| `Config.DefaultSocietyAccountPrefix`   | `string`       | `'society_'`          | Prefijo de cuentas.         |
| `Config.MaxInvoiceAmount`              | `number`       | `1000000`             | Máximo global por factura.  |
| `Config.HighValueInvoiceAmount`        | `number`       | `50000`               | Umbral de auditoría.        |
| `Config.InvoiceCategories`             | `table`        | 7 categorías          | Opciones de clasificación.  |
| `Config.DefaultDueDays`                | `number`       | `7`                   | Vencimiento predeterminado. |
| `Config.DuplicateWindowMinutes`        | `number`       | `5`                   | Ventana anti duplicados.    |
| `Config.DefaultCommission`             | `table`        | 10 %                  | Comisión empresarial base.  |
| `Config.PaymentAccount`                | `string`       | `'bank'`              | Cuenta del pagador.         |
| `Config.AllowPartialPayments`          | `boolean`      | `true`                | Permite abonos parciales.   |
| `Config.AllowPayAll`                   | `boolean`      | `true`                | Permite pagar todas.        |
| `Config.AuditLogs`                     | `boolean`      | `true`                | Auditoría SQL.              |
| `Config.DiscordWebhook`                | `string`       | `''`                  | Webhook opcional.           |
| `Config.OpenInvoiceCommand`            | `string`       | `'openinvoice'`       | Comando de facturas.        |
| `Config.OpenInvoiceKey`                | `string/false` | `'F6'`                | Keybind de facturas.        |
| `Config.OpenPosCommand`                | `string`       | `'openpos'`           | Comando POS.                |
| `Config.OpenPosKey`                    | `string/false` | `'F7'`                | Keybind POS.                |
| `Config.UseFrameworkPlayerNames`       | `boolean`      | `true`                | Usa nombre de personaje.    |
| `Config.DefaultMugshotUrl`             | `string`       | URL                   | Avatar de respaldo.         |

### Idioma

```lua
Config.Locale = 'es'
```

Se incluyen `es` y `en`. Hay dos grupos de traducciones:

* `locales/*.json`: textos Lua procesados por `ox_lib`.
* `web/src/locales/*.json`: textos fuente de la NUI.
* `web/public/*.json`: traducciones compiladas que FiveM sirve realmente.

Después de modificar traducciones web ejecuta `npm run build` dentro de `web`.

### Facturas personales y ACE

```lua
Config.AllowPersonalInvoices = true
Config.CreateInvoicePermission = 'rc-billing.create'
Config.RequireAceForPersonalInvoices = false
Config.AdminAcePermission = 'rc-billing.admin'
```

Comportamiento:

| AllowPersonal | RequireAce | Resultado                              |
| ------------- | ---------- | -------------------------------------- |
| `false`       | Cualquiera | Facturación personal desactivada.      |
| `true`        | `false`    | Todos los jugadores pueden crearla.    |
| `true`        | `true`     | Solo jugadores con el ACE configurado. |

`AdminAcePermission` protege las funciones administrativas del servidor. No uses un permiso genérico concedido a jugadores normales.

### Depuración

```lua
Config.Debug = {
    enabled = false,
    payments = true,
    invoices = true,
    nui = true
}
```

`enabled` es el interruptor global. Los otros campos activan o silencian áreas concretas. Déjalo en `false` en producción salvo durante una investigación: los logs pueden incluir importes, IDs de factura e identificadores internos.

### Empresas

Cada clave debe coincidir exactamente con el nombre interno del trabajo del framework:

```lua
Config.CompanyJobs = {
    ['mechanic'] = {
        label = 'Benny\'s Motorworks',
        minRank = 1,
        bossRank = 3,
        account = 'society_mechanic',
        logo = 'https://example.com/mechanic.png',
        maxInvoiceAmount = 50000,
        commission = {
            enabled = true,
            percent = 8,
            max = 2500
        },
        rankLimits = {
            [1] = 5000,
            [2] = 15000,
            [3] = 50000
        },
        templates = {
            {
                label = 'Revisión básica',
                description = 'Revisión mecánica básica',
                amount = 750,
                category = 'repair'
            }
        }
    }
}
```

#### Campos de empresa

| Campo              | Obligatorio | Descripción                            |
| ------------------ | ----------- | -------------------------------------- |
| `label`            | Sí          | Nombre mostrado en UI y recibos.       |
| `minRank`          | Sí          | Grado numérico mínimo para facturar.   |
| `bossRank`         | Sí          | Grado mínimo para acciones de jefe.    |
| `account`          | Recomendado | Nombre de la cuenta de sociedad.       |
| `logo`             | No          | URL HTTPS visible para clientes.       |
| `maxInvoiceAmount` | No          | Máximo de esta empresa.                |
| `commission`       | No          | Sustituye la comisión predeterminada.  |
| `rankLimits`       | No          | Máximo específico por grado exacto.    |
| `templates`        | No          | Conceptos predefinidos en la interfaz. |

#### Rangos

ESX suele entregar `job.grade` como número. QBCore/Qbox usan habitualmente `job.grade.level`. El bridge normaliza ambos.

El límite efectivo es el más restrictivo entre:

1. `Config.MaxInvoiceAmount`.
2. `company.maxInvoiceAmount`.
3. El valor aplicable en `company.rankLimits`.

No configures un `rankLimits` superior al máximo global esperando que lo amplíe.

#### Cuenta de sociedad

```lua
Config.DefaultSocietyAccountPrefix = 'society_'
```

Si una empresa no define `account`, se construye el nombre concatenando este prefijo y el trabajo. Para `police`, el resultado es `society_police`.

Comprueba cómo espera el identificador tu sistema bancario. Algunos recursos QB usan `police` y otros `society_police`; define `account` explícitamente para evitar ambigüedad.

### Límites e importes

```lua
Config.MaxInvoiceAmount = 1000000
Config.HighValueInvoiceAmount = 50000
```

* `MaxInvoiceAmount` bloquea valores superiores en el servidor.
* `HighValueInvoiceAmount` identifica operaciones importantes para auditoría.
* Los importes se redondean a dos decimales.
* Cero y números negativos se rechazan.

Usa límites razonables para la economía del servidor. Un límite de interfaz no sustituye la validación del servidor, que ya está incluida.

### Categorías

```lua
Config.InvoiceCategories = {
    { id = 'service', label = 'Servicio' },
    { id = 'fine', label = 'Multa' },
    { id = 'medical', label = 'Médico' },
    { id = 'repair', label = 'Reparación' },
    { id = 'sale', label = 'Venta' },
    { id = 'tax', label = 'Tasa' },
    { id = 'other', label = 'Otro' }
}
```

El `id` es persistente y debe mantenerse estable. `label` es presentación. Cuando una integración no especifica categoría se utiliza `service`.

### Vencimiento y duplicados

```lua
Config.DefaultDueDays = 7
Config.DuplicateWindowMinutes = 5
```

`DefaultDueDays` se usa cuando el formulario o export no aporta `dueDays`. Las facturas superadas pasan a `expired` mediante la tarea del servidor.

La protección de duplicados busca una factura reciente con mismo emisor, destinatario, fuente, importe y descripción en estado pendiente. Establece `DuplicateWindowMinutes = 0` para desactivarla.

### Comisiones

```lua
Config.DefaultCommission = {
    enabled = true,
    percent = 10,
    max = nil
}
```

La comisión está incluida en el total: el cliente paga exactamente el importe de la factura.

Para una factura empresarial de 1.000 con comisión del 10 %:

* Cliente paga: 1.000.
* Empleado recibe: 100.
* Sociedad recibe: 900.

`max` limita el valor monetario de la comisión, no el porcentaje. Una empresa puede sobrescribir toda la tabla mediante su campo `commission`. Las facturas personales no aplican comisión.

### Cuenta de pago

```lua
Config.PaymentAccount = 'bank'
```

Valores habituales:

* ESX: `bank` o `money`, según las cuentas disponibles.
* QBCore/Qbox: `bank`, `cash` o `money`; el bridge convierte `money` a `cash`.

Prueba saldo, retirada y devolución con el framework exacto antes de producción.

### Pagos parciales y pago agrupado

```lua
Config.AllowPartialPayments = true
Config.AllowPayAll = true
```

* `AllowPartialPayments` permite abonar una parte inferior al saldo pendiente.
* `AllowPayAll` habilita el flujo que suma y paga todas las facturas elegibles.

Cada abono crea una fila en `invoice_payments`. `paid_amount` acumula los pagos y la factura solo cambia a `paid` al completar `amount`.

### Auditoría y Discord

```lua
Config.AuditLogs = true
Config.DiscordWebhook = ''
Config.DiscordWebhookName = 'rc-billing'
```

La auditoría SQL escribe en `invoice_audit_logs`. El webhook vacío desactiva Discord. Si se configura, determinadas acciones críticas generan un embed.

No incluyas el webhook real en un paquete comercial, captura o repositorio público. Tras una filtración, revócalo desde Discord.

### Comandos y keybinds

```lua
Config.OpenInvoiceCommand = 'openinvoice'
Config.OpenInvoiceKey = 'F6'
Config.OpenPosCommand = 'openpos'
Config.OpenPosKey = 'F7'
```

Usa `false` o `nil` en una tecla para no registrar el keybind. Los comandos deben ser distintos y no incluir `/`.

FiveM guarda modificaciones de keybind por usuario; cambiar el valor predeterminado no reemplaza una asignación que el jugador ya personalizó.

### Nombres y avatar

```lua
Config.UseFrameworkPlayerNames = true
Config.DefaultMugshotUrl = 'https://example.com/default-avatar.png'
```

Con `UseFrameworkPlayerNames = true`, los recibos usan el nombre del personaje. En `false`, el bridge puede recurrir al nombre visible de FiveM según el framework.

La URL del avatar debe ser HTTPS, estable, pública y permitir carga desde Chromium Embedded Framework. Evita URLs temporales o con tokens.

### Configuración mínima de producción

Antes de publicar:

```lua
Config.Debug.enabled = false
Config.DiscordWebhook = '' -- Configúralo únicamente en la instalación privada.
Config.RequireAceForPersonalInvoices = true -- Recomendado si no todos deben facturar.
```

Elimina empresas de demostración que no existan en tu framework y reemplaza logos externos que no controles.


# Integración

## Integración y API

Esta página documenta la superficie pública soportada para conectar otros recursos con RC Billing. Los callbacks NUI y los eventos internos pueden cambiar entre versiones; no deben utilizarse como API externa.

### Export de servidor `CreateInvoice`

Nombre completo:

```lua
exports['rc-billing']:CreateInvoice(data)
```

La llamada es síncrona y debe hacerse desde código de servidor. Devuelve una tabla con `success`, un mensaje y, cuando se crea correctamente, `invoiceId`.

### Campos de entrada

| Campo                 | Tipo     | Obligatorio | Descripción                                   |
| --------------------- | -------- | ----------- | --------------------------------------------- |
| `creatorSource`       | `number` | Sí          | Server ID conectado del personaje emisor.     |
| `recipientSource`     | `number` | Condicional | Server ID conectado del destinatario.         |
| `recipientIdentifier` | `string` | Condicional | Identificador persistente del destinatario.   |
| `recipientDNI`        | `string` | Condicional | Alias de `recipientIdentifier`.               |
| `recipientName`       | `string` | Condicional | Nombre cuando no se usa `recipientSource`.    |
| `amount`              | `number` | Sí          | Importe positivo, sujeto a límites.           |
| `description`         | `string` | Sí          | Entre 3 y 255 caracteres.                     |
| `category`            | `string` | No          | ID de categoría; predeterminado `service`.    |
| `source`              | `string` | No          | `personal` o trabajo del emisor.              |
| `dueDays`             | `number` | No          | Días hasta vencimiento.                       |
| `resource`            | `string` | No          | Nombre del recurso originador para auditoría. |

Debes proporcionar una de estas parejas:

* `recipientSource`, mientras el destinatario está conectado.
* `recipientIdentifier` y `recipientName` para un destinatario persistente.
* `recipientDNI` y `recipientName` como alias del caso anterior.

Cuando existe `recipientSource`, el servidor obtiene identificador y nombre desde el framework e ignora valores de identidad aportados por el llamador.

### Respuesta

#### Correcta

```lua
{
    success = true,
    invoiceId = 1842,
    message = 'Factura #1842 creada correctamente.'
}
```

#### Fallida

```lua
{
    success = false,
    message = 'Descripción demasiado corta.'
}
```

No dependas del texto de `message` para lógica. Está localizado. Usa `success` y registra el mensaje para diagnóstico.

### Ejemplo personal

```lua
RegisterNetEvent('my-resource:server:billPlayer', function(targetSource, amount, reason)
    local src = source

    local result = exports['rc-billing']:CreateInvoice({
        creatorSource = src,
        recipientSource = tonumber(targetSource),
        amount = tonumber(amount),
        description = tostring(reason),
        category = 'service',
        source = 'personal',
        dueDays = 7,
        resource = GetCurrentResourceName()
    })

    if not result.success then
        print(('[my-resource] Invoice failed for source %s: %s'):format(src, result.message))
        return
    end

    print(('[my-resource] Created invoice #%s'):format(result.invoiceId))
end)
```

El export vuelve a comprobar `Config.AllowPersonalInvoices` y ACE. No permite saltarse la política del recurso.

### Ejemplo de empresa

```lua
local result = exports['rc-billing']:CreateInvoice({
    creatorSource = source,
    recipientSource = customerSource,
    amount = 3200,
    description = 'Reparación completa del motor',
    category = 'repair',
    source = 'mechanic',
    dueDays = 3,
    resource = GetCurrentResourceName()
})
```

El trabajo actual del emisor debe coincidir con `source`, estar en `Config.CompanyJobs` y alcanzar `minRank`. El export calcula comisión y sociedad desde la configuración; el recurso integrador no debe calcularlas.

### Ejemplo con destinatario desconectado

```lua
local result = exports['rc-billing']:CreateInvoice({
    creatorSource = source,
    recipientIdentifier = citizenId,
    recipientName = characterName,
    amount = 500,
    description = 'Tasa administrativa',
    category = 'tax',
    source = 'personal',
    resource = GetCurrentResourceName()
})
```

El export guarda la factura, pero no puede abrir el aviso interactivo a un jugador desconectado. El receptor la verá en su historial y recibirá un recordatorio al conectarse.

La factura se crea como `awaiting_acceptance`. El destinatario debe aceptarla para que pase a `pending` y pueda pagarse.

### Recomendaciones de seguridad

No conectes directamente un evento de cliente al export sin validar el contexto de tu propio recurso. Aunque RC Billing valida permisos e importes, tu integración debe decidir cuándo una acción de gameplay autoriza facturar.

Evita este patrón:

```lua
-- No recomendado: el cliente controla todos los datos sin contexto de gameplay.
RegisterNetEvent('my-resource:bill', function(data)
    exports['rc-billing']:CreateInvoice(data)
end)
```

Prefiere construir el payload en servidor usando datos confiables:

```lua
RegisterNetEvent('mechanic:server:finishRepair', function(orderId, customerSource)
    local src = source
    local order = ActiveRepairOrders[orderId]
    if not order or order.mechanicSource ~= src then return end

    exports['rc-billing']:CreateInvoice({
        creatorSource = src,
        recipientSource = customerSource,
        amount = order.serverCalculatedPrice,
        description = order.invoiceDescription,
        category = 'repair',
        source = 'mechanic',
        resource = GetCurrentResourceName()
    })
end)
```

### Compatibilidad de versiones

La API pública estable de la versión actual contiene únicamente `CreateInvoice`. Si una versión futura añade exports, quedarán registrados aquí y en `CHANGELOG.md`.

Para detectar fallos sin acoplarte al idioma:

```lua
local ok, result = pcall(function()
    return exports['rc-billing']:CreateInvoice(payload)
end)

if not ok then
    print(('[integration] rc-billing export unavailable: %s'):format(result))
elseif not result.success then
    print(('[integration] rc-billing rejected invoice: %s'):format(result.message))
end
```

Comprueba que `GetResourceState('rc-billing') == 'started'` si tu integración puede arrancar antes que este recurso.


# Preview

{% embed url="<https://www.youtube.com/watch?v=wCJcM9SbXnc>" %}

## Tienda Tebex

Soon...


# Instalación

Guía paso a paso para dejar el recurso completamente funcional en tu servidor de FiveM.

***

### 1. Requisitos previos

Antes de empezar, asegúrate de tener instalados en tu servidor:

| Dependencia                                                       | Obligatoria | Notas                                                       |
| ----------------------------------------------------------------- | ----------- | ----------------------------------------------------------- |
| [**ox\_lib**](https://github.com/overextended/ox_lib)             | ✅ Sí        | Librería de utilidades (notificaciones, callbacks, etc.)    |
| [**oxmysql**](https://github.com/overextended/oxmysql)            | ✅ Sí        | Driver MySQL para FiveM                                     |
| **Framework** (uno de estos)                                      | ✅ Sí        | **QBCore**, **QBox** o **ESX** — se detecta automáticamente |
| [**ox\_inventory**](https://github.com/overextended/ox_inventory) | ❌ Opcional  | Para gestión de inventario/loadouts avanzada                |
| **MySQL / MariaDB**                                               | ✅ Sí        | Base de datos del servidor                                  |

> **Nota:** El recurso detecta tu framework automáticamente (`Config.framework = 'auto'`). No necesitas configurar nada extra si usas QBCore, QBox o ESX.

***

### 2. Instalar el recurso

1. **Descarga** o clona el recurso.
2. **Copia la carpeta** `rc-pvpsystem` dentro de tu directorio `resources/`:

   ```
   resources/
   └── [tu-carpeta]/
       └── rc-pvpsystem/
           ├── fxmanifest.lua
           ├── config/
           ├── client/
           ├── server/
           ├── web/
           └── ...
   ```
3. Comprueba que la estructura del recurso está completa (debe contener `fxmanifest.lua`, carpetas `client/`, `server/`, `config/`, `web/`, `sql/`, etc.).

***

### 3. Importar la base de datos

El recurso crea las tablas automáticamente al iniciar por primera vez gracias al sistema de migraciones integrado. **No necesitas importar SQL manualmente.**

Sin embargo, si prefieres importarlo a mano o si tienes problemas:

1. Abre tu herramienta de base de datos (HeidiSQL, phpMyAdmin, DBeaver, etc.).
2. Selecciona la base de datos de tu servidor FiveM.
3. Importa el archivo `sql/schema.sql`.

#### Tablas que se crean

Todas las tablas usan el prefijo `rcpvp_` para evitar conflictos con otros recursos.

| Tabla                         | Descripción                             |
| ----------------------------- | --------------------------------------- |
| `rcpvp_meta`                  | Metadatos internos del recurso          |
| `rcpvp_users`                 | Jugadores registrados en el sistema PvP |
| `rcpvp_friends`               | Sistema de amigos                       |
| `rcpvp_lobbies`               | Lobbies/salas creadas                   |
| `rcpvp_lobby_members`         | Miembros de cada lobby                  |
| `rcpvp_matches`               | Historial de partidas                   |
| `rcpvp_match_events`          | Eventos durante partidas (kills, etc.)  |
| `rcpvp_redzone_deaths`        | Muertes en la Redzone                   |
| `rcpvp_redzone_stats`         | Estadísticas de Redzone por jugador     |
| `rcpvp_ratings`               | ELO/rating por jugador y modo           |
| `rcpvp_arena_stats`           | Estadísticas de Arena por jugador       |
| `rcpvp_leaderboard_snapshots` | Snapshots de la tabla de clasificación  |
| `rcpvp_settings`              | Ajustes por jugador                     |
| `rcpvp_sanctions`             | Bans y sanciones                        |

***

### 4. Configurar el server.cfg

Añade las siguientes líneas a tu `server.cfg`. El **orden importa**: las dependencias deben arrancar antes que `rc-pvpsystem`.

```cfg
# --- Dependencias (deben estar ANTES) ---
ensure oxmysql
ensure ox_lib

# --- Tu framework ---
ensure qb-core          # o es_extended / qbox-core según tu caso

# ensure ox_inventory  # Solo si lo usas

# --- El recurso PvP ---
ensure rc-pvpsystem
```

> ⚠️ **Importante:** `rc-pvpsystem` debe ir **después** de `oxmysql`, `ox_lib` y tu framework en el `server.cfg`.

***

### 5. Configuración principal (config.lua)

El archivo de configuración principal está en `config/config.lua`. A continuación se explican las secciones más importantes.

#### 5.1 Framework e inventario

```lua
Config.framework = 'auto'           -- auto | esx | qbcore | standalone
Config.inventoryAdapter = 'auto'    -- auto | ox | qb | esx
Config.notifications = 'auto'       -- auto | nui
```

* **`auto`** es el valor recomendado. El recurso detecta qué tienes instalado.
* Si por algún motivo la detección falla, ponlo manualmente (`'qbcore'`, `'esx'`, etc.).

#### 5.2 Branding (personalización del nombre)

```lua
Config.branding = {
    name = 'TuServidor',           -- Nombre que aparece en la interfaz
    subtitle = 'PvP',              -- Subtítulo
    fullName = 'TuServidorPVP',    -- Para logs y notificaciones
    coinsName = 'Coins',           -- Nombre de tu moneda
    primaryColor = 'FF3072FF',     -- Color principal (hex ARGB)
    accentColor = 'FF00B3FF',      -- Color de acento (hex ARGB)
}
```

#### 5.3 Hub (punto central)

El Hub es el lugar donde los jugadores acceden a la tablet/NUI y esperan partidas.

```lua
Config.hub = {
    location = vector4(187.95, -952.99, 29.09, 331.80),   -- Coordenadas del hub
    npc = {
        enabled = true,                                     -- ¿Mostrar NPC interactivo?
        model = 's_m_m_bouncer_01',                        -- Modelo del NPC
        coords = vector4(187.95, -952.99, 29.09, 331.80), -- Posición del NPC
        scenario = 'WORLD_HUMAN_CLIPBOARD',                -- Animación idle
        label = 'Abrir lobby',                              -- Texto de interacción
        icon = 'fa-solid fa-people-group'                   -- Icono
    }
}
```

Cambia las coordenadas a la ubicación que quieras en tu servidor.

#### 5.4 Tecla para abrir la NUI

```lua
Config.nui = {
    openKey = 'F5',        -- Tecla para abrir la tablet
    perfMode = true,       -- Modo rendimiento
    language = 'es'        -- Idioma de la interfaz (es / en)
}
```

#### 5.5 Relleno con bots

Si no tienes suficientes jugadores, el sistema puede rellenar partidas con bots IA:

```lua
Config.botFill = {
    enabled = true,          -- Activar/desactivar bots
    waitSeconds = 30,        -- Segundos de espera antes de meter bots
    model = 's_m_y_blackops_01',
    weapon = 'WEAPON_CARBINERIFLE',
    accuracy = 80,           -- Precisión (0-100)
    health = 220,
    armour = 120,
    modes = {
        arena = true,        -- Bots en Arena
        deathmatch = true,   -- Bots en Deathmatch
        escalation = true    -- Bots en Escalation
    }
}
```

#### 5.6 Modos de juego

Activa o desactiva cada modo:

```lua
Config.modes = {
    arena = true,            -- 1v1, 2v2, etc. por rondas
    deathmatch = true,       -- FFA Deathmatch
    escalation = true,       -- Escalation (armas progresivas)
    custom = true            -- Partidas personalizadas
}
```

#### 5.7 Redzone (zona PvP libre)

```lua
Config.redzone = {
    enabled = true,          -- Activar la Redzone

    walls = {
        enabled = true,      -- Paredes visuales en los bordes
    },

    zones = {
        {
            id = 'airport',
            name = 'Aeropuerto',
            enabled = true,
            -- ... polígono, centro, blip, etc.
        },
        -- Puedes añadir más zonas aquí
    },

    death = { ... },         -- Config de muerte en redzone
    loot  = { ... },         -- Config de looteo de cadáveres
    extraction = { ... },    -- Config de extracción (tecla G)
}
```

#### 5.8 Munición

```lua
Config.ammo = {
    infinite = false,        -- true = munición infinita
    defaultAmount = 200,     -- Munición al spawnear
    respawnRefill = true,    -- Recargar al respawnear
    refillAmount = 200       -- Cantidad al respawnear
}
```

#### 5.9 Admin

Define qué grupos de permisos pueden usar comandos admin:

```lua
Config.admin = {
    groups = { 'god', 'admin' }
}
```

***

### 6. Configuración de Discord (discord.lua)

El archivo `server/config/discord.lua` permite integrar avatares y datos de Discord.

```lua
local DiscordConfig = {
    enabled = true,               -- Activar integración Discord
    required = true,              -- ¿Obligar Discord vinculado para entrar?
    kickMessage = 'Debes tener Discord vinculado para entrar.',

    botToken = 'TU_BOT_TOKEN',   -- Token de tu bot de Discord
    guildId = 'TU_GUILD_ID',     -- ID de tu servidor de Discord

    useAvatars = true,            -- Mostrar avatares de Discord en la UI
    webhookUrl = ''               -- (Opcional) Webhook para logs
}
```

#### Cómo obtener el bot token y guild ID

1. Ve a [Discord Developer Portal](https://discord.com/developers/applications).
2. Crea una aplicación (o selecciona una existente).
3. En la sección **Bot**, copia el **Token**.
4. Activa los **Privileged Gateway Intents** → **Server Members Intent**.
5. Invita el bot a tu servidor de Discord con el scope `bot` y permiso `View Members`.
6. Para obtener el **Guild ID**: activa el modo desarrollador en Discord (Ajustes → Avanzado), haz clic derecho en tu servidor → "Copiar ID del servidor".

> Si no quieres usar Discord, simplemente pon `enabled = false`.

***

### 7. Sistema de muerte — Compatibilidad con ambulancejob

El recurso incluye su propio sistema de muerte/downed. **Si ya usas `qb-ambulancejob`, `esx_ambulancejob` u otro sistema de muerte, debes desactivarlo.**

En `config/config.lua`:

```lua
Config.death = {
    enabled = false,    -- ← false = el framework/ambulancejob maneja la muerte global
}
```

| `enabled` | Comportamiento                                                                                                         |
| --------- | ---------------------------------------------------------------------------------------------------------------------- |
| `true`    | RC-PVP gestiona toda la muerte (mundo, redzone, partidas)                                                              |
| `false`   | RC-PVP solo gestiona la muerte **dentro** de partidas y redzone. Fuera de estos contextos, tu ambulancejob se encarga. |

> **Recomendación:** Si tienes `qb-ambulancejob` o similar, usa `enabled = false`.

***

### 8. Configurar mapas y zonas

Los mapas de partida se definen en `config/maps.lua`. El recurso viene con mapas preconfigurados:

| Mapa                             | Modo       | Descripción                   |
| -------------------------------- | ---------- | ----------------------------- |
| `arena_training`                 | Arena      | Hangar de entrenamiento (2v2) |
| `docks_dm`                       | Deathmatch | Terminal portuario            |
| *(y más según la configuración)* |            |                               |

Para añadir un mapa nuevo, sigue la estructura existente:

```lua
mi_mapa = {
    id = 'mi_mapa',
    label = 'Mi Mapa Personalizado',
    mode = 'deathmatch',        -- arena | deathmatch | escalation
    teams = 1,                   -- 1 para FFA, 2+ para equipos
    teamSize = 8,
    capacity = 12,
    minPlayers = 2,
    spawns = {
        free = {                 -- Para FFA
            vector4(x, y, z, heading),
            vector4(x, y, z, heading),
            -- ...
        }
    },
    rules = {
        scoreLimit = 30,
        timeLimit = 600,
        loadout = {
            { weapon = 'WEAPON_CARBINERIFLE', ammo = 200 },
        }
    }
}
```

***

### 9. Frontend (NUI)

La interfaz web ya viene **pre-compilada** en `web/build/`. **No necesitas hacer nada** para que funcione.

#### Solo si quieres modificar la interfaz:

El frontend usa **SvelteKit + Vite**. Necesitarás [Node.js LTS](https://nodejs.org/) instalado.

```bash
cd web/
npm install
npm run dev      # Desarrollo local en navegador
npm run build    # Compilar para FiveM (genera web/build/)
```

Tras hacer `npm run build`, reinicia el recurso en el servidor para que los cambios se apliquen.

***

### 10. Primer arranque y verificación

1. **Arranca tu servidor** de FiveM.
2. **Comprueba la consola** del servidor. Deberías ver mensajes del recurso creando las tablas de la base de datos. Si ves errores de conexión MySQL, revisa tu `server.cfg` (connection string de `oxmysql`).
3. **Entra al servidor** con un personaje.
4. **Ve al Hub** — dirígete a las coordenadas configuradas en `Config.hub.location` y verifica que:
   * El NPC aparece (si `npc.enabled = true`).
   * Al interactuar o pulsar la tecla (`F5` por defecto), se abre la tablet/NUI.
5. **Prueba la Redzone** — entra en una de las zonas configuradas (aeropuerto, Grove Street, etc.) y comprueba que:
   * Aparece la notificación de entrada.
   * Las paredes de zona se muestran.
   * El PvP funciona correctamente.
6. **Crea una partida** — desde la tablet, crea una partida de Deathmatch o Arena y verifica que el matchmaking y los spawns funcionan.

#### Comandos útiles para admin

Desde la consola del servidor o el chat (si eres admin):

| Comando                | Descripción                  |
| ---------------------- | ---------------------------- |
| `ensure rc-pvpsystem`  | Iniciar/reiniciar el recurso |
| `restart rc-pvpsystem` | Reiniciar el recurso         |

***

### 11. Solución de problemas

#### "No supported framework detected"

* Comprueba que tu framework (`qb-core`, `es_extended` o `qbox-core`) arranca **antes** que `rc-pvpsystem` en el `server.cfg`.

#### Las tablas no se crean en la base de datos

* Verifica que `oxmysql` está correctamente configurado y arrancado.
* Revisa que la cadena de conexión en tu `server.cfg` es correcta:

  ```cfg
  set mysql_connection_string "mysql://user:password@localhost/tu_base_de_datos?charset=utf8mb4"
  ```
* Prueba importar `sql/schema.sql` manualmente.

#### La NUI no se abre / se ve en blanco

* Comprueba que la carpeta `web/build/` contiene archivos (`index.html`, carpeta `_app/`, etc.).
* Si falta contenido, recompila:

  ```bash
  cd web/
  npm install
  npm run build
  ```

#### Error con ox\_lib

* Asegúrate de tener la última versión de `ox_lib`.
* Debe arrancar antes que `rc-pvpsystem` en el `server.cfg`.

#### Los bots no aparecen

* Verifica que `Config.botFill.enabled = true`.
* Los bots solo se activan tras esperar `Config.botFill.waitSeconds` segundos si no hay suficientes jugadores.

#### Discord: "Debes tener Discord vinculado"

* Si no quieres obligar Discord, pon `DiscordConfig.required = false` en `server/config/discord.lua`.
* Si quieres desactivar completamente Discord, pon `DiscordConfig.enabled = false`.

#### La muerte no funciona correctamente fuera de partidas

* Si usas `qb-ambulancejob` o similar, asegúrate de que `Config.death.enabled = false`.
* Si **no** usas ambulancejob, ponlo a `true` para que RC-PVP gestione la muerte global.

***

### Resumen rápido (checklist)

* [ ] `oxmysql` y `ox_lib` instalados y arrancados
* [ ] Framework (QBCore / ESX / QBox) arrancado antes del recurso
* [ ] Recurso copiado en `resources/`
* [ ] `ensure rc-pvpsystem` en `server.cfg` (después de las dependencias)
* [ ] `config/config.lua` ajustado (hub, branding, modos, redzone)
* [ ] `server/config/discord.lua` configurado (o Discord desactivado)
* [ ] `Config.death.enabled` ajustado según tu sistema de muerte
* [ ] Servidor arrancado y tablas creadas correctamente
* [ ] NUI funcional (carpeta `web/build/` presente)

***

*rc-pvpsystem v1.0.1 — xByMarcos · RealCity Developments*


# Preview


# Instalación

## Guía de Instalación

Esta guía te ayudará a instalar la pantalla de carga `rc-loadingscreen` en tu servidor de FiveM.

### Requisitos

* Un servidor de FiveM en funcionamiento.

### Pasos de Instalación

1. **Descargar el Script**:
   * Descarga los archivos del script desde "Granted Assets" en keymaster.
2. **Copiar en el Directorio de Recursos**:
   * Descomprime el archivo descargado si es necesario.
   * Copia la carpeta completa `rc-loadingscreen` dentro del directorio `resources` de tu servidor FiveM. La ruta debería verse algo así: `.../resources/[RealCity]/rc-loadingscreen/`.
3. **Asegurar el Recurso en `server.cfg`**:

   * Abre el archivo `server.cfg` de tu servidor FiveM.
   * Añade la siguiente línea para asegurarte de que el script se inicie junto con el servidor. Se recomienda colocarlo junto con otros recursos de la interfaz.

   ```cfg
   ensure rc-loadingscreen
   ```
4. **Eliminar otros loadingscreen**:
   * Elimina o mueve fuera del servidor scripts de pantallas de carga que tengas instalado en el servidor
5. **Reiniciar el Servidor**:
   * Guarda los cambios en `server.cfg` y reinicia tu servidor de FiveM.

¡Listo! La pantalla de carga debería aparecer la próxima vez que un jugador se conecte a tu servidor.


# Configuración

## Guía de Configuración

Esta guía detalla cómo configurar y personalizar la pantalla de carga `rc-loadingscreen` para tu servidor. La configuración principal se gestiona a través de dos archivos clave: `web/config.js` para los ajustes de funcionalidad y `web/locales.js` para los textos e idiomas.

***

### Configuración General (`web/config.js`)

Este es el archivo principal para ajustar el comportamiento y la apariencia de la pantalla de carga.

#### Idioma

Define el idioma por defecto de la interfaz. Los idiomas disponibles están en `web/locales.js`.

```javascript
const config = {
    language: 'es', // 'es' para español, 'en' para inglés
```

#### Audio

Controla el volumen de los diferentes efectos de sonido de la interfaz.

```javascript
audio: {
    powerOnVolume: 0.5,   // Volumen del sonido de encendido
    switchVolume: 0.5,    // Volumen del cambio de canal
    staticVolume: 0.3,    // Volumen del ruido estático
    humVolume: 0.3,       // Volumen del zumbido de fondo
},
```

#### YouTube

Configura el vídeo de YouTube que se reproduce de fondo en uno de los canales.

```javascript
youtube: {
    videoId: 'FND0z4NF8Pg', // ID del vídeo de YouTube
    volume: 50              // Volumen del vídeo (0-100)
},
```

#### Logo del Servidor

Activa y personaliza el logo de tu servidor.

```javascript
logo: {
    enabled: true,  // true para mostrar, false para ocultar
    url: './logo_server_el_vinculo.png', // Ruta a tu imagen de logo
    style: {
        width: '150px',
        position: 'absolute',
        top: '30px',
        right: '30px',
        opacity: '0.8'
    }
},
```

#### Teletexto

El teletexto es el menú principal de información. Aquí puedes definir el nombre del servidor, el enlace a Discord, el personal y las diferentes páginas de información.

```javascript
teletext: {
    serverName: "RealCity Developments RP",
    discordURL: 'discord.realcity.dev',
    staff: [
        { "class": "red", "text": "[Fundador] xByMarcos" },
        // ... más miembros del staff
    ],
    pages: [
        {
            id: 'p101',
            area: 'status',
            title: 'BIENVENIDO',
            content: [
                'Bienvenido a <span class="cyan">{serverName}</span>!',
                'Jugadores conectados: {playerCount}',
                'Unete a nuestro Discord: <span class="yellow">{discordURL}</span>'
                // ... más contenido
            ]
        },
        // ... más páginas
    ]
}
```

***

### Localización e Idiomas (`web/locales.js`)

Este archivo contiene todas las cadenas de texto utilizadas en la interfaz, separadas por idioma. Puedes editar los textos existentes o añadir nuevos idiomas.

#### Estructura

El objeto `locales` contiene un objeto por cada idioma (`es`, `en`, etc.).

```javascript
const locales = {
    'es': {
        "page_title": "Retro CRT Loader v5.0 - Final Experience",
        "muted": "SILENCIO",
        "connecting_server": "CONECTANDO CON EL SERVIDOR...",
        // ... más traducciones
    },
    'en': {
        "page_title": "Retro CRT Loader v5.0 - Final Experience",
        "muted": "MUTED",
        "connecting_server": "CONNECTING TO SERVER...",
        // ... más traducciones
    }
};
```

#### Añadir un Nuevo Idioma

1. Copia el bloque completo de un idioma existente (por ejemplo, `'en': { ... }`).
2. Pégalo al final del objeto `locales`.
3. Cambia el código del idioma (ej. `'fr'` para francés).
4. Traduce todos los textos al nuevo idioma.
5. Finalmente, actualiza la opción `language` en `web/config.js` para que coincida con el nuevo código de idioma (ej. `language: 'fr'`).


# Preview

### ¿Qué es rc-persecutions?

`rc-persecutions` es un recurso que permite a los jugadores participar en persecuciones ilegales uno contra uno. Un jugador (el "perseguido") crea una carrera con una apuesta y una duración, y otro jugador (el "perseguidor") puede unirse. El objetivo del perseguidor es mantener el contacto visual con el perseguido hasta que se agote el tiempo, mientras que el perseguido debe escapar. El ganador se lleva el total de la apuesta.

{% embed url="<https://www.youtube.com/watch?ab_channel=RealCityDevelopments&v=eI_gdu1ceEU>" %}

## Tienda Tebex

{% embed url="<https://shop.realcity.dev/es/package/6623661>" %}


# Instalación

## Instalación de rc-persecutions

Este documento proporciona una guía detallada para instalar y configurar correctamente el script `rc-persecutions` en tu servidor de FiveM.

***

### Requisitos

Antes de instalar, asegúrate de tener las siguientes dependencias instaladas y en funcionamiento en tu servidor.

* [**ox\_lib**](https://github.com/overextended/ox_lib)**:** Requerido. Se utiliza para notificaciones, locales (textos) y otras funciones principales.
* [**oxmysql**](https://github.com/overextended/oxmysql)**:** Requerido. Se utiliza para acceder a la base de datos y obtener información de los vehículos.
* [**sleepless\_interact**](https://github.com/Sleepless-Development/sleepless_interact)**:** Opcional. Si se activa en la configuración, proporciona una forma alternativa y más visual para que los jugadores interactúen con el punto de inicio de las persecuciones.
* **Un script de Framework (ESX / QBCore / etc.):** El script está diseñado para funcionar con diferentes frameworks a través de un sistema de "bridge". Deberás configurar cuál utilizas en `config.lua`.

***

### Pasos de Instalación

Sigue estos pasos para instalar el recurso en tu servidor.

#### 1. Descargar y Descomprimir

1. Descarga los archivos del script `rc-persecutions`.
2. Descomprime el archivo `.zip` (si es necesario).
3. Copia la carpeta `rc-persecutions` en el directorio `resources` de tu servidor. La ruta debería verse así: `.../resources/[JerarquiaRP]/rc-persecutions/`.

#### 2. Configurar el Framework y Dependencias

1. Abre el archivo `config.lua`.
2. Localiza la línea `Config.Framework` y establece el nombre de tu framework en minúsculas (por ejemplo, `'esx'`, `'qbcore'`). **Este paso es crucial.**
3. Asegúrate de que las dependencias mencionadas anteriormente (`ox_lib`, `oxmysql`) están correctamente instaladas.

#### 3. Asegurar el Recurso

Añade la siguiente línea a tu archivo `server.cfg` o `resources.cfg`. Es importante que `rc-persecutions` se inicie **después** de todas sus dependencias.

```cfg
ensure rc-persecutions
```

**Orden de inicio recomendado:**

```cfg
ensure oxmysql
ensure ox_lib
ensure es_extended # o tu base de ESX/QB
# ... otros scripts
ensure rc-persecutions
```

#### 4. Configuración Adicional

Revisa la siguiente página para una explicación detallada de todas las opciones disponibles en `config.lua`. Deberás configurar aspectos como:

* El tipo de dinero a utilizar para las apuestas (`RC.MoneyType`).
* La ubicación del lobby de persecuciones (`RC.LobbyLocation`).
* Los límites de apuesta, duración y distancias.

#### 5. Verificación

Inicia tu servidor y comprueba la consola en busca de errores relacionados con `rc-persecutions`. Si no hay errores, dirígete a la ubicación del lobby configurada en el mapa y deberías ver el marcador o la interacción para comenzar una persecución.


# Configuración

## Guía de Configuración de rc-persecutions

Este documento detalla todas las opciones disponibles en el archivo `config.lua` para personalizar el script `rc-persecutions`.

***

### Configuración General

Estas opciones controlan el comportamiento fundamental del script.

#### `RC.MenuType`

Define qué sistema de menús se utilizará para las interacciones.

* **Tipo:** `string`
* **Valores posibles:**
  * `'ox'`: Utiliza los menús de `ox_lib` (recomendado).
  * `'framework'`: Utiliza las funciones de menú nativas del framework que estés usando (puede requerir configuración adicional en el bridge).
* **Por defecto:** `'ox'`

```lua
RC.MenuType = 'ox'
```

#### `RC.EnableSleeplessInteract`

Activa o desactiva el uso de `sleepless_interact` para iniciar las interacciones en el lobby. Si se desactiva, los jugadores deberán presionar una tecla (`E` por defecto) cuando se les indique.

* **Tipo:** `boolean`
* **Valores:** `true` o `false`
* **Por defecto:** `true`

```lua
RC.EnableSleeplessInteract = true
```

#### `RC.MoneyType`

Define el tipo de cuenta de dinero que se usará para las apuestas.

* **Tipo:** `string`
* **Valores comunes:** `'money'`, `'bank'`, `'black_money'`. Debe coincidir con el nombre de la cuenta en tu base de datos y framework.
* **Por defecto:** `'black_money'`

```lua
RC.MoneyType = 'black_money'
```

#### `RC.ShopType`

Define de dónde se obtienen las categorías de los vehículos para las carreras.

* **Tipo:** `string`
* **Valores:**
  * `'default'`: Obtiene las categorías de la tabla `vehicles` de la base de datos.
  * `'s4vehicleshop'`: Utiliza la exportación del script `s4-vehicleshop` para obtener las categorías.
* **Por defecto:** `'default'`

```lua
RC.ShopType = 'default'
```

***

### Ubicaciones y Distancias

#### `RC.LobbyLocation`

Coordenadas `vector3` donde se encuentra el lobby para crear y unirse a persecuciones.

* **Tipo:** `vector3`
* **Por defecto:** `vector3(-1652.8989, -902.9819, 8.377)`

#### `RC.RaceStartCar1` / `RC.RaceStartCar2`

Coordenadas `vec4` (incluyendo el heading) donde aparecerán los dos vehículos al iniciar la persecución.

* **Tipo:** `vec4`

#### `RC.LobbyRadius`

El radio en metros alrededor del `LobbyLocation`. Si un jugador sale de este radio mientras está en el lobby, la interacción se cancela.

* **Tipo:** `float`
* **Por defecto:** `15.0`

***

### Notificaciones a la Policía

#### `RC.NotifyPolice`

Activa o desactiva las notificaciones a la policía cuando comienza una persecución.

* **Tipo:** `boolean`
* **Por defecto:** `true`

#### `RC.NotifyTime`

El tiempo en segundos que debe durar una persecución antes de que se envíe la notificación a la policía.

* **Tipo:** `integer`
* **Por defecto:** `20`

#### `RC.NotifyPoliceEvent`

La función que se ejecuta para notificar a la policía. Por defecto, dispara un evento de `SendAlert:police`. Puedes personalizarlo para que se integre con tu propio sistema de alertas.

* **Tipo:** `function`

```lua
RC.NotifyPoliceEvent = function (plcoords)
    TriggerServerEvent("SendAlert:police", {
        coords = plcoords,
        title = 'Illegal race',
        type = 'GENERAL',
        message = 'There is a illegal race of two cars.',
        job = 'police',
    })
end
```

***

### `RC.PursuitSettings`

Tabla que contiene todos los parámetros que definen las reglas de la persecución.

#### `ReturnWhenFinish`

Si se establece en `true`, los jugadores serán teletransportados de vuelta a la ubicación del lobby al finalizar la persecseción.

* **Tipo:** `boolean`
* **Por defecto:** `true`

#### `EnableBlips`

Muestra u oculta el blip en el mapa del jugador contrario durante la persecución.

* **Tipo:** `boolean`
* **Por defecto:** `true`

#### `MinBet` / `MaxBet`

La cantidad mínima y máxima que los jugadores pueden apostar.

* **Tipo:** `integer`
* **Por defecto:** `MinBet = 5000`, `MaxBet = 100000`

#### `MinDuration` / `MaxDuration`

La duración mínima y máxima en **minutos** que puede tener una persecución.

* **Tipo:** `integer`
* **Por defecto:** `MinDuration = 1`, `MaxDuration = 30`

#### `VisualLossTime`

El tiempo en **segundos** que el "perseguidor" puede estar sin contacto visual con el "perseguido" antes de perder la prueba.

* **Tipo:** `integer`
* **Por defecto:** `10`

#### `MaxDistance`

La distancia máxima en metros que puede haber entre los dos jugadores. Si se supera, el "perseguido" gana.

* **Tipo:** `float`
* **Por defecto:** `450.0`

#### `MinDistance`

La distancia mínima a la que debe estar el "perseguidor" para que el cronómetro de la persecución avance.

* **Tipo:** `float`
* **Por defecto:** `5.0`

***

### `RC.Markers`

Configuración visual del marcador en el suelo en la zona del lobby.

* **`LobbyType`:** El [tipo de marcador](https://docs.fivem.net/docs/client-manual/native-functions/drawmarker/).
* **`LobbyColor`:** El color RGBA del marcador.
* **`LobbySize`:** El tamaño (x, y, z) del marcador.

***

### `RC.Text`

Esta tabla contiene los textos que se muestran al usuario. Utiliza la función `locale()` de `ox_lib` para la traducción, obteniendo los textos de los archivos en la carpeta `/locales`. No se recomienda modificar esta sección directamente; en su lugar, edita los archivos `.json` correspondientes.


# Servers Docs

Comming soon...


