Will you share this
can you make this for hats too?
This guide is to specifically integrate the mask clipping script with dpclothing and illenium-appearance.
I moved the client.lua file from this mask clip script into my dpclothing Client folder, renamed as maskClip.lua
and added to dpclothing fxmanifest.
Then changed line 54 from local function loop()
to RegisterNetEvent('dpc:maskClip', function()
Then comment out/delete lines 107 through 112 to remove the for loop
thread.
In Clothing.lua
of dpclothing, replace ToggleClothing
with this:
function ToggleClothing(which, extra)
if Cooldown then return end
local Toggle = Drawables[which] if extra then Toggle = Extras[which] end
local Ped = PlayerPedId()
local Cur = { -- Lets check what we are currently wearing.
Drawable = GetPedDrawableVariation(Ped, Toggle.Drawable),
Id = Toggle.Drawable,
Ped = Ped,
Texture = GetPedTextureVariation(Ped, Toggle.Drawable),
}
local Gender = IsMpPed(Ped)
if which ~= "Mask" then
if not Gender then Notify(Lang("NotAllowedPed")) return false end -- We cancel the command here if the person is not using a multiplayer model.
end
local Table = Toggle.Table[Gender]
if not Toggle.Table.Standalone then -- "Standalone" is for things that dont require a variant, like the shoes just need to be switched to a specific drawable. Looking back at this i should have planned ahead, but it all works so, meh!
for k,v in pairs(Table) do
if not Toggle.Remember then
if k == Cur.Drawable then
PlayToggleEmote(Toggle.Emote, function() SetPedComponentVariation(Ped, Toggle.Drawable, v, Cur.Texture, 0) end) return true
end
else
if not LastEquipped[which] then
if k == Cur.Drawable then
PlayToggleEmote(Toggle.Emote, function() LastEquipped[which] = Cur SetPedComponentVariation(Ped, Toggle.Drawable, v, Cur.Texture, 0) end) return true
end
else
local Last = LastEquipped[which]
PlayToggleEmote(Toggle.Emote, function() SetPedComponentVariation(Ped, Toggle.Drawable, Last.Drawable, Last.Texture, 0) LastEquipped[which] = false end) return true
end
end
end
Notify(Lang("NoVariants")) return
else
if not LastEquipped[which] then
if Cur.Drawable ~= Table then
PlayToggleEmote(Toggle.Emote, function()
LastEquipped[which] = Cur
SetPedComponentVariation(Ped, Toggle.Drawable, Table, 0, 0)
if Toggle.Table.Extra then
local Extras = Toggle.Table.Extra
for k,v in pairs(Extras) do
local ExtraCur = {Drawable = GetPedDrawableVariation(Ped, v.Drawable), Texture = GetPedTextureVariation(Ped, v.Drawable), Id = v.Drawable}
SetPedComponentVariation(Ped, v.Drawable, v.Id, v.Tex, 0)
LastEquipped[v.Name] = ExtraCur
end
end
end)
if which == "Mask" then
TriggerEvent("dpc:maskClip")
end
return true
end
else
local Last = LastEquipped[which]
PlayToggleEmote(Toggle.Emote, function()
SetPedComponentVariation(Ped, Toggle.Drawable, Last.Drawable, Last.Texture, 0)
LastEquipped[which] = false
if Toggle.Table.Extra then
local Extras = Toggle.Table.Extra
for k,v in pairs(Extras) do
if LastEquipped[v.Name] then
local Last = LastEquipped[v.Name]
SetPedComponentVariation(Ped, Last.Id, Last.Drawable, Last.Texture, 0)
LastEquipped[v.Name] = false
end
end
end
end)
if which == "Mask" then
TriggerEvent("dpc:maskClip")
end
return true
end
end
Notify(Lang("AlreadyWearing")) return false
end
Now for the illenium edits
Replace setPlayerAppearance(appearance)
in game/util.lua with
local function setPlayerAppearance(appearance)
if appearance then
setPlayerModel(appearance.model)
setPedAppearance(cache.ped, appearance)
end
TriggerEvent("dpc:maskClip")
end
TriggerEvent("dpc:maskClip")
added to client/client.lua at following spots:
last line of InitAppearance()
function
TriggerEvent("dpc:maskClip")
end
last line before end, config)
in InitializeCharacter(gender, onSubmit, onCancel)
function
TriggerEvent("dpc:maskClip")
end, config)
end
last line before end, config)
in OpenShop(config, isPedMenu, shopType)
function
TriggerEvent("dpc:maskClip")
end, config)
end, shopType)
end
in illenium-appearance:client:changeOutfit
replace bottom few lines with this
TriggerEvent("dpc:maskClip")
end
Framework.CachePed()
end
end)
and finally replace bottom of illenium-appearance:client:reloadSkin
with this
TriggerEvent("dpc:maskClip")
end)
end)
Shared above
illenium-appearance
Client.lua
local client = client
local reloadSkinTimer = GetGameTimer()
local function LoadPlayerUniform()
lib.callback("illenium-appearance:server:getUniform", false, function(uniformData)
if not uniformData then
return
end
if Config.BossManagedOutfits then
local result = lib.callback.await("illenium-appearance:server:getManagementOutfits", false, uniformData.type, Framework.GetGender())
local uniform = nil
for i = 1, #result, 1 do
if result[i].name == uniformData.name then
uniform = {
type = uniformData.type,
name = result[i].name,
model = result[i].model,
components = result[i].components,
props = result[i].props,
disableSave = true,
}
break
end
end
if not uniform then
TriggerServerEvent("illenium-appearance:server:syncUniform", nil) -- Uniform doesn't exist anymore
return
end
TriggerEvent("illenium-appearance:client:changeOutfit", uniform)
else
local outfits = Config.Outfits[uniformData.jobName][uniformData.gender]
local uniform = nil
for i = 1, #outfits, 1 do
if outfits[i].name == uniformData.label then
uniform = outfits[i]
break
end
end
if not uniform then
TriggerServerEvent("illenium-appearance:server:syncUniform", nil) -- Uniform doesn't exist anymore
return
end
uniform.jobName = uniformData.jobName
uniform.gender = uniformData.gender
TriggerEvent("illenium-appearance:client:loadJobOutfit", uniform)
end
end)
end
function InitAppearance()
Framework.UpdatePlayerData()
lib.callback("illenium-appearance:server:getAppearance", false, function(appearance)
if not appearance then
return
end
client.setPlayerAppearance(appearance)
if Config.PersistUniforms then
LoadPlayerUniform()
end
end)
ResetBlips()
if Config.BossManagedOutfits then
Management.AddItems()
end
RestorePlayerStats()
end
AddEventHandler("onResourceStart", function(resource)
if resource == GetCurrentResourceName() then
InitAppearance()
TriggerEvent("dpc:maskClip")
end
end)
local function getNewCharacterConfig()
local config = GetDefaultConfig()
config.enableExit = false
config.ped = Config.NewCharacterSections.Ped
config.headBlend = Config.NewCharacterSections.HeadBlend
config.faceFeatures = Config.NewCharacterSections.FaceFeatures
config.headOverlays = Config.NewCharacterSections.HeadOverlays
config.components = Config.NewCharacterSections.Components
config.props = Config.NewCharacterSections.Props
config.tattoos = not Config.vms_tattooshop and Config.NewCharacterSections.Tattoos
return config
end
function SetInitialClothes(initial)
client.setPlayerModel(initial.Model)
-- Fix for tattoo's appearing when creating a new character
local ped = cache.ped
client.setPedTattoos(ped, {})
client.setPedComponents(ped, initial.Components)
client.setPedProps(ped, initial.Props)
client.setPedHair(ped, initial.Hair, {})
ClearPedDecorations(ped)
end
function InitializeCharacter(gender, onSubmit, onCancel)
SetInitialClothes(Config.InitialPlayerClothes[gender])
local config = getNewCharacterConfig()
TriggerServerEvent("illenium-appearance:server:ChangeRoutingBucket")
client.startPlayerCustomization(function(appearance)
if (appearance) then
TriggerServerEvent("illenium-appearance:server:saveAppearance", appearance)
if onSubmit then
onSubmit()
end
elseif onCancel then
onCancel()
end
Framework.CachePed()
TriggerServerEvent("illenium-appearance:server:ResetRoutingBucket")
TriggerEvent("dpc:maskClip")
end, config)
end
function OpenShop(config, isPedMenu, shopType)
lib.callback("illenium-appearance:server:hasMoney", false, function(hasMoney, money)
if not hasMoney and not isPedMenu then
lib.notify({
title = "Cannot Enter Shop",
description = "Not enough cash. Need $" .. money,
type = "error",
position = Config.NotifyOptions.position
})
return
end
client.startPlayerCustomization(function(appearance)
if appearance then
if not isPedMenu then
TriggerServerEvent("illenium-appearance:server:chargeCustomer", shopType)
end
TriggerServerEvent("illenium-appearance:server:saveAppearance", appearance)
else
lib.notify({
title = _L("cancelled.title"),
description = _L("cancelled.description"),
type = "inform",
position = Config.NotifyOptions.position
})
end
Framework.CachePed()
TriggerEvent("dpc:maskClip")
end, config)
end, shopType)
end
local function OpenClothingShop(isPedMenu)
local config = GetDefaultConfig()
config.components = true
config.props = true
if isPedMenu then
config.ped = true
config.headBlend = true
config.faceFeatures = true
config.headOverlays = true
config.tattoos = not Config.vms_tattooshop and true
end
OpenShop(config, isPedMenu, "clothing")
end
RegisterNetEvent("illenium-appearance:client:openClothingShop", OpenClothingShop)
RegisterNetEvent("illenium-appearance:client:importOutfitCode", function()
local response = lib.inputDialog(_L("outfits.import.title"), {
{
type = "input",
label = _L("outfits.import.name.label"),
placeholder = _L("outfits.import.name.placeholder"),
default = _L("outfits.import.name.default"),
required = true
},
{
type = "input",
label = _L("outfits.import.code.label"),
placeholder = "XXXXXXXXXXXX",
required = true
}
})
if not response then
return
end
local outfitName = response[1]
local outfitCode = response[2]
if outfitCode ~= nil then
Wait(500)
lib.callback("illenium-appearance:server:importOutfitCode", false, function(success)
if success then
lib.notify({
title = _L("outfits.import.success.title"),
description = _L("outfits.import.success.description"),
type = "success",
position = Config.NotifyOptions.position
})
else
lib.notify({
title = _L("outfits.import.failure.title"),
description = _L("outfits.import.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
end
end, outfitName, outfitCode)
end
end)
RegisterNetEvent("illenium-appearance:client:generateOutfitCode", function(id)
lib.callback("illenium-appearance:server:generateOutfitCode", false, function(code)
if not code then
lib.notify({
title = _L("outfits.generate.failure.title"),
description = _L("outfits.generate.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
return
end
lib.setClipboard(code)
lib.inputDialog(_L("outfits.generate.success.title"), {
{
type = "input",
label = _L("outfits.generate.success.description"),
default = code,
disabled = true
}
})
end, id)
end)
RegisterNetEvent("illenium-appearance:client:saveOutfit", function()
local response = lib.inputDialog(_L("outfits.save.title"), {
{
type = "input",
label = _L("outfits.save.name.label"),
placeholder = _L("outfits.save.name.placeholder"),
required = true
}
})
if not response then
return
end
local outfitName = response[1]
if outfitName then
Wait(500)
lib.callback("illenium-appearance:server:getOutfits", false, function(outfits)
local outfitExists = false
for i = 1, #outfits, 1 do
if outfits[i].name:lower() == outfitName:lower() then
outfitExists = true
break
end
end
if outfitExists then
lib.notify({
title = _L("outfits.save.failure.title"),
description = _L("outfits.save.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
return
end
local pedModel = client.getPedModel(cache.ped)
local pedComponents = client.getPedComponents(cache.ped)
local pedProps = client.getPedProps(cache.ped)
TriggerServerEvent("illenium-appearance:server:saveOutfit", outfitName, pedModel, pedComponents, pedProps)
end)
end
end)
RegisterNetEvent('illenium-appearance:client:updateOutfit', function(outfitID)
if not outfitID then return end
lib.callback("illenium-appearance:server:getOutfits", false, function(outfits)
local outfitExists = false
for i = 1, #outfits, 1 do
if outfits[i].id == outfitID then
outfitExists = true
break
end
end
if not outfitExists then
lib.notify({
title = _L("outfits.update.failure.title"),
description = _L("outfits.update.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
return
end
local pedModel = client.getPedModel(cache.ped)
local pedComponents = client.getPedComponents(cache.ped)
local pedProps = client.getPedProps(cache.ped)
TriggerServerEvent("illenium-appearance:server:updateOutfit", outfitID, pedModel, pedComponents, pedProps)
end)
end)
local function RegisterChangeOutfitMenu(id, parent, outfits, mType)
local changeOutfitMenu = {
id = id,
title = _L("outfits.change.title"),
menu = parent,
options = {}
}
for i = 1, #outfits, 1 do
changeOutfitMenu.options[#changeOutfitMenu.options + 1] = {
title = outfits[i].name,
description = outfits[i].model,
event = "illenium-appearance:client:changeOutfit",
args = {
type = mType,
name = outfits[i].name,
model = outfits[i].model,
components = outfits[i].components,
props = outfits[i].props,
disableSave = mType and true or false
}
}
end
table.sort(changeOutfitMenu.options, function(a, b)
return a.title < b.title
end)
lib.registerContext(changeOutfitMenu)
end
local function RegisterUpdateOutfitMenu(id, parent, outfits)
local updateOutfitMenu = {
id = id,
title = _L("outfits.update.title"),
menu = parent,
options = {}
}
for i = 1, #outfits, 1 do
updateOutfitMenu.options[#updateOutfitMenu.options + 1] = {
title = outfits[i].name,
description = outfits[i].model,
event = "illenium-appearance:client:updateOutfit",
args = outfits[i].id
}
end
table.sort(updateOutfitMenu.options, function(a, b)
return a.title < b.title
end)
lib.registerContext(updateOutfitMenu)
end
local function RegisterGenerateOutfitCodeMenu(id, parent, outfits)
local generateOutfitCodeMenu = {
id = id,
title = _L("outfits.generate.title"),
menu = parent,
options = {}
}
for i = 1, #outfits, 1 do
generateOutfitCodeMenu.options[#generateOutfitCodeMenu.options + 1] = {
title = outfits[i].name,
description = outfits[i].model,
event = "illenium-appearance:client:generateOutfitCode",
args = outfits[i].id
}
end
lib.registerContext(generateOutfitCodeMenu)
end
local function RegisterDeleteOutfitMenu(id, parent, outfits, deleteEvent)
local deleteOutfitMenu = {
id = id,
title = _L("outfits.delete.title"),
menu = parent,
options = {}
}
table.sort(outfits, function(a, b)
return a.name < b.name
end)
for i = 1, #outfits, 1 do
deleteOutfitMenu.options[#deleteOutfitMenu.options + 1] = {
title = string.format(_L("outfits.delete.item.title"), outfits[i].name),
description = string.format(_L("outfits.delete.item.description"), outfits[i].model, (outfits[i].gender and (" - Gender: " .. outfits[i].gender) or "")),
event = deleteEvent,
args = outfits[i].id
}
end
lib.registerContext(deleteOutfitMenu)
end
RegisterNetEvent("illenium-appearance:client:OutfitManagementMenu", function(args)
local bossMenuEvent = "qb-bossmenu:client:OpenMenu"
if args.type == "Gang" then
bossMenuEvent = "qb-gangmenu:client:OpenMenu"
end
local outfits = lib.callback.await("illenium-appearance:server:getManagementOutfits", false, args.type, Framework.GetGender())
local managementMenuID = "illenium_appearance_outfit_management_menu"
local changeManagementOutfitMenuID = "illenium_appearance_change_management_outfit_menu"
local deleteManagementOutfitMenuID = "illenium_appearance_delete_management_outfit_menu"
RegisterChangeOutfitMenu(changeManagementOutfitMenuID, managementMenuID, outfits, args.type)
RegisterDeleteOutfitMenu(deleteManagementOutfitMenuID, managementMenuID, outfits, "illenium-appearance:client:DeleteManagementOutfit")
local managementMenu = {
id = managementMenuID,
title = string.format(_L("outfits.manage.title"), args.type),
options = {
{
title = _L("outfits.change.title"),
description = string.format(_L("outfits.change.description"), args.type),
menu = changeManagementOutfitMenuID,
},
{
title = _L("outfits.save.menuTitle"),
description = string.format(_L("outfits.save.menuDescription"), args.type),
event = "illenium-appearance:client:SaveManagementOutfit",
args = args.type
},
{
title = _L("outfits.delete.title"),
description = string.format(_L("outfits.delete.description"), args.type),
menu = deleteManagementOutfitMenuID,
},
{
title = _L("menu.returnTitle"),
icon = "fa-solid fa-angle-left",
event = bossMenuEvent
}
}
}
lib.registerContext(managementMenu)
lib.showContext(managementMenuID)
end)
RegisterNetEvent("illenium-appearance:client:SaveManagementOutfit", function(mType)
local outfitData = {
Type = mType,
Model = client.getPedModel(cache.ped),
Components = client.getPedComponents(cache.ped),
Props = client.getPedProps(cache.ped)
}
local rankValues
if mType == "Job" then
outfitData.JobName = client.job.name
rankValues = Framework.GetRankInputValues("job")
else
outfitData.JobName = client.gang.name
rankValues = Framework.GetRankInputValues("gang")
end
local dialogResponse = lib.inputDialog(_L("outfits.save.managementTitle"), {
{
label = _L("outfits.save.name.label"),
type = "input",
required = true
},
{
label = _L("outfits.save.gender.label"),
type = "select",
options = {
{
label = _L("outfits.save.gender.male"), value = "male"
},
{
label = _L("outfits.save.gender.female"), value = "female"
}
},
default = "male",
},
{
label = _L("outfits.save.rank.label"),
type = "select",
options = rankValues,
default = "0"
}
})
if not dialogResponse then
return
end
outfitData.Name = dialogResponse[1]
outfitData.Gender = dialogResponse[2]
outfitData.MinRank = tonumber(dialogResponse[3])
TriggerServerEvent("illenium-appearance:server:saveManagementOutfit", outfitData)
end)
local function RegisterWorkOutfitsListMenu(id, parent, menuData)
local menu = {
id = id,
menu = parent,
title = _L("jobOutfits.title"),
options = {}
}
local event = "illenium-appearance:client:loadJobOutfit"
if Config.BossManagedOutfits then
event = "illenium-appearance:client:changeOutfit"
end
if menuData then
for _, v in pairs(menuData) do
menu.options[#menu.options + 1] = {
title = v.name,
event = event,
args = v
}
end
end
lib.registerContext(menu)
end
function OpenMenu(isPedMenu, menuType, menuData)
local mainMenuID = "illenium_appearance_main_menu"
local mainMenu = {
id = mainMenuID
}
local menuItems = {}
local outfits = lib.callback.await("illenium-appearance:server:getOutfits", false)
local changeOutfitMenuID = "illenium_appearance_change_outfit_menu"
local updateOutfitMenuID = "illenium_appearance_update_outfit_menu"
local deleteOutfitMenuID = "illenium_appearance_delete_outfit_menu"
local generateOutfitCodeMenuID = "illenium_appearance_generate_outfit_code_menu"
RegisterChangeOutfitMenu(changeOutfitMenuID, mainMenuID, outfits)
RegisterUpdateOutfitMenu(updateOutfitMenuID, mainMenuID, outfits)
RegisterDeleteOutfitMenu(deleteOutfitMenuID, mainMenuID, outfits, "illenium-appearance:client:deleteOutfit")
RegisterGenerateOutfitCodeMenu(generateOutfitCodeMenuID, mainMenuID, outfits)
local outfitMenuItems = {
{
title = _L("outfits.change.title"),
description = _L("outfits.change.pDescription"),
menu = changeOutfitMenuID
},
{
title = _L("outfits.update.title"),
description = _L("outfits.update.description"),
menu = updateOutfitMenuID
},
{
title = _L("outfits.save.menuTitle"),
description = _L("outfits.save.description"),
event = "illenium-appearance:client:saveOutfit"
},
{
title = _L("outfits.generate.title"),
description = _L("outfits.generate.description"),
menu = generateOutfitCodeMenuID
},
{
title = _L("outfits.delete.title"),
description = _L("outfits.delete.mDescription"),
menu = deleteOutfitMenuID
},
{
title = _L("outfits.import.menuTitle"),
description = _L("outfits.import.description"),
event = "illenium-appearance:client:importOutfitCode"
}
}
if menuType == "default" then
local header = string.format(_L("clothing.title"), Config.ClothingCost)
if isPedMenu then
header = _L("clothing.titleNoPrice")
end
mainMenu.title = _L("clothing.options.title")
menuItems[#menuItems + 1] = {
title = header,
description = _L("clothing.options.description"),
event = "illenium-appearance:client:openClothingShop",
args = isPedMenu
}
for i = 0, #outfitMenuItems, 1 do
menuItems[#menuItems + 1] = outfitMenuItems[i]
end
elseif menuType == "outfit" then
mainMenu.title = _L("clothing.outfits.title")
for i = 0, #outfitMenuItems, 1 do
menuItems[#menuItems + 1] = outfitMenuItems[i]
end
elseif menuType == "job-outfit" then
mainMenu.title = _L("clothing.outfits.title")
menuItems[#menuItems + 1] = {
title = _L("clothing.outfits.civilian.title"),
description = _L("clothing.outfits.civilian.description"),
event = "illenium-appearance:client:reloadSkin",
args = true
}
local workOutfitsMenuID = "illenium_appearance_work_outfits_menu"
RegisterWorkOutfitsListMenu(workOutfitsMenuID, mainMenuID, menuData)
menuItems[#menuItems + 1] = {
title = _L("jobOutfits.title"),
description = _L("jobOutfits.description"),
menu = workOutfitsMenuID
}
end
mainMenu.options = menuItems
lib.registerContext(mainMenu)
lib.showContext(mainMenuID)
end
RegisterNetEvent("illenium-appearance:client:openClothingShopMenu", function(isPedMenu)
if type(isPedMenu) == "table" then
isPedMenu = false
end
OpenMenu(isPedMenu, "default")
end)
RegisterNetEvent("illenium-appearance:client:OpenBarberShop", OpenBarberShop)
RegisterNetEvent("illenium-appearance:client:OpenTattooShop", OpenTattooShop)
RegisterNetEvent("illenium-appearance:client:OpenSurgeonShop", OpenSurgeonShop)
RegisterNetEvent("illenium-appearance:client:changeOutfit", function(data)
local pedModel = client.getPedModel(cache.ped)
local appearanceDB
if pedModel ~= data.model then
local p = promise.new()
lib.callback("illenium-appearance:server:getAppearance", false, function(appearance)
BackupPlayerStats()
if appearance then
client.setPlayerAppearance(appearance)
RestorePlayerStats()
else
lib.notify({
title = _L("outfits.change.failure.title"),
description = _L("outfits.change.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
end
p:resolve(appearance)
end, data.model)
appearanceDB = Citizen.Await(p)
else
appearanceDB = client.getPedAppearance(cache.ped)
end
if appearanceDB then
client.setPedComponents(cache.ped, data.components)
client.setPedProps(cache.ped, data.props)
client.setPedHair(cache.ped, appearanceDB.hair, appearanceDB.tattoos)
if data.disableSave then
TriggerServerEvent("illenium-appearance:server:syncUniform", {
type = data.type,
name = data.name
}) -- Is a uniform
else
local appearance = client.getPedAppearance(cache.ped)
TriggerServerEvent("illenium-appearance:server:saveAppearance", appearance)
end
if Config.vms_tattooshop then
exports['vms_tattooshop']:reloadPlayerTattoos()
TriggerEvent("dpc:maskClip")
end
Framework.CachePed()
end
end)
RegisterNetEvent("illenium-appearance:client:DeleteManagementOutfit", function(id)
TriggerServerEvent("illenium-appearance:server:deleteManagementOutfit", id)
lib.notify({
title = _L("outfits.delete.management.success.title"),
description = _L("outfits.delete.management.success.description"),
type = "success",
position = Config.NotifyOptions.position
})
end)
RegisterNetEvent("illenium-appearance:client:deleteOutfit", function(id)
TriggerServerEvent("illenium-appearance:server:deleteOutfit", id)
lib.notify({
title = _L("outfits.delete.success.title"),
description = _L("outfits.delete.success.failure"),
type = "success",
position = Config.NotifyOptions.position
})
end)
RegisterNetEvent("illenium-appearance:client:openJobOutfitsMenu", function(outfitsToShow)
OpenMenu(nil, "job-outfit", outfitsToShow)
end)
local function InCooldown()
return (GetGameTimer() - reloadSkinTimer) < Config.ReloadSkinCooldown
end
RegisterNetEvent("illenium-appearance:client:reloadSkin", function(bypassChecks)
if not bypassChecks and InCooldown() or Framework.CheckPlayerMeta() or cache.vehicle or IsPedFalling(cache.ped) then
lib.notify({
title = _L("commands.reloadskin.failure.title"),
description = _L("commands.reloadskin.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
return
end
reloadSkinTimer = GetGameTimer()
BackupPlayerStats()
lib.callback("illenium-appearance:server:getAppearance", false, function(appearance)
if not appearance then
return
end
client.setPlayerAppearance(appearance)
if Config.PersistUniforms then
TriggerServerEvent("illenium-appearance:server:syncUniform", nil)
end
RestorePlayerStats()
TriggerEvent("dpc:maskClip")
end)
end)
RegisterNetEvent("illenium-appearance:client:ClearStuckProps", function()
if InCooldown() or Framework.CheckPlayerMeta() then
lib.notify({
title = _L("commands.clearstuckprops.failure.title"),
description = _L("commands.clearstuckprops.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
return
end
reloadSkinTimer = GetGameTimer()
for _, v in pairs(GetGamePool("CObject")) do
if IsEntityAttachedToEntity(cache.ped, v) then
SetEntityAsMissionEntity(v, true, true)
DeleteObject(v)
DeleteEntity(v)
end
end
end)
I installed the version as you said, but that doesn’t work with illenium-appearance if you make the client mask.lua and simply write it into fxmanifest.lua. Everything works perfectly on the client side
here is a picture with your version
and here my version
It is working perfectly fine on our server with illenium. The additions to illenium just force it to load/detect the mask on player load/reload, otherwise the mask clipping often worked backwards of what it was supposed to do. Did extensive testing yesterday with it all.
Just tested it with us but it doesn’t really matter, as long as it works that way
Added our edit to game/util.lua in illenium I missed during my write-up of the above post.
Very nice Script!
v1.1.0 Update
Update improves the following:
- Code structure
- Various clipping errors
- Performance
Thoughts on further expansion into fixes for torsos, hats etc.
I’ve spent the last couple of days researching ways to fix the clipping on other clothing items – torsos in particular – and it seems to be a much greater task than fixing masks.
The fundamental problem is that GTA: Online is very restrictive compared to most FiveM servers, making picking a “most fitting” pair of arms very difficult, as FiveM players usually like to mix and match items that were never meant to be worn together. Unlike masks, that programmatically warp your face and shrink your head based on various flags set on the item itself, most torsos seem to actually be hand-scripted into the game. I’ve managed to somewhat recreate GTA: Online’s behavior up until a certain DLC, but it ultimately led to a lot of infighting when wearing conflicting shirts/jackets. I’m pretty sure GTA: Online picks hands before actually applying shirts. I can’t really do that without making event listeners for every clothing store script out there. I’ve not given up though, and am positive I can get something working. I’ll share my work so far below.
Torso/Arms Progress
---@class HeadBlendData
---@field shapeFirst integer
---@field shapeSecond integer
---@field shapeThird integer
---@field skinFirst integer
---@field skinSecond integer
---@field skinThird integer
---@field shapeMix number
---@field skinMix number
---@field thirdMix number
local freemodeModels<const> = {
[`mp_m_freemode_01`] = 'mp_m_freemode_01',
[`mp_f_freemode_01`] = 'mp_f_freemode_01'
}
---Is the model either of the freemode models?
---@param modelHash integer
local function isFreemodeModel(modelHash)
return freemodeModels[modelHash] ~= nil
end
---Gets draw tag as integer
---@param hashName integer
local function getDLCDrawTag(hashName)
local tag = -1
if hashName == 0 then return tag end
if DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_0`, 0) then tag = 0
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_1`, 0) then tag = 1
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_2`, 0) then tag = 2
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_3`, 0) then tag = 3
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_4`, 0) then tag = 4
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_5`, 0) then tag = 5
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_6`, 0) then tag = 6
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_7`, 0) then tag = 7
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_8`, 0) then tag = 8
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_9`, 0) then tag = 9
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_10`, 0) then tag = 10
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_11`, 0) then tag = 11
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_12`, 0) then tag = 12
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_13`, 0) then tag = 13
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_14`, 0) then tag = 14
elseif DoesShopPedApparelHaveRestrictionTag(hashName, `DRAW_15`, 0) then tag = 15
end
return tag
end
---Get jacket from shirt
---@param pedModelHash integer
---@param shirt integer
---@return integer
local function getJacketFromShirt(pedModelHash, shirt)
local jacket = -99
if freemodeModels[pedModelHash] == 'mp_m_freemode_01' then
if shirt <= 15 then jacket = 0
elseif shirt <= 1 * 16 + 15 then jacket = 1
elseif shirt <= 2 * 16 + 15 then jacket = 0
elseif shirt <= 3 * 16 + 15 then jacket = -99
elseif shirt <= 4 * 16 + 15 then jacket = -99
elseif shirt <= 5 * 16 + 15 then jacket = 5
elseif shirt <= 6 * 16 + 15 then jacket = -99
elseif shirt <= 7 * 16 + 15 then jacket = -99
elseif shirt <= 8 * 16 + 15 then jacket = 8
elseif shirt <= 9 * 16 + 15 then jacket = 9
elseif shirt <= 10 * 16 + 15 then jacket = -99
elseif shirt <= 11 * 16 + 15 then jacket = -99
elseif shirt <= 12 * 16 + 15 then jacket = 12
elseif shirt <= 13 * 16 + 15 then jacket = 13
elseif shirt <= 14 * 16 + 15 then jacket = 1
elseif shirt <= 15 * 16 then jacket = 15
end
else
if shirt <= 15 then jacket = 0
elseif shirt <= 1 * 16 + 15 then jacket = 0
elseif shirt <= 2 * 16 then jacket = -99
elseif shirt <= 3 * 16 then jacket = -99
elseif shirt <= 4 * 16 + 15 then jacket = 4
elseif shirt <= 5 * 16 + 15 then jacket = 5
elseif shirt <= 6 * 16 then jacket = -99
elseif shirt <= 7 * 16 then jacket = -99
elseif shirt <= 8 * 16 then jacket = -99
elseif shirt <= 9 * 16 then jacket = -99
elseif shirt <= 10 * 16 then jacket = -99
elseif shirt <= 11 * 16 + 15 then jacket = 11
elseif shirt <= 12 * 16 + 15 then jacket = 12
elseif shirt <= 13 * 16 + 15 then jacket = 13
elseif shirt <= 14 * 16 then jacket = -99
elseif shirt <= 15 * 16 + 15 then jacket = 15
end
end
return jacket
end
---Get torso for shirt/jacket combo
---@param ped integer
---@param pedModelHash integer
---@param currentShirt integer
---@param currentJacket integer
---@param currentPants integer
---@return integer
local function getTorsoForShirtJacketCombo(ped, pedModelHash, currentShirt, currentJacket, currentPants)
-- To me this function is pretty disgusting, but it's kinda how R* did it
local forcedTorso = -99
local jacketHash<const> = GetHashNameForComponent(ped, 11, currentJacket, 0)
local jacketTag<const> = getDLCDrawTag(jacketHash)
local shirtHash<const> = GetHashNameForComponent(ped, 8, currentShirt, 0)
local shirtTag<const> = getDLCDrawTag(shirtHash)
if freemodeModels[pedModelHash] == 'mp_m_freemode_01' then
if DoesShopPedApparelHaveRestrictionTag(jacketHash, `SILK_ROBE`, 0) then forcedTorso = 14
elseif DoesShopPedApparelHaveRestrictionTag(jacketHash, `SILK_PYJAMAS`, 0) then forcedTorso = 6
elseif shirtHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(shirtHash, `STUNT_DRAW_1`, 0) then forcedTorso = 1
elseif shirtHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(shirtHash, `STUNT_DRAW_2`, 0) then forcedTorso = 4
elseif
DoesShopPedApparelHaveRestrictionTag(jacketHash, `BIKER_VEST`, 0)
and not DoesShopPedApparelHaveRestrictionTag(jacketHash, `JACKET_ONLY`, 0)
then
if currentShirt == 15 * 16 then
if
DoesShopPedApparelHaveRestrictionTag(jacketHash, `BIKER_DRAW_0`, 0)
or DoesShopPedApparelHaveRestrictionTag(jacketHash, `BIKER_DRAW_3`, 0)
or DoesShopPedApparelHaveRestrictionTag(jacketHash, `BIKER_DRAW_13`, 0)
then forcedTorso = 112
elseif DoesShopPedApparelHaveRestrictionTag(jacketHash, `BIKER_DRAW_1`, 0) then forcedTorso = 113
elseif DoesShopPedApparelHaveRestrictionTag(jacketHash, `BIKER_DRAW_2`, 0) then forcedTorso = 114
elseif DoesShopPedApparelHaveRestrictionTag(jacketHash, `BIKER_DRAW_5`, 0) then forcedTorso = 5
end
else
forcedTorso = getTorsoForShirtJacketCombo(ped, pedModelHash, -99, getJacketFromShirt(pedModelHash, currentShirt), currentPants)
if forcedTorso == -99 then forcedTorso = 0 end
end
elseif DoesShopPedApparelHaveRestrictionTag(shirtHash, `OVERCOAT_ACCS`, 0) then forcedTorso = 12
elseif
(jacketHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(jacketHash, `APART_DRAW_15`, 0))
or (jacketHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(jacketHash, `APART_DRAW_24`, 0))
or DoesShopPedApparelHaveRestrictionTag(jacketHash, `BIKER_DRAW_6`, 0)
then
if currentShirt == 15 * 16 then
forcedTorso = 14
elseif
(currentShirt >= 5 * 16 and currentShirt <= 5 * 16 + 15)
or shirtTag == 5
then
forcedTorso = 6
elseif
(currentShirt >= 16 and currentShirt <= 16 + 15)
or shirtTag == 1
then
forcedTorso = 1
else
forcedTorso = 4
end
elseif jacketHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(jacketHash, `LOW2_OPEN_CHECK`, 0) then
if currentShirt == 15 * 16 then
forcedTorso = 14
elseif
(currentShirt >= 5 * 16 and currentShirt <= 5 * 16 + 15)
or shirtTag == 5
then
forcedTorso = 6
else
forcedTorso = 1
end
elseif jacketHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(jacketHash, `HEIST_DRAW_7`, 0) then
if
(currentShirt >= 12 * 16 and currentShirt <= 12 * 16 + 15)
or shirtTag == 12
then
forcedTorso = 0
else
forcedTorso = 11
end
elseif jacketHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(jacketHash, `HEIST_DRAW_9`, 0) then
if
(currentShirt >= 5 * 16 and currentShirt <= 5 * 16 + 15)
or shirtTag == 5
then
forcedTorso = 6
elseif currentShirt == 15 * 16 then
forcedTorso = 14
elseif
(currentShirt >= 16 and currentShirt <= 16 + 15)
or shirtTag == 1
then
forcedTorso = 1
end
elseif jacketHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(jacketHash, `TUX_JACKET`, 0) then
forcedTorso = 12
elseif jacketHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(jacketHash, `SMUG_DRAW_6`, 0) then
forcedTorso = 2
elseif
(currentJacket >= 0 and currentJacket <= 16 + 15)
or jacketTag == 0
or jacketTag == 1
then
forcedTorso = 0
elseif
(currentJacket >= 3 * 16 and currentJacket <= 3 * 16 + 15)
or (currentJacket >= 4 * 16 and currentJacket <= 4 * 16 + 15)
or (currentJacket >= 6 * 16 and currentJacket <= 6 * 16 + 11)
or (currentJacket >= 7 * 16 and currentJacket <= 7 * 16 + 15)
or (currentJacket >= 10 * 16 and currentJacket <= 10 * 16 + 15)
or jacketTag == 3
or jacketTag == 4
or jacketTag == 6
or jacketTag == 7
or jacketTag == 10
then
if
(currentShirt >= 5 * 16 and currentShirt <= 5 * 16 + 15)
or shirtTag == 5
then
forcedTorso = 6
elseif
currentShirt == 15 * 16
or shirtTag == 15
then
forcedTorso = 14
elseif
(currentShirt >= 10 * 16 and currentShirt <= 10 * 16 + 15)
or shirtTag == 10
then
forcedTorso = 4
elseif
(currentShirt >= 11 * 16 and currentShirt <= 11 * 16 + 15)
or shirtTag == 11
then
forcedTorso = 12
elseif
(currentShirt >= 16 and currentShirt <= 16 + 15)
or (currentShirt >= 14 * 16 and currentShirt <= 14 * 16 + 15)
or shirtTag == 1
or shirtTag == 14
then
forcedTorso = 1
elseif
(currentShirt >= 5 * 16 and currentShirt <= 5 * 16 + 15)
or shirtTag == 5
then
forcedTorso = 6
elseif
(currentShirt >= 2 * 16 and currentShirt <= 2 * 16 + 15)
or shirtTag == 2
then
forcedTorso = 4
elseif shirtHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(shirtHash, `SHIRT_BRACES`, 0) then
if shirtHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(shirtHash, `CLOSED_COLLAR`, 0) then
forcedTorso = 4
else
forcedTorso = 1
end
elseif
(currentShirt >= 9 * 16 and currentShirt <= 9 * 16 + 15)
or shirtTag == 9
then
forcedTorso = 1
elseif
DoesShopPedApparelHaveRestrictionTag(shirtHash, `APART_DRAW_2`, 0)
or DoesShopPedApparelHaveRestrictionTag(shirtHash, `APART_DRAW_3`, 0)
then
forcedTorso = 4
end
-- Check for other forced components here?
-- DLC items?
elseif
(currentJacket >= 11 * 16 and currentJacket <= 11 * 16 + 15)
or jacketTag == 11
then
if shirtHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(shirtHash, `SWEAT_VEST`, 0) then
forcedTorso = 6
elseif
(currentShirt >= 3 and currentShirt <= 3 + 15)
or (currentShirt >= 7 * 16 and currentShirt <= 7 * 16 + 15)
or shirtTag == 3
or shirtTag == 7
then
forcedTorso = 11
elseif
(currentShirt >= 6 * 16 and currentShirt <= 6 * 16 + 15)
or shirtTag == 6
then
forcedTorso = 11
elseif shirtHash ~= 0 and DoesShopPedApparelHaveRestrictionTag(shirtHash, `LONG_SLEEVE`, 0) then
forcedTorso = 12
elseif currentShirt == 15 * 16 then
forcedTorso = 15
end
elseif
(currentJacket >= 9 * 16 and currentJacket <= 9 * 16 + 15)
or jacketTag == 9
then
forcedTorso = 0
elseif currentJacket == 15 * 16 then
if currentShirt == 15 * 16 then
forcedTorso = 15
end
end
else
end
return forcedTorso
end
local MAX_DLC_JACKET = 15 * 16 + 1
---Get torso for jacket
---@param ped integer
---@param pedModelHash integer
---@param currentJacket integer
---@param currentShirt integer
---@return integer
local function getTorsoForJacket(ped, pedModelHash, currentJacket, currentShirt)
local forcedTorso = -99
if freemodeModels[pedModelHash] == 'mp_m_freemode_01' then
if currentJacket <= 15 then forcedTorso = 0
elseif currentJacket <= 16 + 15 then forcedTorso = 0
elseif currentJacket <= 2 * 16 + 15 then forcedTorso = 2
elseif currentJacket <= 3 * 16 + 15 then forcedTorso = 1
elseif currentJacket <= 4 * 16 + 15 then forcedTorso = 1
elseif currentJacket <= 5 * 16 + 15 then forcedTorso = 5
elseif currentJacket <= 6 * 16 + 15 then forcedTorso = 12
elseif currentJacket <= 7 * 16 + 15 then forcedTorso = 1
elseif currentJacket <= 8 * 16 + 15 then forcedTorso = 8
elseif currentJacket <= 9 * 16 + 15 then forcedTorso = 0
elseif currentJacket <= 10 * 16 + 15 then forcedTorso = 1
elseif currentJacket <= 11 * 16 + 15 then
local shirtHash<const> = GetHashNameForComponent(ped, 8, currentShirt, 0)
local shirtTag<const> = getDLCDrawTag(shirtHash)
if
(currentShirt >= 6 * 16 and currentShirt <= 6 * 16 + 15)
or (currentShirt >= 7 * 16 and currentShirt <= 7 * 16 + 15)
or (currentShirt >= MAX_DLC_JACKET and shirtTag == 6)
or (currentShirt >= MAX_DLC_JACKET and shirtTag == 7)
or (currentShirt >= MAX_DLC_JACKET and DoesShopPedApparelHaveRestrictionTag(shirtHash, `VEST_SHIRT`, 0))
then
forcedTorso = -99
else
forcedTorso = 11
end
elseif currentJacket <= 12 * 16 + 15 then forcedTorso = 12
elseif currentJacket <= 13 * 16 + 15 then forcedTorso = 11
elseif currentJacket <= 14 * 16 + 15 then forcedTorso = 4
elseif currentJacket == 15 * 16 then forcedTorso = 15
elseif currentJacket >= MAX_DLC_JACKET then
-- DLC item
end
else
end
return forcedTorso
end
---Handles arms fix
---@param ped integer
---@param pedModelHash integer
local function handleArms(ped, pedModelHash)
local currentShirt<const> = GetPedDrawableVariation(ped, 8)
local currentJacket<const> = GetPedDrawableVariation(ped, 11)
local glovelessArms = getTorsoForShirtJacketCombo(ped, pedModelHash, currentShirt, currentJacket, GetPedDrawableVariation(ped, 4))
if glovelessArms == -99 then
glovelessArms = getTorsoForJacket(ped, pedModelHash, currentJacket, currentShirt)
local jacketHash<const> = GetHashNameForComponent(ped, 11, currentJacket, 0)
if
jacketHash == `DLC_MP_VAL_F_JBIB0_0`
or jacketHash == `DLC_MP_VAL_F_JBIB0_1`
or jacketHash == `DLC_MP_VAL_F_JBIB0_2`
or jacketHash == `DLC_MP_VAL_F_JBIB0_3`
or jacketHash == `DLC_MP_VAL_F_JBIB0_4`
or jacketHash == `DLC_MP_VAL_F_JBIB0_5`
then
glovelessArms = 11
elseif
jacketHash == `DLC_MP_STUNT_M_JBIB_5_0`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_1`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_2`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_3`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_4`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_5`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_6`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_7`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_8`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_9`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_10`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_11`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_12`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_13`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_14`
or jacketHash == `DLC_MP_STUNT_M_JBIB_5_15`
then
glovelessArms = 4
elseif
jacketHash == `DLC_MP_STUNT_F_JBIB_5_0`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_1`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_2`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_3`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_4`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_5`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_6`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_7`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_8`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_9`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_10`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_11`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_12`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_13`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_14`
or jacketHash == `DLC_MP_STUNT_F_JBIB_5_15`
then
glovelessArms = 3
end
end
if glovelessArms ~= -99 then
-- Found glovelessArms
-- ???
else
glovelessArms = -1
end
print('glovelessArms', glovelessArms)
--SetPedComponentVariation(ped, 3, glovelessArms, 0, 0)
end
local function fixClothing()
local ped<const> = PlayerPedId()
if not DoesEntityExist(ped) then return end
local pedModelHash<const> = GetEntityModel(ped)
if not isFreemodeModel(pedModelHash) then return end
handleArms(ped, pedModelHash)
end
CreateThread(function()
while true do
fixClothing()
Wait(10)
end
end)
You are a lion, I cant believe that you master this . Hope you will succeed
How do I make addon mask compatible? Awesome script btw
Great job thank you for the free release
Cops and Robbers V will use this fix
this is insane
Any more progress??
Hello, I would like to know if anyone has found the possibility of making this fix mask compatible with dpClothing and the new creator appearance
W
– Dan