# Battlepass

## Overview

<figure><img src="/files/xvcULtWGvvoVBwWYPHqk" alt=""><figcaption></figcaption></figure>

XeX BattlePass is a premium daily reward and battle pass system for FiveM servers. Players claim one reward per day across a 30-day calendar. A premium VIP tier gives access to exclusive bonus rewards. The system features a dark gaming-inspired NUI, Discord webhook logging, and automatic database management.

### Supported Frameworks

| Framework  | Version | Status         |
| ---------- | ------- | -------------- |
| ESX Legacy | 1.6.0+  | ✅ Full Support |
| QBCore     | Latest  | ✅ Full Support |
| QBox       | Latest  | ✅ Full Support |

## Features Summary

| Category    | Feature          | Description                                               |
| ----------- | ---------------- | --------------------------------------------------------- |
| **Rewards** | 30-Day Schedule  | Configurable normal + VIP reward per day                  |
| **Rewards** | 5 Reward Types   | Items, weapons, money, vehicles, furniture                |
| **Rewards** | Daily Claims     | One claim per day, sequential progression                 |
| **Premium** | VIP Tier         | Purchasable premium subscription for exclusive rewards    |
| **Premium** | Renewals         | Extend premium duration with configurable packages        |
| **Premium** | Retroactive      | VIP purchase retroactively awards missed VIP rewards      |
| **UI**      | Dark Theme       | Modern gaming-inspired NUI with Inter font                |
| **UI**      | Progress Grid    | Visual 30-day calendar with claim/locked/available states |
| **System**  | Auto DB          | Tables created automatically on first start               |
| **System**  | Season State     | KVP-based persistence for season day tracking             |
| **System**  | Discord Webhooks | Log every reward claim to Discord                         |
| **System**  | Auto Updater     | Version check via GitHub Gist JSON                        |
| **I18n**    | 2 Languages      | English, Spanish                                          |
| **Compat**  | loaf\_housing    | Furniture reward type integration                         |
| **Compat**  | xex\_lootboxes   | Lootbox item integration                                  |

## Installation

### Requirements

