Integration Examples
Code examples showing how to use uz_AutoShot thumbnails in clothing shops, tattoo shops, vehicle shops, and NUI panels.
Integration Examples
uz_AutoShot photos follow a fixed naming scheme, so your script never has to ask which photos exist. Build the URL from what you already know (gender, category, drawable, model), load it in an <img>, and fall back to a placeholder when the file is missing.
Pass the Base URL to Your NUI
For UIs that render many thumbnails, read the base URL and format once in Lua and build the URLs in JavaScript:
-- client.lua (your script)
SendNUIMessage({
type = 'photoConfig',
baseUrl = exports['uz_AutoShot']:getShotsBaseURL(), -- 'https://cfx-nui-uz_AutoShot/shots'
format = exports['uz_AutoShot']:getPhotoFormat(), -- 'png'
})// nui/photos.js
let BASE = 'https://cfx-nui-uz_AutoShot/shots'
let EXT = 'png'
window.addEventListener('message', ({ data }) => {
if (data.type === 'photoConfig') {
BASE = data.baseUrl
EXT = data.format
}
})
export function clothingUrl(gender, type, id, drawable, texture = 0) {
const folder = type === 'prop' ? `prop_${id}`
: type === 'overlay' ? `overlay_${id}`
: `${id}`
// Texture 0 and overlays have no suffix
const suffix = type !== 'overlay' && texture > 0 ? `_${texture}` : ''
return `${BASE}/${gender}/${folder}/${drawable}${suffix}.${EXT}`
}
export function tattooUrl(gender, collection, overlay) {
// Tattoo files are saved lowercased
return `${BASE}/${gender}/tattoos/${collection.toLowerCase()}/${overlay.toLowerCase()}.${EXT}`
}
export const vehicleUrl = (model) => `${BASE}/vehicles/${model}.${EXT}`
export const objectUrl = (model) => `${BASE}/objects/${model}.${EXT}`clothingUrl('male', 'component', 11, 5)
// → 'https://cfx-nui-uz_AutoShot/shots/male/11/5.png'
clothingUrl('male', 'overlay', 1, 3)
// → 'https://cfx-nui-uz_AutoShot/shots/male/overlay_1/3.png'
tattooUrl('female', 'mpbeach_overlays', 'MP_Bea_F_Back_000')
// → 'https://cfx-nui-uz_AutoShot/shots/female/tattoos/mpbeach_overlays/mp_bea_f_back_000.png'Missing Photos
Not every item has a photo: a category may not be captured yet, or a new addon was added after the last capture. Swap in a placeholder when the image fails to load:
<img src="https://cfx-nui-uz_AutoShot/shots/male/11/5.png"
onerror="this.onerror = null; this.src = 'img/placeholder.png'">Photos captured after the resource started return an error until uz_AutoShot is restarted. See How Photos Are Served.
React: Thumbnail Grid
A React component that displays clothing thumbnails with a placeholder fallback:
import { useState } from 'react'
import { clothingUrl } from './photos'
function Thumb({ src, alt }) {
const [failed, setFailed] = useState(false)
if (failed) return <div className="thumb-placeholder" />
return <img src={src} alt={alt} loading="lazy" onError={() => setFailed(true)} />
}
function ClothingGrid({ gender, type, categoryId, numDrawables }) {
if (!numDrawables) return <div className="empty">No items available</div>
return (
<div className="clothing-grid">
{Array.from({ length: numDrawables }, (_, drawable) => (
<button
key={drawable}
className="clothing-item"
onClick={() => applyClothing({ itemType: type, id: categoryId, drawable, texture: 0 })}
>
<Thumb src={clothingUrl(gender, type, categoryId, drawable)} alt={`Drawable ${drawable}`} />
<span>{drawable}</span>
</button>
))}
</div>
)
}
function applyClothing(item) {
fetch(`https://YOUR_SCRIPT/applyClothing`, {
method: 'POST',
body: JSON.stringify(item),
})
}numDrawables comes from your Lua side, for example GetNumberOfPedDrawableVariations(ped, 11).
.clothing-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 8px;
padding: 12px;
}
.clothing-item {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 4px;
cursor: pointer;
transition: border-color 0.15s;
}
.clothing-item:hover {
border-color: rgba(255, 255, 255, 0.3);
}
.clothing-item img,
.thumb-placeholder {
width: 100%;
aspect-ratio: 1;
object-fit: contain;
}React: Vehicle Gallery
Display vehicle thumbnails from uz_AutoShot:
import { vehicleUrl } from './photos'
// Thumb: the component from the thumbnail grid example above
function VehicleGallery({ vehicles }) {
return (
<div className="clothing-grid">
{vehicles.map((model) => (
<button key={model} className="clothing-item" onClick={() => spawnVehicle(model)}>
<Thumb src={vehicleUrl(model)} alt={model} />
<span>{model}</span>
</button>
))}
</div>
)
}
function spawnVehicle(model) {
fetch(`https://YOUR_SCRIPT/spawnVehicle`, {
method: 'POST',
body: JSON.stringify({ model }),
})
}Lua: Clothing Shop Integration
Example for a clothing shop script that uses uz_AutoShot photos:
-- Open shop with thumbnails from uz_AutoShot
RegisterCommand('clothingshop', function()
local ped = PlayerPedId()
local gender = (GetEntityModel(ped) == `mp_m_freemode_01`) and 'male' or 'female'
-- Build photo URLs for all tops
local items = {}
local numDrawables = GetNumberOfPedDrawableVariations(ped, 11)
for i = 0, numDrawables - 1 do
items[#items + 1] = {
drawable = i,
price = math.random(100, 5000),
photo = exports['uz_AutoShot']:getPhotoURL(gender, 'component', 11, i, 0)
}
end
-- Send to NUI
SendNUIMessage({
type = 'openShop',
category = 'Tops',
items = items,
})
SetNuiFocus(true, true)
end)Lua: Tattoo Shop Integration
Tattoo photos are keyed by collection and overlay name, the same fields every tattoo list has. With a list in the illenium-appearance format:
-- tattoos = { ZONE_TORSO = { { label = '...', collection = 'mpbeach_overlays', hashMale = '...', hashFemale = '...' }, ... }, ... }
local function buildTattooItems(tattoos)
local ped = PlayerPedId()
local gender = (GetEntityModel(ped) == `mp_m_freemode_01`) and 'male' or 'female'
local items = {}
for zone, list in pairs(tattoos) do
for _, tattoo in ipairs(list) do
local overlay = gender == 'male' and tattoo.hashMale or tattoo.hashFemale
if overlay and overlay ~= '' then
items[#items + 1] = {
zone = zone,
label = tattoo.label,
photo = exports['uz_AutoShot']:getTattooURL(gender, tattoo.collection, overlay),
}
end
end
end
return items
endMale and female tattoos are captured separately. Switch the studio ped to Female (or turn on Capture both genders) in /shotmaker so both sets exist.
Lua: Vehicle Dealership Integration
Use vehicle photos in a dealership script:
RegisterCommand('dealership', function()
local vehicles = {}
local models = GetAllVehicleModels()
for _, modelName in ipairs(models) do
local hash = GetHashKey(modelName)
local classId = GetVehicleClassFromName(hash)
vehicles[#vehicles + 1] = {
model = modelName,
class = classId,
price = math.random(10000, 500000),
photo = exports['uz_AutoShot']:getVehiclePhotoURL(modelName)
}
end
SendNUIMessage({
type = 'openDealership',
vehicles = vehicles,
})
SetNuiFocus(true, true)
end)