Configuration

7 min readUpdated Today

Overview

  • shared/config.lua: Currency, prices, custom tattoos, uses, item payment, restrictions, shops and commands
  • server/config_server.lua: Discord webhooks and the rate-limit fallback (server-only, never sent to clients)

The shop runs item/admin-only out of the box: Config.Shops is empty, so there are no public NPCs or blips until you add them.

General

shared/config.lua
1Config.Locale = 'en'
2Config.Currency = 'bank'
3Config.UIPosition = 'right'
4Config.MetaResource = 'ml_tattoos_meta'
5Config.AdminCommand = 'tattooadmin'
6Config.AdminPermission = 'admin'
7Config.PremiumCommand = 'mytattoos'
8Config.StudioCommand = 'mystudio'
  • Locale: Interface language. Loads the matching file in locales/, with English as the fallback for any missing key
  • Currency: Money account used for every charge: 'bank', 'cash' or 'money'
  • UIPosition: Panel side: 'left' or 'right'
  • MetaResource: Name of the companion resource that streams the custom-tattoo overlay slots. Leave as ml_tattoos_meta unless you renamed it
  • AdminCommand: Command that opens the shop straight on the admin tab (admin only)
  • AdminPermission: ml_bridge permission (ACE node or framework group) that grants admin access to the command and the admin panel
  • PremiumCommand: Command that opens a read-only view of the tattoos the player owns
  • StudioCommand: Command that opens the My Studio tab for players who hold uses or own custom tattoos

Currency & Prices

shared/config.lua
1Config.RemovePrice = 1000
2Config.DefaultTattooPrice = 500
3Config.CategoryPrices = {
4    ['mpheist4_overlays']    = 400,
5    ['mplowrider_overlays']  = 500,
6    ['mpluxe_overlays']      = 500,
7    ['mpbeach_overlays']     = 200,
8    -- ...one entry per DLC overlay pack
9}
  • RemovePrice: Cost charged per tattoo removed
  • DefaultTattooPrice: Fallback price when a tattoo has no specific price
  • CategoryPrices: Per-DLC price, keyed by the overlay pack slug. Price priority is per-tattoo override → CategoryPrices[dlc]DefaultTattooPrice

Custom Tattoos

shared/config.lua
1Config.EnableCustomTattoos = true
2Config.CustomTattooPrice = 500
3Config.CustomTattooPrefix = 'MLTattoo'
4
5Config.UserCustom = {
6    enabled = true,
7    imageInput = 'paste',      -- 'paste' | 'upload' | 'both'
8    uploadMaxBytes = 2097152,
9    editWindow = { enabled = true, ttlMinutes = 1440 },
10    maxCredits = { solo = 20, faction = 10, ytd = 10 },
11    refundOnAdminDeleteHours = 24,
12    maxCustomsPerPlayer = 30,
13}
14
15Config.SlotsPerPreset = 100
  • EnableCustomTattoos: Master switch for player custom-image tattoos
  • CustomTattooPrice: Money charged to apply a custom tattoo through the normal shop flow (admins are exempt)
  • CustomTattooPrefix: Naming prefix for custom overlays
  • UserCustom.enabled: Lets players who hold uses create their own custom without admin involvement
  • UserCustom.imageInput: How a player supplies the image: 'paste' a link, 'upload' a PNG in-game (needs FiveManage), or 'both'
  • UserCustom.uploadMaxBytes: Maximum PNG size accepted by the in-game uploader
  • UserCustom.editWindow: Free rework window after creation. ttlMinutes = 1440 gives 24h; after that a rework consumes another use
  • UserCustom.maxCredits: Per-type cap on how many uses a single player can hold at once
  • UserCustom.refundOnAdminDeleteHours: If an admin deletes a user-created custom within this many hours, the creator is refunded one use
  • UserCustom.maxCustomsPerPlayer: Maximum custom tattoos a single player can own
  • SlotsPerPreset: How many unique customs can coexist per body position. Size it to your expected total number of customs; a higher value reserves more streamed pool space. Changes take effect after a server restart

Uses & Optional Donations

Fully optional

This whole layer is opt-in. With no Tebex packages configured it does nothing, and the normal money/item tattoo shop keeps working. Use it only if you want to let players optionally support the server in exchange for custom-tattoo uses.

