439 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local hashesComputed = false
 | 
						|
local PED_TATTOOS = {}
 | 
						|
local pedModelsByHash = {}
 | 
						|
 | 
						|
local function tofloat(num)
 | 
						|
    return num + 0.0
 | 
						|
end
 | 
						|
 | 
						|
local function isPedFreemodeModel(ped)
 | 
						|
    local model = GetEntityModel(ped)
 | 
						|
    return model == `mp_m_freemode_01` or model == `mp_f_freemode_01`
 | 
						|
end
 | 
						|
 | 
						|
local function computePedModelsByHash()
 | 
						|
    for i = 1, #Config.Peds.pedConfig do
 | 
						|
        local peds = Config.Peds.pedConfig[i].peds
 | 
						|
        for j = 1, #peds do
 | 
						|
            pedModelsByHash[joaat(peds[j])] = peds[j]
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
---@param ped number entity id
 | 
						|
---@return string
 | 
						|
--- Get the model name from an entity's model hash
 | 
						|
local function getPedModel(ped)
 | 
						|
    if not hashesComputed then
 | 
						|
        computePedModelsByHash()
 | 
						|
        hashesComputed = true
 | 
						|
    end
 | 
						|
    return pedModelsByHash[GetEntityModel(ped)]
 | 
						|
end
 | 
						|
 | 
						|
---@param ped number entity id
 | 
						|
---@return table<number, table<string, number>>
 | 
						|
local function getPedComponents(ped)
 | 
						|
    local size = #constants.PED_COMPONENTS_IDS
 | 
						|
    local components = table.create(size, 0)
 | 
						|
 | 
						|
    for i = 1, size do
 | 
						|
        local componentId = constants.PED_COMPONENTS_IDS[i]
 | 
						|
        components[i] = {
 | 
						|
            component_id = componentId,
 | 
						|
            drawable = GetPedDrawableVariation(ped, componentId),
 | 
						|
            texture = GetPedTextureVariation(ped, componentId),
 | 
						|
        }
 | 
						|
    end
 | 
						|
 | 
						|
    return components
 | 
						|
end
 | 
						|
 | 
						|
---@param ped number entity id
 | 
						|
---@return table<number, table<string, number>>
 | 
						|
local function getPedProps(ped)
 | 
						|
    local size = #constants.PED_PROPS_IDS
 | 
						|
    local props = table.create(size, 0)
 | 
						|
 | 
						|
    for i = 1, size do
 | 
						|
        local propId = constants.PED_PROPS_IDS[i]
 | 
						|
        props[i] = {
 | 
						|
            prop_id = propId,
 | 
						|
            drawable = GetPedPropIndex(ped, propId),
 | 
						|
            texture = GetPedPropTextureIndex(ped, propId),
 | 
						|
        }
 | 
						|
    end
 | 
						|
    return props
 | 
						|
end
 | 
						|
 | 
						|
local function round(number, decimalPlaces)
 | 
						|
    return tonumber(string.format("%." .. (decimalPlaces or 0) .. "f", number))
 | 
						|
end
 | 
						|
 | 
						|
---@param ped number entity id
 | 
						|
---@return table <number, number>
 | 
						|
---```
 | 
						|
---{ shapeFirst, shapeSecond, shapeThird, skinFirst, skinSecond, skinThird, shapeMix, skinMix, thirdMix }
 | 
						|
---```
 | 
						|
