local reverseCamera local function getRgbColors() local colors = { hair = {}, makeUp = {} } for i = 0, GetNumHairColors() - 1 do colors.hair[i+1] = {GetPedHairRgbColor(i)} end for i = 0, GetNumMakeupColors() - 1 do colors.makeUp[i+1] = {GetPedMakeupRgbColor(i)} end return colors end local playerAppearance local function getAppearance() if not playerAppearance then playerAppearance = client.getPedAppearance(cache.ped) end return playerAppearance end client.getAppearance = getAppearance local function addToBlacklist(item, drawable, drawableId, blacklistSettings) if drawable == drawableId and item.textures then for i = 1, #item.textures do blacklistSettings.textures[#blacklistSettings.textures + 1] = item.textures[i] end end if not item.textures or #item.textures == 0 then blacklistSettings.drawables[#blacklistSettings.drawables + 1] = drawable end end local function listContains(items, item) for i = 1, #items do if items[i] == item then return true end end return false end local function listContainsAny(items, containedItems) for i = 1, #items do if listContains(containedItems, items[i]) then return true end end return false end local function allowedForPlayer(item, allowedAces) return (item.jobs and listContains(item.jobs, client.job.name)) or (item.gangs and listContains(item.gangs, client.gang.name)) or (item.aces and listContainsAny(item.aces, allowedAces) or (item.citizenids and listContains(item.citizenids, client.citizenid))) end local function filterPedModelsForPlayer(pedConfigs) local playerPeds = {} local allowedAces = lib.callback.await("illenium-appearance:server:GetPlayerAces", false) for i = 1, #pedConfigs do local config = pedConfigs[i] if (not config.jobs and not config.gangs and not config.aces and not config.citizenids) or allowedForPlayer(config, allowedAces) then for j = 1, #config.peds do playerPeds[#playerPeds + 1] = config.peds[j] end end end return playerPeds end local function filterTattoosByGender(tattoos) local filtered = {} local gender = client.getPedDecorationType() for k, v in pairs(tattoos) do filtered[k] = {} for i = 1, #v do local tattoo = v[i] if tattoo["hash" .. gender:gsub("^%l", string.upper)] ~= "" then filtered[k][#filtered[k] + 1] = tattoo end end end return filtered end local function filterBlacklistSettings(items, drawableId) local blacklistSettings = { drawables = {}, textures = {} } local allowedAces = lib.callback.await("illenium-appearance:server:GetPlayerAces", false) for i = 1, #items do local item = items[i] if not allowedForPlayer(item, allowedAces) and item.drawables then for j = 0, #item.drawables do addToBlacklist(item, item.drawables[j], drawableId, blacklistSettings) end end end return blacklistSettings end local function componentBlacklistMap(gender, componentId) local genderSettings = Config.Blacklist[gender].components if componentId == 1 then return genderSettings.masks elseif componentId == 3 then return genderSettings.upperBody elseif componentId == 4 then return genderSettings.lowerBody elseif componentId == 5 then return genderSettings.bags elseif componentId == 6 then return genderSettings.shoes elseif componentId == 7 then return genderSettings.scarfAndChains elseif componentId == 8 then return genderSettings.shirts elseif componentId == 9 then return genderSettings.bodyArmor elseif componentId == 10 then return genderSettings.decals elseif componentId == 11 then return genderSettings.jackets end return {} end local function propBlacklistMap(gender, propId) local genderSettings = Config.Blacklist[gender].props if propId == 0 then return genderSettings.hats elseif propId == 1 then return genderSettings.glasses elseif propId == 2 then return genderSettings.ear elseif propId == 6 then return genderSettings.watches elseif propId == 7 then return genderSettings.bracelets end return {} end local function getComponentSettings(ped, componentId) local drawableId = GetPedDrawableVariation(ped, componentId) local gender = client.getPedDecorationType() local blacklistSettings = { drawables = {}, textures = {} } if client.isPedFreemodeModel(ped) then blacklistSettings = filterBlacklistSettings(componentBlacklistMap(gender, componentId), drawableId) end return { component_id = componentId, drawable = { min = 0, max = GetNumberOfPedDrawableVariations(ped, componentId) - 1 }, texture = { min = 0, max = GetNumberOfPedTextureVariations(ped, componentId, drawableId) - 1 }, blacklist = blacklistSettings } end client.getComponentSettings = getComponentSettings local function getPropSettings(ped, propId) local drawableId = GetPedPropIndex(ped, propId) local gender = client.getPedDecorationType() local blacklistSettings = { drawables = {}, textures = {} } if client.isPedFreemodeModel(ped) then blacklistSettings = filterBlacklistSettings(propBlacklistMap(gender, propId), drawableId) end local settings = { prop_id = propId, drawable = { min = -1, max = GetNumberOfPedPropDrawableVariations(ped, propId) - 1 }, texture = { min = -1, max = GetNumberOfPedPropTextureVariations(ped, propId, drawableId) - 1 }, blacklist = blacklistSettings } return settings end client.getPropSettings = getPropSettings local function getHairSettings(ped) local colors = getRgbColors() local gender = client.getPedDecorationType() local blacklistSettings = { drawables = {}, textures = {} } if client.isPedFreemodeModel(ped) then blacklistSettings = filterBlacklistSettings(Config.Blacklist[gender].hair, GetPedDrawableVariation(ped, 2)) end local settings = { style = { min = 0, max = GetNumberOfPedDrawableVariations(ped, 2) - 1 }, color = { items = colors.hair }, highlight = { items = colors.hair }, texture = { min = 0, max = GetNumberOfPedTextureVariations(ped, 2, GetPedDrawableVariation(ped, 2)) - 1 }, blacklist = blacklistSettings } return settings end client.getHairSettings = getHairSettings local function getAppearanceSettings() local ped = { model = { items = filterPedModelsForPlayer(Config.Peds.pedConfig) } } local tattoos = { items = filterTattoosByGender(Config.Tattoos), opacity = { min = 0.1, max = 1, factor = 0.1, } } local components = {} for i = 1, #constants.PED_COMPONENTS_IDS do components[i] = getComponentSettings(cache.ped, constants.PED_COMPONENTS_IDS[i]) end local props = {} for i = 1, #constants.PED_PROPS_IDS do props[i] = getPropSettings(cache.ped, constants.PED_PROPS_IDS[i]) end local headBlend = { shapeFirst = { min = 0, max = 45 }, shapeSecond = { min = 0, max = 45 }, shapeThird = { min = 0, max = 45 }, skinFirst = { min = 0, max = 45 }, skinSecond = { min = 0, max = 45 }, skinThird = { min = 0, max = 45 }, shapeMix = { min = 0, max = 1, factor = 0.1, }, skinMix = { min = 0, max = 1, factor = 0.1, }, thirdMix = { min = 0, max = 1, factor = 0.1, } } local size = #constants.FACE_FEATURES local faceFeatures = table.create(0, size) for i = 1, size do local feature = constants.FACE_FEATURES[i] faceFeatures[feature] = { min = -1, max = 1, factor = 0.1} end local colors = getRgbColors() local colorMap = { beard = colors.hair, eyebrows = colors.hair, chestHair = colors.hair, makeUp = colors.makeUp, blush = colors.makeUp, lipstick = colors.makeUp, } size = #constants.HEAD_OVERLAYS local headOverlays = table.create(0, size) for i = 1, size do local overlay = constants.HEAD_OVERLAYS[i] local settings = { style = { min = 0, max = GetPedHeadOverlayNum(i - 1) - 1 }, opacity = { min = 0, max = 1, factor = 0.1, } } if colorMap[overlay] then settings.color = { items = colorMap[overlay] } end headOverlays[overlay] = settings end local eyeColor = { min = 0, max = 30 } return { ped = ped, components = components, props = props, headBlend = headBlend, faceFeatures = faceFeatures, headOverlays = headOverlays, hair = getHairSettings(cache.ped), eyeColor = eyeColor, tattoos = tattoos } end client.getAppearanceSettings = getAppearanceSettings local config function client.getConfig() return config end local isCameraInterpolating local currentCamera local cameraHandle local function setCamera(key) if not isCameraInterpolating then if key ~= "current" then currentCamera = key end local coords, point = table.unpack(constants.CAMERAS[currentCamera]) local reverseFactor = reverseCamera and -1 or 1 if cameraHandle then local camCoords = GetOffsetFromEntityInWorldCoords(cache.ped, coords.x * reverseFactor, coords.y * reverseFactor, coords.z * reverseFactor) local camPoint = GetOffsetFromEntityInWorldCoords(cache.ped, point.x, point.y, point.z) local tmpCamera = CreateCameraWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z, 0.0, 0.0, 0.0, 49.0, false, 0) PointCamAtCoord(tmpCamera, camPoint.x, camPoint.y, camPoint.z) SetCamActiveWithInterp(tmpCamera, cameraHandle, 1000, 1, 1) isCameraInterpolating = true CreateThread(function() repeat Wait(500) until not IsCamInterpolating(cameraHandle) and IsCamActive(tmpCamera) DestroyCam(cameraHandle, false) cameraHandle = tmpCamera isCameraInterpolating = false end) else local camCoords = GetOffsetFromEntityInWorldCoords(cache.ped, coords.x, coords.y, coords.z) local camPoint = GetOffsetFromEntityInWorldCoords(cache.ped, point.x, point.y, point.z) cameraHandle = CreateCameraWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z, 0.0, 0.0, 0.0, 49.0, false, 0) PointCamAtCoord(cameraHandle, camPoint.x, camPoint.y, camPoint.z) SetCamActive(cameraHandle, true) end end end client.setCamera = setCamera function client.rotateCamera(direction) if not isCameraInterpolating then local coords, point = table.unpack(constants.CAMERAS[currentCamera]) local offset = constants.OFFSETS[currentCamera] local sideFactor = direction == "left" and 1 or -1 local reverseFactor = reverseCamera and -1 or 1 local camCoords = GetOffsetFromEntityInWorldCoords( cache.ped, (coords.x + offset.x) * sideFactor * reverseFactor, (coords.y + offset.y) * reverseFactor, coords.z ) local camPoint = GetOffsetFromEntityInWorldCoords(cache.ped, point.x, point.y, point.z) local tmpCamera = CreateCameraWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z, 0.0, 0.0, 0.0, 49.0, false, 0) PointCamAtCoord(tmpCamera, camPoint.x, camPoint.y, camPoint.z) SetCamActiveWithInterp(tmpCamera, cameraHandle, 1000, 1, 1) isCameraInterpolating = true CreateThread(function() repeat Wait(500) until not IsCamInterpolating(cameraHandle) and IsCamActive(tmpCamera) DestroyCam(cameraHandle, false) cameraHandle = tmpCamera isCameraInterpolating = false end) end end local playerCoords local function pedTurn(ped, angle) reverseCamera = not reverseCamera local sequenceTaskId = OpenSequenceTask() if sequenceTaskId then TaskGoStraightToCoord(0, playerCoords.x, playerCoords.y, playerCoords.z, 8.0, -1, GetEntityHeading(ped) - angle, 0.1) TaskStandStill(0, -1) CloseSequenceTask(sequenceTaskId) ClearPedTasks(ped) TaskPerformSequence(ped, sequenceTaskId) ClearSequenceTask(sequenceTaskId) end end client.pedTurn = pedTurn local function wearClothes(data, typeClothes) local dataClothes = constants.DATA_CLOTHES[typeClothes] local animationsOn = dataClothes.animations.on local components = dataClothes.components[client.getPedDecorationType()] local appliedComponents = data.components local props = dataClothes.props[client.getPedDecorationType()] local appliedProps = data.props RequestAnimDict(animationsOn.dict) while not HasAnimDictLoaded(animationsOn.dict) do Wait(0) end for i = 1, #components do local componentId = components[i][1] for j = 1, #appliedComponents do local applied = appliedComponents[j] if applied.component_id == componentId then SetPedComponentVariation(cache.ped, componentId, applied.drawable, applied.texture, 2) end end end for i = 1, #props do local propId = props[i][1] for j = 1, #appliedProps do local applied = appliedProps[j] if applied.prop_id == propId then SetPedPropIndex(cache.ped, propId, applied.drawable, applied.texture, true) end end end TaskPlayAnim(cache.ped, animationsOn.dict, animationsOn.anim, 3.0, 3.0, animationsOn.duration, animationsOn.move, 0, false, false, false) end client.wearClothes = wearClothes local function removeClothes(typeClothes) local dataClothes = constants.DATA_CLOTHES[typeClothes] local animationsOff = dataClothes.animations.off local components = dataClothes.components[client.getPedDecorationType()] local props = dataClothes.props[client.getPedDecorationType()] RequestAnimDict(animationsOff.dict) while not HasAnimDictLoaded(animationsOff.dict) do Wait(0) end for i = 1, #components do local component = components[i] SetPedComponentVariation(cache.ped, component[1], component[2], 0, 2) end for i = 1, #props do ClearPedProp(cache.ped, props[i][1]) end TaskPlayAnim(cache.ped, animationsOff.dict, animationsOff.anim, 3.0, 3.0, animationsOff.duration, animationsOff.move, 0, false, false, false) end client.removeClothes = removeClothes local playerHeading function client.getHeading() return playerHeading end local callback function client.startPlayerCustomization(cb, conf) playerAppearance = client.getPedAppearance(cache.ped) playerCoords = GetEntityCoords(cache.ped, true) playerHeading = GetEntityHeading(cache.ped) BackupPlayerStats() callback = cb config = conf reverseCamera = false isCameraInterpolating = false setCamera("default") SetNuiFocus(true, true) SetNuiFocusKeepInput(false) RenderScriptCams(true, false, 0, true, true) SetEntityInvincible(cache.ped, Config.InvincibleDuringCustomization) TaskStandStill(cache.ped, -1) if Config.HideRadar then DisplayRadar(false) end SendNuiMessage(json.encode({ type = "appearance_display", payload = { asynchronous = Config.AsynchronousLoading } })) end function client.exitPlayerCustomization(appearance) RenderScriptCams(false, false, 0, true, true) DestroyCam(cameraHandle, false) SetNuiFocus(false, false) if Config.HideRadar then DisplayRadar(true) end ClearPedTasksImmediately(cache.ped) SetEntityInvincible(cache.ped, false) SendNuiMessage(json.encode({ type = "appearance_hide", payload = {} })) if not appearance then client.setPlayerAppearance(getAppearance()) else client.setPedTattoos(cache.ped, appearance.tattoos) end RestorePlayerStats() if callback then callback(appearance) end callback = nil config = nil playerAppearance = nil playerCoords = nil cameraHandle = nil currentCamera = nil reverseCamera = nil isCameraInterpolating = nil end AddEventHandler("onResourceStop", function(resource) if resource == GetCurrentResourceName() then SetNuiFocus(false, false) SetNuiFocusKeepInput(false) end end) exports("startPlayerCustomization", client.startPlayerCustomization)