A use is a token that lets a player create one custom tattoo. Uses come in three types: solo (a personal custom), faction (a custom shared with the player's gang/faction), and ytd (unlocks one installed Full-Body pack permanently).

shared/config.lua
1Config.TebexPackages = {
2    ['ml_tattoos_solo_1']    = { type = 'solo',    amount = 1, eur = 15, money = 15000, source = 'tebex' },
3    ['ml_tattoos_solo_3']    = { type = 'solo',    amount = 3, eur = 30, money = 40000, source = 'tebex' },
4    ['ml_tattoos_faction_1'] = { type = 'faction', amount = 1, eur = 40, money = 30000, source = 'tebex' },
5    ['ml_tattoos_ytd_1']     = { type = 'ytd',     amount = 1, eur = 30, money = 25000, source = 'tebex' },
6}
7
8Config.AllowedConsumerResources = {
9    -- 'ml_donator_coins',
10}
  • TebexPackages: Maps a Tebex package id to a use grant. Match each key to the package identifier on your Tebex store
  • type: 'solo', 'faction' or 'ytd'
  • amount: How many uses the package grants
  • eur: Real-money price on your Tebex store; money: in-game price (charged from Config.Currency)
  • source: Which rail the player may use: 'tebex' (real-money donation through your store, delivered by webhook), 'money' (in-game cash at the money price), or 'both'. Defaults to 'tebex'
  • AllowedConsumerResources: Server resources permitted to grant/consume/refund uses through the exports (e.g. a coin-donator system). Read access is always open

When any package uses source = 'tebex', set these convars in server.cfg so ml_tebex can verify and deliver donations:

server.cfg
1set ml_tebex_webhook_secret "your-webhook-secret"
2set ml_tebex_plugin_secret "your-plugin-secret"

Uses can also be granted by staff from the admin panel, or by another resource through exports.ml_tattoos:GrantTattooCredit (see Developer).

Full-Body (YTD) Packs

shared/config.lua
1Config.YtdPacks = {
2    -- ['oldschool'] = {
3    --     label      = 'Old School Full Body',
4    --     collection = 'mlytd_oldschool',
5    --     thumbnail  = 'https://i.imgur.com/xxxxxxx.png',
6    --     tattoos = {
7    --         { hash = 'OS_SLEEVE_L', zone = 'ZONE_LEFT_ARM', label = 'Left sleeve' },
8    --     },
9    --     -- allowedFactions / allowedIdentifiers restrict who can unlock it
10    -- },
11}
  • YtdPacks: Declares each separately-installed full-body pack (its .ytd dictionaries plus a decoration collection). One ytd use unlocks one whole pack permanently per player
  • collection: The decoration collection name shipped inside the pack
  • tattoos: Every overlay in the pack a player can apply, with its zone and label
  • allowedFactions / allowedIdentifiers: Optional. Omit both for an open pack; otherwise only matched factions or identifiers see and unlock it

Item Payment

shared/config.lua
1Config.EnableItemPayment = false
2Config.ItemPerTattoo = { item = 'tattoo_kit', count = 1 }
3
4Config.UnlockableHashes = {
5    -- ['MP_MP_Heist4_Tat_000_M'] = { item = 'rare_tattoo_scroll', count = 1 },
6}
7
8Config.ItemPresets = {
9    tattoo_kit_basic = {
10        label = 'Basic Tattoo Kit', limit = 1, pay = 'item', count = 1,
11        categories = { 'mphipster_overlays', 'mpbeach_overlays' },
12        allowCustom = false,
13    },
14}
  • EnableItemPayment: When true, tattoos cost an item instead of money. Off by default
  • ItemPerTattoo: The item and quantity consumed per tattoo while item payment is on
  • UnlockableHashes: Locks specific tattoo hashes behind owning an item (the item is checked, not consumed)
  • ItemPresets: Each usable item opens the shop pre-filtered to a single tattoo. pay = 'item' consumes the item, 'free' applies for free, 'shop' charges money. Filter with zones, categories, hashes, allowCustom

Shops, NPC & Clothing

shared/config.lua
1Config.UseTarget = true
2Config.InteractDistance = 2.5
3Config.Shops = {}
4
5Config.UseNPC = false
6Config.NPC = { model = 'mp_m_waremech_01', scenario = 'WORLD_HUMAN_CLIPBOARD', invincible = true, freeze = true }
7
8Config.EnableClothesHide = true
9Config.ArtistScene = { enabled = true, duration = 4000, animDict = 'mini@repair', animName = 'fixing_a_player', flag = 49 }
  • UseTarget: true uses ox_target on shop locations, false uses proximity + TextUI
  • InteractDistance: Interaction distance (metres) for a shop
  • Shops: Public shop locations (coords, heading, label, blip). Empty by default for an item/admin-only flow
  • UseNPC / NPC: Spawn a tattoo-artist ped at each shop and its appearance/behaviour
  • EnableClothesHide: Strips clothes while inside the shop so tattoos are visible (configurable via Config.InvisibleClothes)
  • ArtistScene: Animation played on the player when a checkout succeeds

Admin Grant

shared/config.lua
1Config.AdminGrantCommand = 'opentattoo'
2Config.AdminGrantDefaults = { limit = 1, customOnly = false, allowCustom = true, duration = 120000 }
  • AdminGrantCommand: Admin command that opens the shop on a target player in free mode: /opentattoo <playerId> [limit] [customOnly]
  • AdminGrantDefaults: Default grant limit, whether to show only customs, whether to include the custom catalog, and the grant lifetime in ms

Webhooks

server/config_server.lua
1ServerConfig.DiscordWebhook = GetConvar('ml_tattoos_webhook', '')
2ServerConfig.AdminWebhook   = GetConvar('ml_tattoos_admin_webhook', ServerConfig.DiscordWebhook)
3ServerConfig.RateLimitCooldown = 500
  • DiscordWebhook: Player-action log webhook, read from the ml_tattoos_webhook convar so no URL ships in the build
  • AdminWebhook: Admin-action log webhook (ml_tattoos_admin_webhook); falls back to the main webhook when unset
  • RateLimitCooldown: Fallback minimum ms between repeated actions from one player
Webhooks via convars

Set the URLs in server.cfg: setr ml_tattoos_webhook "https://discord.com/api/webhooks/..." (and optionally ml_tattoos_admin_webhook).