local function getPedHeadBlend(ped)
 | 
						|
    -- GET_PED_HEAD_BLEND_DATA
 | 
						|
    local shapeFirst, shapeSecond, shapeThird, skinFirst, skinSecond, skinThird, shapeMix, skinMix, thirdMix = Citizen.InvokeNative(0x2746BD9D88C5C5D0, ped, Citizen.PointerValueIntInitialized(0), Citizen.PointerValueIntInitialized(0), Citizen.PointerValueIntInitialized(0), Citizen.PointerValueIntInitialized(0), Citizen.PointerValueIntInitialized(0), Citizen.PointerValueIntInitialized(0), Citizen.PointerValueFloatInitialized(0), Citizen.PointerValueFloatInitialized(0), Citizen.PointerValueFloatInitialized(0))
 | 
						|
 | 
						|
    shapeMix = tonumber(string.sub(shapeMix, 0, 4))
 | 
						|
    if shapeMix > 1 then shapeMix = 1 end
 | 
						|
 | 
						|
    skinMix = tonumber(string.sub(skinMix, 0, 4))
 | 
						|
    if skinMix > 1 then skinMix = 1 end
 | 
						|
 | 
						|
    if not thirdMix then
 | 
						|
        thirdMix = 0
 | 
						|
    end
 | 
						|
    thirdMix = tonumber(string.sub(thirdMix, 0, 4))
 | 
						|
    if thirdMix > 1 then thirdMix = 1 end
 | 
						|
 | 
						|
 | 
						|
    return {
 | 
						|
        shapeFirst = shapeFirst,
 | 
						|
        shapeSecond = shapeSecond,
 | 
						|
        shapeThird = shapeThird,
 | 
						|
        skinFirst = skinFirst,
 | 
						|
        skinSecond = skinSecond,
 | 
						|
        skinThird = skinThird,
 | 
						|
        shapeMix = shapeMix,
 | 
						|
        skinMix = skinMix,
 | 
						|
        thirdMix = thirdMix
 | 
						|
    }
 | 
						|
end
 | 
						|
 | 
						|
---@param ped number entity id
 | 
						|
---@return table<number, table<string, number>>
 | 
						|
local function getPedFaceFeatures(ped)
 | 
						|
    local size = #constants.FACE_FEATURES
 | 
						|
    local faceFeatures = table.create(0, size)
 | 
						|
 | 
						|
    for i = 1, size do
 | 
						|
        local feature = constants.FACE_FEATURES[i]
 | 
						|
        faceFeatures[feature] = round(GetPedFaceFeature(ped, i-1), 1)
 | 
						|
    end
 | 
						|
 | 
						|
    return faceFeatures
 | 
						|
end
 | 
						|
 | 
						|
---@param ped number entity id
 | 
						|
---@return table<number, table<string, number>>
 | 
						|
local function getPedHeadOverlays(ped)
 | 
						|
    local size = #constants.HEAD_OVERLAYS
 | 
						|
    local headOverlays = table.create(0, size)
 | 
						|
 | 
						|
    for i = 1, size do
 | 
						|
        local overlay = constants.HEAD_OVERLAYS[i]
 | 
						|
        local _, value, _, firstColor, secondColor, opacity = GetPedHeadOverlayData(ped, i-1)
 | 
						|
 | 
						|
        if value ~= 255 then
 | 
						|
            opacity = round(opacity, 1)
 | 
						|
        else
 | 
						|
            value = 0
 | 
						|
            opacity = 0
 | 
						|
        end
 | 
						|
 | 
						|
        headOverlays[overlay] = {style = value, opacity = opacity, color = firstColor, secondColor = secondColor}
 | 
						|
    end
 | 
						|
 | 
						|
    return headOverlays
 | 
						|
end
 | 
						|
 | 
						|
---@param ped number entity id
 | 
						|
---@return table<string, number>
 | 
						|
local function getPedHair(ped)
 | 
						|
    return {
 | 
						|
        style = GetPedDrawableVariation(ped, 2),
 | 
						|
        color = GetPedHairColor(ped),
 | 
						|
        highlight = GetPedHairHighlightColor(ped),
 | 
						|
        texture = GetPedTextureVariation(ped, 2)
 | 
						|
    }
 | 
						|
end
 | 
						|
 | 
						|
local function getPedDecorationType()
 | 
						|
    local pedModel = GetEntityModel(cache.ped)
 | 
						|
    local decorationType
 | 
						|
 | 
						|
    if pedModel == `mp_m_freemode_01` then
 | 
						|
        decorationType = "male"
 | 
						|
    elseif pedModel == `mp_f_freemode_01` then
 | 
						|
        decorationType = "female"
 | 
						|
    else
 | 
						|
        decorationType = IsPedMale(cache.ped) and "male" or "female"
 | 
						|
    end
 | 
						|
 | 
						|
    return decorationType
 | 
						|
end
 | 
						|
 | 
						|
