> For the complete documentation index, see [llms.txt](https://codeforge.gitbook.io/codeforge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://codeforge.gitbook.io/codeforge/sleek-series/sleek-tattoo.md).

# SLEEK TATTOO

{% embed url="<https://youtu.be/X4VxSNKciNU?si=3pv4bpKSYFx_5Hjm>" %}

##

Tattoo shop resource for FiveM — business management, NPC employees, custom addon tattoos, henna, minigames, and a modern UI.

### Features

* Auto-detection of ESX / QBCore — no manual framework config needed
* 6 pre-configured parlor locations
* Full business system with worker/owner ranks and revenue split
* NPC employees that generate passive income
* Supply system — parlors consume supplies per tattoo
* Henna (temporary tattoos) — auto-expire after configurable real-time days
* Tattooing minigame — optional ox\_lib skillCheck
* Duty system — clock in/out with auto-off-duty on distance
* Custom addon tattoos with a **CUSTOM** badge in the shop UI
* Per-parlor tattoo removal station
* Full GTA vanilla catalog + custom collections
* Modern NUI with configurable colors, blur, particles, and sounds
* ox\_target / qb-target or classic marker + keypress
* 9 banking integrations (ESX, QB, neon-boss, OKOK, FD, TGG, WASABI, CODEM, JUSTBANKING)
* 5 clothing resource integrations (illenium-appearance, rcore\_clothing, fivem-appearance, qb-clothing, esx\_skin)
* Discord webhooks for sale logging
* Multi-language (EN, ES, FR, DE, IT, TR, ZH, AR)

### Requirements

| Dependency                                                                                              |   Required  |
| ------------------------------------------------------------------------------------------------------- | :---------: |
| [ox\_lib](https://github.com/overextended/ox_lib)                                                       |     Yes     |
| [oxmysql](https://github.com/overextended/oxmysql) or mysql-async                                       |     Yes     |
| ESX or QBCore                                                                                           |     Yes     |
| A clothing resource (illenium-appearance, rcore\_clothing, fivem-appearance, qb-clothing, or esx\_skin) | Recommended |
| ox\_target or qb-target                                                                                 |   Optional  |

### Installation

#### Step 1 — Place the resource

Copy `forge-tattoo` into your server's `resources/` directory.

#### Step 2 — Database

The resource **auto-creates** all required tables on first start (`Cfg.AutoMigrateDB = true` by default). No manual SQL import is needed.

Tables created:

* `forge_business` — parlor balances, supply levels, NPC employees
* A `tattoos` column in your framework's player/users table

#### Step 3 — server.cfg

```cfg
ensure ox_lib
ensure forge-tattoo
```

`ox_lib` must start **before** `forge-tattoo`.

#### Step 4 — Create jobs (if using business mode)

If your parlors use `isBusiness = true`, register the job names in your framework.

**ESX:**

```sql
INSERT INTO `jobs` (`name`, `label`) VALUES
('tattoo1', 'Ink Studio - El Burro'),
('tattoo2', 'Ink Studio - Vinewood'),
('tattoo3', 'Ink Studio - Chumash'),
('tattoo4', 'Ink Studio - Vespucci'),
('tattoo5', 'Ink Studio - Sandy Shores'),
('tattoo6', 'Ink Studio - Paleto Bay');
```

**QBCore** — add equivalent entries in `qb-core/shared/jobs.lua`.

#### Step 5 — Start your server

The resource auto-detects your framework and clothing resource. Edit the files inside `config/` to customize.

### Configuration

All configuration lives in the `config/` folder:

| File                | Purpose                                                                       |
| ------------------- | ----------------------------------------------------------------------------- |
| `config.lua`        | Master config — economy, NPCs, supplies, henna, minigame, UI, markers, camera |
| `shops.lua`         | Parlor locations, coordinates, NPC spawn, business settings                   |
| `tattoos.lua`       | Full tattoo catalog — vanilla GTA + custom addon entries                      |
| `translation.lua`   | UI and notification text (8 languages)                                        |
| `bridge_client.lua` | Client framework bridge (usually no edits needed)                             |
| `bridge_server.lua` | Server framework bridge (usually no edits needed)                             |

#### Auto-detection

The framework is detected automatically:

* `es_extended` running → ESX mode
* `qb-core` running → QBCore mode

The clothing resource is auto-detected in this priority order: illenium-appearance → rcore\_clothing → fivem-appearance → qb-clothing → esx\_skin.

### Parlor Locations

Defined in `config/shops.lua`. Ships with 6 locations:

| Key               | Location         | Default Job |
| ----------------- | ---------------- | ----------- |
| `parlor_elburro`  | El Burro Heights | `tattoo1`   |
| `parlor_vinewood` | Vinewood         | `tattoo2`   |
| `parlor_chumash`  | Chumash          | `tattoo3`   |
| `parlor_vespucci` | Vespucci Beach   | `tattoo4`   |
| `parlor_sandy`    | Sandy Shores     | `tattoo5`   |
| `parlor_paleto`   | Paleto Bay       | `tattoo6`   |

#### Adding a new parlor

1. Copy an existing parlor entry in `shops.lua`
2. Change the key name (e.g. `'parlor_legion'`)
3. Update all coordinates
4. Set a new `assignedJob` and register it in your framework
5. Restart the resource

#### Non-business mode

Set `isBusiness = false` on any parlor to make it a public self-service shop — no job required.

#### Key parlor fields

```lua
['parlor_myshop'] = {
    blipCoords    = vector3(x, y, z),
    npcModel      = "u_m_y_tattoo_01",
    npcSpawnPoint = vector4(x, y, z, heading),
    headingFront  = 197.46,     -- Customer heading for front tattoos
    headingBack   = 22.31,      -- Customer heading for back tattoos

    seats = {
        [1] = {
            markerPos    = vector3(...),
            workerPos    = vector4(...),
            seatPos      = vector4(...),
            targetExtent = vec(1.0, 2.35, 1.85),
            occupied     = false,
        },
    },

    removalEnabled = true,
    removalPoint   = vector3(x, y, z),
    dutyPoint      = vector4(x, y, z, heading),

    isBusiness   = true,
    assignedJob  = "tattoo1",
    societyKey   = "tattoo1",
    workerRanks  = { 0, 1 },
    ownerRanks   = { 2, 3 },
}
```

### Business & Economy

#### Revenue split

```lua
Cfg.CompanySharePercent = 80   -- Goes to the parlor balance
Cfg.WorkerSharePercent  = 20   -- Goes to the worker's pocket
```

#### Payment routing

**Built-in balance** (default) — stored in `forge_business`, managed via in-game panel:

```lua
Cfg.UseBuiltinBalance = true
```

**External banking** — route money to your society/banking resource:

```lua
Cfg.UseBuiltinBalance = false
Cfg.SocietyMethod = 'ESX'   -- ESX, QB, FORGE, OKOK, FD, TGG, WASABI, CODEM, JUSTBANKING
```

Each parlor's `societyKey` must match what the banking resource expects.

#### Neon-Boss integration

```lua
Cfg.UseNeonBossMenu = true
```

Automatically sets `UseBuiltinBalance = false` and routes all money through neon-boss. Each parlor needs a `bossMenuPoint` coordinate and the `assignedJob` must be registered in neon-boss.

### NPC Employees

Parlors can hire virtual NPC artists from a rotating candidate pool. NPCs earn a salary and generate passive income.

#### How it works

* Candidates are defined in `Cfg.NPCPool` with name, ped model, and skill level (1–4)
* The available-for-hire list rotates every `Cfg.NPCRotationMinutes` (default: 120 min)
* Each parlor holds up to `Cfg.MaxNPCEmployees` (default: 3) NPCs
* Salary is paid from the parlor balance every `NPCSalaryIntervalMin` minutes
* Owners hire/fire through the management panel

#### Passive income

When `Cfg.EnableExtraSales = true`, each NPC generates revenue every `ExtraSaleIntervalMin` minutes:

```
Income = ExtraSaleBase × (Level ^ ExtraSaleLevelExp) × HappinessMod × TeamBonus
```

| Level | Income/tick | Income/hr | Salary/hr | Net/hr |
| :---: | ----------: | --------: | --------: | -----: |
|   1   |         $60 |      $120 |      $150 |  -$130 |
|   2   |        $170 |      $340 |      $300 |   -$60 |
|   3   |        $312 |      $624 |      $450 |   +$74 |
|   4   |        $480 |      $960 |      $600 |  +$260 |

> Level 1–2 NPCs need real customer purchases to break even. With the team bonus, 3 NPCs get ×1.30.

#### NPC happiness

|     Level     | Modifier | Cause                                                        |
| :-----------: | :------: | ------------------------------------------------------------ |
|   3 (Happy)   |   ×1.00  | Supplies above threshold                                     |
|    2 (Okay)   |   ×0.75  | Normal state                                                 |
|  1 (Unhappy)  |   ×0.35  | Supplies below `SupplyAngerThreshold` (50)                   |
| 0 (Miserable) |   ×0.00  | Supplies below `SupplyCriticalThreshold` (10) — NPC may quit |

### Supply System

Parlors consume supply units per tattoo applied/removed. Owners restock through the management panel.

| Setting              | Default | Description                  |
| -------------------- | :-----: | ---------------------------- |
| `DefaultSupplyMax`   |   100   | Max supply capacity          |
| `SupplyPerTattoo`    |    2    | Units consumed per tattoo    |
| `SupplyPerRemoval`   |    1    | Units consumed per removal   |
| `SupplyUnitPrice`    |   $50   | Cost per supply unit         |
| `SupplyBuyIncrement` |    10   | Step size for the buy button |

If supplies run out, tattoos cannot be applied. Supply level also affects NPC happiness (see above).

### Henna (Temporary Tattoos)

```lua
Cfg.EnableHenna       = true
Cfg.HennaDurationDays = 3      -- Real-time days before the tattoo fades
```

Henna tattoos use the same application flow as permanent tattoos and are marked differently in the catalog.

### Minigame

Optional skill check before a worker can apply a tattoo. Only affects real player workers, not NPC-only parlors.

```lua
Cfg.UseMiniGame        = true
Cfg.MinigameDifficulty = 'easy'   -- 'easy', 'medium', 'hard'
```

Uses `ox_lib` skillCheck.

### Duty System

Workers clock in/out at the parlor's `dutyPoint`. The system auto-clocks workers off if they leave the area.

```lua
Cfg.DutyMaxDistance   = 40.0    -- Max distance before auto-clock-off
Cfg.DutyCheckInterval = 5000    -- Check interval in ms
```

### Interaction Modes

**Target mode** (ox\_target / qb-target):

```lua
Cfg.TargetMode   = true
Cfg.TargetScript = 'ox_target'   -- or 'qb-target'
```

**Marker mode** (default):

```lua
Cfg.TargetMode = false
```

Each interaction type (seats, removal, duty, management) has its own marker style configurable in `Cfg.MarkerStyles`.

### UI Customization

The full color palette is configurable in `Cfg.UIColors`:

```lua
Cfg.UIColors = {
    Accent     = { Primary = '#00c5ff', Muted = '#31afd4', Bright = '#00e0ff' },
    Background = { Deep = '#0a0e1a', Surface = '#212534' },
    Text       = { Primary = '#ffffff', Secondary = '#e0e4f0', Muted = '#969696' },
    -- ...
}
```

```lua
Cfg.EnableBlur      = true    -- Blur game world behind the UI
Cfg.EnableParticles = true    -- Floating particles on UI background
Cfg.UIVolume        = 0.4     -- Sound FX volume (0.0 – 1.0)
```

### Camera & Preview

When browsing tattoos, a studio outfit is applied so clothes don't hide the preview. Both the outfit and camera angles are configurable:

```lua
Cfg.StudioOutfit = {
    male   = { sex = 0, arms = 15, tshirt_1 = 15, torso_1 = 91, pants_1 = 61, ... },
    female = { sex = 1, arms = 15, tshirt_1 = 34, torso_1 = 101, pants_1 = 15, ... },
}
```

Camera presets per body zone are in `Cfg.StandingCamAngles` and `Cfg.ProcessCam`.

### Inventory Items

Optionally require physical items to apply tattoos:

```lua
Cfg.RequireTattooGun = false
Cfg.TattooGunItem    = 'tattoo_gun'
Cfg.RequireInkSupply = false
Cfg.InkSupplyItem    = 'tattoo_ink'
Cfg.ConsumeInkOnUse  = false
```

### Webhooks

Discord webhook notifications for sales. Configure the webhook URL in `bridge_server.lua`. Sends embeds with sale title, description, and player identifier.

### Custom Addon Tattoos

Forge Tattoo supports custom addon tattoos that appear with a **CUSTOM** badge in the shop UI.

#### Step 1 — Create the texture

* Use **OpenIV** to extract an existing GTA tattoo `.ytd` as a template, or start from scratch
* Create your design in a graphics editor (Photoshop, GIMP, etc.)
* Save as `.png` with transparency

#### Step 2 — Prepare the .ytd file

1. Name it following the convention: `forge_[name]_[zone].ytd` (e.g. `forge_skull_face.ytd`)
2. Copy any existing `.ytd` from `stream/` as a base, rename it
3. Open in OpenIV → delete the existing texture → import your `.png`
4. Place the finished `.ytd` in the `stream/` folder

#### Step 3 — Register in the XML

Open `ADDON/forge_overlays_custom.xml` and add an `<Item>` entry for **male** and **female**:

```xml
<Item>
    <uvPos x="0.500000" y="0.680000" />
    <scale x="0.100000" y="0.100000" />
    <rotation value="0.000000" />
    <nameHash>forge_skull_face_M</nameHash>
    <txdHash>forge_skull_face</txdHash>
    <txtHash>forge_skull_face</txtHash>
    <zone>ZONE_HEAD</zone>
    <type>TYPE_TATTOO</type>
    <faction>FM</faction>
    <garment>ALL</garment>
    <gender>GENDER_MALE</gender>
</Item>
```

For the female version, use `_F` suffix in `nameHash`, set `faction` to `FM_F`, and `gender` to `GENDER_FEMALE`.

| Field                 | Description                                                                                     |
| --------------------- | ----------------------------------------------------------------------------------------------- |
| `uvPos`               | Position on the body UV map                                                                     |
| `scale`               | Size on the body (default `0.1 / 0.1`)                                                          |
| `nameHash`            | Unique ID — must end in `_M` or `_F`                                                            |
| `txdHash` / `txtHash` | Must match the `.ytd` filename (without extension)                                              |
| `zone`                | `ZONE_HEAD`, `ZONE_TORSO`, `ZONE_LEFT_ARM`, `ZONE_RIGHT_ARM`, `ZONE_LEFT_LEG`, `ZONE_RIGHT_LEG` |
| `faction`             | `FM` (male), `FM_F` (female)                                                                    |

#### Step 4 — Add to the catalog

In `config/tattoos.lua`, find the `forge_overlays_custom` collection and add:

```lua
ctat('Skull Face', 'forge_skull_face_M', 'forge_skull_face_F', 800, 1050, 1, 'f_head'),
```

Parameters: `ctat(label, hashMale, hashFemale, cost, eraseCost, pose, camera)`

| Parameter | Description                                                                                              |
| --------- | -------------------------------------------------------------------------------------------------------- |
| `pose`    | `1` = face-down (front), `2` = face-up (back)                                                            |
| `camera`  | Preset string (`'f_head'`, `'f_neck'`, `'b_torso'`, etc.) or custom `{cameraOffset, lookAtOffset}` table |

**Camera presets:** `f_head`, `f_neck`, `b_neck`, `b_face`, `b_face_r`, `b_face_l`, `b_torso`, `b_torso_r`, `b_torso_l`, `b_belly`

For arms/legs, use a custom offset table:

```lua
ctat('Arm Tat', 'hash_M', 'hash_F', 500, 660, 1, {vec(0.712, 0.472, -0.815), vec(0.212, 0.462, -0.665)})
```

#### Step 5 — Restart and test

```
ensure forge-tattoo
```

Your tattoo will appear in the shop with the **CUSTOM** badge.

### Translations

```lua
Cfg.Locale = 'en'   -- 'en', 'es', 'fr', 'de', 'it', 'tr', 'zh', 'ar'
```

All strings are in `config/translation.lua`. To add a language, copy the `en` block and translate the values.

### Skin Manager Compatibility

Most skin managers call `ClearPedDecorations()` when loading a player's appearance, which **removes all tattoo overlays**. The skin manager must call forge-tattoo's export to re-apply them:

```lua
exports['forge-tattoo']:ReloadPlayerInk()
```

#### Does my skin manager need modification?

| Skin Manager              |    Needs Modification?    |
| ------------------------- | :-----------------------: |
| esx\_skin / skinchanger   | No — works out of the box |
| illenium-appearance (ESX) |          **Yes**          |
| illenium-appearance (QB)  |          **Yes**          |
| fivem-appearance (ESX)    |          **Yes**          |
| qb-clothing (QB)          |          **Yes**          |
| rcore\_clothing           |  No — compatible natively |

#### Pre-modified downloads

Don't want to modify it yourself? Use the version that already includes full forge compatibility:

{% file src="/files/x0wZXf10dSuif6CXXu0b" %}

{% file src="/files/m5iNeQPekpBlkjUihACB" %}

{% file src="/files/rrEXoR9ko0VcOBIenK5T" %}

{% file src="/files/agfn4jcGTlq9TDizKrmG" %}

> These versions auto-detect all three forge scripts (tattoo, barber, clothes). When a forge script is running, the skin manager disables its own overlapping shop and calls the appropriate export after every appearance load.

#### What the modifications do

1. **Auto-detection** — checks `GetResourceState` at startup for each forge script
2. **Shop deactivation** — skips creating the built-in tattoo/barber/clothing blips, zones, and targets when the corresponding forge script is running
3. **Overlay sync** — calls `ReloadPlayerInk()` after every `ClearPedDecorations` to re-apply tattoos

#### Manual modification guide

<details>

<summary><strong>illenium-appearance (ESX &#x26; QB)</strong></summary>

**1.** In `shared/config.lua`, add:

```lua
Config.ForgeScripts = {
    tattoo  = GetResourceState('forge-tattoo')  ~= 'missing',
    barber  = GetResourceState('forge-barber')  ~= 'missing',
    clothes = GetResourceState('forge-clothes') ~= 'missing',
}
```

**2.** In `game/util.lua`, add this helper:

```lua
local function syncForgeOverlays()
    if Config.ForgeScripts.tattoo then exports['forge-tattoo']:ReloadPlayerInk() end
end
```

**3.** In `client/blips.lua`, `client/zones.lua`, and `client/target/target.lua` — skip shop creation when the corresponding `Config.ForgeScripts.*` is true.

**4.** Call `syncForgeOverlays()` after every place that loads a full appearance (`setPedAppearance`, outfit changes, `LoadSkin`).

**5.** Guard the tattoo tab: `config.tattoos = not Config.RCoreTattoosCompatibility and not Config.ForgeScripts.tattoo and (...)`

</details>

<details>

<summary><strong>fivem-appearance (ESX)</strong></summary>

**1.** In `configuration/config.lua`, add:

```lua
Config.Forge = {
    barber  = GetResourceState('forge-barber')  ~= 'missing',
    tattoo  = GetResourceState('forge-tattoo')  ~= 'missing',
    clothes = GetResourceState('forge-clothes') ~= 'missing',
}
```

**2.** In `client/functions.lua`, add:

```lua
local function syncForgeOverlays()
    if Config.Forge.tattoo then exports['forge-tattoo']:ReloadPlayerInk() end
end
```

**3.** Wrap `Config.ClothingShops`, `Config.BarberShops`, and `Config.TattooShops` in `if not Config.Forge.X then ... end`.

**4.** Call `syncForgeOverlays()` after every `setPlayerAppearance` / `setPedAppearance`.

</details>

<details>

<summary><strong>qb-clothing (QB)</strong></summary>

**1.** In `config.lua`, add:

```lua
Config.Forge = {
    tattoo  = GetResourceState('forge-tattoo')  ~= 'missing',
    clothes = GetResourceState('forge-clothes') ~= 'missing',
    barber  = GetResourceState('forge-barber')  ~= 'missing',
}
```

**2.** At the top of `client/main.lua`, add:

```lua
local function syncForgeOverlays()
    if Config.Forge.tattoo then exports['forge-tattoo']:ReloadPlayerInk() end
end
```

**3.** Wrap clothing and barber store entries in `config.lua` with `if not Config.Forge.clothes then ... end` / `if not Config.Forge.barber then ... end`.

**4.** Call `syncForgeOverlays()` after skin load functions (`ChangeToSkinNoUpdate`, `reloadSkin`, `loadPlayerClothing`).

</details>

### Support

[Join our Discord](https://discord.gg/UTVssdrXRV) — installation help, bug reports, feature requests, and free custom tattoo designs.

### License

© CodeForge — All rights reserved.
