Configuration
Overview
shared/config.lua: Currency, prices, custom tattoos, uses, item payment, restrictions, shops and commandsserver/config_server.lua: Discord webhooks and the rate-limit fallback (server-only, never sent to clients)
The shop ships item and admin only: Config.Shops is empty, so there are no public NPCs or blips until you add them.
General
1Config.Debug = false
2Config.Locale = 'en'
3Config.Currency = 'bank'
4Config.UIPosition = 'right'
5Config.MetaResource = 'ml_tattoos_meta'
6Config.AdminCommand = 'tattooadmin'
7Config.AdminPermission = 'admin'
8Config.PremiumCommand = 'mytattoos'
9Config.StudioCommand = 'mystudio'Debug: console debug output. Also registers thetattoodebugandtestv1test commandsLocale: Interface language. Loads the matching file inlocales/, with English as the fallback for any missing keyCurrency: 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 asml_tattoos_metaunless you renamed itAdminCommand: Command that opens the shop straight on the admin tab (admin only)AdminPermission:ml_bridgepermission (ACE node or framework group) that grants admin access to the command and the admin panelPremiumCommand: Command that opens a read-only view of the tattoos the player ownsStudioCommand: Command that opens the My Studio tab for players who hold uses or own custom tattoos.falseremoves the command
Currency & Prices
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}RemovePrice: Cost charged per tattoo removedDefaultTattooPrice: Fallback price when a tattoo has no specific priceCategoryPrices: Per-DLC price, keyed by the overlay pack slug. Price priority is per-tattoo override →CategoryPrices[dlc]→DefaultTattooPrice
Custom Tattoos
1Config.EnableCustomTattoos = true
2Config.CustomTattooPrice = 500
3Config.CustomTattooPrefix = 'MLTattoo'
4
5Config.UserCustom = {
6 enabled = true,
7 payment = 'uses',
8 imageInput = 'both',
9 uploadMaxBytes = 2097152,
10 uploadMaxDimension = 2048,
11 extraImageHosts = {},
12 editWindow = { enabled = true, ttlMinutes = 1440 },
13 maxCredits = { solo = 20, faction = 10, ytd = 10 },
14 refundOnAdminDeleteHours = 24,
15 maxCustomsPerPlayer = 30,
16}
17
18Config.SlotsPerPreset = 100
19Config.SlotNameFormat = 'MLSlot_%03d_%03d'EnableCustomTattoos: Master switch for player custom-image tattoosCustomTattooPrice: money charged to create a custom whenUserCustom.payment = 'money'CustomTattooPrefix: Naming prefix for custom overlaysUserCustom.enabled: Lets players who hold uses create their own custom without admin involvementUserCustom.payment: how creating a custom is paid:'uses'spends a use,'money'chargesCustomTattooPrice,'free'costs nothingUserCustom.imageInput: How a player supplies the image:'paste'a link,'upload'a PNG in game, or'both'. Upload needs FiveManage; without it the upload button hides and paste staysUserCustom.uploadMaxBytes: Maximum PNG size accepted by the in-game uploaderUserCustom.uploadMaxDimension: largest PNG width or height, in pixelsUserCustom.extraImageHosts: extra HTTPS hosts allowed for pasted image links, on top of i.imgur.com, i.ibb.co, raw.githubusercontent.com and the FiveManage CDNUserCustom.editWindow: free rework window of the server rework callback, 24 hours by default. My Studio has no edit button for itUserCustom.maxCredits: Per-type cap on how many uses a single player can hold at onceUserCustom.refundOnAdminDeleteHours: If an admin deletes a user-created custom within this many hours, the creator is refunded one useUserCustom.maxCustomsPerPlayer: Maximum custom tattoos a single player can ownSlotsPerPreset,SlotNameFormat: number and naming of the overlay slots in the companion resource. Keep them matchingml_tattoos_metaSlotsPerPreset: 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
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).
1Config.TebexPackages = {
2 ['ml_tattoos_solo_1'] = { type = 'solo', amount = 1, eur = 15, money = 15000, source = 'tebex' },
3 ['ml_tattoos_solo_2'] = { type = 'solo', amount = 2, eur = 24, money = 25000, source = 'tebex' },
4 ['ml_tattoos_solo_3'] = { type = 'solo', amount = 3, eur = 30, money = 40000, source = 'tebex' },
5 ['ml_tattoos_faction_1'] = { type = 'faction', amount = 1, eur = 40, money = 30000, source = 'tebex' },
6 ['ml_tattoos_ytd_1'] = { type = 'ytd', amount = 1, eur = 30, money = 25000, source = 'tebex' },
7}
8
9Config.AllowedConsumerResources = {
10}TebexPackages: Maps a Tebex package id to a use grant. Match each key to the package identifier on your Tebex storetype:'solo','faction'or'ytd'amount: How many uses the package grantseur: Real-money price on your Tebex store;money: in-game price (charged fromConfig.Currency)source: Which payment method the player may use:'tebex'(real-money donation through your store, delivered by webhook),'money'(in-game cash at themoneyprice), 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:
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
1Config.YtdPacks = {
2}YtdPacks: Declares each separately-installed full-body pack (its.ytddictionaries plus a decoration collection). Oneytduse unlocks one whole pack permanently per playercollection: The decoration collection name shipped inside the packtattoos: Every overlay in the pack a player can apply, with its zone and labelallowedFactions / allowedIdentifiers: Optional. Omit both for an open pack; otherwise only matched factions or identifiers see and unlock it
Item Payment
1Config.EnableItemPayment = false
2Config.ItemPerTattoo = { item = 'tattoo_kit', count = 1 }
3
4Config.UnlockableHashes = {
5}
6
7Config.ItemPresets = {
8 tattoo_kit_basic = {
9 label = 'Basic Tattoo Kit', limit = 1, pay = 'item', count = 1,
10 categories = { 'mphipster_overlays', 'mpbeach_overlays', 'mpchristmas2_overlays', 'mpsum2_overlays' },
11 allowCustom = false,
12 },
13}EnableItemPayment: Whentrue, tattoos cost an item instead of money. Off by defaultItemPerTattoo: The item and quantity consumed per tattoo while item payment is onUnlockableHashes: Locks specific tattoo hashes behind owning an item (the item is checked, not consumed)ItemPresets: each usable item opens the shop filtered to its categories. The file shipstattoo_kit_basic,tattoo_kit_premium,tattoo_kit_biker,tattoo_kit_custom(custom tattoos only) andtattoo_kit_full.pay='item'consumes the item,'free'applies for free,'shop'charges money. Filter withzones,categories,hashes,allowCustom
Shops, NPC & Clothing
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:trueuses the target system detected byml_bridgeon shop locations,falseuses proximity and TextUI. Skipped at locations withUseNPC = trueInteractDistance: Interaction distance (metres) for a shopShops: Public shop locations (coords, heading, label, blip). Empty by default for an item/admin-only flowUseNPC / NPC: Spawn a tattoo-artist ped at each shop and its appearance/behaviourEnableClothesHide: Strips clothes while inside the shop so tattoos are visible (configurable viaConfig.InvisibleClothes)ArtistScene: Animation played on the player when a checkout succeeds
Admin Grant
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
UI Theme
1Config.UITheme = {
2 preset = nil,
3 colors = {
4 primary = nil,
5 accent = nil,
6 background = nil,
7 surface = nil,
8 text = nil,
9 },
10}preset: theme preset of the shop.nilfollows the shared theme set inml_bridgecolors: CSS hex values for single colors. Anynilvalue follows the shared theme
Webhooks
1ServerConfig.DiscordWebhook = GetConvar('ml_tattoos_webhook', '')
2ServerConfig.AdminWebhook = GetConvar('ml_tattoos_admin_webhook', ServerConfig.DiscordWebhook)
3ServerConfig.RateLimitCooldown = 500DiscordWebhook: Player-action log webhook, read from theml_tattoos_webhookconvar so no URL ships in the buildAdminWebhook: Admin-action log webhook (ml_tattoos_admin_webhook); falls back to the main webhook when unsetRateLimitCooldown: Fallback minimum ms between repeated actions from one player
Set the URLs in server.cfg: setr ml_tattoos_webhook "https://discord.com/api/webhooks/..." (and optionally ml_tattoos_admin_webhook).