local function getPedAppearance(ped)
 | 
						|
    local eyeColor = GetPedEyeColor(ped)
 | 
						|
 | 
						|
    return {
 | 
						|
        model = getPedModel(ped) or "mp_m_freemode_01",
 | 
						|
        headBlend = getPedHeadBlend(ped),
 | 
						|
        faceFeatures = getPedFaceFeatures(ped),
 | 
						|
        headOverlays = getPedHeadOverlays(ped),
 | 
						|
        components = getPedComponents(ped),
 | 
						|
        props = getPedProps(ped),
 | 
						|
        hair = getPedHair(ped),
 | 
						|
        tattoos = client.getPedTattoos(),
 | 
						|
        eyeColor = eyeColor < #constants.EYE_COLORS and eyeColor or 0
 | 
						|
    }
 | 
						|
end
 | 
						|
 | 
						|
local function setPlayerModel(model)
 | 
						|
    if type(model) == "string" then model = joaat(model) end
 | 
						|
 | 
						|
    if IsModelInCdimage(model) then
 | 
						|
        RequestModel(model)
 | 
						|
        while not HasModelLoaded(model) do Wait(0) end
 | 
						|
 | 
						|
        SetPlayerModel(cache.playerId, model)
 | 
						|
        Wait(150)
 | 
						|
        SetModelAsNoLongerNeeded(model)
 | 
						|
 | 
						|
        if isPedFreemodeModel(cache.ped) then
 | 
						|
            SetPedDefaultComponentVariation(cache.ped)
 | 
						|
             -- Check if the model is male or female, then change the face mix based on this.
 | 
						|
             if model == `mp_m_freemode_01` then
 | 
						|
                SetPedHeadBlendData(cache.ped, 0, 0, 0, 0, 0, 0, 0, 0, 0, false)
 | 
						|
            elseif model == `mp_f_freemode_01` then
 | 
						|
                SetPedHeadBlendData(cache.ped, 45, 21, 0, 20, 15, 0, 0.3, 0.1, 0, false)
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        PED_TATTOOS = {}
 | 
						|
        return cache.ped
 | 
						|
    end
 | 
						|
 | 
						|
    return cache.playerId
 | 
						|
end
 | 
						|
 | 
						|
