Scripts/resources/[il]/illenium-appearance/client/client.lua
2024-12-29 20:49:12 +01:00

757 lines
26 KiB
Lua

local client = client
local reloadSkinTimer = GetGameTimer()
local function LoadPlayerUniform(reset)
if reset then
TriggerServerEvent("illenium-appearance:server:syncUniform", nil)
return
end
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()
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.RCoreTattoosCompatibility 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")
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()
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.RCoreTattoosCompatibility 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 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,
}
}
}
Management.AddBackMenuItem(managementMenu, args)
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
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.description"),
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
LoadPlayerUniform(bypassChecks)
end
RestorePlayerStats()
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)