* FiveM Server Build 5181+
* [oxmysql](https://github.com/overextended/oxmysql)
* Framework: ESX Legacy or QBCore

### Optional Dependencies

| Resource                                                                                 | Purpose               |
| ---------------------------------------------------------------------------------------- | --------------------- |
| [loaf\_housing](https://store.loaf-scripts.com/package/4310850)                          | Furniture reward type |
| [Deco Items Pack](https://www.gta5-mods.com/maps/murmods-furniture-pack-add-on-sp-fivem) | Props for furniture   |
| [xex\_lootboxes](https://xex.tebex.io/package/5496291)                                   | Lootbox item rewards  |

### Quick Start

{% stepper %}
{% step %}

### Place resource

Place 'xex\_battlepass' in your resources/\[xex]/ folder
{% endstep %}

{% step %}

### Add to server.cfg

Add to server.cfg:

```
ensure oxmysql
ensure xex_battlepass
```

{% endstep %}

{% step %}

### Configure

Configure config.lua
{% endstep %}

{% step %}

### Restart

Restart server (database tables are created automatically)
{% endstep %}
{% endstepper %}

> Note: Database tables (`user_battlepass`, `user_premium`) and season state (KVP) are created automatically. No manual SQL import is required.

## Configuration

### General Settings

```lua
Config.ModActivated = true            -- Master on/off switch
Config.Debug = false                  -- Enable debug commands
Config.MenuCommand = 'battlepass'     -- Command to open battle pass UI
Config.PremiumCommand = 'premium'     -- Command to open premium shop
Config.ExtraDaysToEnd = 5             -- Grace days after day 30 before season resets
```

| Option                  | Default        | Description                                                     |
| ----------------------- | -------------- | --------------------------------------------------------------- |
| `Config.ModActivated`   | `true`         | Master switch for the entire system                             |
| `Config.Debug`          | `false`        | Enables debug commands (getbpdata, restclaimedbpday, restbpday) |
| `Config.MenuCommand`    | `'battlepass'` | Chat command to open the battle pass calendar                   |
| `Config.PremiumCommand` | `'premium'`    | Chat command to open the premium shop                           |
| `Config.ExtraDaysToEnd` | `5`            | Days of grace period after all 30 days have passed              |

### Framework Settings

```lua
Config.Framework = 'esx'              -- 'esx' | 'qb'
Config.Language = 'en'                -- 'en' | 'es'
Config.CheckForUpdates = true
```

### Premium System

```lua
Config.PremiumCoin = 'bank'           -- Account used for premium payments

Config.PremiumOptions = {
    {
        months = 1,
        price = 50000,
        label = '1 Month VIP',
        img = 'vip_1month.png',
    },
    -- ... more packages
}
```

### Reward Schedule (Days 1-30)

Each day defines a `normal` reward (for all players) and a `vip` reward (premium only):

```lua
Config.Schedule = {
    {
        day = 1,
        items = {
            normal = {
                type = 'item',
                name = 'bread',
                label = 'Bread',
                quantity = 10,
                img = 'bread.png',
            },
            vip = {
                type = 'furniture',
                name = 'murm_dec_pack_chair_1',
                label = 'Deco Chair',
                quantity = 1,
                img = 'chair.png',
            },
        },
    },
    -- ... days 2-30
}
```

### Reward Types

| Type        | Fields                                       | Description                           |
| ----------- | -------------------------------------------- | ------------------------------------- |
| `item`      | `name`, `label`, `quantity`, `img`           | Inventory item (e.g., bread, bandage) |
| `weapon`    | `name`, `label`, `img`                       | Weapon (quantity always 1)            |
| `money`     | `name` (account), `label`, `quantity`, `img` | Currency (money, bank, black\_money)  |
| `vehicle`   | `name` (spawn name), `label`, `img`          | Vehicle (spawned + stored in garage)  |
| `furniture` | `name` (object hash), `label`, `img`         | loaf\_housing furniture piece         |

### Discord Webhooks

```lua
Config.WebhookEnabled = true
Config.Webhook = 'https://discord.com/api/webhooks/...'
Config.BotName = 'BattlePass'
Config.WebhookServerName = 'My Server'
Config.IconUrl = 'https://i.imgur.com/...'
Config.DateFormat = '%d/%m/%Y %H:%M'
```

## Database

Tables are **automatically created** on first start:

### user\_battlepass

Tracks player claim progress per season.

### user\_premium

Tracks premium subscription start/end dates.

If you need to create them manually, use the included `scripts.sql` file.

## How It Works

### Complete Flow

```
Player types /battlepass → Client requests data from server
→ Server checks player's claim history + premium status + current day
→ NUI shows 30-day grid:
   ├── Green: Already claimed
   ├── Gold: Available today (click to claim)
   ├── Locked: Future days
   └── VIP badge: Premium-only rewards
Player types /premium → Premium shop opens
   ├── Select package (1 month, 3 months, etc.)
   └── Purchase with bank account
```

### Daily Claim Logic

* Server tracks global "current day" via KVP (`xex_bp:state`)
* Player can only claim the current day if all previous days are claimed
* Each claim is recorded in `user_battlepass` table
* Normal rewards given to all players
* VIP rewards given only to premium subscribers

### Premium VIP System

* Players purchase VIP through `/premium` command
* VIP subscription has start and end dates stored in `user_premium`
* VIP unlocks exclusive `vip` tier rewards on each day
* Multiple package durations can be configured (1 month, 3 months, etc.)
* Renewal extends the existing end date

### Season Lifecycle

1. Season starts at day 1
2. Each real day advances the season by 1
3. After day 30, grace period of `Config.ExtraDaysToEnd` days
4. After grace period, season resets (KVP cleared, new season begins)

### Retroactive VIP Rewards

When a player purchases VIP mid-season, they retroactively receive all VIP rewards for previously claimed days. This ensures no VIP content is missed.

## NUI Interface

### Battle Pass Grid

* 30-day calendar grid layout
* Visual states: Claimed (green check), Available (golden glow), Locked (grey)
* Normal reward icon on left, VIP reward icon on right
* Claim button for available days
* VIP badge for premium rewards

### Premium Shop

* Package cards with duration, price, and image
* Purchase confirmation
* Active subscription status display
* Renewal option for existing subscribers

## Exports & Events

### Client Exports

```lua
-- Check if current player has active premium
local hasPremium = exports['xex_battlepass']:isPremium()
-- Returns: boolean
```

### Server Exports

```lua
-- Check if a specific player has active premium
local hasPremium = exports['xex_battlepass']:isPremium(playerId)
-- Returns: boolean

-- Trigger manual update check
exports['xex_battlepass']:CheckForUpdates()
```

### Server Callbacks

| Callback                         | Parameters   | Returns | Description                        |
| -------------------------------- | ------------ | ------- | ---------------------------------- |
| `xex_battlePass:canClaim`        | —            | boolean | Check if player can claim today    |
| `xex_battlePass:claimDayRewards` | —            | result  | Process and deliver today's reward |
| `xex_battlePass:buy`             | packageIndex | result  | Purchase premium subscription      |
| `xex_battlePass:renew`           | packageIndex | result  | Renew premium subscription         |

### Net Events

| Event                          | Direction       | Description                        |
| ------------------------------ | --------------- | ---------------------------------- |
| `xex_battlePass:loadData`      | Server → Client | Send battle pass state to client   |
| `xex_battlePass:rewardVehicle` | Server → Client | Spawn rewarded vehicle client-side |

### NUI Callbacks

| Callback      | Description                              |
| ------------- | ---------------------------------------- |
| `claimDay`    | Claim the current available day's reward |
| `closeButton` | Close the battle pass UI                 |

### Commands

| Command             | Context        | Description                    |
| ------------------- | -------------- | ------------------------------ |
| `/battlepass`       | Client         | Opens the battle pass calendar |
| `/premium`          | Client         | Opens the premium shop         |
| `battlepass_update` | Server Console | Force update check             |

### Debug Commands

> Only available when `Config.Debug = true`

| Command             | Description                                  |
| ------------------- | -------------------------------------------- |
| `/getbpdata`        | Reload your battle pass data from server     |
| `/restclaimedbpday` | Roll back your last claimed day              |
| `/restbpday`        | Roll back the global battle pass day counter |

## Localization

### Supported Languages

| Code | Language |
| ---- | -------- |
| `en` | English  |
| `es` | Español  |

### Adding Languages

Add a new entry in `locales.lua`:

```lua
Locales['fr'] = {
    ['received'] = 'Reçu',
    ['daily_limit'] = 'Déjà réclamé aujourd\'hui',
    -- ... all keys
}
```

Then set `Config.Language = 'fr'` in `config.lua`.

## File Structure

```
xex_battlepass/
├── client/
│   ├── esx.lua               # ESX client logic
│   ├── qb.lua                # QBCore client logic
│   └── scrow.lua             # Shared NUI bridge (escrow)
├── server/
│   ├── esx.lua               # ESX server — rewards, premium, claims
│   ├── qb.lua                # QBCore server mirror
│   ├── scrow.lua             # Auto DB creation, KVP helpers (escrow)
│   └── updater.lua           # Version checker
├── ui/
│   ├── ui.html               # NUI markup
│   ├── style.css             # Dark themed styles
│   ├── script.js             # NUI logic (vanilla JS)
│   └── img/                  # Reward images
├── config.lua                # Full configuration (563 lines)
├── locales.lua               # Translations (en/es)
├── scripts.sql               # Database schema (optional manual import)
├── fxmanifest.lua            # Resource manifest
├── readme.md                 # Quick start readme
└── DOCS.md                   # This documentation
```

## Troubleshooting

| Issue                           | Solution                                                                  |
| ------------------------------- | ------------------------------------------------------------------------- |
| UI doesn't open                 | Verify `Config.ModActivated = true` and resource is started               |
| "Missing locale key" in console | Add the missing key to `locales.lua`                                      |
| Rewards not given               | Check `Config.Schedule` — reward `name` must match your inventory items   |
| Webhook not working             | Verify `Config.WebhookEnabled = true` and URL is valid                    |
| oxmysql errors                  | Ensure `oxmysql` is started before `xex_battlepass` in server.cfg         |
| Premium purchase fails          | Check `Config.PremiumCoin` matches a valid account type (`bank`, `money`) |
| Vehicle reward doesn't spawn    | Check vehicle model name in `Config.Schedule` is valid                    |
| Furniture reward fails          | Requires `loaf_housing` resource installed and running                    |
| Season not advancing            | Check KVP state — use debug commands to inspect/reset                     |
| Day counter stuck               | Use `/restbpday` (debug mode) to correct the global counter               |

## Changelog

### v2.0.0

* Migrated UI from jQuery to vanilla JavaScript
* Redesigned UI with dark gaming aesthetic (Inter font, CSS custom properties)
* Migrated from mysql-async to oxmysql
* Added centralized XeX updater system
* Fixed critical bugs: undefined variables, typos, exposed webhook URLs
* Fixed security: removed exploitable net event for notifications
* Modernized ESX/QB init patterns (exports instead of events)
* Improved identifier matching (prefix-based instead of loose `string.find`)
* Added nil guards throughout server logic
* Cleaned up duplicate NUI callbacks
* Removed debug prints from production code
* Added proper database indexes and column types
* Full code cleanup and section organization

### v1.x

* Initial release with ESX + QBCore support

## Support

| Channel | Link                                                   |
| ------- | ------------------------------------------------------ |
| Discord | [discord.gg/9yY3Jxnhh8](https://discord.gg/9yY3Jxnhh8) |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jesus-gimenez.gitbook.io/xex-scripts/premium-mods/battlepass.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