local function setPedHeadBlend(ped, headBlend)
 | 
						|
    if headBlend and isPedFreemodeModel(ped) then
 | 
						|
        SetPedHeadBlendData(ped, headBlend.shapeFirst, headBlend.shapeSecond, headBlend.shapeThird, headBlend.skinFirst, headBlend.skinSecond, headBlend.skinThird, tofloat(headBlend.shapeMix or 0), tofloat(headBlend.skinMix or 0), tofloat(headBlend.thirdMix or 0), false)
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedFaceFeatures(ped, faceFeatures)
 | 
						|
    if faceFeatures then
 | 
						|
        for k, v in pairs(constants.FACE_FEATURES) do
 | 
						|
            SetPedFaceFeature(ped, k-1, tofloat(faceFeatures[v]))
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedHeadOverlays(ped, headOverlays)
 | 
						|
    if headOverlays then
 | 
						|
        for k, v in pairs(constants.HEAD_OVERLAYS) do
 | 
						|
            local headOverlay = headOverlays[v]
 | 
						|
            SetPedHeadOverlay(ped, k-1, headOverlay.style, tofloat(headOverlay.opacity))
 | 
						|
 | 
						|
            if headOverlay.color then
 | 
						|
                local colorType = 1
 | 
						|
                if v == "blush" or v == "lipstick" or v == "makeUp" then
 | 
						|
                    colorType = 2
 | 
						|
                end
 | 
						|
 | 
						|
                SetPedHeadOverlayColor(ped, k-1, colorType, headOverlay.color, headOverlay.secondColor)
 | 
						|
            end
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function applyAutomaticFade(ped, style)
 | 
						|
    local gender = getPedDecorationType()
 | 
						|
    local hairDecoration = constants.HAIR_DECORATIONS[gender][style]
 | 
						|
 | 
						|
    if(hairDecoration) then
 | 
						|
        AddPedDecorationFromHashes(ped, hairDecoration[1], hairDecoration[2])
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setTattoos(ped, tattoos, style)
 | 
						|
    local isMale = client.getPedDecorationType() == "male"
 | 
						|
    ClearPedDecorations(ped)
 | 
						|
    if Config.AutomaticFade then
 | 
						|
        tattoos["ZONE_HAIR"] = {}
 | 
						|
        PED_TATTOOS["ZONE_HAIR"] = {}
 | 
						|
        applyAutomaticFade(ped, style or GetPedDrawableVariation(ped, 2))
 | 
						|
    end
 | 
						|
    for k in pairs(tattoos) do
 | 
						|
        for i = 1, #tattoos[k] do
 | 
						|
            local tattoo = tattoos[k][i]
 | 
						|
            local tattooGender = isMale and tattoo.hashMale or tattoo.hashFemale
 | 
						|
            for _ = 1, (tattoo.opacity or 0.1) * 10 do
 | 
						|
                AddPedDecorationFromHashes(ped, joaat(tattoo.collection), joaat(tattooGender))
 | 
						|
            end
 | 
						|
        end
 | 
						|
    end
 | 
						|
    if Config.RCoreTattoosCompatibility then
 | 
						|
        TriggerEvent("rcore_tattoos:applyOwnedTattoos")
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedHair(ped, hair, tattoos)
 | 
						|
    if hair then
 | 
						|
        SetPedComponentVariation(ped, 2, hair.style, hair.texture, 0)
 | 
						|
        SetPedHairColor(ped, hair.color, hair.highlight)
 | 
						|
        if isPedFreemodeModel(ped) then
 | 
						|
            setTattoos(ped, tattoos or PED_TATTOOS, hair.style)
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedEyeColor(ped, eyeColor)
 | 
						|
    if eyeColor then
 | 
						|
        SetPedEyeColor(ped, eyeColor)
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedComponent(ped, component)
 | 
						|
    if component then
 | 
						|
        if isPedFreemodeModel(ped) and (component.component_id == 0 or component.component_id == 2) then
 | 
						|
            return
 | 
						|
        end
 | 
						|
 | 
						|
        SetPedComponentVariation(ped, component.component_id, component.drawable, component.texture, 0)
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedComponents(ped, components)
 | 
						|
    if components then
 | 
						|
        for _, v in pairs(components) do
 | 
						|
            setPedComponent(ped, v)
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedProp(ped, prop)
 | 
						|
    if prop then
 | 
						|
        if prop.drawable == -1 then
 | 
						|
            ClearPedProp(ped, prop.prop_id)
 | 
						|
        else
 | 
						|
            SetPedPropIndex(ped, prop.prop_id, prop.drawable, prop.texture, false)
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedProps(ped, props)
 | 
						|
    if props then
 | 
						|
        for _, v in pairs(props) do
 | 
						|
            setPedProp(ped, v)
 | 
						|
        end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedTattoos(ped, tattoos)
 | 
						|
    PED_TATTOOS = tattoos
 | 
						|
    setTattoos(ped, tattoos)
 | 
						|
end
 | 
						|
 | 
						|
local function getPedTattoos()
 | 
						|
    return PED_TATTOOS
 | 
						|
end
 | 
						|
 | 
						|
local function addPedTattoo(ped, tattoos)
 | 
						|
    setTattoos(ped, tattoos)
 | 
						|
end
 | 
						|
 | 
						|
local function removePedTattoo(ped, tattoos)
 | 
						|
    setTattoos(ped, tattoos)
 | 
						|
end
 | 
						|
 | 
						|
