Public paste
Undefined
By: Guest | Date: Feb 2 2023 02:53 | Format: None | Expires: never | Size: 40.46 KB | Hits: 172

  1. local client = client
  2.  
  3. local currentZone = nil
  4. local MenuItemId = nil
  5.  
  6. local ManagementItemIDs = {
  7.     Gang = nil,
  8.     Boss = nil
  9. }
  10.  
  11. local reloadSkinTimer = GetGameTimer()
  12.  
  13. local TargetPeds = {
  14.     Store = {},
  15.     ClothingRoom = {},
  16.     PlayerOutfitRoom = {}
  17. }
  18.  
  19. local Zones = {
  20.     Store = {},
  21.     ClothingRoom = {},
  22.     PlayerOutfitRoom = {}
  23. }
  24.  
  25. local function RemoveTargetPeds(peds)
  26.     for i = 1, #peds, 1 do
  27.         DeletePed(peds[i])
  28.     end
  29. end
  30.  
  31. local function RemoveTargets()
  32.     if Config.EnablePedsForShops then
  33.         RemoveTargetPeds(TargetPeds.Store)
  34.     else
  35.         for k, v in pairs(Config.Stores) do
  36.             Target.RemoveZone(v.type .. k)
  37.         end
  38.     end
  39.  
  40.     if Config.EnablePedsForClothingRooms then
  41.         RemoveTargetPeds(TargetPeds.ClothingRoom)
  42.     else
  43.         for k, v in pairs(Config.ClothingRooms) do
  44.             Target.RemoveZone("clothing_" .. (v.job or v.gang) .. k)
  45.         end
  46.     end
  47.  
  48.     if Config.EnablePedsForPlayerOutfitRooms then
  49.         RemoveTargetPeds(TargetPeds.PlayerOutfitRoom)
  50.     else
  51.         for k in pairs(Config.PlayerOutfitRooms) do
  52.             Target.RemoveZone("playeroutfitroom_" .. k)
  53.         end
  54.     end
  55. end
  56.  
  57. local function RemoveZones()
  58.     for i = 1, #Zones.Store do
  59.         Zones.Store[i]:remove()
  60.     end
  61.     for i = 1, #Zones.ClothingRoom do
  62.         Zones.ClothingRoom[i]:remove()
  63.     end
  64.     for i = 1, #Zones.PlayerOutfitRoom do
  65.         Zones.PlayerOutfitRoom[i]:remove()
  66.     end
  67. end
  68.  
  69. local function LoadPlayerUniform()
  70.     lib.callback("illenium-appearance:server:getUniform", false, function(uniformData)
  71.         if not uniformData then
  72.             return
  73.         end
  74.         if Config.BossManagedOutfits then
  75.             local result = lib.callback.await("illenium-appearance:server:getManagementOutfits", false, uniformData.type, Framework.GetGender())
  76.             local uniform = nil
  77.             for i = 1, #result, 1 do
  78.                 if result[i].name == uniformData.name then
  79.                     uniform = {
  80.                         type = uniformData.type,
  81.                         name = result[i].name,
  82.                         model = result[i].model,
  83.                         components = result[i].components,
  84.                         props = result[i].props,
  85.                         disableSave = true,
  86.                     }
  87.                     break
  88.                 end
  89.             end
  90.  
  91.             if not uniform then
  92.                 TriggerServerEvent("illenium-appearance:server:syncUniform", nil) -- Uniform doesn't exist anymore
  93.                 return
  94.             end
  95.  
  96.             TriggerEvent("illenium-appearance:client:changeOutfit", uniform)
  97.         else
  98.             local outfits = Config.Outfits[uniformData.jobName][uniformData.gender]
  99.             local uniform = nil
  100.             for i = 1, #outfits, 1 do
  101.                 if outfits[i].name == uniformData.label then
  102.                     uniform = outfits[i]
  103.                     break
  104.                 end
  105.             end
  106.  
  107.             if not uniform then
  108.                 TriggerServerEvent("illenium-appearance:server:syncUniform", nil) -- Uniform doesn't exist anymore
  109.                 return
  110.             end
  111.  
  112.             uniform.jobName = uniformData.jobName
  113.             uniform.gender = uniformData.gender
  114.  
  115.             TriggerEvent("illenium-appearance:client:loadJobOutfit", uniform)
  116.         end
  117.     end)
  118. end
  119.  
  120. local function RemoveManagementMenuItems()
  121.     if ManagementItemIDs.Boss then
  122.         exports["qb-management"]:RemoveBossMenuItem(ManagementItemIDs.Boss)
  123.     end
  124.     if ManagementItemIDs.Gang then
  125.         exports["qb-management"]:RemoveGangMenuItem(ManagementItemIDs.Gang)
  126.     end
  127. end
  128.  
  129. local function AddManagementMenuItems()
  130.     local eventName = "illenium-appearance:client:OutfitManagementMenu"
  131.     local menuItem = {
  132.         header = "Outfit Management",
  133.         icon = "fa-solid fa-shirt",
  134.         params = {
  135.             event = eventName,
  136.             args = {
  137.                 backEvent = eventName
  138.             }
  139.         }
  140.     }
  141.     menuItem.txt = "Manage outfits for Job"
  142.     menuItem.params.args.type = "Job"
  143.     ManagementItemIDs.Boss = exports["qb-management"]:AddBossMenuItem(menuItem)
  144.  
  145.     menuItem.txt = "Manage outfits for Gang"
  146.     menuItem.params.args.type = "Gang"
  147.     ManagementItemIDs.Gang = exports["qb-management"]:AddGangMenuItem(menuItem)
  148. end
  149.  
  150. local function RemoveRadialMenuOption()
  151.     if MenuItemId then
  152.         exports["qb-radialmenu"]:RemoveOption(MenuItemId)
  153.         MenuItemId = nil
  154.     end
  155. end
  156.  
  157. function InitAppearance()
  158.     Framework.UpdatePlayerData()
  159.     lib.callback("illenium-appearance:server:getAppearance", false, function(appearance)
  160.         if not appearance then
  161.             return
  162.         end
  163.  
  164.         client.setPlayerAppearance(appearance)
  165.         if Config.PersistUniforms then
  166.             LoadPlayerUniform()
  167.         end
  168.     end)
  169.     ResetBlips()
  170.     if Config.BossManagedOutfits then
  171.         AddManagementMenuItems()
  172.     end
  173.     RestorePlayerStats()
  174. end
  175.  
  176. AddEventHandler("onResourceStart", function(resource)
  177.     if resource == GetCurrentResourceName() then
  178.         InitAppearance()
  179.     end
  180. end)
  181.  
  182. AddEventHandler("onResourceStop", function(resource)
  183.     if resource == GetCurrentResourceName() then
  184.         if Config.UseTarget and Target.IsTargetStarted() then
  185.             RemoveTargets()
  186.         else
  187.             RemoveZones()
  188.         end
  189.         if Config.UseRadialMenu and GetResourceState("qb-radialmenu") == "started" then
  190.             RemoveRadialMenuOption()
  191.         end
  192.         if Config.BossManagedOutfits and GetResourceState("qb-management") == "started" then
  193.             RemoveManagementMenuItems()
  194.         end
  195.     end
  196. end)
  197.  
  198.  
  199.  
  200. local function getNewCharacterConfig()
  201.     local config = GetDefaultConfig()
  202.     config.enableExit   = false
  203.  
  204.     config.ped          = Config.NewCharacterSections.Ped
  205.     config.headBlend    = Config.NewCharacterSections.HeadBlend
  206.     config.faceFeatures = Config.NewCharacterSections.FaceFeatures
  207.     config.headOverlays = Config.NewCharacterSections.HeadOverlays
  208.     config.components   = Config.NewCharacterSections.Components
  209.     config.props        = Config.NewCharacterSections.Props
  210.     config.tattoos      = not Config.RCoreTattoosCompatibility and Config.NewCharacterSections.Tattoos
  211.  
  212.     return config
  213. end
  214.  
  215. function InitializeCharacter(gender, onSubmit, onCancel)
  216.     local skin = "mp_m_freemode_01"
  217.     if gender == "Female" then
  218.         skin = "mp_f_freemode_01"
  219.     end
  220.     client.setPlayerModel(skin)
  221.     -- Fix for tattoo's appearing when creating a new character
  222.     local ped = PlayerPedId()
  223.     client.setPedTattoos(ped, {})
  224.     client.setPedComponents(ped, Config.InitialPlayerClothes[gender].Components)
  225.     client.setPedProps(ped, Config.InitialPlayerClothes[gender].Props)
  226.     client.setPedHair(ped, Config.InitialPlayerClothes[gender].Hair, {})
  227.     ClearPedDecorations(ped)
  228.     local config = getNewCharacterConfig()
  229.     client.startPlayerCustomization(function(appearance)
  230.         if (appearance) then
  231.             TriggerServerEvent("illenium-appearance:server:saveAppearance", appearance)
  232.             if onSubmit then
  233.                 onSubmit()
  234.             end
  235.         elseif onCancel then
  236.             onCancel()
  237.         end
  238.         Framework.CachePed()
  239.     end, config)
  240. end
  241.  
  242. function OpenShop(config, isPedMenu, shopType)
  243.     lib.callback("illenium-appearance:server:hasMoney", false, function(hasMoney, money)
  244.         if not hasMoney and not isPedMenu then
  245.             lib.notify({
  246.                 title = "Cannot Enter Shop",
  247.                 description = "Not enough cash. Need $" .. money,
  248.                 type = "error",
  249.                 position = Config.NotifyOptions.position
  250.             })
  251.             return
  252.         end
  253.  
  254.         client.startPlayerCustomization(function(appearance)
  255.             if appearance then
  256.                 if not isPedMenu then
  257.                     TriggerServerEvent("illenium-appearance:server:chargeCustomer", shopType)
  258.                 end
  259.                 TriggerServerEvent("illenium-appearance:server:saveAppearance", appearance)
  260.             else
  261.                 lib.notify({
  262.                     title = "Cancelled Customization",
  263.                     description = "Customization not saved",
  264.                     type = "inform",
  265.                     position = Config.NotifyOptions.position
  266.                 })
  267.             end
  268.             Framework.CachePed()
  269.         end, config)
  270.     end, shopType)
  271. end
  272.  
  273. local function OpenClothingShop(isPedMenu)
  274.     local config = GetDefaultConfig()
  275.     config.components = true
  276.     config.props = true
  277.  
  278.     if isPedMenu then
  279.         config.ped = true
  280.         config.headBlend = true
  281.         config.faceFeatures = true
  282.         config.headOverlays = true
  283.         config.tattoos = not Config.RCoreTattoosCompatibility and true
  284.     end
  285.     OpenShop(config, isPedMenu, "clothing")
  286. end
  287.  
  288. local function OpenBarberShop()
  289.     local config = GetDefaultConfig()
  290.     config.headOverlays = true
  291.     OpenShop(config, false, "barber")
  292. end
  293.  
  294. local function OpenTattooShop()
  295.     local config = GetDefaultConfig()
  296.     config.tattoos = true
  297.     OpenShop(config, false, "tattoo")
  298. end
  299.  
  300. local function OpenSurgeonShop()
  301.     local config = GetDefaultConfig()
  302.     config.headBlend = true
  303.     config.faceFeatures = true
  304.     OpenShop(config, false, "surgeon")
  305. end
  306.  
  307. RegisterNetEvent("illenium-appearance:client:openClothingShop", OpenClothingShop)
  308.  
  309. RegisterNetEvent("illenium-appearance:client:importOutfitCode", function()
  310.     local response = lib.inputDialog("Enter outfit code", {
  311.         {
  312.             type = "input",
  313.             label = "Name the Outfit",
  314.             placeholder = "A nice outfit",
  315.             default = "Imported Outfit"
  316.         },
  317.         {
  318.             type = "input",
  319.             label = "Outfit Code",
  320.             placeholder = "XXXXXXXXXXXX"
  321.         }
  322.     })
  323.  
  324.     if not response then
  325.         return
  326.     end
  327.  
  328.     local outfitName = response[1]
  329.     local outfitCode = response[2]
  330.     if outfitCode ~= nil then
  331.         Wait(500)
  332.         lib.callback("illenium-appearance:server:importOutfitCode", false, function(success)
  333.             if success then
  334.                 lib.notify({
  335.                     title = "Outfit Imported",
  336.                     description = "You can now change to the outfit using the outfit menu",
  337.                     type = "success",
  338.                     position = Config.NotifyOptions.position
  339.                 })
  340.             else
  341.                 lib.notify({
  342.                     title = "Import Failure",
  343.                     description = "Invalid outfit code",
  344.                     type = "error",
  345.                     position = Config.NotifyOptions.position
  346.                 })
  347.             end
  348.         end, outfitName, outfitCode)
  349.     end
  350. end)
  351.  
  352. RegisterNetEvent("illenium-appearance:client:generateOutfitCode", function(id)
  353.     lib.callback("illenium-appearance:server:generateOutfitCode", false, function(code)
  354.         if not code then
  355.             lib.notify({
  356.                 title = "Something went wrong",
  357.                 description = "Code generation failed for the outfit",
  358.                 type = "error",
  359.                 position = Config.NotifyOptions.position
  360.             })
  361.             return
  362.         end
  363.         lib.setClipboard(code)
  364.         lib.inputDialog("Outfit Code Generated", {
  365.             {
  366.                 type = "input",
  367.                 label = "Here is your outfit code",
  368.                 default = code,
  369.                 disabled = true
  370.             }
  371.         })
  372.     end, id)
  373. end)
  374.  
  375. RegisterNetEvent("illenium-appearance:client:saveOutfit", function()
  376.     local response = lib.inputDialog("Name your outfit", {
  377.         {
  378.             type = "input",
  379.             label = "Outfit Name",
  380.             placeholder = "Very cool outfit"
  381.         }
  382.     })
  383.  
  384.     if not response then
  385.         return
  386.     end
  387.  
  388.     local outfitName = response[1]
  389.     if outfitName ~= nil then
  390.         Wait(500)
  391.         lib.callback("illenium-appearance:server:getOutfits", false, function(outfits)
  392.             local outfitExists = false
  393.             for i = 1, #outfits, 1 do
  394.                 if outfits[i].name == outfitName then
  395.                     outfitExists = true
  396.                     break
  397.                 end
  398.             end
  399.  
  400.             if outfitExists then
  401.                 lib.notify({
  402.                     title = "Save Failed",
  403.                     description = "Outfit with this name already exists",
  404.                     type = "error",
  405.                     position = Config.NotifyOptions.position
  406.                 })
  407.                 return
  408.             end
  409.  
  410.             local playerPed = PlayerPedId()
  411.             local pedModel = client.getPedModel(playerPed)
  412.             local pedComponents = client.getPedComponents(playerPed)
  413.             local pedProps = client.getPedProps(playerPed)
  414.  
  415.             TriggerServerEvent("illenium-appearance:server:saveOutfit", outfitName, pedModel, pedComponents, pedProps)
  416.         end)
  417.     end
  418. end)
  419.  
  420. local function RegisterChangeOutfitMenu(id, parent, outfits, mType)
  421.     local changeOutfitMenu = {
  422.         id = id,
  423.         title = "Change Outfit",
  424.         menu = parent,
  425.         options = {}
  426.     }
  427.     for i = 1, #outfits, 1 do
  428.         changeOutfitMenu.options[#changeOutfitMenu.options + 1] = {
  429.             title = outfits[i].name,
  430.             description = outfits[i].model,
  431.             event = "illenium-appearance:client:changeOutfit",
  432.             args = {
  433.                 type = mType,
  434.                 name = outfits[i].name,
  435.                 model = outfits[i].model,
  436.                 components = outfits[i].components,
  437.                 props = outfits[i].props,
  438.                 disableSave = mType and true or false
  439.             }
  440.         }
  441.     end
  442.  
  443.     lib.registerContext(changeOutfitMenu)
  444. end
  445.  
  446. local function RegisterGenerateOutfitCodeMenu(id, parent, outfits)
  447.     local generateOutfitCodeMenu = {
  448.         id = id,
  449.         title = "Generate Outfit Code",
  450.         menu = parent,
  451.         options = {}
  452.     }
  453.     for i = 1, #outfits, 1 do
  454.         generateOutfitCodeMenu.options[#generateOutfitCodeMenu.options + 1] = {
  455.             title = outfits[i].name,
  456.             description = outfits[i].model,
  457.             event = "illenium-appearance:client:generateOutfitCode",
  458.             args = outfits[i].id
  459.         }
  460.     end
  461.  
  462.     lib.registerContext(generateOutfitCodeMenu)
  463. end
  464.  
  465. local function RegisterDeleteOutfitMenu(id, parent, outfits, deleteEvent)
  466.     local deleteOutfitMenu = {
  467.         id = id,
  468.         title = "Delete Outfit",
  469.         menu = parent,
  470.         options = {}
  471.     }
  472.     for i = 1, #outfits, 1 do
  473.         deleteOutfitMenu.options[#deleteOutfitMenu.options + 1] = {
  474.             title = 'Delete "' .. outfits[i].name .. '"',
  475.             description = "Model: " .. outfits[i].model .. (outfits[i].gender and (" - Gender: " .. outfits[i].gender) or ""),
  476.             event = deleteEvent,
  477.             args = outfits[i].id
  478.         }
  479.     end
  480.  
  481.     lib.registerContext(deleteOutfitMenu)
  482. end
  483.  
  484. RegisterNetEvent("illenium-appearance:client:OutfitManagementMenu", function(args)
  485.     local bossMenuEvent = "qb-bossmenu:client:OpenMenu"
  486.     if args.type == "Gang" then
  487.         bossMenuEvent = "qb-gangmenu:client:OpenMenu"
  488.     end
  489.  
  490.     local outfits = lib.callback.await("illenium-appearance:server:getManagementOutfits", false, args.type, Framework.GetGender())
  491.     local managementMenuID = "illenium_appearance_outfit_management_menu"
  492.     local changeManagementOutfitMenuID = "illenium_appearance_change_management_outfit_menu"
  493.     local deleteManagementOutfitMenuID = "illenium_appearance_delete_management_outfit_menu"
  494.  
  495.     RegisterChangeOutfitMenu(changeManagementOutfitMenuID, managementMenuID, outfits, args.type)
  496.     RegisterDeleteOutfitMenu(deleteManagementOutfitMenuID, managementMenuID, outfits, "illenium-appearance:client:DeleteManagementOutfit")
  497.     local managementMenu = {
  498.         id = managementMenuID,
  499.         title = "👔 | Manage " .. args.type .. " Outfits",
  500.         options = {
  501.             {
  502.                 title = "Change Outfit",
  503.                 description = "Pick from any of your currently saved "  .. args.type .. " outfits",
  504.                 menu = changeManagementOutfitMenuID,
  505.             },
  506.             {
  507.                 title = "Save current Outfit",
  508.                 description = "Save your current outfit as " .. args.type .. " outfit",
  509.                 event = "illenium-appearance:client:SaveManagementOutfit",
  510.                 args = args.type
  511.             },
  512.             {
  513.                 title = "Delete Outfit",
  514.                 description = "Delete a saved " .. args.type .. " outfit",
  515.                 menu = deleteManagementOutfitMenuID,
  516.             },
  517.             {
  518.                 title = "Return",
  519.                 icon = "fa-solid fa-angle-left",
  520.                 event = bossMenuEvent
  521.             }
  522.         }
  523.     }
  524.  
  525.     lib.registerContext(managementMenu)
  526.     lib.showContext(managementMenuID)
  527. end)
  528.  
  529. RegisterNetEvent("illenium-appearance:client:SaveManagementOutfit", function(mType)
  530.     local playerPed = PlayerPedId()
  531.     local outfitData = {
  532.         Type = mType,
  533.         Model = client.getPedModel(playerPed),
  534.         Components = client.getPedComponents(playerPed),
  535.         Props = client.getPedProps(playerPed)
  536.     }
  537.  
  538.     local rankValues
  539.  
  540.     if mType == "Job" then
  541.         outfitData.JobName = client.job.name
  542.         rankValues = Framework.GetRankInputValues("job")
  543.  
  544.     else
  545.         outfitData.JobName = client.gang.name
  546.         rankValues = Framework.GetRankInputValues("gang")
  547.     end
  548.  
  549.     local dialogResponse = lib.inputDialog("Management Outfit Details", {
  550.             {
  551.                 label = "Outfit Name",
  552.                 type = "input",
  553.             },
  554.             {
  555.                 label = "Gender",
  556.                 type = "select",
  557.                 options = {
  558.                     {
  559.                         label = "Male", value = "male"
  560.                     },
  561.                     {
  562.                         label = "Female", value = "female"
  563.                     }
  564.                 },
  565.                 default = "male"
  566.             },
  567.             {
  568.                 label = "Minimum Rank",
  569.                 type = "select",
  570.                 options = rankValues,
  571.                 default = "0"
  572.             }
  573.         })
  574.  
  575.     if not dialogResponse then
  576.         return
  577.     end
  578.  
  579.  
  580.     outfitData.Name = dialogResponse[1]
  581.     outfitData.Gender = dialogResponse[2]
  582.     outfitData.MinRank = tonumber(dialogResponse[3])
  583.  
  584.     TriggerServerEvent("illenium-appearance:server:saveManagementOutfit", outfitData)
  585.  
  586. end)
  587.  
  588. local function RegisterWorkOutfitsListMenu(id, parent, menuData)
  589.     local menu = {
  590.         id = id,
  591.         menu = parent,
  592.         title = "Work Outfits",
  593.         options = {}
  594.     }
  595.     local event = "illenium-appearance:client:loadJobOutfit"
  596.     if Config.BossManagedOutfits then
  597.         event = "illenium-appearance:client:changeOutfit"
  598.     end
  599.     if menuData then
  600.         for _, v in pairs(menuData) do
  601.             menu.options[#menu.options + 1] = {
  602.                 title = v.name,
  603.                 event = event,
  604.                 args = v
  605.             }
  606.         end
  607.     end
  608.     lib.registerContext(menu)
  609. end
  610.  
  611. function OpenMenu(isPedMenu, menuType, menuData)
  612.     local mainMenuID = "illenium_appearance_main_menu"
  613.     local mainMenu = {
  614.         id = mainMenuID
  615.     }
  616.     local menuItems = {}
  617.  
  618.     local outfits = lib.callback.await("illenium-appearance:server:getOutfits", false)
  619.     local changeOutfitMenuID = "illenium_appearance_change_outfit_menu"
  620.     local deleteOutfitMenuID = "illenium_appearance_delete_outfit_menu"
  621.     local generateOutfitCodeMenuID = "illenium_appearance_generate_outfit_code_menu"
  622.  
  623.     RegisterChangeOutfitMenu(changeOutfitMenuID, mainMenuID, outfits)
  624.     RegisterDeleteOutfitMenu(deleteOutfitMenuID, mainMenuID, outfits, "illenium-appearance:client:deleteOutfit")
  625.     RegisterGenerateOutfitCodeMenu(generateOutfitCodeMenuID, mainMenuID, outfits)
  626.     local outfitMenuItems = {
  627.         {
  628.             title = "Change Outfit",
  629.             description = "Pick from any of your currently saved outfits",
  630.             menu = changeOutfitMenuID
  631.         },
  632.         {
  633.             title = "Save New Outfit",
  634.             description = "Save a new outfit you can use later on",
  635.             event = "illenium-appearance:client:saveOutfit"
  636.         },
  637.         {
  638.             title = "Generate Outfit Code",
  639.             description = "Generate an outfit code for sharing",
  640.             menu = generateOutfitCodeMenuID
  641.         },
  642.         {
  643.             title = "Delete Outfit",
  644.             description = "Delete any of your saved outfits",
  645.             menu = deleteOutfitMenuID
  646.         },
  647.         {
  648.             title = "Import Outfit",
  649.             description = "Import an outfit from a sharing code",
  650.             event = "illenium-appearance:client:importOutfitCode"
  651.         }
  652.     }
  653.     if menuType == "default" then
  654.         local header = "Buy Clothing - $" .. Config.ClothingCost
  655.         if isPedMenu then
  656.             header = "Change Clothing"
  657.         end
  658.         mainMenu.title = "👔 | Clothing Store Options"
  659.         menuItems[#menuItems + 1] = {
  660.             title = header,
  661.             description = "Pick from a wide range of items to wear",
  662.             event = "illenium-appearance:client:openClothingShop",
  663.             args = isPedMenu
  664.         }
  665.         for i = 0, #outfitMenuItems, 1 do
  666.             menuItems[#menuItems + 1] = outfitMenuItems[i]
  667.         end
  668.     elseif menuType == "outfit" then
  669.         mainMenu.title = "👔 | Outfit Options"
  670.         for i = 0, #outfitMenuItems, 1 do
  671.             menuItems[#menuItems + 1] = outfitMenuItems[i]
  672.         end
  673.     elseif menuType == "job-outfit" then
  674.         mainMenu.title = "👔 | Outfit Options"
  675.         menuItems[#menuItems + 1] = {
  676.             title = "Civilian Outfit",
  677.             description = "Put on your clothes",
  678.             event = "illenium-appearance:client:reloadSkin"
  679.         }
  680.  
  681.         local workOutfitsMenuID = "illenium_appearance_work_outfits_menu"
  682.         RegisterWorkOutfitsListMenu(workOutfitsMenuID, mainMenuID, menuData)
  683.  
  684.         menuItems[#menuItems + 1] = {
  685.             title = "Work Outfits",
  686.             description = "Pick from any of your work outfits",
  687.             menu = workOutfitsMenuID
  688.         }
  689.     end
  690.     mainMenu.options = menuItems
  691.  
  692.     lib.registerContext(mainMenu)
  693.     lib.showContext(mainMenuID)
  694. end
  695.  
  696. RegisterNetEvent("illenium-appearance:client:openClothingShopMenu", function(isPedMenu)
  697.     if type(isPedMenu) == "table" then
  698.         isPedMenu = false
  699.     end
  700.     OpenMenu(isPedMenu, "default")
  701. end)
  702.  
  703. RegisterNetEvent("illenium-appearance:client:OpenBarberShop", function()
  704.     OpenBarberShop()
  705. end)
  706.  
  707. RegisterNetEvent("illenium-appearance:client:OpenTattooShop", function()
  708.     OpenTattooShop()
  709. end)
  710.  
  711. RegisterNetEvent("illenium-appearance:client:OpenSurgeonShop", function()
  712.     OpenSurgeonShop()
  713. end)
  714.  
  715. RegisterNetEvent("illenium-appearance:client:changeOutfit", function(data)
  716.     local playerPed = PlayerPedId()
  717.     local pedModel = client.getPedModel(playerPed)
  718.     local appearanceDB
  719.     if pedModel ~= data.model then
  720.         local p = promise.new()
  721.         lib.callback("illenium-appearance:server:getAppearance", false, function(appearance)
  722.             BackupPlayerStats()
  723.             if appearance then
  724.                 client.setPlayerAppearance(appearance)
  725.                 RestorePlayerStats()
  726.             else
  727.                 lib.notify({
  728.                     title = "Something went wrong",
  729.                     description = "The outfit that you're trying to change to, does not have a base appearance",
  730.                     type = "error",
  731.                     position = Config.NotifyOptions.position
  732.                 })
  733.             end
  734.             p:resolve(appearance)
  735.         end, data.model)
  736.         appearanceDB = Citizen.Await(p)
  737.     else
  738.         appearanceDB = client.getPedAppearance(playerPed)
  739.     end
  740.     if appearanceDB then
  741.         playerPed = PlayerPedId()
  742.         client.setPedComponents(playerPed, data.components)
  743.         client.setPedProps(playerPed, data.props)
  744.         client.setPedHair(playerPed, appearanceDB.hair, appearanceDB.tattoos)
  745.  
  746.         if data.disableSave then
  747.             TriggerServerEvent("illenium-appearance:server:syncUniform", {
  748.                 type = data.type,
  749.                 name = data.name
  750.             }) -- Is a uniform
  751.         else
  752.             local appearance = client.getPedAppearance(playerPed)
  753.             TriggerServerEvent("illenium-appearance:server:saveAppearance", appearance)
  754.         end
  755.         Framework.CachePed()
  756.     end
  757. end)
  758.  
  759. RegisterNetEvent("illenium-appearance:client:DeleteManagementOutfit", function(id)
  760.     TriggerServerEvent("illenium-appearance:server:deleteManagementOutfit", id)
  761.     lib.notify({
  762.         title = "Success",
  763.         description = "Outfit Deleted",
  764.         type = "success",
  765.         position = Config.NotifyOptions.position
  766.     })
  767. end)
  768.  
  769. RegisterNetEvent("illenium-appearance:client:deleteOutfit", function(id)
  770.     TriggerServerEvent("illenium-appearance:server:deleteOutfit", id)
  771.     lib.notify({
  772.         title = "Success",
  773.         description = "Outfit Deleted",
  774.         type = "success",
  775.         position = Config.NotifyOptions.position
  776.     })
  777. end)
  778.  
  779. RegisterNetEvent("illenium-appearance:client:openJobOutfitsMenu", function(outfitsToShow)
  780.     OpenMenu(nil, "job-outfit", outfitsToShow)
  781. end)
  782.  
  783. local function InCooldown()
  784.     return (GetGameTimer() - reloadSkinTimer) < Config.ReloadSkinCooldown
  785. end
  786.  
  787. RegisterNetEvent("illenium-appearance:client:reloadSkin", function()
  788.     local playerPed = PlayerPedId()
  789.  
  790.     if InCooldown() or Framework.CheckPlayerMeta() or IsPedInAnyVehicle(playerPed, true) or IsPedFalling(playerPed) then
  791.         lib.notify({
  792.             title = "Error",
  793.             description = "You cannot use reloadskin right now",
  794.             type = "error",
  795.             position = Config.NotifyOptions.position
  796.         })
  797.         return
  798.     end
  799.  
  800.     reloadSkinTimer = GetGameTimer()
  801.     BackupPlayerStats()
  802.  
  803.     lib.callback("illenium-appearance:server:getAppearance", false, function(appearance)
  804.         if not appearance then
  805.             return
  806.         end
  807.         client.setPlayerAppearance(appearance)
  808.         if Config.PersistUniforms then
  809.             TriggerServerEvent("illenium-appearance:server:syncUniform", nil)
  810.         end
  811.         RestorePlayerStats()
  812.     end)
  813. end)
  814.  
  815. RegisterNetEvent("illenium-appearance:client:ClearStuckProps", function()
  816.     if InCooldown() or Framework.CheckPlayerMeta() then
  817.         lib.notify({
  818.             title = "Error",
  819.             description = "You cannot use clearstuckprops right now",
  820.             type = "error",
  821.             position = Config.NotifyOptions.position
  822.         })
  823.         return
  824.     end
  825.  
  826.     reloadSkinTimer = GetGameTimer()
  827.     local playerPed = PlayerPedId()
  828.  
  829.     for _, v in pairs(GetGamePool("CObject")) do
  830.       if IsEntityAttachedToEntity(playerPed, v) then
  831.         SetEntityAsMissionEntity(v, true, true)
  832.         DeleteObject(v)
  833.         DeleteEntity(v)
  834.       end
  835.     end
  836. end)
  837.  
  838. RegisterNetEvent("qb-radialmenu:client:onRadialmenuOpen", function()
  839.     if not currentZone then
  840.         RemoveRadialMenuOption()
  841.         return
  842.     end
  843.     local event, title
  844.     if currentZone.name == "clothingRoom" then
  845.         event = "illenium-appearance:client:OpenClothingRoom"
  846.         title = "Clothing Room"
  847.     elseif currentZone.name == "playerOutfitRoom" then
  848.         event = "illenium-appearance:client:OpenPlayerOutfitRoom"
  849.         title = "Player Outfits"
  850.     elseif currentZone.name == "clothing" then
  851.         event = "illenium-appearance:client:openClothingShopMenu"
  852.         title = "Clothing Shop"
  853.     elseif currentZone.name == "barber" then
  854.         event = "illenium-appearance:client:OpenBarberShop"
  855.         title = "Barber Shop"
  856.     elseif currentZone.name == "tattoo" then
  857.         event = "illenium-appearance:client:OpenTattooShop"
  858.         title = "Tattoo Shop"
  859.     elseif currentZone.name == "surgeon" then
  860.         event = "illenium-appearance:client:OpenSurgeonShop"
  861.         title = "Surgeon Shop"
  862.     end
  863.  
  864.     MenuItemId = exports["qb-radialmenu"]:AddOption({
  865.         id = "open_clothing_menu",
  866.         title = title,
  867.         icon = "shirt",
  868.         type = "client",
  869.         event = event,
  870.         shouldClose = true
  871.     }, MenuItemId)
  872. end)
  873.  
  874. local function isPlayerAllowedForOutfitRoom(outfitRoom)
  875.     local isAllowed = false
  876.     local count = #outfitRoom.citizenIDs
  877.     for i = 1, count, 1 do
  878.         if Framework.IsPlayerAllowed(outfitRoom.citizenIDs[i]) then
  879.             isAllowed = true
  880.             break
  881.         end
  882.     end
  883.     return isAllowed or not outfitRoom.citizenIDs or count == 0
  884. end
  885.  
  886. local function OpenOutfitRoom(outfitRoom)
  887.     local isAllowed = isPlayerAllowedForOutfitRoom(outfitRoom)
  888.     if isAllowed then
  889.         TriggerEvent("qb-clothing:client:openOutfitMenu")
  890.     end
  891. end
  892.  
  893. local function getPlayerJobOutfits(clothingRoom)
  894.     local outfits = {}
  895.     local gender = Framework.GetGender()
  896.     local gradeLevel = clothingRoom.job and Framework.GetJobGrade() or Framework.GetGangGrade()
  897.     local jobName = clothingRoom.job and client.job.name or client.gang.name
  898.  
  899.     if Config.BossManagedOutfits then
  900.         local mType = clothingRoom.job and "Job" or "Gang"
  901.         local result = lib.callback.await("illenium-appearance:server:getManagementOutfits", false, mType, gender)
  902.         for i = 1, #result, 1 do
  903.             outfits[#outfits + 1] = {
  904.                 type = mType,
  905.                 model = result[i].model,
  906.                 components = result[i].components,
  907.                 props = result[i].props,
  908.                 disableSave = true,
  909.                 name = result[i].name
  910.             }
  911.         end
  912.     else
  913.         for i = 1, #Config.Outfits[jobName][gender], 1 do
  914.             for _, v in pairs(Config.Outfits[jobName][gender][i].grades) do
  915.                 if v == gradeLevel then
  916.                     outfits[#outfits + 1] = Config.Outfits[jobName][gender][i]
  917.                     outfits[#outfits].gender = gender
  918.                     outfits[#outfits].jobName = jobName
  919.                 end
  920.             end
  921.         end
  922.     end
  923.  
  924.     return outfits
  925. end
  926.  
  927. RegisterNetEvent("illenium-appearance:client:OpenClothingRoom", function()
  928.     local clothingRoom = Config.ClothingRooms[currentZone.index]
  929.     local outfits = getPlayerJobOutfits(clothingRoom)
  930.     TriggerEvent("illenium-appearance:client:openJobOutfitsMenu", outfits)
  931. end)
  932.  
  933. RegisterNetEvent("illenium-appearance:client:OpenPlayerOutfitRoom", function()
  934.     local outfitRoom = Config.PlayerOutfitRooms[currentZone.index]
  935.     OpenOutfitRoom(outfitRoom)
  936. end)
  937.  
  938. local function CheckDuty()
  939.     return not Config.OnDutyOnlyClothingRooms or (Config.OnDutyOnlyClothingRooms and client.job.onduty)
  940. end
  941.  
  942. local function lookupZoneIndexFromID(zones, id)
  943.     for i = 1, #zones do
  944.         if zones[i].id == id then
  945.             return i
  946.         end
  947.     end
  948. end
  949.  
  950. local function onStoreEnter(data)
  951.     local index = lookupZoneIndexFromID(Zones.Store, data.id)
  952.     local store = Config.Stores[index]
  953.  
  954.     local jobName = (store.job and client.job.name) or (store.gang and client.gang.name)
  955.     if jobName == (store.job or store.gang) then
  956.         currentZone = {
  957.             name = store.type,
  958.             index = index
  959.         }
  960.         local prefix = Config.UseRadialMenu and "" or "[E] "
  961.         if currentZone.name == "clothing" then
  962.             lib.showTextUI(prefix .. "Clothing Store - Price: $" .. Config.ClothingCost, Config.TextUIOptions)
  963.         elseif currentZone.name == "barber" then
  964.             lib.showTextUI(prefix .. "Barber - Price: $" .. Config.BarberCost, Config.TextUIOptions)
  965.         elseif currentZone.name == "tattoo" then
  966.             lib.showTextUI(prefix .. "Tattoo Shop - Price: $" .. Config.TattooCost, Config.TextUIOptions)
  967.         elseif currentZone.name == "surgeon" then
  968.             lib.showTextUI(prefix .. "Plastic Surgeon - Price: $" .. Config.SurgeonCost, Config.TextUIOptions)
  969.         end
  970.     end
  971. end
  972.  
  973. local function onClothingRoomEnter(data)
  974.     local index = lookupZoneIndexFromID(Zones.ClothingRoom, data.id)
  975.     local clothingRoom = Config.ClothingRooms[index]
  976.  
  977.     local jobName = clothingRoom.job and client.job.name or client.gang.name
  978.     if jobName == (clothingRoom.job or clothingRoom.gang) then
  979.         if CheckDuty() or clothingRoom.gang then
  980.             currentZone = {
  981.                 name = "clothingRoom",
  982.                 index = index
  983.             }
  984.             local prefix = Config.UseRadialMenu and "" or "[E] "
  985.             lib.showTextUI(prefix .. "Clothing Room", Config.TextUIOptions)
  986.         end
  987.     end
  988. end
  989.  
  990. local function onPlayerOutfitRoomEnter(data)
  991.     local index = lookupZoneIndexFromID(Zones.PlayerOutfitRoom, data.id)
  992.     local playerOutfitRoom = Config.PlayerOutfitRooms[index]
  993.  
  994.     local isAllowed = isPlayerAllowedForOutfitRoom(playerOutfitRoom)
  995.     if isAllowed then
  996.         currentZone = {
  997.             name = "playerOutfitRoom",
  998.             index = index
  999.         }
  1000.         local prefix = Config.UseRadialMenu and "" or "[E] "
  1001.         lib.showTextUI(prefix .. "Outfits", Config.TextUIOptions)
  1002.     end
  1003. end
  1004.  
  1005. local function onZoneExit()
  1006.     currentZone = nil
  1007.     lib.hideTextUI()
  1008. end
  1009.  
  1010. local function SetupZone(store, onEnter, onExit)
  1011.     if Config.RCoreTattoosCompatibility and store.type == "tattoo" then
  1012.         return
  1013.     end
  1014.  
  1015.     if Config.UseRadialMenu or store.usePoly then
  1016.         return lib.zones.poly({
  1017.             points = store.points,
  1018.             debug = Config.Debug,
  1019.             onEnter = onEnter,
  1020.             onExit = onExit
  1021.         })
  1022.     end
  1023.  
  1024.     return lib.zones.box({
  1025.         coords = store.coords,
  1026.         size = store.size,
  1027.         rotation = store.rotation,
  1028.         debug = Config.Debug,
  1029.         onEnter = onEnter,
  1030.         onExit = onExit
  1031.     })
  1032. end
  1033.  
  1034. local function SetupStoreZones()
  1035.     for _, v in pairs(Config.Stores) do
  1036.         Zones.Store[#Zones.Store + 1] = SetupZone(v, onStoreEnter, onZoneExit)
  1037.     end
  1038. end
  1039.  
  1040. local function SetupClothingRoomZones()
  1041.     for _, v in pairs(Config.ClothingRooms) do
  1042.         Zones.ClothingRoom[#Zones.ClothingRoom + 1] = SetupZone(v, onClothingRoomEnter, onZoneExit)
  1043.     end
  1044. end
  1045.  
  1046. local function SetupPlayerOutfitRoomZones()
  1047.     for _, v in pairs(Config.PlayerOutfitRooms) do
  1048.         Zones.PlayerOutfitRoom[#Zones.PlayerOutfitRoom + 1] = SetupZone(v, onPlayerOutfitRoomEnter, onZoneExit)
  1049.     end
  1050. end
  1051.  
  1052. local function SetupZones()
  1053.     SetupStoreZones()
  1054.     SetupClothingRoomZones()
  1055.     SetupPlayerOutfitRoomZones()
  1056. end
  1057.  
  1058. local function EnsurePedModel(pedModel)
  1059.     RequestModel(pedModel)
  1060.     while not HasModelLoaded(pedModel) do
  1061.         Wait(10)
  1062.     end
  1063. end
  1064.  
  1065. local function CreatePedAtCoords(pedModel, coords, scenario)
  1066.     pedModel = type(pedModel) == "string" and joaat(pedModel) or pedModel
  1067.     EnsurePedModel(pedModel)
  1068.     local ped = CreatePed(0, pedModel, coords.x, coords.y, coords.z - 0.98, coords.w, false, false)
  1069.     TaskStartScenarioInPlace(ped, scenario, true)
  1070.     FreezeEntityPosition(ped, true)
  1071.     SetEntityVisible(ped, true)
  1072.     SetEntityInvincible(ped, true)
  1073.     PlaceObjectOnGroundProperly(ped)
  1074.     SetBlockingOfNonTemporaryEvents(ped, true)
  1075.     return ped
  1076. end
  1077.  
  1078. local function SetupStoreTarget(targetConfig, action, k, v)
  1079.     local parameters = {
  1080.         options = {{
  1081.             type = "client",
  1082.             action = action,
  1083.             icon = targetConfig.icon,
  1084.             label = targetConfig.label
  1085.         }},
  1086.         distance = targetConfig.distance,
  1087.         rotation = v.rotation
  1088.     }
  1089.  
  1090.     if Config.EnablePedsForShops then
  1091.         TargetPeds.Store[k] = CreatePedAtCoords(v.targetModel or targetConfig.model, v.coords, v.targetScenario or targetConfig.scenario)
  1092.         Target.AddTargetEntity(TargetPeds.Store[k], parameters)
  1093.     else
  1094.         Target.AddBoxZone(v.type .. k, v.coords, v.size, parameters)
  1095.     end
  1096. end
  1097.  
  1098. local function SetupStoreTargets()
  1099.     for k, v in pairs(Config.Stores) do
  1100.         local targetConfig = Config.TargetConfig[v.type]
  1101.         local action
  1102.  
  1103.         if v.type == "barber" then
  1104.             action = OpenBarberShop
  1105.         elseif v.type == "clothing" then
  1106.             action = function()
  1107.                 TriggerEvent("illenium-appearance:client:openClothingShopMenu")
  1108.             end
  1109.         elseif v.type == "tattoo" then
  1110.             action = OpenTattooShop
  1111.         elseif v.type == "surgeon" then
  1112.             action = OpenSurgeonShop
  1113.         end
  1114.  
  1115.         if not (Config.RCoreTattoosCompatibility and v.type == "tattoo") then
  1116.             SetupStoreTarget(targetConfig, action, k, v)
  1117.         end
  1118.     end
  1119. end
  1120.  
  1121. local function SetupClothingRoomTargets()
  1122.     for k, v in pairs(Config.ClothingRooms) do
  1123.         local targetConfig = Config.TargetConfig["clothingroom"]
  1124.         local action = function()
  1125.             local outfits = getPlayerJobOutfits(v)
  1126.             TriggerEvent("illenium-appearance:client:openJobOutfitsMenu", outfits)
  1127.         end
  1128.  
  1129.         local parameters = {
  1130.             options = {{
  1131.                 type = "client",
  1132.                 action = action,
  1133.                 icon = targetConfig.icon,
  1134.                 label = targetConfig.label,
  1135.                 canInteract = v.job and CheckDuty or nil,
  1136.                 job = v.job,
  1137.                 gang = v.gang
  1138.             }},
  1139.             distance = targetConfig.distance,
  1140.             rotation = v.rotation
  1141.         }
  1142.  
  1143.         if Config.EnablePedsForClothingRooms then
  1144.             TargetPeds.ClothingRoom[k] = CreatePedAtCoords(v.targetModel or targetConfig.model, v.coords, v.targetScenario or targetConfig.scenario)
  1145.             Target.AddTargetEntity(TargetPeds.ClothingRoom[k], parameters)
  1146.         else
  1147.             local key = "clothing_" .. (v.job or v.gang) .. k
  1148.             Target.AddBoxZone(key, v.coords, v.size, parameters)
  1149.         end
  1150.     end
  1151. end
  1152.  
  1153. local function SetupPlayerOutfitRoomTargets()
  1154.     for k, v in pairs(Config.PlayerOutfitRooms) do
  1155.         local targetConfig = Config.TargetConfig["playeroutfitroom"]
  1156.  
  1157.         local parameters = {
  1158.             options = {{
  1159.                 type = "client",
  1160.                 action = function()
  1161.                     OpenOutfitRoom(v)
  1162.                 end,
  1163.                 icon = targetConfig.icon,
  1164.                 label = targetConfig.label,
  1165.                 canInteract = function()
  1166.                     return isPlayerAllowedForOutfitRoom(v)
  1167.                 end
  1168.             }},
  1169.             distance = targetConfig.distance,
  1170.             rotation = v.rotation
  1171.         }
  1172.  
  1173.         if Config.EnablePedsForPlayerOutfitRooms then
  1174.             TargetPeds.PlayerOutfitRoom[k] = CreatePedAtCoords(v.targetModel or targetConfig.model, v.coords, v.targetScenario or targetConfig.scenario)
  1175.             Target.AddTargetEntity(TargetPeds.ClothingRoom[k], parameters)
  1176.         else
  1177.             Target.AddBoxZone("playeroutfitroom_" .. k, v.coords, v.size, parameters)
  1178.         end
  1179.     end
  1180. end
  1181.  
  1182. local function SetupTargets()
  1183.     SetupStoreTargets()
  1184.     SetupClothingRoomTargets()
  1185.     SetupPlayerOutfitRoomTargets()
  1186. end
  1187.  
  1188. local function ZonesLoop()
  1189.     Wait(1000)
  1190.     while true do
  1191.         local sleep = 1000
  1192.         if currentZone then
  1193.             sleep = 5
  1194.             if IsControlJustReleased(0, 38) then
  1195.                 if currentZone.name == "clothingRoom" then
  1196.                     local clothingRoom = Config.ClothingRooms[currentZone.index]
  1197.                     local outfits = getPlayerJobOutfits(clothingRoom)
  1198.                     TriggerEvent("illenium-appearance:client:openJobOutfitsMenu", outfits)
  1199.                 elseif currentZone.name == "playerOutfitRoom" then
  1200.                     local outfitRoom = Config.PlayerOutfitRooms[currentZone.index]
  1201.                     OpenOutfitRoom(outfitRoom)
  1202.                 elseif currentZone.name == "clothing" then
  1203.                     TriggerEvent("illenium-appearance:client:openClothingShopMenu")
  1204.                 elseif currentZone.name == "barber" then
  1205.                     OpenBarberShop()
  1206.                 elseif currentZone.name == "tattoo" then
  1207.                     OpenTattooShop()
  1208.                 elseif currentZone.name == "surgeon" then
  1209.                     OpenSurgeonShop()
  1210.                 end
  1211.             end
  1212.         end
  1213.         Wait(sleep)
  1214.     end
  1215. end
  1216.  
  1217. CreateThread(function()
  1218.     if Config.UseTarget then
  1219.         SetupTargets()
  1220.     else
  1221.         SetupZones()
  1222.         if not Config.UseRadialMenu then
  1223.             ZonesLoop()
  1224.         end
  1225.     end
  1226. end)
  1227.