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
 | |
| }
 | 