local function setPreviewTattoo(ped, tattoos, tattoo)
 | 
						|
    local isMale = client.getPedDecorationType() == "male"
 | 
						|
    local tattooGender = isMale and tattoo.hashMale or tattoo.hashFemale
 | 
						|
 | 
						|
    ClearPedDecorations(ped)
 | 
						|
    for _ = 1, (tattoo.opacity or 0.1) * 10 do
 | 
						|
        AddPedDecorationFromHashes(ped, joaat(tattoo.collection), tattooGender)
 | 
						|
    end
 | 
						|
    for k in pairs(tattoos) do
 | 
						|
        for i = 1, #tattoos[k] do
 | 
						|
            local aTattoo = tattoos[k][i]
 | 
						|
            if aTattoo.name ~= tattoo.name then
 | 
						|
                local aTattooGender = isMale and aTattoo.hashMale or aTattoo.hashFemale
 | 
						|
                for _ = 1, (aTattoo.opacity or 0.1) * 10 do
 | 
						|
                    AddPedDecorationFromHashes(ped, joaat(aTattoo.collection), joaat(aTattooGender))
 | 
						|
                end
 | 
						|
            end
 | 
						|
        end
 | 
						|
    end
 | 
						|
    if Config.AutomaticFade then
 | 
						|
        applyAutomaticFade(ped, GetPedDrawableVariation(ped, 2))
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPedAppearance(ped, appearance)
 | 
						|
    if appearance then
 | 
						|
        setPedComponents(ped, appearance.components)
 | 
						|
        setPedProps(ped, appearance.props)
 | 
						|
 | 
						|
        if appearance.headBlend and isPedFreemodeModel(ped) then setPedHeadBlend(ped, appearance.headBlend) end
 | 
						|
        if appearance.faceFeatures then setPedFaceFeatures(ped, appearance.faceFeatures) end
 | 
						|
        if appearance.headOverlays then setPedHeadOverlays(ped, appearance.headOverlays) end
 | 
						|
        if appearance.hair then setPedHair(ped, appearance.hair, appearance.tattoos) end
 | 
						|
        if appearance.eyeColor then setPedEyeColor(ped, appearance.eyeColor) end
 | 
						|
        if appearance.tattoos then setPedTattoos(ped, appearance.tattoos) end
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
local function setPlayerAppearance(appearance)
 | 
						|
    if appearance then
 | 
						|
        setPlayerModel(appearance.model)
 | 
						|
        setPedAppearance(cache.ped, appearance)
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
exports("getPedModel", getPedModel)
 | 
						|
exports("getPedComponents", getPedComponents)
 | 
						|
exports("getPedProps", getPedProps)
 | 
						|
exports("getPedHeadBlend", getPedHeadBlend)
 | 
						|
exports("getPedFaceFeatures", getPedFaceFeatures)
 | 
						|
exports("getPedHeadOverlays", getPedHeadOverlays)
 | 
						|
exports("getPedHair", getPedHair)
 | 
						|
exports("getPedAppearance", getPedAppearance)
 | 
						|
 | 
						|
exports("setPlayerModel", setPlayerModel)
 | 
						|
exports("setPedHeadBlend", setPedHeadBlend)
 | 
						|
exports("setPedFaceFeatures", setPedFaceFeatures)
 | 
						|
exports("setPedHeadOverlays", setPedHeadOverlays)
 | 
						|
exports("setPedHair", setPedHair)
 | 
						|
exports("setPedEyeColor", setPedEyeColor)
 | 
						|
exports("setPedComponent", setPedComponent)
 | 
						|
exports("setPedComponents", setPedComponents)
 | 
						|
exports("setPedProp", setPedProp)
 | 
						|
exports("setPedProps", setPedProps)
 | 
						|
exports("setPlayerAppearance", setPlayerAppearance)
 | 
						|
exports("setPedAppearance", setPedAppearance)
 | 
						|
exports("setPedTattoos", setPedTattoos)
 | 
						|
 | 
						|
client = {
 | 
						|
    getPedAppearance = getPedAppearance,
 | 
						|
    setPlayerModel = setPlayerModel,
 | 
						|
    setPedHeadBlend = setPedHeadBlend,
 | 
						|
    setPedFaceFeatures = setPedFaceFeatures,
 | 
						|
    setPedHair = setPedHair,
 | 
						|
    setPedHeadOverlays = setPedHeadOverlays,
 | 
						|
    setPedEyeColor = setPedEyeColor,
 | 
						|
    setPedComponent = setPedComponent,
 | 
						|
    setPedProp = setPedProp,
 | 
						|
    setPlayerAppearance = setPlayerAppearance,
 | 
						|
    setPedAppearance = setPedAppearance,
 | 
						|
    getPedDecorationType = getPedDecorationType,
 | 
						|
    isPedFreemodeModel = isPedFreemodeModel,
 | 
						|
    setPreviewTattoo = setPreviewTattoo,
 | 
						|
    setPedTattoos = setPedTattoos,
 | 
						|
    getPedTattoos = getPedTattoos,
 | 
						|
    addPedTattoo = addPedTattoo,
 | 
						|
    removePedTattoo = removePedTattoo,
 | 
						|
    getPedModel = getPedModel,
 | 
						|
    setPedComponents = setPedComponents,
 | 
						|
    setPedProps = setPedProps,
 | 
						|
    getPedComponents = getPedComponents,
 | 
						|
    getPedProps = getPedProps
 | 
						|
}
 |