This commit is contained in:
Hawk 2024-12-29 20:49:12 +01:00
parent 2678716618
commit 9a7bcfa270
No known key found for this signature in database
GPG Key ID: 2890D5366F8BAC14
691 changed files with 110377 additions and 0 deletions

View File

@ -0,0 +1,85 @@
local Blips = {}
local client = client
local function ShowBlip(blipConfig, blip)
if blip.job and blip.job ~= client.job.name then
return false
elseif blip.gang and blip.gang ~= client.gang.name then
return false
end
if Config.RCoreTattoosCompatibility and blip.type == "tattoo" then
return false
end
return (blipConfig.Show and blip.showBlip == nil) or blip.showBlip
end
local function CreateBlip(blipConfig, coords)
local blip = AddBlipForCoord(coords.x, coords.y, coords.z)
SetBlipSprite(blip, blipConfig.Sprite)
SetBlipColour(blip, blipConfig.Color)
SetBlipScale(blip, blipConfig.Scale)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString(blipConfig.Name)
EndTextCommandSetBlipName(blip)
return blip
end
local function SetupBlips()
for k, _ in pairs(Config.Stores) do
local blipConfig = Config.Blips[Config.Stores[k].type]
if ShowBlip(blipConfig, Config.Stores[k]) then
local blip = CreateBlip(blipConfig, Config.Stores[k].coords)
Blips[#Blips + 1] = blip
end
end
end
function ResetBlips()
if Config.ShowNearestShopOnly then
return
end
for i = 1, #Blips do
RemoveBlip(Blips[i])
end
Blips = {}
SetupBlips()
end
local function ShowNearestShopBlip()
for k in pairs(Config.Blips) do
Blips[k] = 0
end
while true do
local coords = GetEntityCoords(cache.ped)
for shopType, blipConfig in pairs(Config.Blips) do
local closest = 1000000
local closestCoords
for _, shop in pairs(Config.Stores) do
if shop.type == shopType and ShowBlip(blipConfig, shop) then
local dist = #(coords - vector3(shop.coords.xyz))
if dist < closest then
closest = dist
closestCoords = shop.coords
end
end
end
if DoesBlipExist(Blips[shopType]) then
RemoveBlip(Blips[shopType])
end
if closestCoords then
Blips[shopType] = CreateBlip(blipConfig, closestCoords)
end
end
Wait(Config.NearestShopBlipUpdateDelay)
end
end
if Config.ShowNearestShopOnly then
CreateThread(ShowNearestShopBlip)
end

View File

@ -0,0 +1,756 @@
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)

View File

@ -0,0 +1,83 @@
function CheckDuty()
return not Config.OnDutyOnlyClothingRooms or (Config.OnDutyOnlyClothingRooms and client.job.onduty)
end
function IsPlayerAllowedForOutfitRoom(outfitRoom)
local isAllowed = false
local count = #outfitRoom.citizenIDs
for i = 1, count, 1 do
if Framework.IsPlayerAllowed(outfitRoom.citizenIDs[i]) then
isAllowed = true
break
end
end
return isAllowed or not outfitRoom.citizenIDs or count == 0
end
function GetPlayerJobOutfits(job)
local outfits = {}
local gender = Framework.GetGender()
local gradeLevel = job and Framework.GetJobGrade() or Framework.GetGangGrade()
local jobName = job and client.job.name or client.gang.name
if Config.BossManagedOutfits then
local mType = job and "Job" or "Gang"
local result = lib.callback.await("illenium-appearance:server:getManagementOutfits", false, mType, gender)
for i = 1, #result, 1 do
outfits[#outfits + 1] = {
type = mType,
model = result[i].model,
components = result[i].components,
props = result[i].props,
disableSave = true,
name = result[i].name
}
end
elseif Config.Outfits[jobName] and Config.Outfits[jobName][gender] then
for i = 1, #Config.Outfits[jobName][gender], 1 do
for _, v in pairs(Config.Outfits[jobName][gender][i].grades) do
if v == gradeLevel then
outfits[#outfits + 1] = Config.Outfits[jobName][gender][i]
outfits[#outfits].gender = gender
outfits[#outfits].jobName = jobName
end
end
end
end
return outfits
end
function OpenOutfitRoom(outfitRoom)
local isAllowed = IsPlayerAllowedForOutfitRoom(outfitRoom)
if isAllowed then
OpenMenu(nil, "outfit")
end
end
function OpenBarberShop()
local config = GetDefaultConfig()
config.headOverlays = true
OpenShop(config, false, "barber")
end
function OpenTattooShop()
local config = GetDefaultConfig()
config.tattoos = true
OpenShop(config, false, "tattoo")
end
function OpenSurgeonShop()
local config = GetDefaultConfig()
config.headBlend = true
config.faceFeatures = true
OpenShop(config, false, "surgeon")
end
AddEventHandler("onResourceStop", function(resource)
if resource == GetCurrentResourceName() then
if Config.BossManagedOutfits then
Management.RemoveItems()
end
end
end)

View File

@ -0,0 +1,41 @@
local function getComponentConfig()
return {
masks = not Config.DisableComponents.Masks,
upperBody = not Config.DisableComponents.UpperBody,
lowerBody = not Config.DisableComponents.LowerBody,
bags = not Config.DisableComponents.Bags,
shoes = not Config.DisableComponents.Shoes,
scarfAndChains = not Config.DisableComponents.ScarfAndChains,
bodyArmor = not Config.DisableComponents.BodyArmor,
shirts = not Config.DisableComponents.Shirts,
decals = not Config.DisableComponents.Decals,
jackets = not Config.DisableComponents.Jackets
}
end
local function getPropConfig()
return {
hats = not Config.DisableProps.Hats,
glasses = not Config.DisableProps.Glasses,
ear = not Config.DisableProps.Ear,
watches = not Config.DisableProps.Watches,
bracelets = not Config.DisableProps.Bracelets
}
end
function GetDefaultConfig()
return {
ped = false,
headBlend = false,
faceFeatures = false,
headOverlays = false,
components = false,
componentConfig = getComponentConfig(),
props = false,
propConfig = getPropConfig(),
tattoos = false,
enableExit = true,
hasTracker = Config.PreventTrackerRemoval and Framework.HasTracker(),
automaticFade = Config.AutomaticFade
}
end

View File

@ -0,0 +1,57 @@
if not Framework.ESX() then return end
local client = client
local firstSpawn = false
AddEventHandler("esx_skin:resetFirstSpawn", function()
firstSpawn = true
end)
AddEventHandler("esx_skin:playerRegistered", function()
if(firstSpawn) then
InitializeCharacter(Framework.GetGender(true))
end
end)
RegisterNetEvent("skinchanger:loadSkin2", function(ped, skin)
if not skin.model then skin.model = "mp_m_freemode_01" end
client.setPedAppearance(ped, skin)
Framework.CachePed()
end)
RegisterNetEvent("skinchanger:getSkin", function(cb)
while not Framework.PlayerData do
Wait(1000)
end
lib.callback("illenium-appearance:server:getAppearance", false, function(appearance)
cb(appearance)
Framework.CachePed()
end)
end)
RegisterNetEvent("skinchanger:loadSkin", function(skin, cb)
if skin.model then
client.setPlayerAppearance(skin)
else -- add validation invisible when failed registration (maybe server restarted when apply skin)
SetInitialClothes(Config.InitialPlayerClothes[Framework.GetGender(true)])
end
if Framework.PlayerData and Framework.PlayerData.loadout then
TriggerEvent("esx:restoreLoadout")
end
Framework.CachePed()
if cb ~= nil then
cb()
end
end)
RegisterNetEvent("skinchanger:loadClothes", function(_, clothes)
local components = Framework.ConvertComponents(clothes, client.getPedComponents(cache.ped))
local props = Framework.ConvertProps(clothes, client.getPedProps(cache.ped))
client.setPedComponents(cache.ped, components)
client.setPedProps(cache.ped, props)
end)
RegisterNetEvent("esx_skin:openSaveableMenu", function(onSubmit, onCancel)
InitializeCharacter(Framework.GetGender(true), onSubmit, onCancel)
end)

View File

@ -0,0 +1,85 @@
if not Framework.ESX() then return end
local ESX = exports["es_extended"]:getSharedObject()
Framework.PlayerData = nil
RegisterNetEvent("esx:playerLoaded", function(xPlayer)
Framework.PlayerData = xPlayer
client.job = Framework.PlayerData.job
client.gang = Framework.PlayerData.gang
client.citizenid = Framework.PlayerData.identifier
InitAppearance()
end)
RegisterNetEvent("esx:onPlayerLogout", function()
Framework.PlayerData = nil
end)
RegisterNetEvent("esx:setJob", function(job)
Framework.PlayerData.job = job
client.job = Framework.PlayerData.job
client.gang = Framework.PlayerData.job
end)
local function getRankInputValues(rankList)
local rankValues = {}
for k, v in pairs(rankList) do
rankValues[#rankValues + 1] = {
label = v.label,
value = v.grade
}
end
return rankValues
end
function Framework.GetPlayerGender()
Framework.PlayerData = ESX.GetPlayerData()
if Framework.PlayerData.sex == "f" then
return "Female"
end
return "Male"
end
function Framework.UpdatePlayerData()
local data = ESX.GetPlayerData()
if data.job then
Framework.PlayerData = data
client.job = Framework.PlayerData.job
client.gang = Framework.PlayerData.job
end
client.citizenid = Framework.PlayerData.identifier
end
function Framework.HasTracker()
return false
end
function Framework.CheckPlayerMeta()
Framework.PlayerData = ESX.GetPlayerData()
return Framework.PlayerData.dead or IsPedCuffed(Framework.PlayerData.ped)
end
function Framework.IsPlayerAllowed(citizenid)
return citizenid == Framework.PlayerData.identifier
end
function Framework.GetRankInputValues(type)
local jobGrades = lib.callback.await("illenium-appearance:server:esx:getGradesForJob", false, client[type].name)
return getRankInputValues(jobGrades)
end
function Framework.GetJobGrade()
return client.job.grade
end
function Framework.GetGangGrade()
return client.gang.grade
end
function Framework.CachePed()
ESX.SetPlayerData("ped", cache.ped)
end
function Framework.RestorePlayerArmour()
return nil
end

View File

@ -0,0 +1,11 @@
function Framework.GetGender(isNew)
if isNew or not Config.GenderBasedOnPed then
return Framework.GetPlayerGender()
end
local model = client.getPedModel(cache.ped)
if model == "mp_f_freemode_01" then
return "Female"
end
return "Male"
end

View File

@ -0,0 +1,30 @@
if not Framework.QBCore() then return end
local client = client
-- Backwards Compatible Events
RegisterNetEvent("qb-clothing:client:openMenu", function()
local config = GetDefaultConfig()
config.ped = true
config.headBlend = true
config.faceFeatures = true
config.headOverlays = true
config.components = true
config.props = true
config.tattoos = true
OpenShop(config, true, "all")
end)
RegisterNetEvent("qb-clothing:client:openOutfitMenu", function()
OpenMenu(nil, "outfit")
end)
RegisterNetEvent("qb-clothing:client:loadOutfit", LoadJobOutfit)
RegisterNetEvent("qb-multicharacter:client:chooseChar", function()
client.setPedTattoos(cache.ped, {})
ClearPedDecorations(cache.ped)
TriggerServerEvent("illenium-appearance:server:resetOutfitCache")
end)

View File

@ -0,0 +1,107 @@
if not Framework.QBCore() then return end
local client = client
local QBCore = exports["qb-core"]:GetCoreObject()
local PlayerData = QBCore.Functions.GetPlayerData()
local function getRankInputValues(rankList)
local rankValues = {}
for k, v in pairs(rankList) do
rankValues[#rankValues + 1] = {
label = v.name,
value = k
}
end
return rankValues
end
local function setClientParams()
client.job = PlayerData.job
client.gang = PlayerData.gang
client.citizenid = PlayerData.citizenid
end
function Framework.GetPlayerGender()
if PlayerData.charinfo.gender == 1 then
return "Female"
end
return "Male"
end
function Framework.UpdatePlayerData()
PlayerData = QBCore.Functions.GetPlayerData()
setClientParams()
end
function Framework.HasTracker()
return QBCore.Functions.GetPlayerData().metadata["tracker"]
end
function Framework.CheckPlayerMeta()
return PlayerData.metadata["isdead"] or PlayerData.metadata["inlaststand"] or PlayerData.metadata["ishandcuffed"]
end
function Framework.IsPlayerAllowed(citizenid)
return citizenid == PlayerData.citizenid
end
function Framework.GetRankInputValues(type)
local grades = QBCore.Shared.Jobs[client.job.name].grades
if type == "gang" then
grades = QBCore.Shared.Gangs[client.gang.name].grades
end
return getRankInputValues(grades)
end
function Framework.GetJobGrade()
return client.job.grade.level
end
function Framework.GetGangGrade()
return client.gang.grade.level
end
RegisterNetEvent("QBCore:Client:OnJobUpdate", function(JobInfo)
PlayerData.job = JobInfo
client.job = JobInfo
ResetBlips()
end)
RegisterNetEvent("QBCore:Client:OnGangUpdate", function(GangInfo)
PlayerData.gang = GangInfo
client.gang = GangInfo
ResetBlips()
end)
RegisterNetEvent("QBCore:Client:SetDuty", function(duty)
if PlayerData and PlayerData.job then
PlayerData.job.onduty = duty
client.job = PlayerData.job
end
end)
RegisterNetEvent("QBCore:Client:OnPlayerLoaded", function()
InitAppearance()
end)
RegisterNetEvent("qb-clothes:client:CreateFirstCharacter", function()
QBCore.Functions.GetPlayerData(function(pd)
PlayerData = pd
setClientParams()
InitializeCharacter(Framework.GetGender(true))
end)
end)
function Framework.CachePed()
return nil
end
function Framework.RestorePlayerArmour()
Framework.UpdatePlayerData()
if PlayerData and PlayerData.metadata then
Wait(1000)
SetPedArmour(cache.ped, PlayerData.metadata["armor"])
end
end

View File

@ -0,0 +1,190 @@
if not Framework.QBCore() then return end
local client = client
local skinData = {
["face2"] = {
item = 0,
texture = 0,
defaultItem = 0,
defaultTexture = 0,
},
["facemix"] = {
skinMix = 0,
shapeMix = 0,
defaultSkinMix = 0.0,
defaultShapeMix = 0.0,
},
}
RegisterNetEvent("illenium-appearance:client:migration:load-qb-clothing-skin", function(playerSkin)
local model = playerSkin.model
model = model ~= nil and tonumber(model) or false
Citizen.CreateThread(function()
lib.requestModel(model, 1000)
SetPlayerModel(cache.playerId, model)
Wait(150)
SetPedComponentVariation(cache.ped, 0, 0, 0, 2)
TriggerEvent("illenium-appearance:client:migration:load-qb-clothing-clothes", playerSkin, cache.ped)
SetModelAsNoLongerNeeded(model)
end)
end)
RegisterNetEvent("illenium-appearance:client:migration:load-qb-clothing-clothes", function(playerSkin, ped)
local data = json.decode(playerSkin.skin)
if ped == nil then ped = cache.ped end
for i = 0, 11 do
SetPedComponentVariation(ped, i, 0, 0, 0)
end
for i = 0, 7 do
ClearPedProp(ped, i)
end
-- Face
if not data["facemix"] or not data["face2"] then
data["facemix"] = skinData["facemix"]
data["facemix"].shapeMix = data["facemix"].defaultShapeMix
data["facemix"].skinMix = data["facemix"].defaultSkinMix
data["face2"] = skinData["face2"]
end
SetPedHeadBlendData(ped, data["face"].item, data["face2"].item, nil, data["face"].texture, data["face2"].texture, nil, data["facemix"].shapeMix, data["facemix"].skinMix, nil, true)
-- Pants
SetPedComponentVariation(ped, 4, data["pants"].item, 0, 0)
SetPedComponentVariation(ped, 4, data["pants"].item, data["pants"].texture, 0)
-- Hair
SetPedComponentVariation(ped, 2, data["hair"].item, 0, 0)
SetPedHairColor(ped, data["hair"].texture, data["hair"].texture)
-- Eyebrows
SetPedHeadOverlay(ped, 2, data["eyebrows"].item, 1.0)
SetPedHeadOverlayColor(ped, 2, 1, data["eyebrows"].texture, 0)
-- Beard
SetPedHeadOverlay(ped, 1, data["beard"].item, 1.0)
SetPedHeadOverlayColor(ped, 1, 1, data["beard"].texture, 0)
-- Blush
SetPedHeadOverlay(ped, 5, data["blush"].item, 1.0)
SetPedHeadOverlayColor(ped, 5, 1, data["blush"].texture, 0)
-- Lipstick
SetPedHeadOverlay(ped, 8, data["lipstick"].item, 1.0)
SetPedHeadOverlayColor(ped, 8, 1, data["lipstick"].texture, 0)
-- Makeup
SetPedHeadOverlay(ped, 4, data["makeup"].item, 1.0)
SetPedHeadOverlayColor(ped, 4, 1, data["makeup"].texture, 0)
-- Ageing
SetPedHeadOverlay(ped, 3, data["ageing"].item, 1.0)
SetPedHeadOverlayColor(ped, 3, 1, data["ageing"].texture, 0)
-- Arms
SetPedComponentVariation(ped, 3, data["arms"].item, 0, 2)
SetPedComponentVariation(ped, 3, data["arms"].item, data["arms"].texture, 0)
-- T-Shirt
SetPedComponentVariation(ped, 8, data["t-shirt"].item, 0, 2)
SetPedComponentVariation(ped, 8, data["t-shirt"].item, data["t-shirt"].texture, 0)
-- Vest
SetPedComponentVariation(ped, 9, data["vest"].item, 0, 2)
SetPedComponentVariation(ped, 9, data["vest"].item, data["vest"].texture, 0)
-- Torso 2
SetPedComponentVariation(ped, 11, data["torso2"].item, 0, 2)
SetPedComponentVariation(ped, 11, data["torso2"].item, data["torso2"].texture, 0)
-- Shoes
SetPedComponentVariation(ped, 6, data["shoes"].item, 0, 2)
SetPedComponentVariation(ped, 6, data["shoes"].item, data["shoes"].texture, 0)
-- Mask
SetPedComponentVariation(ped, 1, data["mask"].item, 0, 2)
SetPedComponentVariation(ped, 1, data["mask"].item, data["mask"].texture, 0)
-- Badge
SetPedComponentVariation(ped, 10, data["decals"].item, 0, 2)
SetPedComponentVariation(ped, 10, data["decals"].item, data["decals"].texture, 0)
-- Accessory
SetPedComponentVariation(ped, 7, data["accessory"].item, 0, 2)
SetPedComponentVariation(ped, 7, data["accessory"].item, data["accessory"].texture, 0)
-- Bag
SetPedComponentVariation(ped, 5, data["bag"].item, 0, 2)
SetPedComponentVariation(ped, 5, data["bag"].item, data["bag"].texture, 0)
-- Hat
if data["hat"].item ~= -1 and data["hat"].item ~= 0 then
SetPedPropIndex(ped, 0, data["hat"].item, data["hat"].texture, true)
else
ClearPedProp(ped, 0)
end
-- Glass
if data["glass"].item ~= -1 and data["glass"].item ~= 0 then
SetPedPropIndex(ped, 1, data["glass"].item, data["glass"].texture, true)
else
ClearPedProp(ped, 1)
end
-- Ear
if data["ear"].item ~= -1 and data["ear"].item ~= 0 then
SetPedPropIndex(ped, 2, data["ear"].item, data["ear"].texture, true)
else
ClearPedProp(ped, 2)
end
-- Watch
if data["watch"].item ~= -1 and data["watch"].item ~= 0 then
SetPedPropIndex(ped, 6, data["watch"].item, data["watch"].texture, true)
else
ClearPedProp(ped, 6)
end
-- Bracelet
if data["bracelet"].item ~= -1 and data["bracelet"].item ~= 0 then
SetPedPropIndex(ped, 7, data["bracelet"].item, data["bracelet"].texture, true)
else
ClearPedProp(ped, 7)
end
if data["eye_color"].item ~= -1 and data["eye_color"].item ~= 0 then
SetPedEyeColor(ped, data["eye_color"].item)
end
if data["moles"].item ~= -1 and data["moles"].item ~= 0 then
SetPedHeadOverlay(ped, 9, data["moles"].item, (data["moles"].texture / 10))
end
SetPedFaceFeature(ped, 0, (data["nose_0"].item / 10))
SetPedFaceFeature(ped, 1, (data["nose_1"].item / 10))
SetPedFaceFeature(ped, 2, (data["nose_2"].item / 10))
SetPedFaceFeature(ped, 3, (data["nose_3"].item / 10))
SetPedFaceFeature(ped, 4, (data["nose_4"].item / 10))
SetPedFaceFeature(ped, 5, (data["nose_5"].item / 10))
SetPedFaceFeature(ped, 6, (data["eyebrown_high"].item / 10))
SetPedFaceFeature(ped, 7, (data["eyebrown_forward"].item / 10))
SetPedFaceFeature(ped, 8, (data["cheek_1"].item / 10))
SetPedFaceFeature(ped, 9, (data["cheek_2"].item / 10))
SetPedFaceFeature(ped, 10,(data["cheek_3"].item / 10))
SetPedFaceFeature(ped, 11, (data["eye_opening"].item / 10))
SetPedFaceFeature(ped, 12, (data["lips_thickness"].item / 10))
SetPedFaceFeature(ped, 13, (data["jaw_bone_width"].item / 10))
SetPedFaceFeature(ped, 14, (data["jaw_bone_back_lenght"].item / 10))
SetPedFaceFeature(ped, 15, (data["chimp_bone_lowering"].item / 10))
SetPedFaceFeature(ped, 16, (data["chimp_bone_lenght"].item / 10))
SetPedFaceFeature(ped, 17, (data["chimp_bone_width"].item / 10))
SetPedFaceFeature(ped, 18, (data["chimp_hole"].item / 10))
SetPedFaceFeature(ped, 19, (data["neck_thikness"].item / 10))
local appearance = client.getPedAppearance(ped)
TriggerServerEvent("illenium-appearance:server:migrate-qb-clothing-skin", playerSkin.citizenid, appearance)
end)

View File

@ -0,0 +1,27 @@
if not Config.BossManagedOutfits then return end
if Framework.ESX() then return end
function Management.RemoveItems()
if GetResourceState(Management.ResourceName) ~= "started" then return end
if Management.ItemIDs.Boss then
exports[Management.ResourceName]:RemoveBossMenuItem(Management.ItemIDs.Boss)
end
if Management.ItemIDs.Gang then
exports[Management.ResourceName]:RemoveGangMenuItem(Management.ItemIDs.Gang)
end
end
function Management.AddBackMenuItem(managementMenu, args)
local bossMenuEvent = "qb-bossmenu:client:OpenMenu"
if args.type == "Gang" then
bossMenuEvent = "qb-gangmenu:client:OpenMenu"
end
managementMenu.options[#managementMenu.options+1] = {
title = _L("menu.returnTitle"),
icon = "fa-solid fa-angle-left",
event = bossMenuEvent
}
end

View File

@ -0,0 +1,15 @@
if not Config.BossManagedOutfits then return end
if not Framework.ESX() then return end
function Management.RemoveItems()
-- Do nothing
end
function Management.AddItems()
-- Do nothing
end
function Management.AddBackMenuItem()
-- Do nothing
end

View File

@ -0,0 +1,26 @@
if not Config.BossManagedOutfits then return end
Management = {}
Management.ItemIDs = {
Gang = nil,
Boss = nil
}
function Management.IsQB()
local resName = "qb-management"
if GetResourceState(resName) ~= "missing" then
Management.ResourceName = resName
return true
end
return false
end
function Management.IsQBX()
local resName = "qbx_management"
if GetResourceState(resName) ~= "missing" then
Management.ResourceName = resName
return true
end
return false
end

View File

@ -0,0 +1,21 @@
if not Config.BossManagedOutfits then return end
if not Management.IsQB() then return end
function Management.AddItems()
local menuItem = {
header = _L("outfitManagement.title"),
icon = "fa-solid fa-shirt",
params = {
event = "illenium-appearance:client:OutfitManagementMenu",
args = {}
}
}
menuItem.txt = _L("outfitManagement.jobText")
menuItem.params.args.type = "Job"
Management.ItemIDs.Boss = exports[Management.ResourceName]:AddBossMenuItem(menuItem)
menuItem.txt = _L("outfitManagement.gangText")
menuItem.params.args.type = "Gang"
Management.ItemIDs.Gang = exports[Management.ResourceName]:AddGangMenuItem(menuItem)
end

View File

@ -0,0 +1,19 @@
if not Config.BossManagedOutfits then return end
if not Management.IsQBX() then return end
function Management.AddItems()
local menuItem = {
title = _L("outfitManagement.title"),
icon = "fa-solid fa-shirt",
event = "illenium-appearance:client:OutfitManagementMenu",
args = {}
}
menuItem.description = _L("outfitManagement.jobText")
menuItem.args.type = "Job"
Management.ItemIDs.Boss = exports[Management.ResourceName]:AddBossMenuItem(menuItem)
menuItem.description = _L("outfitManagement.gangText")
menuItem.args.type = "Gang"
Management.ItemIDs.Gang = exports[Management.ResourceName]:AddGangMenuItem(menuItem)
end

View File

@ -0,0 +1,142 @@
local function typeof(var)
local _type = type(var);
if (_type ~= "table" and _type ~= "userdata") then
return _type;
end
local _meta = getmetatable(var);
if (_meta ~= nil and _meta._NAME ~= nil) then
return _meta._NAME;
else
return _type;
end
end
function LoadJobOutfit(oData)
local ped = cache.ped
local data = oData.outfitData
if typeof(data) ~= "table" then
data = json.decode(data)
end
-- Pants
if data["pants"] ~= nil then
SetPedComponentVariation(ped, 4, data["pants"].item, data["pants"].texture, 0)
end
-- Arms
if data["arms"] ~= nil then
SetPedComponentVariation(ped, 3, data["arms"].item, data["arms"].texture, 0)
end
-- T-Shirt
if data["t-shirt"] ~= nil then
SetPedComponentVariation(ped, 8, data["t-shirt"].item, data["t-shirt"].texture, 0)
end
-- Vest
if data["vest"] ~= nil then
SetPedComponentVariation(ped, 9, data["vest"].item, data["vest"].texture, 0)
end
-- Torso 2
if data["torso2"] ~= nil then
SetPedComponentVariation(ped, 11, data["torso2"].item, data["torso2"].texture, 0)
end
-- Shoes
if data["shoes"] ~= nil then
SetPedComponentVariation(ped, 6, data["shoes"].item, data["shoes"].texture, 0)
end
-- Badge
if data["decals"] ~= nil then
SetPedComponentVariation(ped, 10, data["decals"].item, data["decals"].texture, 0)
end
-- Accessory
local tracker = Config.TrackerClothingOptions
if data["accessory"] ~= nil then
if Framework.HasTracker() then
SetPedComponentVariation(ped, 7, tracker.drawable, tracker.texture, 0)
else
SetPedComponentVariation(ped, 7, data["accessory"].item, data["accessory"].texture, 0)
end
else
if Framework.HasTracker() then
SetPedComponentVariation(ped, 7, tracker.drawable, tracker.texture, 0)
else
local drawableId = GetPedDrawableVariation(ped, 7)
if drawableId ~= -1 then
local textureId = GetPedTextureVariation(ped, 7)
if drawableId == tracker.drawable and textureId == tracker.texture then
SetPedComponentVariation(ped, 7, -1, 0, 2)
end
end
end
end
-- Mask
if data["mask"] ~= nil then
SetPedComponentVariation(ped, 1, data["mask"].item, data["mask"].texture, 0)
end
-- Bag
if data["bag"] ~= nil then
SetPedComponentVariation(ped, 5, data["bag"].item, data["bag"].texture, 0)
end
-- Hat
if data["hat"] ~= nil then
if data["hat"].item ~= -1 and data["hat"].item ~= 0 then
SetPedPropIndex(ped, 0, data["hat"].item, data["hat"].texture, true)
else
ClearPedProp(ped, 0)
end
end
-- Glass
if data["glass"] ~= nil then
if data["glass"].item ~= -1 and data["glass"].item ~= 0 then
SetPedPropIndex(ped, 1, data["glass"].item, data["glass"].texture, true)
else
ClearPedProp(ped, 1)
end
end
-- Ear
if data["ear"] ~= nil then
if data["ear"].item ~= -1 and data["ear"].item ~= 0 then
SetPedPropIndex(ped, 2, data["ear"].item, data["ear"].texture, true)
else
ClearPedProp(ped, 2)
end
end
local length = 0
for _ in pairs(data) do
length = length + 1
end
if Config.PersistUniforms and length > 1 then
TriggerServerEvent("illenium-appearance:server:syncUniform", {
jobName = oData.jobName,
gender = oData.gender,
label = oData.name
})
end
end
RegisterNetEvent("illenium-appearance:client:loadJobOutfit", LoadJobOutfit)
RegisterNetEvent("illenium-appearance:client:openOutfitMenu", function()
OpenMenu(nil, "outfit")
end)
RegisterNetEvent("illenium-apearance:client:outfitsCommand", function(isJob)
local outfits = GetPlayerJobOutfits(isJob)
TriggerEvent("illenium-appearance:client:openJobOutfitsMenu", outfits)
end)

View File

@ -0,0 +1,5 @@
lib.onCache('ped', function(value)
if Config.AlwaysKeepProps then
SetPedCanLosePropsOnDamage(value, false, 0)
end
end)

View File

@ -0,0 +1,17 @@
if not Radial.IsOX() then return end
function Radial.Add(title, event)
lib.addRadialItem({
id = Radial.MenuID,
icon = "shirt",
label = title,
event = event,
onSelect = function()
TriggerEvent(event)
end
})
end
function Radial.Remove()
lib.removeRadialItem(Radial.MenuID)
end

View File

@ -0,0 +1,16 @@
if not Radial.IsQBX() and not Radial.IsQB() then return end
function Radial.Add(title, event)
exports[Radial.ResourceName]:AddOption({
id = Radial.MenuID,
title = title,
icon = "shirt",
type = "client",
event = event,
shouldClose = true
}, Radial.MenuID)
end
function Radial.Remove()
exports[Radial.ResourceName]:RemoveOption(Radial.MenuID)
end

View File

@ -0,0 +1,71 @@
Radial = {}
Radial.MenuID = "open_clothing_menu"
local radialOptionAdded = false
function Radial.IsOX()
local resName = "ox_lib"
if GetResourceState(resName) ~= "missing" and Config.UseOxRadial then
Radial.ResourceName = resName
return true
end
return false
end
function Radial.IsQB()
local resName = "qb-radialmenu"
if GetResourceState(resName) ~= "missing" then
Radial.ResourceName = resName
return true
end
return false
end
function Radial.IsQBX()
local resName = "qbx_radialmenu"
if GetResourceState(resName) ~= "missing" then
Radial.ResourceName = resName
return true
end
return false
end
function Radial.AddOption(currentZone)
if not Config.UseRadialMenu then return end
if not currentZone then
Radial.Remove()
return
end
local event, title
local zoneEvents = {
clothingRoom = {"illenium-appearance:client:OpenClothingRoom", _L("menu.title")},
playerOutfitRoom = {"illenium-appearance:client:OpenPlayerOutfitRoom", _L("menu.outfitsTitle")},
clothing = {"illenium-appearance:client:openClothingShopMenu", _L("menu.clothingShopTitle")},
barber = {"illenium-appearance:client:OpenBarberShop", _L("menu.barberShopTitle")},
tattoo = {"illenium-appearance:client:OpenTattooShop", _L("menu.tattooShopTitle")},
surgeon = {"illenium-appearance:client:OpenSurgeonShop", _L("menu.surgeonShopTitle")},
}
if zoneEvents[currentZone.name] then
event, title = table.unpack(zoneEvents[currentZone.name])
end
Radial.Add(title, event)
radialOptionAdded = true
end
function Radial.RemoveOption()
if radialOptionAdded then
Radial.Remove()
radialOptionAdded = false
end
end
AddEventHandler("onResourceStop", function(resource)
if resource == GetCurrentResourceName() then
if Config.UseOxRadial and GetResourceState("ox_lib") == "started" or GetResourceState("qb-radialmenu") == "started" then
Radial.RemoveOption()
end
end
end)

View File

@ -0,0 +1,28 @@
local stats = nil
local function ResetRechargeMultipliers()
SetPlayerHealthRechargeMultiplier(cache.playerId, 0.0)
SetPlayerHealthRechargeLimit(cache.playerId, 0.0)
end
function BackupPlayerStats()
stats = {
health = GetEntityHealth(cache.ped),
armour = GetPedArmour(cache.ped)
}
end
function RestorePlayerStats()
if stats then
SetEntityMaxHealth(cache.ped, 200)
Wait(1000) -- Safety Delay
SetEntityHealth(cache.ped, stats.health)
SetPedArmour(cache.ped, stats.armour)
ResetRechargeMultipliers()
stats = nil
return
end
-- If no stats are backed up, restore from the framework
Framework.RestorePlayerArmour()
end

View File

@ -0,0 +1,55 @@
if not Config.UseTarget then return end
if not Target.IsOX() then return end
local ZoneIDMap = {}
local function convert(options)
local distance = options.distance
options = options.options
for _, v in pairs(options) do
v.onSelect = v.action
v.distance = v.distance or distance
v.name = v.name or v.label
v.groups = v.job or v.gang
v.type = nil
v.action = nil
v.job = nil
v.gang = nil
v.qtarget = true
end
return options
end
function Target.RemoveZone(zone)
exports["ox_target"]:removeZone(ZoneIDMap[zone])
end
function Target.AddTargetEntity(entity, parameters)
exports["ox_target"]:addLocalEntity(entity, convert(parameters))
end
function Target.AddBoxZone(name, coords, size, parameters)
local rotation = parameters.rotation
ZoneIDMap[name] = exports["ox_target"]:addBoxZone({
coords = coords,
size = size,
rotation = rotation,
debug = Config.Debug,
options = convert(parameters)
})
end
function Target.AddPolyZone(name, points, parameters)
ZoneIDMap[name] = exports["ox_target"]:addPolyZone({
points = points,
debug = Config.Debug,
options = convert(parameters)
})
end
function Target.IsTargetStarted()
return GetResourceState("ox_target") == "started"
end

View File

@ -0,0 +1,34 @@
if not Config.UseTarget then return end
if not Target.IsQB() then return end
function Target.RemoveZone(zone)
exports["qb-target"]:RemoveZone(zone)
end
function Target.AddTargetEntity(entity, parameters)
exports["qb-target"]:AddTargetEntity(entity, parameters)
end
function Target.AddBoxZone(name, coords, size, parameters)
exports["qb-target"]:AddBoxZone(name, coords, size.x, size.y, {
name = name,
debugPoly = Config.Debug,
minZ = coords.z - 2,
maxZ = coords.z + 2,
heading = coords.w
}, parameters)
end
function Target.AddPolyZone(name, points, parameters)
exports["qb-target"]:AddPolyZone(name, points, {
name = name,
debugPoly = Config.Debug,
minZ = points[1].z - 2,
maxZ = points[1].z + 2
}, parameters)
end
function Target.IsTargetStarted()
return GetResourceState("qb-target") == "started"
end

View File

@ -0,0 +1,192 @@
if not Config.UseTarget then return end
local TargetPeds = {
Store = {},
ClothingRoom = {},
PlayerOutfitRoom = {}
}
Target = {}
function Target.IsOX()
return GetResourceState("ox_target") ~= "missing"
end
function Target.IsQB()
return GetResourceState("qb-target") ~= "missing"
end
local function RemoveTargetPeds(peds)
for i = 1, #peds, 1 do
DeletePed(peds[i])
end
end
local function RemoveTargets()
if Config.EnablePedsForShops then
RemoveTargetPeds(TargetPeds.Store)
else
for k, v in pairs(Config.Stores) do
Target.RemoveZone(v.type .. k)
end
end
if Config.EnablePedsForClothingRooms then
RemoveTargetPeds(TargetPeds.ClothingRoom)
else
for k, v in pairs(Config.ClothingRooms) do
Target.RemoveZone("clothing_" .. (v.job or v.gang) .. k)
end
end
if Config.EnablePedsForPlayerOutfitRooms then
RemoveTargetPeds(TargetPeds.PlayerOutfitRoom)
else
for k in pairs(Config.PlayerOutfitRooms) do
Target.RemoveZone("playeroutfitroom_" .. k)
end
end
end
AddEventHandler("onResourceStop", function(resource)
if resource == GetCurrentResourceName() then
if Target.IsTargetStarted() then
RemoveTargets()
end
end
end)
local function CreatePedAtCoords(pedModel, coords, scenario)
pedModel = type(pedModel) == "string" and joaat(pedModel) or pedModel
lib.requestModel(pedModel)
local ped = CreatePed(0, pedModel, coords.x, coords.y, coords.z - 0.98, coords.w, false, false)
TaskStartScenarioInPlace(ped, scenario, true)
FreezeEntityPosition(ped, true)
SetEntityVisible(ped, true)
SetEntityInvincible(ped, true)
PlaceObjectOnGroundProperly(ped)
SetBlockingOfNonTemporaryEvents(ped, true)
return ped
end
local function SetupStoreTarget(targetConfig, action, k, v)
local parameters = {
options = {{
type = "client",
action = action,
icon = targetConfig.icon,
label = targetConfig.label
}},
distance = targetConfig.distance,
rotation = v.rotation
}
if Config.EnablePedsForShops then
TargetPeds.Store[k] = CreatePedAtCoords(v.targetModel or targetConfig.model, v.coords, v.targetScenario or targetConfig.scenario)
Target.AddTargetEntity(TargetPeds.Store[k], parameters)
elseif v.usePoly then
Target.AddPolyZone(v.type .. k, v.points, parameters)
else
Target.AddBoxZone(v.type .. k, v.coords, v.size, parameters)
end
end
local function SetupStoreTargets()
for k, v in pairs(Config.Stores) do
local targetConfig = Config.TargetConfig[v.type]
local action
if v.type == "barber" then
action = OpenBarberShop
elseif v.type == "clothing" then
action = function()
TriggerEvent("illenium-appearance:client:openClothingShopMenu")
end
elseif v.type == "tattoo" then
action = OpenTattooShop
elseif v.type == "surgeon" then
action = OpenSurgeonShop
end
if not (Config.RCoreTattoosCompatibility and v.type == "tattoo") then
SetupStoreTarget(targetConfig, action, k, v)
end
end
end
local function SetupClothingRoomTargets()
for k, v in pairs(Config.ClothingRooms) do
local targetConfig = Config.TargetConfig["clothingroom"]
local action = function()
local outfits = GetPlayerJobOutfits(v.job)
TriggerEvent("illenium-appearance:client:openJobOutfitsMenu", outfits)
end
local parameters = {
options = {{
type = "client",
action = action,
icon = targetConfig.icon,
label = targetConfig.label,
canInteract = v.job and CheckDuty or nil,
job = v.job,
gang = v.gang
}},
distance = targetConfig.distance,
rotation = v.rotation
}
local key = "clothing_" .. (v.job or v.gang) .. k
if Config.EnablePedsForClothingRooms then
TargetPeds.ClothingRoom[k] = CreatePedAtCoords(v.targetModel or targetConfig.model, v.coords, v.targetScenario or targetConfig.scenario)
Target.AddTargetEntity(TargetPeds.ClothingRoom[k], parameters)
elseif v.usePoly then
Target.AddPolyZone(key, v.points, parameters)
else
Target.AddBoxZone(key, v.coords, v.size, parameters)
end
end
end
local function SetupPlayerOutfitRoomTargets()
for k, v in pairs(Config.PlayerOutfitRooms) do
local targetConfig = Config.TargetConfig["playeroutfitroom"]
local parameters = {
options = {{
type = "client",
action = function()
OpenOutfitRoom(v)
end,
icon = targetConfig.icon,
label = targetConfig.label,
canInteract = function()
return IsPlayerAllowedForOutfitRoom(v)
end
}},
distance = targetConfig.distance,
rotation = v.rotation
}
if Config.EnablePedsForPlayerOutfitRooms then
TargetPeds.PlayerOutfitRoom[k] = CreatePedAtCoords(v.targetModel or targetConfig.model, v.coords, v.targetScenario or targetConfig.scenario)
Target.AddTargetEntity(TargetPeds.PlayerOutfitRoom[k], parameters)
elseif v.usePoly then
Target.AddPolyZone("playeroutfitroom_" .. k, v.points, parameters)
else
Target.AddBoxZone("playeroutfitroom_" .. k, v.coords, v.size, parameters)
end
end
end
local function SetupTargets()
SetupStoreTargets()
SetupClothingRoomTargets()
SetupPlayerOutfitRoomTargets()
end
CreateThread(function()
if Config.UseTarget then
SetupTargets()
end
end)

View File

@ -0,0 +1,197 @@
if Config.UseTarget then return end
local currentZone = nil
local Zones = {
Store = {},
ClothingRoom = {},
PlayerOutfitRoom = {}
}
local function RemoveZones()
for i = 1, #Zones.Store do
if Zones.Store[i]["remove"] then
Zones.Store[i]:remove()
end
end
for i = 1, #Zones.ClothingRoom do
Zones.ClothingRoom[i]:remove()
end
for i = 1, #Zones.PlayerOutfitRoom do
Zones.PlayerOutfitRoom[i]:remove()
end
end
local function lookupZoneIndexFromID(zones, id)
for i = 1, #zones do
if zones[i].id == id then
return i
end
end
end
local function onStoreEnter(data)
local index = lookupZoneIndexFromID(Zones.Store, data.id)
local store = Config.Stores[index]
local jobName = (store.job and client.job.name) or (store.gang and client.gang.name)
if jobName == (store.job or store.gang) then
currentZone = {
name = store.type,
index = index
}
local prefix = Config.UseRadialMenu and "" or "[E] "
if currentZone.name == "clothing" then
lib.showTextUI(prefix .. string.format(_L("textUI.clothing"), Config.ClothingCost), Config.TextUIOptions)
elseif currentZone.name == "barber" then
lib.showTextUI(prefix .. string.format(_L("textUI.barber"), Config.BarberCost), Config.TextUIOptions)
elseif currentZone.name == "tattoo" then
lib.showTextUI(prefix .. string.format(_L("textUI.tattoo"), Config.TattooCost), Config.TextUIOptions)
elseif currentZone.name == "surgeon" then
lib.showTextUI(prefix .. string.format(_L("textUI.surgeon"), Config.SurgeonCost), Config.TextUIOptions)
end
Radial.AddOption(currentZone)
end
end
local function onClothingRoomEnter(data)
local index = lookupZoneIndexFromID(Zones.ClothingRoom, data.id)
local clothingRoom = Config.ClothingRooms[index]
local jobName = clothingRoom.job and client.job.name or client.gang.name
if jobName == (clothingRoom.job or clothingRoom.gang) then
if CheckDuty() or clothingRoom.gang then
currentZone = {
name = "clothingRoom",
index = index
}
local prefix = Config.UseRadialMenu and "" or "[E] "
lib.showTextUI(prefix .. _L("textUI.clothingRoom"), Config.TextUIOptions)
Radial.AddOption(currentZone)
end
end
end
local function onPlayerOutfitRoomEnter(data)
local index = lookupZoneIndexFromID(Zones.PlayerOutfitRoom, data.id)
local playerOutfitRoom = Config.PlayerOutfitRooms[index]
local isAllowed = IsPlayerAllowedForOutfitRoom(playerOutfitRoom)
if isAllowed then
currentZone = {
name = "playerOutfitRoom",
index = index
}
local prefix = Config.UseRadialMenu and "" or "[E] "
lib.showTextUI(prefix .. _L("textUI.playerOutfitRoom"), Config.TextUIOptions)
Radial.AddOption(currentZone)
end
end
local function onZoneExit()
currentZone = nil
Radial.RemoveOption()
lib.hideTextUI()
end
local function SetupZone(store, onEnter, onExit)
if Config.RCoreTattoosCompatibility and store.type == "tattoo" then
return {}
end
if Config.UseRadialMenu or store.usePoly then
return lib.zones.poly({
points = store.points,
debug = Config.Debug,
onEnter = onEnter,
onExit = onExit
})
end
return lib.zones.box({
coords = store.coords,
size = store.size,
rotation = store.rotation,
debug = Config.Debug,
onEnter = onEnter,
onExit = onExit
})
end
local function SetupStoreZones()
for _, v in pairs(Config.Stores) do
Zones.Store[#Zones.Store + 1] = SetupZone(v, onStoreEnter, onZoneExit)
end
end
local function SetupClothingRoomZones()
for _, v in pairs(Config.ClothingRooms) do
Zones.ClothingRoom[#Zones.ClothingRoom + 1] = SetupZone(v, onClothingRoomEnter, onZoneExit)
end
end
local function SetupPlayerOutfitRoomZones()
for _, v in pairs(Config.PlayerOutfitRooms) do
Zones.PlayerOutfitRoom[#Zones.PlayerOutfitRoom + 1] = SetupZone(v, onPlayerOutfitRoomEnter, onZoneExit)
end
end
local function SetupZones()
SetupStoreZones()
SetupClothingRoomZones()
SetupPlayerOutfitRoomZones()
end
local function ZonesLoop()
Wait(1000)
while true do
local sleep = 1000
if currentZone then
sleep = 5
if IsControlJustReleased(0, 38) then
if currentZone.name == "clothingRoom" then
local clothingRoom = Config.ClothingRooms[currentZone.index]
local outfits = GetPlayerJobOutfits(clothingRoom.job)
TriggerEvent("illenium-appearance:client:openJobOutfitsMenu", outfits)
elseif currentZone.name == "playerOutfitRoom" then
local outfitRoom = Config.PlayerOutfitRooms[currentZone.index]
OpenOutfitRoom(outfitRoom)
elseif currentZone.name == "clothing" then
TriggerEvent("illenium-appearance:client:openClothingShopMenu")
elseif currentZone.name == "barber" then
OpenBarberShop()
elseif currentZone.name == "tattoo" then
OpenTattooShop()
elseif currentZone.name == "surgeon" then
OpenSurgeonShop()
end
end
end
Wait(sleep)
end
end
CreateThread(function()
SetupZones()
if not Config.UseRadialMenu then
ZonesLoop()
end
end)
AddEventHandler("onResourceStop", function(resource)
if resource == GetCurrentResourceName() then
RemoveZones()
end
end)
RegisterNetEvent("illenium-appearance:client:OpenClothingRoom", function()
local clothingRoom = Config.ClothingRooms[currentZone.index]
local outfits = GetPlayerJobOutfits(clothingRoom.job)
TriggerEvent("illenium-appearance:client:openJobOutfitsMenu", outfits)
end)
RegisterNetEvent("illenium-appearance:client:OpenPlayerOutfitRoom", function()
local outfitRoom = Config.PlayerOutfitRooms[currentZone.index]
OpenOutfitRoom(outfitRoom)
end)

View File

@ -0,0 +1,82 @@
fx_version 'cerulean'
game "gta5"
author "snakewiz & iLLeniumStudios"
description "A flexible player customization script for FiveM servers."
repository "https://github.com/iLLeniumStudios/illenium-appearance"
version "v5.6.1"
lua54 "yes"
client_scripts {
"game/constants.lua",
"game/util.lua",
"game/customization.lua",
"game/nui.lua",
"client/outfits.lua",
"client/common.lua",
"client/zones.lua",
"client/framework/framework.lua",
"client/framework/qb/compatibility.lua",
"client/framework/qb/main.lua",
"client/framework/qb/migrate.lua",
"client/framework/esx/compatibility.lua",
"client/framework/esx/main.lua",
"client/target/target.lua",
"client/target/qb.lua",
"client/target/ox.lua",
"client/management/management.lua",
"client/management/common.lua",
"client/management/qb.lua",
"client/management/qbx.lua",
"client/management/esx.lua",
"client/radial/radial.lua",
"client/radial/qb.lua",
"client/radial/ox.lua",
"client/stats.lua",
"client/defaults.lua",
"client/blips.lua",
"client/props.lua",
"client/client.lua",
}
server_scripts {
"@oxmysql/lib/MySQL.lua",
"server/database/database.lua",
"server/database/jobgrades.lua",
"server/database/managementoutfits.lua",
"server/database/playeroutfitcodes.lua",
"server/database/playeroutfits.lua",
"server/database/players.lua",
"server/database/playerskins.lua",
"server/database/users.lua",
"server/framework/qb/main.lua",
"server/framework/qb/migrate.lua",
"server/framework/esx/main.lua",
"server/framework/esx/migrate.lua",
"server/framework/esx/callbacks.lua",
"server/framework/esx/management.lua",
"server/util.lua",
"server/server.lua",
"server/permissions.lua"
}
shared_scripts {
"shared/config.lua",
"shared/blacklist.lua",
"shared/peds.lua",
"shared/tattoos.lua",
"shared/theme.lua",
"shared/framework/framework.lua",
"shared/framework/esx/util.lua",
"locales/locales.lua",
"locales/da.lua",
"@ox_lib/init.lua"
}
files {
"web/dist/index.html",
"web/dist/assets/*.js"
}
ui_page "web/dist/index.html"

View File

@ -0,0 +1,366 @@
constants = {}
constants.PED_COMPONENTS_IDS = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
constants.PED_PROPS_IDS = {0, 1, 2, 6, 7}
constants.FACE_FEATURES = {
"noseWidth",
"nosePeakHigh",
"nosePeakSize",
"noseBoneHigh",
"nosePeakLowering",
"noseBoneTwist",
"eyeBrownHigh",
"eyeBrownForward",
"cheeksBoneHigh",
"cheeksBoneWidth",
"cheeksWidth",
"eyesOpening",
"lipsThickness",
"jawBoneWidth",
"jawBoneBackSize",
"chinBoneLowering",
"chinBoneLenght",
"chinBoneSize",
"chinHole",
"neckThickness",
}
constants.HEAD_OVERLAYS = {
"blemishes",
"beard",
"eyebrows",
"ageing",
"makeUp",
"blush",
"complexion",
"sunDamage",
"lipstick",
"moleAndFreckles",
"chestHair",
"bodyBlemishes",
}
-- Thanks to rootcause for the eye colors names and hair decorations hashes.
constants.EYE_COLORS = {
"Grøn",
"Emerald",
"Lyseblå",
"Mørkeblå",
"Lysebrun",
"Mørkebrun",
"Hazel",
"Mørkegrå",
"Lysegrå",
"Pink",
"Gul",
"Lilla",
"Sort",
"Hint af grå",
"Tequila Sunrise",
"Atomic",
"Warp",
"ECola",
"Space Ranger",
"Ying Yang",
"Bullseye",
"Lizard",
"Dragon",
"Extra Terrestrial",
"Goat",
"Smiley",
"Possessed",
"Demon",
"Infected",
"Alien",
"Undead",
"Zombie",
}
constants.HAIR_DECORATIONS = {
male = {
[0] = { `mpbeach_overlays`, `FM_Hair_Fuzz` },
[1] = { `multiplayer_overlays`, `NG_M_Hair_001` },
[2] = { `multiplayer_overlays`, `NG_M_Hair_002` },
[3] = { `multiplayer_overlays`, `NG_M_Hair_003` },
[4] = { `multiplayer_overlays`, `NG_M_Hair_004` },
[5] = { `multiplayer_overlays`, `NG_M_Hair_005` },
[6] = { `multiplayer_overlays`, `NG_M_Hair_006` },
[7] = { `multiplayer_overlays`, `NG_M_Hair_007` },
[8] = { `multiplayer_overlays`, `NG_M_Hair_008` },
[9] = { `multiplayer_overlays`, `NG_M_Hair_009` },
[10] = { `multiplayer_overlays`, `NG_M_Hair_013` },
[11] = { `multiplayer_overlays`, `NG_M_Hair_002` },
[12] = { `multiplayer_overlays`, `NG_M_Hair_011` },
[13] = { `multiplayer_overlays`, `NG_M_Hair_012` },
[14] = { `multiplayer_overlays`, `NG_M_Hair_014` },
[15] = { `multiplayer_overlays`, `NG_M_Hair_015` },
[16] = { `multiplayer_overlays`, `NGBea_M_Hair_000` },
[17] = { `multiplayer_overlays`, `NGBea_M_Hair_001` },
[18] = { `multiplayer_overlays`, `NGBus_M_Hair_000` },
[19] = { `multiplayer_overlays`, `NGBus_M_Hair_001` },
[20] = { `multiplayer_overlays`, `NGHip_M_Hair_000` },
[21] = { `multiplayer_overlays`, `NGHip_M_Hair_001` },
[22] = { `multiplayer_overlays`, `NGInd_M_Hair_000` },
[24] = { `mplowrider_overlays`, `LR_M_Hair_000` },
[25] = { `mplowrider_overlays`, `LR_M_Hair_001` },
[26] = { `mplowrider_overlays`, `LR_M_Hair_002` },
[27] = { `mplowrider_overlays`, `LR_M_Hair_003` },
[28] = { `mplowrider2_overlays`, `LR_M_Hair_004` },
[29] = { `mplowrider2_overlays`, `LR_M_Hair_005` },
[30] = { `mplowrider2_overlays`, `LR_M_Hair_006` },
[31] = { `mpbiker_overlays`, `MP_Biker_Hair_000_M` },
[32] = { `mpbiker_overlays`, `MP_Biker_Hair_001_M` },
[33] = { `mpbiker_overlays`, `MP_Biker_Hair_002_M` },
[34] = { `mpbiker_overlays`, `MP_Biker_Hair_003_M` },
[35] = { `mpbiker_overlays`, `MP_Biker_Hair_004_M` },
[36] = { `mpbiker_overlays`, `MP_Biker_Hair_005_M` },
[37] = { `multiplayer_overlays`, `NG_M_Hair_001` },
[38] = { `multiplayer_overlays`, `NG_M_Hair_002` },
[39] = { `multiplayer_overlays`, `NG_M_Hair_003` },
[40] = { `multiplayer_overlays`, `NG_M_Hair_004` },
[41] = { `multiplayer_overlays`, `NG_M_Hair_005` },
[42] = { `multiplayer_overlays`, `NG_M_Hair_006` },
[43] = { `multiplayer_overlays`, `NG_M_Hair_007` },
[44] = { `multiplayer_overlays`, `NG_M_Hair_008` },
[45] = { `multiplayer_overlays`, `NG_M_Hair_009` },
[46] = { `multiplayer_overlays`, `NG_M_Hair_013` },
[47] = { `multiplayer_overlays`, `NG_M_Hair_002` },
[48] = { `multiplayer_overlays`, `NG_M_Hair_011` },
[49] = { `multiplayer_overlays`, `NG_M_Hair_012` },
[50] = { `multiplayer_overlays`, `NG_M_Hair_014` },
[51] = { `multiplayer_overlays`, `NG_M_Hair_015` },
[52] = { `multiplayer_overlays`, `NGBea_M_Hair_000` },
[53] = { `multiplayer_overlays`, `NGBea_M_Hair_001` },
[54] = { `multiplayer_overlays`, `NGBus_M_Hair_000` },
[55] = { `multiplayer_overlays`, `NGBus_M_Hair_001` },
[56] = { `multiplayer_overlays`, `NGHip_M_Hair_000` },
[57] = { `multiplayer_overlays`, `NGHip_M_Hair_001` },
[58] = { `multiplayer_overlays`, `NGInd_M_Hair_000` },
[59] = { `mplowrider_overlays`, `LR_M_Hair_000` },
[60] = { `mplowrider_overlays`, `LR_M_Hair_001` },
[61] = { `mplowrider_overlays`, `LR_M_Hair_002` },
[62] = { `mplowrider_overlays`, `LR_M_Hair_003` },
[63] = { `mplowrider2_overlays`, `LR_M_Hair_004` },
[64] = { `mplowrider2_overlays`, `LR_M_Hair_005` },
[65] = { `mplowrider2_overlays`, `LR_M_Hair_006` },
[66] = { `mpbiker_overlays`, `MP_Biker_Hair_000_M` },
[67] = { `mpbiker_overlays`, `MP_Biker_Hair_001_M` },
[68] = { `mpbiker_overlays`, `MP_Biker_Hair_002_M` },
[69] = { `mpbiker_overlays`, `MP_Biker_Hair_003_M` },
[70] = { `mpbiker_overlays`, `MP_Biker_Hair_004_M` },
[71] = { `mpbiker_overlays`, `MP_Biker_Hair_005_M` },
[72] = { `mpgunrunning_overlays`, `MP_Gunrunning_Hair_M_000_M` },
[73] = { `mpgunrunning_overlays`, `MP_Gunrunning_Hair_M_001_M` },
[74] = { `mpVinewood_overlays`, `MP_Vinewood_Hair_M_000_M` },
[75] = { `mptuner_overlays`, `MP_Tuner_Hair_001_M` },
[76] = { `mpsecurity_overlays`, `MP_Security_Hair_001_M` },
},
female = {
[0] = { `mpbeach_overlays`, `FM_Hair_Fuzz` },
[1] = { `multiplayer_overlays`, `NG_F_Hair_001` },
[2] = { `multiplayer_overlays`, `NG_F_Hair_002` },
[3] = { `multiplayer_overlays`, `NG_F_Hair_003` },
[4] = { `multiplayer_overlays`, `NG_F_Hair_004` },
[5] = { `multiplayer_overlays`, `NG_F_Hair_005` },
[6] = { `multiplayer_overlays`, `NG_F_Hair_006` },
[7] = { `multiplayer_overlays`, `NG_F_Hair_007` },
[8] = { `multiplayer_overlays`, `NG_F_Hair_008` },
[9] = { `multiplayer_overlays`, `NG_F_Hair_009` },
[10] = { `multiplayer_overlays`, `NG_F_Hair_010` },
[11] = { `multiplayer_overlays`, `NG_F_Hair_011` },
[12] = { `multiplayer_overlays`, `NG_F_Hair_012` },
[13] = { `multiplayer_overlays`, `NG_F_Hair_013` },
[14] = { `multiplayer_overlays`, `NG_M_Hair_014` },
[15] = { `multiplayer_overlays`, `NG_M_Hair_015` },
[16] = { `multiplayer_overlays`, `NGBea_F_Hair_000` },
[17] = { `multiplayer_overlays`, `NGBea_F_Hair_001` },
[18] = { `multiplayer_overlays`, `NG_F_Hair_007` },
[19] = { `multiplayer_overlays`, `NGBus_F_Hair_000` },
[20] = { `multiplayer_overlays`, `NGBus_F_Hair_001` },
[21] = { `multiplayer_overlays`, `NGBea_F_Hair_001` },
[22] = { `multiplayer_overlays`, `NGHip_F_Hair_000` },
[23] = { `multiplayer_overlays`, `NGInd_F_Hair_000` },
[25] = { `mplowrider_overlays`, `LR_F_Hair_000` },
[26] = { `mplowrider_overlays`, `LR_F_Hair_001` },
[27] = { `mplowrider_overlays`, `LR_F_Hair_002` },
[28] = { `mplowrider2_overlays`, `LR_F_Hair_003` },
[29] = { `mplowrider2_overlays`, `LR_F_Hair_003` },
[30] = { `mplowrider2_overlays`, `LR_F_Hair_004` },
[31] = { `mplowrider2_overlays`, `LR_F_Hair_006` },
[32] = { `mpbiker_overlays`, `MP_Biker_Hair_000_F` },
[33] = { `mpbiker_overlays`, `MP_Biker_Hair_001_F` },
[34] = { `mpbiker_overlays`, `MP_Biker_Hair_002_F` },
[35] = { `mpbiker_overlays`, `MP_Biker_Hair_003_F` },
[36] = { `multiplayer_overlays`, `NG_F_Hair_003` },
[37] = { `mpbiker_overlays`, `MP_Biker_Hair_006_F` },
[38] = { `mpbiker_overlays`, `MP_Biker_Hair_004_F` },
[39] = { `multiplayer_overlays`, `NG_F_Hair_001` },
[40] = { `multiplayer_overlays`, `NG_F_Hair_002` },
[41] = { `multiplayer_overlays`, `NG_F_Hair_003` },
[42] = { `multiplayer_overlays`, `NG_F_Hair_004` },
[43] = { `multiplayer_overlays`, `NG_F_Hair_005` },
[44] = { `multiplayer_overlays`, `NG_F_Hair_006` },
[45] = { `multiplayer_overlays`, `NG_F_Hair_007` },
[46] = { `multiplayer_overlays`, `NG_F_Hair_008` },
[47] = { `multiplayer_overlays`, `NG_F_Hair_009` },
[48] = { `multiplayer_overlays`, `NG_F_Hair_010` },
[49] = { `multiplayer_overlays`, `NG_F_Hair_011` },
[50] = { `multiplayer_overlays`, `NG_F_Hair_012` },
[51] = { `multiplayer_overlays`, `NG_F_Hair_013` },
[52] = { `multiplayer_overlays`, `NG_M_Hair_014` },
[53] = { `multiplayer_overlays`, `NG_M_Hair_015` },
[54] = { `multiplayer_overlays`, `NGBea_F_Hair_000` },
[55] = { `multiplayer_overlays`, `NGBea_F_Hair_001` },
[56] = { `multiplayer_overlays`, `NG_F_Hair_007` },
[57] = { `multiplayer_overlays`, `NGBus_F_Hair_000` },
[58] = { `multiplayer_overlays`, `NGBus_F_Hair_001` },
[59] = { `multiplayer_overlays`, `NGBea_F_Hair_001` },
[60] = { `multiplayer_overlays`, `NGHip_F_Hair_000` },
[61] = { `multiplayer_overlays`, `NGInd_F_Hair_000` },
[62] = { `mplowrider_overlays`, `LR_F_Hair_000` },
[63] = { `mplowrider_overlays`, `LR_F_Hair_001` },
[64] = { `mplowrider_overlays`, `LR_F_Hair_002` },
[65] = { `mplowrider2_overlays`, `LR_F_Hair_003` },
[66] = { `mplowrider2_overlays`, `LR_F_Hair_003` },
[67] = { `mplowrider2_overlays`, `LR_F_Hair_004` },
[68] = { `mplowrider2_overlays`, `LR_F_Hair_006` },
[69] = { `mpbiker_overlays`, `MP_Biker_Hair_000_F` },
[70] = { `mpbiker_overlays`, `MP_Biker_Hair_001_F` },
[71] = { `mpbiker_overlays`, `MP_Biker_Hair_002_F` },
[72] = { `mpbiker_overlays`, `MP_Biker_Hair_003_F` },
[73] = { `multiplayer_overlays`, `NG_F_Hair_003` },
[74] = { `mpbiker_overlays`, `MP_Biker_Hair_006_F` },
[75] = { `mpbiker_overlays`, `MP_Biker_Hair_004_F` },
[76] = { `mpgunrunning_overlays`, `MP_Gunrunning_Hair_F_000_F` },
[77] = { `mpgunrunning_overlays`, `MP_Gunrunning_Hair_F_001_F` },
[78] = { `mpVinewood_overlays`, `MP_Vinewood_Hair_F_000_F` },
[79] = { `mptuner_overlays`, `MP_Tuner_Hair_000_F` },
[80] = { `mpsecurity_overlays`, `MP_Security_Hair_000_F` },
},
}
constants.DATA_CLOTHES = {
head = {
animations = {
on = {
dict = "mp_masks@standard_car@ds@",
anim = "put_on_mask",
move = 51,
duration = 600
},
off = {
dict = "missheist_agency2ahelmet",
anim = "take_off_helmet_stand",
move = 51,
duration = 1200
}
},
components = {
male = {
{1, 0}
},
female = {
{1, 0}
}
},
props = {
male = {
{0, -1}
},
female = {}
}
},
body = {
animations = {
on = {
dict = "clothingtie",
anim = "try_tie_negative_a",
move = 51,
duration = 1200
},
off = {
dict = "clothingtie",
anim = "try_tie_negative_a",
move = 51,
duration = 1200
}
},
components = {
male = {
{11, 252},
{3, 15},
{8, 15},
{10, 0},
{5, 0}
},
female = {
{11, 15},
{8, 14},
{3, 15},
{10, 0},
{5, 0}
}
},
props = {
male = {},
female = {}
}
},
bottom = {
animations = {
on = {
dict = "re@construction",
anim = "out_of_breath",
move = 51,
duration = 1300
},
off = {
dict = "re@construction",
anim = "out_of_breath",
move = 51,
duration = 1300
}
},
components = {
male = {
{4, 61},
{6, 34}
},
female = {
{4, 15},
{6, 35}
}
},
props = {
male = {},
female = {}
}
}
}
constants.CAMERAS = {
default = {
vec3(0, 2.2, 0.2),
vec3(0, 0, -0.05),
},
head = {
vec3(0, 0.9, 0.65),
vec3(0, 0, 0.6),
},
body = {
vec3(0, 1.2, 0.2),
vec3(0, 0, 0.2),
},
bottom = {
vec3(0, 0.98, -0.7),
vec3(0, 0, -0.9),
},
}
constants.OFFSETS = {
default = vec2(1.5, -1),
head = vec2(0.7, -0.45),
body = vec2(1.2, -0.45),
bottom = vec2(0.7, -0.45),
}

View File

@ -0,0 +1,611 @@
local reverseCamera
local function getRgbColors()
local colors = {
hair = {},
makeUp = {}
}
for i = 0, GetNumHairColors() - 1 do
colors.hair[i+1] = {GetPedHairRgbColor(i)}
end
for i = 0, GetNumMakeupColors() - 1 do
colors.makeUp[i+1] = {GetPedMakeupRgbColor(i)}
end
return colors
end
local playerAppearance
local function getAppearance()
if not playerAppearance then
playerAppearance = client.getPedAppearance(cache.ped)
end
return playerAppearance
end
client.getAppearance = getAppearance
local function addToBlacklist(item, drawable, drawableId, blacklistSettings)
if drawable == drawableId and item.textures then
for i = 1, #item.textures do
blacklistSettings.textures[#blacklistSettings.textures + 1] = item.textures[i]
end
end
if not item.textures or #item.textures == 0 then
blacklistSettings.drawables[#blacklistSettings.drawables + 1] = drawable
end
end
local function listContains(items, item)
for i = 1, #items do
if items[i] == item then
return true
end
end
return false
end
local function listContainsAny(items, containedItems)
for i = 1, #items do
if listContains(containedItems, items[i]) then
return true
end
end
return false
end
local function allowedForPlayer(item, allowedAces)
return (item.jobs and listContains(item.jobs, client.job.name)) or (item.gangs and listContains(item.gangs, client.gang.name)) or (item.aces and listContainsAny(item.aces, allowedAces) or (item.citizenids and listContains(item.citizenids, client.citizenid)))
end
local function filterPedModelsForPlayer(pedConfigs)
local playerPeds = {}
local allowedAces = lib.callback.await("illenium-appearance:server:GetPlayerAces", false)
for i = 1, #pedConfigs do
local config = pedConfigs[i]
if (not config.jobs and not config.gangs and not config.aces and not config.citizenids) or allowedForPlayer(config, allowedAces) then
for j = 1, #config.peds do
playerPeds[#playerPeds + 1] = config.peds[j]
end
end
end
return playerPeds
end
local function filterTattoosByGender(tattoos)
local filtered = {}
local gender = client.getPedDecorationType()
for k, v in pairs(tattoos) do
filtered[k] = {}
for i = 1, #v do
local tattoo = v[i]
if tattoo["hash" .. gender:gsub("^%l", string.upper)] ~= "" then
filtered[k][#filtered[k] + 1] = tattoo
end
end
end
return filtered
end
local function filterBlacklistSettings(items, drawableId)
local blacklistSettings = {
drawables = {},
textures = {}
}
local allowedAces = lib.callback.await("illenium-appearance:server:GetPlayerAces", false)
for i = 1, #items do
local item = items[i]
if not allowedForPlayer(item, allowedAces) and item.drawables then
for j = 0, #item.drawables do
addToBlacklist(item, item.drawables[j], drawableId, blacklistSettings)
end
end
end
return blacklistSettings
end
local function componentBlacklistMap(gender, componentId)
local genderSettings = Config.Blacklist[gender].components
if componentId == 1 then
return genderSettings.masks
elseif componentId == 3 then
return genderSettings.upperBody
elseif componentId == 4 then
return genderSettings.lowerBody
elseif componentId == 5 then
return genderSettings.bags
elseif componentId == 6 then
return genderSettings.shoes
elseif componentId == 7 then
return genderSettings.scarfAndChains
elseif componentId == 8 then
return genderSettings.shirts
elseif componentId == 9 then
return genderSettings.bodyArmor
elseif componentId == 10 then
return genderSettings.decals
elseif componentId == 11 then
return genderSettings.jackets
end
return {}
end
local function propBlacklistMap(gender, propId)
local genderSettings = Config.Blacklist[gender].props
if propId == 0 then
return genderSettings.hats
elseif propId == 1 then
return genderSettings.glasses
elseif propId == 2 then
return genderSettings.ear
elseif propId == 6 then
return genderSettings.watches
elseif propId == 7 then
return genderSettings.bracelets
end
return {}
end
local function getComponentSettings(ped, componentId)
local drawableId = GetPedDrawableVariation(ped, componentId)
local gender = client.getPedDecorationType()
local blacklistSettings = {
drawables = {},
textures = {}
}
if client.isPedFreemodeModel(ped) then
blacklistSettings = filterBlacklistSettings(componentBlacklistMap(gender, componentId), drawableId)
end
return {
component_id = componentId,
drawable = {
min = 0,
max = GetNumberOfPedDrawableVariations(ped, componentId) - 1
},
texture = {
min = 0,
max = GetNumberOfPedTextureVariations(ped, componentId, drawableId) - 1
},
blacklist = blacklistSettings
}
end
client.getComponentSettings = getComponentSettings
local function getPropSettings(ped, propId)
local drawableId = GetPedPropIndex(ped, propId)
local gender = client.getPedDecorationType()
local blacklistSettings = {
drawables = {},
textures = {}
}
if client.isPedFreemodeModel(ped) then
blacklistSettings = filterBlacklistSettings(propBlacklistMap(gender, propId), drawableId)
end
local settings = {
prop_id = propId,
drawable = {
min = -1,
max = GetNumberOfPedPropDrawableVariations(ped, propId) - 1
},
texture = {
min = -1,
max = GetNumberOfPedPropTextureVariations(ped, propId, drawableId) - 1
},
blacklist = blacklistSettings
}
return settings
end
client.getPropSettings = getPropSettings
local function getHairSettings(ped)
local colors = getRgbColors()
local gender = client.getPedDecorationType()
local blacklistSettings = {
drawables = {},
textures = {}
}
if client.isPedFreemodeModel(ped) then
blacklistSettings = filterBlacklistSettings(Config.Blacklist[gender].hair, GetPedDrawableVariation(ped, 2))
end
local settings = {
style = {
min = 0,
max = GetNumberOfPedDrawableVariations(ped, 2) - 1
},
color = {
items = colors.hair
},
highlight = {
items = colors.hair
},
texture = {
min = 0,
max = GetNumberOfPedTextureVariations(ped, 2, GetPedDrawableVariation(ped, 2)) - 1
},
blacklist = blacklistSettings
}
return settings
end
client.getHairSettings = getHairSettings
local function getAppearanceSettings()
local ped = {
model = {
items = filterPedModelsForPlayer(Config.Peds.pedConfig)
}
}
local tattoos = {
items = filterTattoosByGender(Config.Tattoos),
opacity = {
min = 0.1,
max = 1,
factor = 0.1,
}
}
local components = {}
for i = 1, #constants.PED_COMPONENTS_IDS do
components[i] = getComponentSettings(cache.ped, constants.PED_COMPONENTS_IDS[i])
end
local props = {}
for i = 1, #constants.PED_PROPS_IDS do
props[i] = getPropSettings(cache.ped, constants.PED_PROPS_IDS[i])
end
local headBlend = {
shapeFirst = {
min = 0,
max = 45
},
shapeSecond = {
min = 0,
max = 45
},
shapeThird = {
min = 0,
max = 45
},
skinFirst = {
min = 0,
max = 45
},
skinSecond = {
min = 0,
max = 45
},
skinThird = {
min = 0,
max = 45
},
shapeMix = {
min = 0,
max = 1,
factor = 0.1,
},
skinMix = {
min = 0,
max = 1,
factor = 0.1,
},
thirdMix = {
min = 0,
max = 1,
factor = 0.1,
}
}
local size = #constants.FACE_FEATURES
local faceFeatures = table.create(0, size)
for i = 1, size do
local feature = constants.FACE_FEATURES[i]
faceFeatures[feature] = { min = -1, max = 1, factor = 0.1}
end
local colors = getRgbColors()
local colorMap = {
beard = colors.hair,
eyebrows = colors.hair,
chestHair = colors.hair,
makeUp = colors.makeUp,
blush = colors.makeUp,
lipstick = colors.makeUp,
}
size = #constants.HEAD_OVERLAYS
local headOverlays = table.create(0, size)
for i = 1, size do
local overlay = constants.HEAD_OVERLAYS[i]
local settings = {
style = {
min = 0,
max = GetPedHeadOverlayNum(i - 1) - 1
},
opacity = {
min = 0,
max = 1,
factor = 0.1,
}
}
if colorMap[overlay] then
settings.color = {
items = colorMap[overlay]
}
end
headOverlays[overlay] = settings
end
local eyeColor = {
min = 0,
max = 30
}
return {
ped = ped,
components = components,
props = props,
headBlend = headBlend,
faceFeatures = faceFeatures,
headOverlays = headOverlays,
hair = getHairSettings(cache.ped),
eyeColor = eyeColor,
tattoos = tattoos
}
end
client.getAppearanceSettings = getAppearanceSettings
local config
function client.getConfig() return config end
local isCameraInterpolating
local currentCamera
local cameraHandle
local function setCamera(key)
if not isCameraInterpolating then
if key ~= "current" then
currentCamera = key
end
local coords, point = table.unpack(constants.CAMERAS[currentCamera])
local reverseFactor = reverseCamera and -1 or 1
if cameraHandle then
local camCoords = GetOffsetFromEntityInWorldCoords(cache.ped, coords.x * reverseFactor, coords.y * reverseFactor, coords.z * reverseFactor)
local camPoint = GetOffsetFromEntityInWorldCoords(cache.ped, point.x, point.y, point.z)
local tmpCamera = CreateCameraWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z, 0.0, 0.0, 0.0, 49.0, false, 0)
PointCamAtCoord(tmpCamera, camPoint.x, camPoint.y, camPoint.z)
SetCamActiveWithInterp(tmpCamera, cameraHandle, 1000, 1, 1)
isCameraInterpolating = true
CreateThread(function()
repeat Wait(500)
until not IsCamInterpolating(cameraHandle) and IsCamActive(tmpCamera)
DestroyCam(cameraHandle, false)
cameraHandle = tmpCamera
isCameraInterpolating = false
end)
else
local camCoords = GetOffsetFromEntityInWorldCoords(cache.ped, coords.x, coords.y, coords.z)
local camPoint = GetOffsetFromEntityInWorldCoords(cache.ped, point.x, point.y, point.z)
cameraHandle = CreateCameraWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z, 0.0, 0.0, 0.0, 49.0, false, 0)
PointCamAtCoord(cameraHandle, camPoint.x, camPoint.y, camPoint.z)
SetCamActive(cameraHandle, true)
end
end
end
client.setCamera = setCamera
function client.rotateCamera(direction)
if not isCameraInterpolating then
local coords, point = table.unpack(constants.CAMERAS[currentCamera])
local offset = constants.OFFSETS[currentCamera]
local sideFactor = direction == "left" and 1 or -1
local reverseFactor = reverseCamera and -1 or 1
local camCoords = GetOffsetFromEntityInWorldCoords(
cache.ped,
(coords.x + offset.x) * sideFactor * reverseFactor,
(coords.y + offset.y) * reverseFactor,
coords.z
)
local camPoint = GetOffsetFromEntityInWorldCoords(cache.ped, point.x, point.y, point.z)
local tmpCamera = CreateCameraWithParams("DEFAULT_SCRIPTED_CAMERA", camCoords.x, camCoords.y, camCoords.z, 0.0, 0.0, 0.0, 49.0, false, 0)
PointCamAtCoord(tmpCamera, camPoint.x, camPoint.y, camPoint.z)
SetCamActiveWithInterp(tmpCamera, cameraHandle, 1000, 1, 1)
isCameraInterpolating = true
CreateThread(function()
repeat Wait(500)
until not IsCamInterpolating(cameraHandle) and IsCamActive(tmpCamera)
DestroyCam(cameraHandle, false)
cameraHandle = tmpCamera
isCameraInterpolating = false
end)
end
end
local playerCoords
local function pedTurn(ped, angle)
reverseCamera = not reverseCamera
local sequenceTaskId = OpenSequenceTask()
if sequenceTaskId then
TaskGoStraightToCoord(0, playerCoords.x, playerCoords.y, playerCoords.z, 8.0, -1, GetEntityHeading(ped) - angle, 0.1)
TaskStandStill(0, -1)
CloseSequenceTask(sequenceTaskId)
ClearPedTasks(ped)
TaskPerformSequence(ped, sequenceTaskId)
ClearSequenceTask(sequenceTaskId)
end
end
client.pedTurn = pedTurn
local function wearClothes(data, typeClothes)
local dataClothes = constants.DATA_CLOTHES[typeClothes]
local animationsOn = dataClothes.animations.on
local components = dataClothes.components[client.getPedDecorationType()]
local appliedComponents = data.components
local props = dataClothes.props[client.getPedDecorationType()]
local appliedProps = data.props
RequestAnimDict(animationsOn.dict)
while not HasAnimDictLoaded(animationsOn.dict) do
Wait(0)
end
for i = 1, #components do
local componentId = components[i][1]
for j = 1, #appliedComponents do
local applied = appliedComponents[j]
if applied.component_id == componentId then
SetPedComponentVariation(cache.ped, componentId, applied.drawable, applied.texture, 2)
end
end
end
for i = 1, #props do
local propId = props[i][1]
for j = 1, #appliedProps do
local applied = appliedProps[j]
if applied.prop_id == propId then
SetPedPropIndex(cache.ped, propId, applied.drawable, applied.texture, true)
end
end
end
TaskPlayAnim(cache.ped, animationsOn.dict, animationsOn.anim, 3.0, 3.0, animationsOn.duration, animationsOn.move, 0, false, false, false)
end
client.wearClothes = wearClothes
local function removeClothes(typeClothes)
local dataClothes = constants.DATA_CLOTHES[typeClothes]
local animationsOff = dataClothes.animations.off
local components = dataClothes.components[client.getPedDecorationType()]
local props = dataClothes.props[client.getPedDecorationType()]
RequestAnimDict(animationsOff.dict)
while not HasAnimDictLoaded(animationsOff.dict) do
Wait(0)
end
for i = 1, #components do
local component = components[i]
SetPedComponentVariation(cache.ped, component[1], component[2], 0, 2)
end
for i = 1, #props do
ClearPedProp(cache.ped, props[i][1])
end
TaskPlayAnim(cache.ped, animationsOff.dict, animationsOff.anim, 3.0, 3.0, animationsOff.duration, animationsOff.move, 0, false, false, false)
end
client.removeClothes = removeClothes
local playerHeading
function client.getHeading() return playerHeading end
local callback
function client.startPlayerCustomization(cb, conf)
playerAppearance = client.getPedAppearance(cache.ped)
playerCoords = GetEntityCoords(cache.ped, true)
playerHeading = GetEntityHeading(cache.ped)
BackupPlayerStats()
callback = cb
config = conf
reverseCamera = false
isCameraInterpolating = false
setCamera("default")
SetNuiFocus(true, true)
SetNuiFocusKeepInput(false)
RenderScriptCams(true, false, 0, true, true)
SetEntityInvincible(cache.ped, Config.InvincibleDuringCustomization)
TaskStandStill(cache.ped, -1)
if Config.HideRadar then DisplayRadar(false) end
SendNuiMessage(json.encode({
type = "appearance_display",
payload = {
asynchronous = Config.AsynchronousLoading
}
}))
end
function client.exitPlayerCustomization(appearance)
RenderScriptCams(false, false, 0, true, true)
DestroyCam(cameraHandle, false)
SetNuiFocus(false, false)
if Config.HideRadar then DisplayRadar(true) end
ClearPedTasksImmediately(cache.ped)
SetEntityInvincible(cache.ped, false)
SendNuiMessage(json.encode({
type = "appearance_hide",
payload = {}
}))
if not appearance then
client.setPlayerAppearance(getAppearance())
else
client.setPedTattoos(cache.ped, appearance.tattoos)
end
RestorePlayerStats()
if callback then
callback(appearance)
end
callback = nil
config = nil
playerAppearance = nil
playerCoords = nil
cameraHandle = nil
currentCamera = nil
reverseCamera = nil
isCameraInterpolating = nil
end
AddEventHandler("onResourceStop", function(resource)
if resource == GetCurrentResourceName() then
SetNuiFocus(false, false)
SetNuiFocusKeepInput(false)
end
end)
exports("startPlayerCustomization", client.startPlayerCustomization)

View File

@ -0,0 +1,136 @@
local client = client
RegisterNUICallback("appearance_get_locales", function(_, cb)
cb(Locales[GetConvar("illenium-appearance:locale", "en")].UI)
end)
RegisterNUICallback("appearance_get_settings", function(_, cb)
cb({ appearanceSettings = client.getAppearanceSettings() })
end)
RegisterNUICallback("appearance_get_data", function(_, cb)
Wait(250)
local appearanceData = client.getAppearance()
if appearanceData.tattoos then
client.setPedTattoos(cache.ped, appearanceData.tattoos)
end
cb({ config = client.getConfig(), appearanceData = appearanceData })
end)
RegisterNUICallback("appearance_set_camera", function(camera, cb)
cb(1)
client.setCamera(camera)
end)
RegisterNUICallback("appearance_turn_around", function(_, cb)
cb(1)
client.pedTurn(cache.ped, 180.0)
end)
RegisterNUICallback("appearance_rotate_camera", function(direction, cb)
cb(1)
client.rotateCamera(direction)
end)
RegisterNUICallback("appearance_change_model", function(model, cb)
local playerPed = client.setPlayerModel(model)
SetEntityHeading(cache.ped, client.getHeading())
SetEntityInvincible(playerPed, true)
TaskStandStill(playerPed, -1)
cb({
appearanceSettings = client.getAppearanceSettings(),
appearanceData = client.getPedAppearance(playerPed)
})
end)
RegisterNUICallback("appearance_change_component", function(component, cb)
client.setPedComponent(cache.ped, component)
cb(client.getComponentSettings(cache.ped, component.component_id))
end)
RegisterNUICallback("appearance_change_prop", function(prop, cb)
client.setPedProp(cache.ped, prop)
cb(client.getPropSettings(cache.ped, prop.prop_id))
end)
RegisterNUICallback("appearance_change_head_blend", function(headBlend, cb)
cb(1)
client.setPedHeadBlend(cache.ped, headBlend)
end)
RegisterNUICallback("appearance_change_face_feature", function(faceFeatures, cb)
cb(1)
client.setPedFaceFeatures(cache.ped, faceFeatures)
end)
RegisterNUICallback("appearance_change_head_overlay", function(headOverlays, cb)
cb(1)
client.setPedHeadOverlays(cache.ped, headOverlays)
end)
RegisterNUICallback("appearance_change_hair", function(hair, cb)
client.setPedHair(cache.ped, hair)
cb(client.getHairSettings(cache.ped))
end)
RegisterNUICallback("appearance_change_eye_color", function(eyeColor, cb)
cb(1)
client.setPedEyeColor(cache.ped, eyeColor)
end)
RegisterNUICallback("appearance_apply_tattoo", function(data, cb)
local paid = not data.tattoo or not Config.ChargePerTattoo or lib.callback.await("illenium-appearance:server:payForTattoo", false, data.tattoo)
if paid then
client.addPedTattoo(cache.ped, data.updatedTattoos or data)
end
cb(paid)
end)
RegisterNUICallback("appearance_preview_tattoo", function(previewTattoo, cb)
cb(1)
client.setPreviewTattoo(cache.ped, previewTattoo.data, previewTattoo.tattoo)
end)
RegisterNUICallback("appearance_delete_tattoo", function(data, cb)
cb(1)
client.removePedTattoo(cache.ped, data)
end)
RegisterNUICallback("appearance_wear_clothes", function(dataWearClothes, cb)
cb(1)
client.wearClothes(dataWearClothes.data, dataWearClothes.key)
end)
RegisterNUICallback("appearance_remove_clothes", function(clothes, cb)
cb(1)
client.removeClothes(clothes)
end)
RegisterNUICallback("appearance_save", function(appearance, cb)
cb(1)
client.wearClothes(appearance, "head")
client.wearClothes(appearance, "body")
client.wearClothes(appearance, "bottom")
client.exitPlayerCustomization(appearance)
end)
RegisterNUICallback("appearance_exit", function(_, cb)
cb(1)
client.exitPlayerCustomization()
end)
RegisterNUICallback("rotate_left", function(_, cb)
cb(1)
client.pedTurn(cache.ped, 10.0)
end)
RegisterNUICallback("rotate_right", function(_, cb)
cb(1)
client.pedTurn(cache.ped, -10.0)
end)
RegisterNUICallback("get_theme_configuration", function(_, cb)
cb(Config.Theme)
end)

View File

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

View File

@ -0,0 +1,367 @@
Locales["da"] = {
UI = {
modal = {
save = {
title = "Gem ændringer!",
description = "Ikke at du bliver kønnere, men ja..."
},
exit = {
title = "Glem ændringer",
description = "Du forbliver grim"
},
accept = "Ja",
decline = "Nej"
},
ped = {
title = "Ped",
model = "Model"
},
headBlend = {
title = "Oprindelse",
shape = {
title = "Ansigt",
firstOption = "Far",
secondOption = "Mor",
mix = "Mix"
},
skin = {
title = "Hud",
firstOption = "Far",
secondOption = "Mor",
mix = "Mix"
},
race = {
title = "Race",
shape = "Form",
skin = "Hud",
mix = "Mix"
}
},
faceFeatures = {
title = "Ansigtstræk",
nose = {
title = "Næse",
width = "Bredde",
height = "Højde",
size = "Størrelse",
boneHeight = "Benhøjde",
boneTwist = "Krumning",
peakHeight = "Næsespidshøjde"
},
eyebrows = {
title = "Øjenbryn",
height = "Højde",
depth = "Dybde",
},
cheeks = {
title = "Kinder",
boneHeight = "Benhøjde",
boneWidth = "Knoglebens bredde",
width = "Bredde"
},
eyesAndMouth = {
title = "Øjne og mund",
eyesOpening = "Øjenåbning",
lipsThickness = "Læber tykkelse"
},
jaw = {
title = "Kæbe",
width = "Bredde",
size = "Størrelse"
},
chin = {
title = "Hage",
lowering = "Sænkning",
length = "Længde",
size = "Størrelse",
hole = "Kløftstørrelse"
},
neck = {
title = "Hals",
thickness = "Tykkelse"
}
},
headOverlays = {
title = "Udseende",
hair = {
title = "Hår",
style = "Stil",
color = "Farve",
highlight = "Highlight",
texture = "Tekstur",
fade = "Fade"
},
opacity = "Gennemsigtighed",
style = "Stil",
color = "Farve",
secondColor = "Anden farve",
blemishes = "Urenheder",
beard = "Skæg",
eyebrows = "Øjenbryn",
ageing = "Aldring",
makeUp = "Makeup",
blush = "Blush",
complexion = "Kompleksion",
sunDamage = "Solskader",
lipstick = "Læbestift",
moleAndFreckles = "Fødselsmærker og fregner",
chestHair = "Brysthår",
bodyBlemishes = "Kropsurenheder",
eyeColor = "Øjenfarve",
},
components = {
title = "Tøj",
drawable = "Variant",
texture = "Tekstur",
mask = "Masker",
upperBody = "Overkrop",
lowerBody = "Underkrop",
bags = "Tasker og faldskærme",
shoes = "Sko",
scarfAndChains = "Hals-tilbehør",
shirt = "Trøjer",
bodyArmor = "Kropsbeskyttelse",
decals = "Mærkater",
jackets = "Jakker",
head = "Hoved",
},
props = {
title = "Tilbehør",
drawable = "Variant",
texture = "Tekstur",
hats = "Hovedbeklædning",
glasses = "Briller",
ear = "Øre",
watches = "Ure",
bracelets = "Armbånd"
},
tattoos = {
title = "Tatoveringer",
items = {
ZONE_TORSO = "Overkrop",
ZONE_HEAD = "Hoved",
ZONE_LEFT_ARM = "Venstre arm",
ZONE_RIGHT_ARM = "Højre arm",
ZONE_LEFT_LEG = "Venstre ben",
ZONE_RIGHT_LEG = "Højre ben",
},
apply = "Tilføj",
delete = "Fjern",
deleteAll = "Fjern alle",
opacity = "Gennemsigtighed"
}
},
outfitManagement = {
title = "Outfit håndtering",
jobText = "Håndter outfits for job",
gangText = "Håndter outfits for bande",
},
cancelled = {
title = "Annulleret",
description = "Handlingen blev annulleret"
},
outfits = {
import = {
title = "Indtast outfit kode",
menuTitle = "Importer outfit fra kode",
description = "Importer et outfit fra en share-code",
name = {
label = "Outfit navn",
placeholder = "Swagger",
default = "Importeret outfit"
},
code = {
label = "Outfit kode",
},
success = {
title = "Outfit importeret",
description = "Du kan nu skifte til outfittet ved at bruge outfit menuen"
},
failure = {
title = "Noget gik galt",
description = "Ugyldig outfit kode"
}
},
generate = {
title = "Generer outfit kode",
description = "Generer en share-code for dit nuværende outfit",
failure = {
title = "Noget gik galt",
description = "Kunne ikke generere outfit kode"
},
success = {
title = "Outfit kode genereret",
description = "Her er din outfit kode"
}
},
save = {
menuTitle = "Gem nuværende outfit",
menuDescription = "Gem dit nuværende outfit som %s outfit",
description = "Gem dit nuværende outfit",
title = "Navngiv dit outfit",
managementTitle = "Håndter outfit detaljer",
name = {
label = "Outfit navn",
placeholder = "Swagger",
},
gender = {
label = "Køn",
male = "Mand",
female = "Kvinde"
},
rank = {
label = "Minimum grad"
},
failure = {
title = "Noget gik galt",
description = "Outfit med dette navn findes allerede"
},
success = {
title = "Outfit gemt",
description = "Outfit %s blev gemt"
}
},
update = {
title = "Opdater outfit",
description = "Opdater dit nuværende outfit til et eksisterende outfit",
failure = {
title = "Noget gik galt",
description = "Outfittet eksisterer ikke"
},
success = {
title = "Outfit opdateret",
description = "Outfit %s blev opdateret"
}
},
change = {
title = "Skift outfit",
description = "Skift til et af dine %s outfits",
pDescription = "Vælg et af dine gemte outfits",
failure = {
title = "Noget gik galt",
description = "Det outfit du prøver at skifte til, har ikke et grund-udseende",
}
},
delete = {
title = "Slet outfit",
description = "Slet et gemt %s outfit",
mDescription = "Slet et af dine gemte outfits",
item = {
title = 'Slet "%s"',
description = "Model: %s%s"
},
success = {
title = "Outflit slettet",
description = "Det valgte outfit blev slettet"
}
},
manage = {
title = "👔 | Håndter %s outfits"
}
},
jobOutfits = {
title = "Arbejdstøj",
description = "Skift til et sæt arbejdstøj",
},
menu = {
returnTitle = "Tilbage",
title = "Skifterum",
outfitsTitle = "Spiller outfits",
clothingShopTitle = "Tøkbutik",
barberShopTitle = "Frisør",
tattooShopTitle = "Tatovør",
surgeonShopTitle = "Plastikkirurg",
},
clothing = {
title = "Køb tøj - %d,-",
titleNoPrice = "Skift tøj",
options = {
title = "👔 | Tøjbutik muligheder",
description = "Vælg fra et bredt udvalg af tøj"
},
outfits = {
title = "👔 | Outfit muligheder",
civilian = {
title = "Civilt tøj",
description = "Tag tøj på"
}
}
},
commands = {
reloadskin = {
title = "Genindlæser din karakter",
failure = {
title = "Noget gik galt",
description = "Du kan ikke bruge reloadskin lige nu"
}
},
clearstuckprops = {
title = "Fjerner alle props fra din karakter",
failure = {
title = "Noget gik galt",
description = "Du kan ikke bruge clearstuckprops lige nu"
}
},
pedmenu = {
title = "Åben / Giv tøj menuen",
failure = {
title = "Noget gik galt",
description = "Spilleren er ikke online"
}
},
joboutfits = {
title = "Åben arbedstøj menu"
},
gangoutfits = {
title = "Åben bandetøj menu"
},
bossmanagedoutfits = {
title = "Åben chef outfit menu"
}
},
textUI = {
clothing = "Tøjbutik - Pris: %d,-",
barber = "Frisør - Pris: %d,-",
tattoo = "Tatovør - Pris: %d,-",
surgeon = "Plastikkirurg - Pris: %d,-",
clothingRoom = "Omklædningsrum",
playerOutfitRoom = "Outfits"
},
migrate = {
success = {
title = "Success",
description = "Migrering færdig. %s skins migreret",
descriptionSingle = "Migrer skin"
},
skip = {
title = "Information",
description = "Sprang skin over"
},
typeError = {
title = "Noget gik galt",
description = "Ugyldig type"
}
},
purchase = {
tattoo = {
success = {
title = "Success",
description = "Købte %s tatovering for %s,-"
},
failure = {
title = "Kunne ikke tatovere",
description = "Du har ikke råd..."
}
},
store = {
success = {
title = "Success",
description = "Købte %s for %s,-"
},
failure = {
title = "Tyveri!",
description = "Du havde ikke nok penge! Du prøvede nok at stjæle, hva?"
}
}
}
}

View File

@ -0,0 +1,17 @@
Locales = {}
function _L(key)
local lang = GetConvar("illenium-appearance:locale", "da")
if not Locales[lang] then
lang = "da"
end
local value = Locales[lang]
for k in key:gmatch("[^.]+") do
value = value[k]
if not value then
print("Missing locale for: " .. key)
return ""
end
end
return value
end

View File

@ -0,0 +1 @@
Database = {}

View File

@ -0,0 +1,5 @@
Database.JobGrades = {}
function Database.JobGrades.GetByJobName(name)
return MySQL.query.await("SELECT grade,name,label FROM job_grades WHERE job_name = ?", {name})
end

View File

@ -0,0 +1,30 @@
Database.ManagementOutfits = {}
function Database.ManagementOutfits.GetAllByJob(type, jobName, gender)
local query = "SELECT * FROM management_outfits WHERE type = ? AND job_name = ?"
local queryArgs = {type, jobName}
if gender then
query = query .. " AND gender = ?"
queryArgs[#queryArgs + 1] = gender
end
return MySQL.query.await(query, queryArgs)
end
function Database.ManagementOutfits.Add(outfitData)
return MySQL.insert.await("INSERT INTO management_outfits (job_name, type, minrank, name, gender, model, props, components) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", {
outfitData.JobName,
outfitData.Type,
outfitData.MinRank,
outfitData.Name,
outfitData.Gender,
outfitData.Model,
json.encode(outfitData.Props),
json.encode(outfitData.Components)
})
end
function Database.ManagementOutfits.DeleteByID(id)
MySQL.query.await("DELETE FROM management_outfits WHERE id = ?", {id})
end

View File

@ -0,0 +1,17 @@
Database.PlayerOutfitCodes = {}
function Database.PlayerOutfitCodes.GetByCode(code)
return MySQL.single.await("SELECT * FROM player_outfit_codes WHERE code = ?", {code})
end
function Database.PlayerOutfitCodes.GetByOutfitID(outfitID)
return MySQL.single.await("SELECT * FROM player_outfit_codes WHERE outfitID = ?", {outfitID})
end
function Database.PlayerOutfitCodes.Add(outfitID, code)
return MySQL.insert.await("INSERT INTO player_outfit_codes (outfitid, code) VALUES (?, ?)", {outfitID, code})
end
function Database.PlayerOutfitCodes.DeleteByOutfitID(id)
MySQL.query.await("DELETE FROM player_outfit_codes WHERE outfitid = ?", {id})
end

View File

@ -0,0 +1,36 @@
Database.PlayerOutfits = {}
function Database.PlayerOutfits.GetAllByCitizenID(citizenid)
return MySQL.query.await("SELECT * FROM player_outfits WHERE citizenid = ?", {citizenid})
end
function Database.PlayerOutfits.GetByID(id)
return MySQL.single.await("SELECT * FROM player_outfits WHERE id = ?", {id})
end
function Database.PlayerOutfits.GetByOutfit(name, citizenid) -- for validate duplicate name before insert
return MySQL.single.await("SELECT * FROM player_outfits WHERE outfitname = ? AND citizenid = ?", {name, citizenid})
end
function Database.PlayerOutfits.Add(citizenID, outfitName, model, components, props)
return MySQL.insert.await("INSERT INTO player_outfits (citizenid, outfitname, model, components, props) VALUES (?, ?, ?, ?, ?)", {
citizenID,
outfitName,
model,
components,
props
})
end
function Database.PlayerOutfits.Update(outfitID, model, components, props)
return MySQL.update.await("UPDATE player_outfits SET model = ?, components = ?, props = ? WHERE id = ?", {
model,
components,
props,
outfitID
})
end
function Database.PlayerOutfits.DeleteByID(id)
MySQL.query.await("DELETE FROM player_outfits WHERE id = ?", {id})
end

View File

@ -0,0 +1,5 @@
Database.Players = {}
function Database.Players.GetAll()
return MySQL.query.await("SELECT * FROM players")
end

View File

@ -0,0 +1,34 @@
Database.PlayerSkins = {}
function Database.PlayerSkins.UpdateActiveField(citizenID, active)
MySQL.update.await("UPDATE playerskins SET active = ? WHERE citizenid = ?", {active, citizenID}) -- Make all the skins inactive / active
end
function Database.PlayerSkins.DeleteByModel(citizenID, model)
MySQL.query.await("DELETE FROM playerskins WHERE citizenid = ? AND model = ?", {citizenID, model})
end
function Database.PlayerSkins.Add(citizenID, model, appearance, active)
MySQL.insert.await("INSERT INTO playerskins (citizenid, model, skin, active) VALUES (?, ?, ?, ?)", {citizenID, model, appearance, active})
end
function Database.PlayerSkins.GetByCitizenID(citizenID, model)
local query = "SELECT skin FROM playerskins WHERE citizenid = ?"
local queryArgs = {citizenID}
if model ~= nil then
query = query .. " AND model = ?"
queryArgs[#queryArgs + 1] = model
else
query = query .. " AND active = ?"
queryArgs[#queryArgs + 1] = 1
end
return MySQL.scalar.await(query, queryArgs)
end
function Database.PlayerSkins.DeleteByCitizenID(citizenID)
MySQL.query.await("DELETE FROM playerskins WHERE citizenid = ?", { citizenID })
end
function Database.PlayerSkins.GetAll()
return MySQL.query.await("SELECT * FROM playerskins")
end

View File

@ -0,0 +1,13 @@
Database.Users = {}
function Database.Users.UpdateSkinForUser(citizenID, skin)
return MySQL.update.await("UPDATE users SET skin = ? WHERE identifier = ?", {skin, citizenID})
end
function Database.Users.GetSkinByCitizenID(citizenID)
return MySQL.single.await("SELECT skin FROM users WHERE identifier = ?", {citizenID})
end
function Database.Users.GetAll()
return MySQL.query.await("SELECT * FROM users")
end

View File

@ -0,0 +1,18 @@
if not Framework.ESX() then return end
local ESX = exports["es_extended"]:getSharedObject()
ESX.RegisterServerCallback("esx_skin:getPlayerSkin", function(source, cb)
local Player = ESX.GetPlayerFromId(source)
local appearance = Framework.GetAppearance(Player.identifier)
cb(appearance, {
skin_male = Player.job.skin_male,
skin_female = Player.job.skin_female
})
end)
lib.callback.register("illenium-appearance:server:esx:getGradesForJob", function(_, jobName)
return Database.JobGrades.GetByJobName(jobName)
end)

View File

@ -0,0 +1,63 @@
if not Framework.ESX() then return end
local ESX = exports["es_extended"]:getSharedObject()
function Framework.GetPlayerID(src)
local Player = ESX.GetPlayerFromId(src)
if Player then
return Player.identifier
end
end
function Framework.HasMoney(src, type, money)
if type == "cash" then
type = "money"
end
local Player = ESX.GetPlayerFromId(src)
return Player.getAccount(type).money >= money
end
function Framework.RemoveMoney(src, type, money)
if type == "cash" then
type = "money"
end
local Player = ESX.GetPlayerFromId(src)
if Player.getAccount(type).money >= money then
Player.removeAccountMoney(type, money)
return true
end
return false
end
function normalizeGrade(job)
job.grade = {
level = job.grade
}
return job
end
function Framework.GetJob(src)
local Player = ESX.GetPlayerFromId(src)
return normalizeGrade(Player.getJob())
end
function Framework.GetGang(src)
local Player = ESX.GetPlayerFromId(src)
return normalizeGrade(Player.getJob())
end
function Framework.SaveAppearance(appearance, citizenID)
Database.Users.UpdateSkinForUser(citizenID, json.encode(appearance))
end
function Framework.GetAppearance(citizenID)
local user = Database.Users.GetSkinByCitizenID(citizenID)
if user then
local skin = json.decode(user.skin)
if skin then
skin.sex = skin.model == "mp_m_freemode_01" and 0 or 1
return skin
end
end
return nil
end

View File

@ -0,0 +1,28 @@
if not Framework.ESX() then return end
if Config.BossManagedOutfits then
function isBoss(grades, grade)
local highestGrade = grades[1].grade
for i = 2, #grades do
if grades[i].grade > highestGrade then
highestGrade = grades[i].grade
end
end
return highestGrade == grade
end
lib.addCommand("bossmanagedoutfits", { help = _L("commands.bossmanagedoutfits.title"), }, function(source)
local job = Framework.GetJob(source)
local grades = Database.JobGrades.GetByJobName(job.name)
if not grades then
return
end
if not isBoss(grades, job.grade.level) then
return
end
TriggerClientEvent("illenium-appearance:client:OutfitManagementMenu", source, {
type = "Job"
})
end)
end

View File

@ -0,0 +1,156 @@
if not Framework.ESX() then return end
local function tofloat(num)
return num + 0.0
end
local function convertSkinToNewFormat(oldSkin, gender)
local skin = {
components = Framework.ConvertComponents(oldSkin),
eyeColor = oldSkin.eye_color,
faceFeatures = {
chinBoneLenght = tofloat((oldSkin.chin_2 or 0) / 10),
noseBoneTwist = tofloat((oldSkin.nose_6 or 0) / 10),
nosePeakHigh = tofloat((oldSkin.nose_2 or 0) / 10),
jawBoneWidth = tofloat((oldSkin.jaw_1 or 0) / 10),
cheeksWidth = tofloat((oldSkin.cheeks_3 or 0) / 10),
eyeBrownHigh = tofloat((oldSkin.eyebrows_5 or 0) / 10),
chinHole = tofloat((oldSkin.chin_4 or 0) / 10),
jawBoneBackSize = tofloat((oldSkin.jaw_2 or 0) / 10),
eyesOpening = tofloat((oldSkin.eye_squint or 0) / 10),
lipsThickness = tofloat((oldSkin.lip_thickness or 0) / 10),
nosePeakSize = tofloat((oldSkin.nose_3 or 0) / 10),
eyeBrownForward = tofloat((oldSkin.eyebrows_6 or 0) / 10),
neckThickness = tofloat((oldSkin.neck_thickness or 0) / 10),
chinBoneSize = tofloat((oldSkin.chin_3 or 0) / 10),
chinBoneLowering = tofloat((oldSkin.chin_1 or 0) / 10),
cheeksBoneWidth = tofloat((oldSkin.cheeks_2 or 0) / 10),
nosePeakLowering = tofloat((oldSkin.nose_5 or 0) / 10),
noseBoneHigh = tofloat((oldSkin.nose_4 or 0) / 10),
cheeksBoneHigh = tofloat((oldSkin.cheeks_1 or 0) / 10),
noseWidth = tofloat((oldSkin.nose_1 or 0) / 10)
},
hair = {
highlight = oldSkin.hair_color_2 or 0,
texture = oldSkin.hair_2 or 0,
style = oldSkin.hair_1 or 0,
color = oldSkin.hair_color_1 or 0
},
headBlend = {
thirdMix = 0,
skinSecond = oldSkin.dad or 0,
skinMix = tofloat((oldSkin.skin_md_weight or 0) / 100),
skinThird = 0,
shapeFirst = oldSkin.mom or 0,
shapeThird = 0,
shapeMix = tofloat((oldSkin.face_md_weight or 0) / 100),
shapeSecond = oldSkin.dad or 0,
skinFirst = oldSkin.mom or 0
},
headOverlays = {
complexion = {
opacity = tofloat((oldSkin.complexion_2 or 0) / 10),
color = 0,
style = oldSkin.complexion_1 or 0,
secondColor = 0
},
lipstick = {
opacity = tofloat((oldSkin.lipstick_2 or 0) / 10),
color = oldSkin.lipstick_3 or 0,
style = oldSkin.lipstick_1 or 0,
secondColor = oldSkin.lipstick_4 or 0
},
eyebrows = {
opacity = tofloat((oldSkin.eyebrows_2 or 0) / 10),
color = oldSkin.eyebrows_3 or 0,
style = oldSkin.eyebrows_1 or 0,
secondColor = oldSkin.eyebrows_4 or 0
},
beard = {
opacity = tofloat((oldSkin.beard_2 or 0) / 10),
color = oldSkin.beard_3 or 0,
style = oldSkin.beard_1 or 0,
secondColor = oldSkin.beard_4 or 0
},
blush = {
opacity = tofloat((oldSkin.blush_2 or 0) / 10),
color = oldSkin.blush_3 or 0,
style = oldSkin.blush_1 or 0,
secondColor = oldSkin.blush_4 or 0
},
ageing = {
opacity = tofloat((oldSkin.age_2 or 0) / 10),
color = 0,
style = oldSkin.age_1 or 0,
secondColor = 0
},
blemishes = {
opacity = tofloat((oldSkin.blemishes_2 or 0) / 10),
color = 0,
style = oldSkin.blemishes_1 or 0,
secondColor = 0
},
chestHair = {
opacity = tofloat((oldSkin.chest_2 or 0) / 10),
color = oldSkin.chest_3 or 0,
style = oldSkin.chest_1 or 0,
secondColor = 0
},
bodyBlemishes = {
opacity = tofloat((oldSkin.bodyb_2 or 0) / 10),
color = oldSkin.bodyb_3 or 0,
style = oldSkin.bodyb_1 or 0,
secondColor = oldSkin.bodyb_4 or 0
},
moleAndFreckles = {
opacity = tofloat((oldSkin.moles_2 or 0) / 10),
color = 0,
style = oldSkin.moles_1 or 0,
secondColor = 0
},
sunDamage = {
opacity = tofloat((oldSkin.sun_2 or 0) / 10),
color = 0,
style = oldSkin.sun_1 or 0,
secondColor = 0
},
makeUp = {
opacity = tofloat((oldSkin.makeup_2 or 0) / 10),
color = oldSkin.makeup_3 or 0,
style = oldSkin.makeup_1 or 0,
secondColor = oldSkin.makeup_4 or 0
}
},
model = gender == "m" and "mp_m_freemode_01" or "mp_f_freemode_01",
props = Framework.ConvertProps(oldSkin),
tattoos = {}
}
return skin
end
lib.addCommand("migrateskins", { help = "Migrate skins", restricted = "group.admin" }, function(source)
local users = Database.Users.GetAll()
local convertedSkins = 0
if users then
for i = 1, #users do
local user = users[i]
if user.skin then
local oldSkin = json.decode(user.skin)
if oldSkin.hair_1 then -- Convert only if its an old skin
local skin = json.encode(convertSkinToNewFormat(oldSkin, user.sex))
local affectedRows = Database.Users.UpdateSkinForUser(user.identifier, skin)
if affectedRows then
convertedSkins += 1
end
end
end
end
end
lib.notify(source, {
title = _L("migrate.success.title"),
description = string.format(_L("migrate.success.description"), tostring(convertedSkins)),
type = "success",
position = Config.NotifyOptions.position
})
end)

View File

@ -0,0 +1,43 @@
if not Framework.QBCore() then return end
local QBCore = exports["qb-core"]:GetCoreObject()
function Framework.GetPlayerID(src)
local Player = QBCore.Functions.GetPlayer(src)
if Player then
return Player.PlayerData.citizenid
end
end
function Framework.HasMoney(src, type, money)
local Player = QBCore.Functions.GetPlayer(src)
return Player.PlayerData.money[type] >= money
end
function Framework.RemoveMoney(src, type, money)
local Player = QBCore.Functions.GetPlayer(src)
return Player.Functions.RemoveMoney(type, money)
end
function Framework.GetJob(src)
local Player = QBCore.Functions.GetPlayer(src)
return Player.PlayerData.job
end
function Framework.GetGang(src)
local Player = QBCore.Functions.GetPlayer(src)
return Player.PlayerData.gang
end
function Framework.SaveAppearance(appearance, citizenID)
Database.PlayerSkins.UpdateActiveField(citizenID, 0)
Database.PlayerSkins.DeleteByModel(citizenID, appearance.model)
Database.PlayerSkins.Add(citizenID, appearance.model, json.encode(appearance), 1)
end
function Framework.GetAppearance(citizenID, model)
local result = Database.PlayerSkins.GetByCitizenID(citizenID, model)
if result then
return json.decode(result)
end
end

View File

@ -0,0 +1,97 @@
if not Framework.QBCore() then return end
local continue = false
local function MigrateFivemAppearance(source)
local allPlayers = Database.Players.GetAll()
local playerSkins = {}
for i=1, #allPlayers, 1 do
if allPlayers[i].skin then
playerSkins[#playerSkins+1] = {
citizenID = allPlayers[i].citizenid,
skin = allPlayers[i].skin
}
end
end
for i=1, #playerSkins, 1 do
Database.PlayerSkins.Add(playerSkins[i].citizenID, json.decode(playerSkins[i].skin).model, playerSkins[i].skin, 1)
end
lib.notify(source, {
title = _L("migrate.success.title"),
description = string.format(_L("migrate.success.description"), tostring(#playerSkins)),
type = "success",
position = Config.NotifyOptions.position
})
end
local function MigrateQBClothing(source)
local allPlayerSkins = Database.PlayerSkins.GetAll()
local migrated = 0
for i=1, #allPlayerSkins, 1 do
if not tonumber(allPlayerSkins[i].model) then
lib.notify(source, {
title = _L("migrate.skip.title"),
description = _L("migrate.skip.description"),
type = "inform",
position = Config.NotifyOptions.position
})
else
TriggerClientEvent("illenium-appearance:client:migration:load-qb-clothing-skin", source, allPlayerSkins[i])
while not continue do
Wait(10)
end
continue = false
migrated = migrated + 1
end
end
TriggerClientEvent("illenium-appearance:client:reloadSkin", source)
lib.notify(source, {
title = _L("migrate.success.title"),
description = string.format(_L("migrate.success.description"), tostring(migrated)),
type = "success",
position = Config.NotifyOptions.position
})
end
RegisterNetEvent("illenium-appearance:server:migrate-qb-clothing-skin", function(citizenid, appearance)
local src = source
Database.PlayerSkins.DeleteByCitizenID(citizenid)
Database.PlayerSkins.Add(citizenid, appearance.model, json.encode(appearance), 1)
continue = true
lib.notify(src, {
id = "illenium_appearance_skin_migrated",
title = _L("migrate.success.title"),
description = _L("migrate.success.descriptionSingle"),
type = "success",
position = Config.NotifyOptions.position
})
end)
lib.addCommand("migrateskins", {
help = "Migrate skins",
params = {
{
name = "resourceName",
type = "string",
},
},
restricted = "group.god"
}, function(source, args)
local resourceName = args.resourceName
if resourceName == "fivem-appearance" then
MigrateFivemAppearance(source)
elseif resourceName == "qb-clothing" then
CreateThread(function()
MigrateQBClothing(source)
end)
else
lib.notify(source, {
title = _L("migrate.typeError.title"),
description = _L("migrate.typeError.description"),
type = "error",
position = Config.NotifyOptions.position
})
end
end)

View File

@ -0,0 +1,44 @@
local resetTimer = GetGameTimer()
local allAces = {}
lib.callback.register("illenium-appearance:server:GetPlayerAces", function()
local src = source
local allowedAces = {}
for k in pairs(allAces) do
if IsPlayerAceAllowed(src, k) then
allowedAces[#allowedAces + 1] = k
end
end
return allowedAces
end)
local function findAceFromSecurityMessage(message)
local words = {}
for word in message:gmatch("%S+") do words[#words + 1] = word end
return words[3]
end
RegisterConsoleListener(function(channel, message)
if channel ~= "security" then
return
end
local ace = findAceFromSecurityMessage(message)
if ace and ((GetGameTimer() - resetTimer) > Config.ACEResetCooldown) then
allAces = {}
end
if ace then
allAces[ace] = true
resetTimer = GetGameTimer()
end
end)
if Config.EnableACEPermissions then
CreateThread(function()
while true do
ExecuteCommand("list_aces")
Wait(Config.ACEListCooldown)
end
end)
end

View File

@ -0,0 +1,365 @@
local outfitCache = {}
local uniformCache = {}
local function getMoneyForShop(shopType)
local money = 0
if shopType == "clothing" then
money = Config.ClothingCost
elseif shopType == "barber" then
money = Config.BarberCost
elseif shopType == "tattoo" then
money = Config.TattooCost
elseif shopType == "surgeon" then
money = Config.SurgeonCost
end
return money
end
local function getOutfitsForPlayer(citizenid)
outfitCache[citizenid] = {}
local result = Database.PlayerOutfits.GetAllByCitizenID(citizenid)
for i = 1, #result, 1 do
outfitCache[citizenid][#outfitCache[citizenid] + 1] = {
id = result[i].id,
name = result[i].outfitname,
model = result[i].model,
components = json.decode(result[i].components),
props = json.decode(result[i].props)
}
end
end
local function GenerateUniqueCode()
local code, exists
repeat
code = GenerateNanoID(Config.OutfitCodeLength)
exists = Database.PlayerOutfitCodes.GetByCode(code)
until not exists
return code
end
-- Callback(s)
lib.callback.register("illenium-appearance:server:generateOutfitCode", function(_, outfitID)
local existingOutfitCode = Database.PlayerOutfitCodes.GetByOutfitID(outfitID)
if not existingOutfitCode then
local code = GenerateUniqueCode()
local id = Database.PlayerOutfitCodes.Add(outfitID, code)
if not id then
print("Something went wrong while generating outfit code")
return
end
return code
end
return existingOutfitCode.code
end)
lib.callback.register("illenium-appearance:server:importOutfitCode", function(source, outfitName, outfitCode)
local citizenID = Framework.GetPlayerID(source)
local existingOutfitCode = Database.PlayerOutfitCodes.GetByCode(outfitCode)
if not existingOutfitCode then
return nil
end
local playerOutfit = Database.PlayerOutfits.GetByID(existingOutfitCode.outfitid)
if not playerOutfit then
return
end
if playerOutfit.citizenid == citizenID then return end -- Validation when someone tried to duplicate own outfit
if Database.PlayerOutfits.GetByOutfit(outfitName, citizenID) then return end -- Validation duplicate outfit name, if validate on local id, someone can "spam error" server-sided
local id = Database.PlayerOutfits.Add(citizenID, outfitName, playerOutfit.model, playerOutfit.components, playerOutfit.props)
if not id then
print("Something went wrong while importing the outfit")
return
end
outfitCache[citizenID][#outfitCache[citizenID] + 1] = {
id = id,
name = outfitName,
model = playerOutfit.model,
components = json.decode(playerOutfit.components),
props = json.decode(playerOutfit.props)
}
return true
end)
lib.callback.register("illenium-appearance:server:getAppearance", function(source, model)
local citizenID = Framework.GetPlayerID(source)
return Framework.GetAppearance(citizenID, model)
end)
lib.callback.register("illenium-appearance:server:hasMoney", function(source, shopType)
local money = getMoneyForShop(shopType)
if Framework.HasMoney(source, "cash", money) or Framework.HasMoney(source, "bank", money) then
return true, money
else
return false, money
end
end)
lib.callback.register("illenium-appearance:server:payForTattoo", function(source, tattoo)
local src = source
local cost = tattoo.cost or Config.TattooCost
if Framework.RemoveMoney(src, "cash", cost) or Framework.RemoveMoney(src, "bank", cost) then
lib.notify(src, {
title = _L("purchase.tattoo.success.title"),
description = string.format(_L("purchase.tattoo.success.description"), tattoo.label, cost),
type = "success",
position = Config.NotifyOptions.position
})
return true
else
lib.notify(src, {
title = _L("purchase.tattoo.failure.title"),
description = _L("purchase.tattoo.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
return false
end
end)
lib.callback.register("illenium-appearance:server:getOutfits", function(source)
local citizenID = Framework.GetPlayerID(source)
if outfitCache[citizenID] == nil then
getOutfitsForPlayer(citizenID)
end
return outfitCache[citizenID]
end)
lib.callback.register("illenium-appearance:server:getManagementOutfits", function(source, mType, gender)
local job = Framework.GetJob(source)
if mType == "Gang" then
job = Framework.GetGang(source)
end
local grade = tonumber(job.grade.level)
local managementOutfits = {}
local result = Database.ManagementOutfits.GetAllByJob(mType, job.name, gender)
for i = 1, #result, 1 do
if grade >= result[i].minrank then
managementOutfits[#managementOutfits + 1] = {
id = result[i].id,
name = result[i].name,
model = result[i].model,
gender = result[i].gender,
components = json.decode(result[i].components),
props = json.decode(result[i].props)
}
end
end
return managementOutfits
end)
lib.callback.register("illenium-appearance:server:getUniform", function(source)
return uniformCache[Framework.GetPlayerID(source)]
end)
RegisterServerEvent("illenium-appearance:server:saveAppearance", function(appearance)
local src = source
local citizenID = Framework.GetPlayerID(src)
if appearance ~= nil then
Framework.SaveAppearance(appearance, citizenID)
end
end)
RegisterServerEvent("illenium-appearance:server:chargeCustomer", function(shopType)
local label
local src = source
local money = getMoneyForShop(shopType)
if Framework.RemoveMoney(src, "cash", money) or Framework.RemoveMoney(src, "bank", money) then
if shopType == "clothing" then
label = "tøj"
elseif shopType == "barber" then
label = "hårklipning"
elseif shopType == "tattoo" then
label = "tatovering"
elseif shopType == "surgeon" then
label = "kirurgi"
end
lib.notify(src, {
title = _L("purchase.store.success.title"),
description = string.format(_L("purchase.store.success.description"), label, money),
type = "success",
position = Config.NotifyOptions.position
})
else
lib.notify(src, {
title = _L("purchase.store.failure.title"),
description = _L("purchase.store.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
end
end)
RegisterNetEvent("illenium-appearance:server:saveOutfit", function(name, model, components, props)
local src = source
local citizenID = Framework.GetPlayerID(src)
if outfitCache[citizenID] == nil then
getOutfitsForPlayer(citizenID)
end
if model and components and props then
local id = Database.PlayerOutfits.Add(citizenID, name, model, json.encode(components), json.encode(props))
if not id then
return
end
outfitCache[citizenID][#outfitCache[citizenID] + 1] = {
id = id,
name = name,
model = model,
components = components,
props = props
}
lib.notify(src, {
title = _L("outfits.save.success.title"),
description = string.format(_L("outfits.save.success.description"), name),
type = "success",
position = Config.NotifyOptions.position
})
end
end)
RegisterNetEvent("illenium-appearance:server:updateOutfit", function(id, model, components, props)
local src = source
local citizenID = Framework.GetPlayerID(src)
if outfitCache[citizenID] == nil then
getOutfitsForPlayer(citizenID)
end
if model and components and props then
if not Database.PlayerOutfits.Update(id, model, json.encode(components), json.encode(props)) then return end
local outfitName = ""
for i = 1, #outfitCache[citizenID], 1 do
local outfit = outfitCache[citizenID][i]
if outfit.id == id then
outfit.model = model
outfit.components = components
outfit.props = props
outfitName = outfit.name
break
end
end
lib.notify(src, {
title = _L("outfits.update.success.title"),
description = string.format(_L("outfits.update.success.description"), outfitName),
type = "success",
position = Config.NotifyOptions.position
})
end
end)
RegisterNetEvent("illenium-appearance:server:saveManagementOutfit", function(outfitData)
local src = source
local id = Database.ManagementOutfits.Add(outfitData)
if not id then
return
end
lib.notify(src, {
title = _L("outfits.save.success.title"),
description = string.format(_L("outfits.save.success.description"), outfitData.Name),
type = "success",
position = Config.NotifyOptions.position
})
end)
RegisterNetEvent("illenium-appearance:server:deleteManagementOutfit", function(id)
Database.ManagementOutfits.DeleteByID(id)
end)
RegisterNetEvent("illenium-appearance:server:syncUniform", function(uniform)
local src = source
uniformCache[Framework.GetPlayerID(src)] = uniform
end)
RegisterNetEvent("illenium-appearance:server:deleteOutfit", function(id)
local src = source
local citizenID = Framework.GetPlayerID(src)
Database.PlayerOutfitCodes.DeleteByOutfitID(id)
Database.PlayerOutfits.DeleteByID(id)
for k, v in ipairs(outfitCache[citizenID]) do
if v.id == id then
table.remove(outfitCache[citizenID], k)
break
end
end
end)
RegisterNetEvent("illenium-appearance:server:resetOutfitCache", function()
local src = source
local citizenID = Framework.GetPlayerID(src)
if citizenID then
outfitCache[citizenID] = nil
end
end)
RegisterNetEvent("illenium-appearance:server:ChangeRoutingBucket", function()
local src = source
SetPlayerRoutingBucket(src, src)
end)
RegisterNetEvent("illenium-appearance:server:ResetRoutingBucket", function()
local src = source
SetPlayerRoutingBucket(src, 0)
end)
if Config.EnablePedMenu then
lib.addCommand("pedmenu", {
help = _L("commands.pedmenu.title"),
params = {
{
name = "playerID",
type = "number",
help = "Target player's server id",
optional = true
},
},
restricted = Config.PedMenuGroup
}, function(source, args)
local target = source
if args.playerID then
local citizenID = Framework.GetPlayerID(args.playerID)
if citizenID then
target = args.playerID
else
lib.notify(source, {
title = _L("commands.pedmenu.failure.title"),
description = _L("commands.pedmenu.failure.description"),
type = "error",
position = Config.NotifyOptions.position
})
return
end
end
TriggerClientEvent("illenium-appearance:client:openClothingShopMenu", target, true)
end)
end
if Config.EnableJobOutfitsCommand then
lib.addCommand("joboutfits", { help = _L("commands.joboutfits.title"), }, function(source)
TriggerClientEvent("illenium-apearance:client:outfitsCommand", source, true)
end)
lib.addCommand("gangoutfits", { help = _L("commands.gangoutfits.title"), }, function(source)
TriggerClientEvent("illenium-apearance:client:outfitsCommand", source)
end)
end
lib.addCommand("reloadskin", { help = _L("commands.reloadskin.title") }, function(source)
TriggerClientEvent("illenium-appearance:client:reloadSkin", source)
end)
lib.addCommand("clearstuckprops", { help = _L("commands.clearstuckprops.title") }, function(source)
TriggerClientEvent("illenium-appearance:client:ClearStuckProps", source)
end)
lib.versionCheck("iLLeniumStudios/illenium-appearance")

View File

@ -0,0 +1,12 @@
math.randomseed(os.time())
local urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"
function GenerateNanoID(size)
local id = ""
for _ = 1, size do
local randomIndex = math.random(64)
id = id .. urlAlphabet:sub(randomIndex,randomIndex)
end
return id
end

View File

@ -0,0 +1,46 @@
Config.Blacklist = {
male = {
hair = {},
components = {
masks = {},
upperBody = {},
lowerBody = {},
bags = {},
shoes = {},
scarfAndChains = {},
shirts = {},
bodyArmor = {},
decals = {},
jackets = {}
},
props = {
hats = {},
glasses = {},
ear = {},
watches = {},
bracelets = {}
}
},
female = {
hair = {},
components = {
masks = {},
upperBody = {},
lowerBody = {},
bags = {},
shoes = {},
scarfAndChains = {},
shirts = {},
bodyArmor = {},
decals = {},
jackets = {}
},
props = {
hats = {},
glasses = {},
ear = {},
watches = {},
bracelets = {}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,96 @@
if not Framework.ESX() then return end
function Framework.ConvertComponents(oldSkin, components)
return {
{
component_id = 0,
drawable = (components and components[1].drawable) or 0,
texture = (components and components[1].texture) or 0
},
{
component_id = 1,
drawable = oldSkin.mask_1 or (components and components[2].drawable) or 0,
texture = oldSkin.mask_2 or (components and components[2].texture) or 0
},
{
component_id = 2,
drawable = (components and components[3].drawable) or 0,
texture = (components and components[3].texture) or 0
},
{
component_id = 3,
drawable = oldSkin.arms or (components and components[4].drawable) or 0,
texture = oldSkin.arms_2 or (components and components[4].texture) or 0,
},
{
component_id = 4,
drawable = oldSkin.pants_1 or (components and components[5].drawable) or 0,
texture = oldSkin.pants_2 or (components and components[5].texture) or 0
},
{
component_id = 5,
drawable = oldSkin.bags_1 or (components and components[6].drawable) or 0,
texture = oldSkin.bags_2 or (components and components[6].texture) or 0
},
{
component_id = 6,
drawable = oldSkin.shoes_1 or (components and components[7].drawable) or 0,
texture = oldSkin.shoes_2 or (components and components[7].texture) or 0
},
{
component_id = 7,
drawable = oldSkin.chain_1 or (components and components[8].drawable) or 0,
texture = oldSkin.chain_2 or (components and components[8].texture) or 0
},
{
component_id = 8,
drawable = oldSkin.tshirt_1 or (components and components[9].drawable) or 0,
texture = oldSkin.tshirt_2 or (components and components[9].texture) or 0
},
{
component_id = 9,
drawable = oldSkin.bproof_1 or (components and components[10].drawable) or 0,
texture = oldSkin.bproof_2 or (components and components[10].texture) or 0
},
{
component_id = 10,
drawable = oldSkin.decals_1 or (components and components[11].drawable) or 0,
texture = oldSkin.decals_2 or (components and components[11].texture) or 0
},
{
component_id = 11,
drawable = oldSkin.torso_1 or (components and components[12].drawable) or 0,
texture = oldSkin.torso_2 or (components and components[12].texture) or 0
}
}
end
function Framework.ConvertProps(oldSkin, props)
return {
{
texture = oldSkin.helmet_2 or (props and props[1].texture) or -1,
drawable = oldSkin.helmet_1 or (props and props[1].drawable) or -1,
prop_id = 0
},
{
texture = oldSkin.glasses_2 or (props and props[2].texture) or -1,
drawable = oldSkin.glasses_1 or (props and props[2].drawable) or -1,
prop_id = 1
},
{
texture = oldSkin.ears_2 or (props and props[3].texture) or -1,
drawable = oldSkin.ears_1 or (props and props[3].drawable) or -1,
prop_id = 2
},
{
texture = oldSkin.watches_2 or (props and props[4].texture) or -1,
drawable = oldSkin.watches_1 or (props and props[4].drawable) or -1,
prop_id = 6
},
{
texture = oldSkin.bracelets_2 or (props and props[5].texture) or -1,
drawable = oldSkin.bracelets_1 or (props and props[5].drawable) or -1,
prop_id = 7
}
}
end

View File

@ -0,0 +1,9 @@
Framework = {}
function Framework.ESX()
return GetResourceState("es_extended") ~= "missing"
end
function Framework.QBCore()
return GetResourceState("qb-core") ~= "missing"
end

View File

@ -0,0 +1,839 @@
Config.Peds = {
pedConfig = {
{
peds = {
"mp_m_freemode_01",
"mp_f_freemode_01",
"a_f_m_beach_01",
"a_f_m_bevhills_01",
"a_f_m_bevhills_02",
"a_f_m_bodybuild_01",
"a_f_m_business_02",
"a_f_m_downtown_01",
"a_f_m_eastsa_01",
"a_f_m_eastsa_02",
"a_f_m_fatbla_01",
"a_f_m_fatcult_01",
"a_f_m_fatwhite_01",
"a_f_m_ktown_01",
"a_f_m_ktown_02",
"a_f_m_prolhost_01",
"a_f_m_salton_01",
"a_f_m_skidrow_01",
"a_f_m_soucent_01",
"a_f_m_soucent_02",
"a_f_m_soucentmc_01",
"a_f_m_tourist_01",
"a_f_m_tramp_01",
"a_f_m_trampbeac_01",
"a_f_o_genstreet_01",
"a_f_o_indian_01",
"a_f_o_ktown_01",
"a_f_o_salton_01",
"a_f_o_soucent_01",
"a_f_o_soucent_02",
"a_f_y_beach_01",
"a_f_y_bevhills_01",
"a_f_y_bevhills_02",
"a_f_y_bevhills_03",
"a_f_y_bevhills_04",
"a_f_y_business_01",
"a_f_y_business_02",
"a_f_y_business_03",
"a_f_y_business_04",
"a_f_y_clubcust_01",
"a_f_y_clubcust_02",
"a_f_y_clubcust_03",
"a_f_y_eastsa_01",
"a_f_y_eastsa_02",
"a_f_y_eastsa_03",
"a_f_y_epsilon_01",
"a_f_y_femaleagent",
"a_f_y_fitness_01",
"a_f_y_fitness_02",
"a_f_y_genhot_01",
"a_f_y_golfer_01",
"a_f_y_hiker_01",
"a_f_y_hippie_01",
"a_f_y_hipster_01",
"a_f_y_hipster_02",
"a_f_y_hipster_03",
"a_f_y_hipster_04",
"a_f_y_indian_01",
"a_f_y_juggalo_01",
"a_f_y_runner_01",
"a_f_y_rurmeth_01",
"a_f_y_scdressy_01",
"a_f_y_skater_01",
"a_f_y_soucent_01",
"a_f_y_soucent_02",
"a_f_y_soucent_03",
"a_f_y_tennis_01",
"a_f_y_topless_01",
"a_f_y_tourist_01",
"a_f_y_tourist_02",
"a_f_y_vinewood_01",
"a_f_y_vinewood_02",
"a_f_y_vinewood_03",
"a_f_y_vinewood_04",
"a_f_y_yoga_01",
"a_f_y_gencaspat_01",
"a_f_y_smartcaspat_01",
"a_m_m_acult_01",
"a_m_m_afriamer_01",
"a_m_m_beach_01",
"a_m_m_beach_02",
"a_m_m_bevhills_01",
"a_m_m_bevhills_02",
"a_m_m_business_01",
"a_m_m_eastsa_01",
"a_m_m_eastsa_02",
"a_m_m_farmer_01",
"a_m_m_fatlatin_01",
"a_m_m_genfat_01",
"a_m_m_genfat_02",
"a_m_m_golfer_01",
"a_m_m_hasjew_01",
"a_m_m_hillbilly_01",
"a_m_m_hillbilly_02",
"a_m_m_indian_01",
"a_m_m_ktown_01",
"a_m_m_malibu_01",
"a_m_m_mexcntry_01",
"a_m_m_mexlabor_01",
"a_m_m_og_boss_01",
"a_m_m_paparazzi_01",
"a_m_m_polynesian_01",
"a_m_m_prolhost_01",
"a_m_m_rurmeth_01",
"a_m_m_salton_01",
"a_m_m_salton_02",
"a_m_m_salton_03",
"a_m_m_salton_04",
"a_m_m_skater_01",
"a_m_m_skidrow_01",
"a_m_m_socenlat_01",
"a_m_m_soucent_01",
"a_m_m_soucent_02",
"a_m_m_soucent_03",
"a_m_m_soucent_04",
"a_m_m_stlat_02",
"a_m_m_tennis_01",
"a_m_m_tourist_01",
"a_m_m_tramp_01",
"a_m_m_trampbeac_01",
"a_m_m_tranvest_01",
"a_m_m_tranvest_02",
"a_m_o_acult_01",
"a_m_o_acult_02",
"a_m_o_beach_01",
"a_m_o_genstreet_01",
"a_m_o_ktown_01",
"a_m_o_salton_01",
"a_m_o_soucent_01",
"a_m_o_soucent_02",
"a_m_o_soucent_03",
"a_m_o_tramp_01",
"a_m_y_acult_01",
"a_m_y_acult_02",
"a_m_y_beach_01",
"a_m_y_beach_02",
"a_m_y_beach_03",
"a_m_y_beachvesp_01",
"a_m_y_beachvesp_02",
"a_m_y_bevhills_01",
"a_m_y_bevhills_02",
"a_m_y_breakdance_01",
"a_m_y_busicas_01",
"a_m_y_business_01",
"a_m_y_business_02",
"a_m_y_business_03",
"a_m_y_clubcust_01",
"a_m_y_clubcust_02",
"a_m_y_clubcust_03",
"a_m_y_cyclist_01",
"a_m_y_dhill_01",
"a_m_y_downtown_01",
"a_m_y_eastsa_01",
"a_m_y_eastsa_02",
"a_m_y_epsilon_01",
"a_m_y_epsilon_02",
"a_m_y_gay_01",
"a_m_y_gay_02",
"a_m_y_genstreet_01",
"a_m_y_genstreet_02",
"a_m_y_golfer_01",
"a_m_y_hasjew_01",
"a_m_y_hiker_01",
"a_m_y_hippy_01",
"a_m_y_hipster_01",
"a_m_y_hipster_02",
"a_m_y_hipster_03",
"a_m_y_indian_01",
"a_m_y_jetski_01",
"a_m_y_juggalo_01",
"a_m_y_ktown_01",
"a_m_y_ktown_02",
"a_m_y_latino_01",
"a_m_y_methhead_01",
"a_m_y_mexthug_01",
"a_m_y_motox_01",
"a_m_y_motox_02",
"a_m_y_musclbeac_01",
"a_m_y_musclbeac_02",
"a_m_y_polynesian_01",
"a_m_y_roadcyc_01",
"a_m_y_runner_01",
"a_m_y_runner_02",
"a_m_y_salton_01",
"a_m_y_skater_01",
"a_m_y_skater_02",
"a_m_y_soucent_01",
"a_m_y_soucent_02",
"a_m_y_soucent_03",
"a_m_y_soucent_04",
"a_m_y_stbla_01",
"a_m_y_stbla_02",
"a_m_y_stlat_01",
"a_m_y_stwhi_01",
"a_m_y_stwhi_02",
"a_m_y_sunbathe_01",
"a_m_y_surfer_01",
"a_m_y_vindouche_01",
"a_m_y_vinewood_01",
"a_m_y_vinewood_02",
"a_m_y_vinewood_03",
"a_m_y_vinewood_04",
"a_m_y_yoga_01",
"a_m_m_mlcrisis_01",
"a_m_y_gencaspat_01",
"a_m_y_smartcaspat_01",
"a_c_boar",
"a_c_cat_01",
"a_c_chickenhawk",
"a_c_chimp",
"a_c_chop",
"a_c_cormorant",
"a_c_cow",
"a_c_coyote",
"a_c_crow",
"a_c_deer",
"a_c_dolphin",
"a_c_fish",
"a_c_hen",
"a_c_humpback",
"a_c_husky",
"a_c_killerwhale",
"a_c_mtlion",
"a_c_pig",
"a_c_pigeon",
"a_c_poodle",
"a_c_pug",
"a_c_rabbit_01",
"a_c_rat",
"a_c_retriever",
"a_c_rhesus",
"a_c_rottweiler",
"a_c_seagull",
"a_c_sharkhammer",
"a_c_sharktiger",
"a_c_shepherd",
"a_c_stingray",
"a_c_westy",
"cs_amandatownley",
"cs_andreas",
"cs_ashley",
"cs_bankman",
"cs_barry",
"cs_beverly",
"cs_brad",
"cs_bradcadaver",
"cs_carbuyer",
"cs_casey",
"cs_chengsr",
"cs_chrisformage",
"cs_clay",
"cs_dale",
"cs_davenorton",
"cs_debra",
"cs_denise",
"cs_devin",
"cs_dom",
"cs_dreyfuss",
"cs_drfriedlander",
"cs_fabien",
"cs_fbisuit_01",
"cs_floyd",
"cs_guadalope",
"cs_gurk",
"cs_hunter",
"cs_janet",
"cs_jewelass",
"cs_jimmyboston",
"cs_jimmydisanto",
"cs_joeminuteman",
"cs_johnnyklebitz",
"cs_josef",
"cs_josh",
"cs_karen_daniels",
"cs_lamardavis",
"cs_lazlow",
"cs_lazlow_2",
"cs_lestercrest",
"cs_lifeinvad_01",
"cs_magenta",
"cs_manuel",
"cs_marnie",
"cs_martinmadrazo",
"cs_maryann",
"cs_michelle",
"cs_milton",
"cs_molly",
"cs_movpremf_01",
"cs_movpremmale",
"cs_mrk",
"cs_mrs_thornhill",
"cs_mrsphillips",
"cs_natalia",
"cs_nervousron",
"cs_nigel",
"cs_old_man1a",
"cs_old_man2",
"cs_omega",
"cs_orleans",
"cs_paper",
"cs_patricia",
"cs_priest",
"cs_prolsec_02",
"cs_russiandrunk",
"cs_siemonyetarian",
"cs_solomon",
"cs_stevehains",
"cs_stretch",
"cs_tanisha",
"cs_taocheng",
"cs_taostranslator",
"cs_tenniscoach",
"cs_terry",
"cs_tom",
"cs_tomepsilon",
"cs_tracydisanto",
"cs_wade",
"cs_zimbor",
"csb_abigail",
"csb_agent",
"csb_alan",
"csb_anita",
"csb_anton",
"csb_avon",
"csb_ballasog",
"csb_bogdan",
"csb_bride",
"csb_bryony",
"csb_burgerdrug",
"csb_car3guy1",
"csb_car3guy2",
"csb_chef",
"csb_chef2",
"csb_chin_goon",
"csb_cletus",
"csb_cop",
"csb_customer",
"csb_denise_friend",
"csb_dix",
"csb_djblamadon",
"csb_englishdave",
"csb_fos_rep",
"csb_g",
"csb_groom",
"csb_grove_str_dlr",
"csb_hao",
"csb_hugh",
"csb_imran",
"csb_jackhowitzer",
"csb_janitor",
"csb_maude",
"csb_money",
"csb_mp_agent14",
"csb_mrs_r",
"csb_mweather",
"csb_ortega",
"csb_oscar",
"csb_paige",
"csb_popov",
"csb_porndudes",
"csb_prologuedriver",
"csb_prolsec",
"csb_ramp_gang",
"csb_ramp_hic",
"csb_ramp_hipster",
"csb_ramp_marine",
"csb_ramp_mex",
"csb_rashcosvki",
"csb_reporter",
"csb_roccopelosi",
"csb_screen_writer",
"csb_sol",
"csb_stripper_01",
"csb_stripper_02",
"csb_talcc",
"csb_talmm",
"csb_tonya",
"csb_tonyprince",
"csb_trafficwarden",
"csb_undercover",
"csb_vagspeak",
"csb_agatha",
"csb_avery",
"csb_brucie2",
"csb_thornton",
"csb_tomcasino",
"csb_vincent",
"g_f_importexport_01",
"g_f_importexport_01",
"g_f_y_ballas_01",
"g_f_y_families_01",
"g_f_y_lost_01",
"g_f_y_vagos_01",
"g_m_importexport_01",
"g_m_m_armboss_01",
"g_m_m_armgoon_01",
"g_m_m_armlieut_01",
"g_m_m_chemwork_01",
"g_m_m_chiboss_01",
"g_m_m_chicold_01",
"g_m_m_chigoon_01",
"g_m_m_chigoon_02",
"g_m_m_korboss_01",
"g_m_m_mexboss_01",
"g_m_m_mexboss_02",
"g_m_y_armgoon_02",
"g_m_y_azteca_01",
"g_m_y_ballaeast_01",
"g_m_y_ballaorig_01",
"g_m_y_ballasout_01",
"g_m_y_famca_01",
"g_m_y_famdnf_01",
"g_m_y_famfor_01",
"g_m_y_korean_01",
"g_m_y_korean_02",
"g_m_y_korlieut_01",
"g_m_y_lost_01",
"g_m_y_lost_02",
"g_m_y_lost_03",
"g_m_y_mexgang_01",
"g_m_y_mexgoon_01",
"g_m_y_mexgoon_02",
"g_m_y_mexgoon_03",
"g_m_y_pologoon_01",
"g_m_y_pologoon_02",
"g_m_y_salvaboss_01",
"g_m_y_salvagoon_01",
"g_m_y_salvagoon_02",
"g_m_y_salvagoon_03",
"g_m_y_strpunk_01",
"g_m_y_strpunk_02",
"g_m_m_casrn_01",
"mp_f_bennymech_01",
"mp_f_boatstaff_01",
"mp_f_cardesign_01",
"mp_f_chbar_01",
"mp_f_cocaine_01",
"mp_f_counterfeit_01",
"mp_f_deadhooker",
"mp_f_execpa_01",
"mp_f_execpa_02",
"mp_f_forgery_01",
"mp_f_helistaff_01",
"mp_f_meth_01",
"mp_f_misty_01",
"mp_f_stripperlite",
"mp_f_weed_01",
"mp_g_m_pros_01",
"mp_m_avongoon",
"mp_m_boatstaff_01",
"mp_m_bogdangoon",
"mp_m_claude_01",
"mp_m_cocaine_01",
"mp_m_counterfeit_01",
"mp_m_exarmy_01",
"mp_m_execpa_01",
"mp_m_famdd_01",
"mp_m_fibsec_01",
"mp_m_forgery_01",
"mp_m_g_vagfun_01",
"mp_m_marston_01",
"mp_m_meth_01",
"mp_m_niko_01",
"mp_m_securoguard_01",
"mp_m_shopkeep_01",
"mp_m_waremech_01",
"mp_m_weapexp_01",
"mp_m_weapwork_01",
"mp_m_weed_01",
"mp_s_m_armoured_01",
"s_f_m_fembarber",
"s_f_m_maid_01",
"s_f_m_shop_high",
"s_f_m_sweatshop_01",
"s_f_y_airhostess_01",
"s_f_y_bartender_01",
"s_f_y_baywatch_01",
"s_f_y_clubbar_01",
"s_f_y_cop_01",
"s_f_y_factory_01",
"s_f_y_hooker_01",
"s_f_y_hooker_02",
"s_f_y_hooker_03",
"s_f_y_migrant_01",
"s_f_y_movprem_01",
"s_f_y_ranger_01",
"s_f_y_scrubs_01",
"s_f_y_sheriff_01",
"s_f_y_shop_low",
"s_f_y_shop_mid",
"s_f_y_stripper_01",
"s_f_y_stripper_02",
"s_f_y_stripperlite",
"s_f_y_sweatshop_01",
"s_f_y_casino_01",
"s_m_m_ammucountry",
"s_m_m_armoured_01",
"s_m_m_armoured_02",
"s_m_m_autoshop_01",
"s_m_m_autoshop_02",
"s_m_m_bouncer_01",
"s_m_m_ccrew_01",
"s_m_m_chemsec_01",
"s_m_m_ciasec_01",
"s_m_m_cntrybar_01",
"s_m_m_dockwork_01",
"s_m_m_doctor_01",
"s_m_m_fiboffice_01",
"s_m_m_fiboffice_02",
"s_m_m_fibsec_01",
"s_m_m_gaffer_01",
"s_m_m_gardener_01",
"s_m_m_gentransport",
"s_m_m_hairdress_01",
"s_m_m_highsec_01",
"s_m_m_highsec_02",
"s_m_m_janitor",
"s_m_m_lathandy_01",
"s_m_m_lifeinvad_01",
"s_m_m_linecook",
"s_m_m_lsmetro_01",
"s_m_m_mariachi_01",
"s_m_m_marine_01",
"s_m_m_marine_02",
"s_m_m_migrant_01",
"s_m_m_movalien_01",
"s_m_m_movprem_01",
"s_m_m_movspace_01",
"s_m_m_paramedic_01",
"s_m_m_pilot_01",
"s_m_m_pilot_02",
"s_m_m_postal_01",
"s_m_m_postal_02",
"s_m_m_prisguard_01",
"s_m_m_scientist_01",
"s_m_m_security_01",
"s_m_m_snowcop_01",
"s_m_m_strperf_01",
"s_m_m_strpreach_01",
"s_m_m_strvend_01",
"s_m_m_trucker_01",
"s_m_m_ups_01",
"s_m_m_ups_02",
"s_m_o_busker_01",
"s_m_y_airworker",
"s_m_y_ammucity_01",
"s_m_y_armymech_01",
"s_m_y_autopsy_01",
"s_m_y_barman_01",
"s_m_y_baywatch_01",
"s_m_y_blackops_01",
"s_m_y_blackops_02",
"s_m_y_blackops_03",
"s_m_y_busboy_01",
"s_m_y_chef_01",
"s_m_y_clown_01",
"s_m_y_clubbar_01",
"s_m_y_construct_01",
"s_m_y_construct_02",
"s_m_y_cop_01",
"s_m_y_dealer_01",
"s_m_y_devinsec_01",
"s_m_y_dockwork_01",
"s_m_y_doorman_01",
"s_m_y_dwservice_01",
"s_m_y_dwservice_02",
"s_m_y_factory_01",
"s_m_y_fireman_01",
"s_m_y_garbage",
"s_m_y_grip_01",
"s_m_y_hwaycop_01",
"s_m_y_marine_01",
"s_m_y_marine_02",
"s_m_y_marine_03",
"s_m_y_mime",
"s_m_y_pestcont_01",
"s_m_y_pilot_01",
"s_m_y_prismuscl_01",
"s_m_y_prisoner_01",
"s_m_y_ranger_01",
"s_m_y_robber_01",
"s_m_y_sheriff_01",
"s_m_y_shop_mask",
"s_m_y_strvend_01",
"s_m_y_swat_01",
"s_m_y_uscg_01",
"s_m_y_valet_01",
"s_m_y_waiter_01",
"s_m_y_waretech_01",
"s_m_y_winclean_01",
"s_m_y_xmech_01",
"s_m_y_xmech_02",
"s_m_y_casino_01",
"s_m_y_westsec_01",
"hc_driver",
"hc_gunman",
"hc_hacker",
"ig_abigail",
"ig_agent",
"ig_amandatownley",
"ig_andreas",
"ig_ashley",
"ig_avon",
"ig_ballasog",
"ig_bankman",
"ig_barry",
"ig_benny",
"ig_bestmen",
"ig_beverly",
"ig_brad",
"ig_bride",
"ig_car3guy1",
"ig_car3guy2",
"ig_casey",
"ig_chef",
"ig_chef2",
"ig_chengsr",
"ig_chrisformage",
"ig_clay",
"ig_claypain",
"ig_cletus",
"ig_dale",
"ig_davenorton",
"ig_denise",
"ig_devin",
"ig_dix",
"ig_djblamadon",
"ig_djblamrupert",
"ig_djblamryans",
"ig_djdixmanager",
"ig_djgeneric_01",
"ig_djsolfotios",
"ig_djsoljakob",
"ig_djsolmanager",
"ig_djsolmike",
"ig_djsolrobt",
"ig_djtalaurelia",
"ig_djtalignazio",
"ig_dom",
"ig_dreyfuss",
"ig_drfriedlander",
"ig_englishdave",
"ig_fabien",
"ig_fbisuit_01",
"ig_floyd",
"ig_g",
"ig_groom",
"ig_hao",
"ig_hunter",
"ig_janet",
"ig_jay_norris",
"ig_jewelass",
"ig_jimmyboston",
"ig_jimmyboston_02",
"ig_jimmydisanto",
"ig_joeminuteman",
"ig_johnnyklebitz",
"ig_josef",
"ig_josh",
"ig_karen_daniels",
"ig_kerrymcintosh",
"ig_kerrymcintosh_02",
"ig_lacey_jones_02",
"ig_lamardavis",
"ig_lazlow",
"ig_lazlow_2",
"ig_lestercrest",
"ig_lestercrest_2",
"ig_lifeinvad_01",
"ig_lifeinvad_02",
"ig_magenta",
"ig_malc",
"ig_manuel",
"ig_marnie",
"ig_maryann",
"ig_maude",
"ig_michelle",
"ig_milton",
"ig_molly",
"ig_money",
"ig_mp_agent14",
"ig_mrk",
"ig_mrs_thornhill",
"ig_mrsphillips",
"ig_natalia",
"ig_nervousron",
"ig_nigel",
"ig_old_man1a",
"ig_old_man2",
"ig_omega",
"ig_oneil",
"ig_orleans",
"ig_ortega",
"ig_paige",
"ig_paper",
"ig_patricia",
"ig_popov",
"ig_priest",
"ig_prolsec_02",
"ig_ramp_gang",
"ig_ramp_hic",
"ig_ramp_hipster",
"ig_ramp_mex",
"ig_rashcosvki",
"ig_roccopelosi",
"ig_russiandrunk",
"ig_sacha",
"ig_screen_writer",
"ig_siemonyetarian",
"ig_sol",
"ig_solomon",
"ig_stevehains",
"ig_stretch",
"ig_talcc",
"ig_talina",
"ig_talmm",
"ig_tanisha",
"ig_taocheng",
"ig_taostranslator",
"ig_tenniscoach",
"ig_terry",
"ig_tomepsilon",
"ig_tonya",
"ig_tonyprince",
"ig_tracydisanto",
"ig_trafficwarden",
"ig_tylerdix",
"ig_tylerdix_02",
"ig_vagspeak",
"ig_wade",
"ig_zimbor",
"player_one",
"player_two",
"player_zero",
"ig_agatha",
"ig_avery",
"ig_brucie2",
"ig_thornton",
"ig_tomcasino",
"ig_vincent",
"u_f_m_corpse_01",
"u_f_m_miranda",
"u_f_m_miranda_02",
"u_f_m_promourn_01",
"u_f_o_moviestar",
"u_f_o_prolhost_01",
"u_f_y_bikerchic",
"u_f_y_comjane",
"u_f_y_corpse_01",
"u_f_y_corpse_02",
"u_f_y_danceburl_01",
"u_f_y_dancelthr_01",
"u_f_y_dancerave_01",
"u_f_y_hotposh_01",
"u_f_y_jewelass_01",
"u_f_y_mistress",
"u_f_y_poppymich",
"u_f_y_poppymich_02",
"u_f_y_princess",
"u_f_y_spyactress",
"u_f_m_casinocash_01",
"u_f_m_casinoshop_01",
"u_f_m_debbie_01",
"u_f_o_carol",
"u_f_o_eileen",
"u_f_y_beth",
"u_f_y_lauren",
"u_f_y_taylor",
"u_m_m_aldinapoli",
"u_m_m_bankman",
"u_m_m_bikehire_01",
"u_m_m_doa_01",
"u_m_m_edtoh",
"u_m_m_fibarchitect",
"u_m_m_filmdirector",
"u_m_m_glenstank_01",
"u_m_m_griff_01",
"u_m_m_jesus_01",
"u_m_m_jewelsec_01",
"u_m_m_jewelthief",
"u_m_m_markfost",
"u_m_m_partytarget",
"u_m_m_prolsec_01",
"u_m_m_promourn_01",
"u_m_m_rivalpap",
"u_m_m_spyactor",
"u_m_m_streetart_01",
"u_m_m_willyfist",
"u_m_o_filmnoir",
"u_m_o_finguru_01",
"u_m_o_taphillbilly",
"u_m_o_tramp_01",
"u_m_y_abner",
"u_m_y_antonb",
"u_m_y_babyd",
"u_m_y_baygor",
"u_m_y_burgerdrug_01",
"u_m_y_chip",
"u_m_y_corpse_01",
"u_m_y_cyclist_01",
"u_m_y_danceburl_01",
"u_m_y_dancelthr_01",
"u_m_y_dancerave_01",
"u_m_y_fibmugger_01",
"u_m_y_guido_01",
"u_m_y_gunvend_01",
"u_m_y_hippie_01",
"u_m_y_imporage",
"u_m_y_juggernaut_01",
"u_m_y_justin",
"u_m_y_mani",
"u_m_y_militarybum",
"u_m_y_paparazzi",
"u_m_y_party_01",
"u_m_y_pogo_01",
"u_m_y_prisoner_01",
"u_m_y_proldriver_01",
"u_m_y_rsranger_01",
"u_m_y_sbike",
"u_m_y_smugmech_01",
"u_m_y_staggrm_01",
"u_m_y_tattoo_01",
"u_m_y_zombie_01",
"u_m_m_blane",
"u_m_m_curtis",
"u_m_m_vince",
"u_m_o_dean",
"u_m_y_caleb",
"u_m_y_croupthief_01",
"u_m_y_gabriel",
"u_m_y_ushi"
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
Config.Theme = {
currentTheme = "qb-core",
themes = {
{
id = "default",
borderRadius = "4px",
fontColor = "255, 255, 255",
fontColorHover = "255, 255, 255",
fontColorSelected = "0, 0, 0",
fontFamily = "Inter",
primaryBackground = "0, 0, 0",
primaryBackgroundSelected = "255, 255, 255",
secondaryBackground = "0, 0, 0",
scaleOnHover = false,
sectionFontWeight = "normal",
smoothBackgroundTransition = false
},
{
id = "qb-core",
borderRadius = "3vh",
fontColor = "255, 255, 255",
fontColorHover = "255, 255, 255",
fontColorSelected = "255, 255, 255",
fontFamily = "Poppins",
primaryBackground = "220, 20, 60",
primaryBackgroundSelected = "220, 20, 60",
secondaryBackground = "23, 23, 23",
scaleOnHover = true,
sectionFontWeight = "bold",
smoothBackgroundTransition = true
},
{
id = "project-sloth",
borderRadius = "6vh",
fontColor = "255, 255, 255",
fontColorHover = "255, 255, 255",
fontColorSelected = "255, 255, 255",
fontFamily = "Inter",
primaryBackground = "2, 241, 181",
primaryBackgroundSelected = "2, 241, 181",
secondaryBackground = "27, 24, 69",
scaleOnHover = true,
sectionFontWeight = "bold",
smoothBackgroundTransition = false
},
{
id = "not-heavily-inspired",
borderRadius = "10vh",
fontColor = "255, 255, 255",
fontColorHover = "255, 255, 255",
fontColorSelected = "255, 255, 255",
fontFamily = "Inter",
primaryBackground = "149, 239, 119",
primaryBackgroundSelected = "242, 163, 101",
secondaryBackground = "25, 46, 70",
scaleOnHover = true,
sectionFontWeight = "bold",
smoothBackgroundTransition = false
}
}
}

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS `management_outfits` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_name` varchar(50) NOT NULL,
`type` varchar(50) NOT NULL,
`minrank` int(11) NOT NULL DEFAULT 0,
`name` varchar(50) NOT NULL DEFAULT 'Cool Outfit',
`gender` varchar(50) NOT NULL DEFAULT 'male',
`model` varchar(50) DEFAULT NULL,
`props` varchar(1000) DEFAULT NULL,
`components` varchar(1500) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4;

View File

@ -0,0 +1,22 @@
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE TABLE IF NOT EXISTS `player_outfit_codes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`outfitid` int(11) NOT NULL,
`code` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `FK_player_outfit_codes_player_outfits` (`outfitid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

View File

@ -0,0 +1,24 @@
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE TABLE IF NOT EXISTS `player_outfits` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(50) DEFAULT NULL,
`outfitname` varchar(50) NOT NULL DEFAULT '0',
`model` varchar(50) DEFAULT NULL,
`props` varchar(1000) DEFAULT NULL,
`components` varchar(1500) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `citizenid_outfitname_model` (`citizenid`,`outfitname`,`model`),
KEY `citizenid` (`citizenid`)
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

View File

@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS `playerskins` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(255) NOT NULL,
`model` varchar(255) NOT NULL,
`skin` text NOT NULL,
`active` tinyint(4) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
KEY `citizenid` (`citizenid`),
KEY `active` (`active`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>UI</title>
<script type="module" crossorigin src="./assets/index.b8e72b46.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -0,0 +1,174 @@
local QBCore = exports[Config.Core]:GetCoreObject()
local beanseat = nil
local sitting = false
local Chairs = {}
CreateThread(function()
for k, v in pairs(Config.Chairs) do
Chairs["beanmachine"..k] =
exports['qb-target']:AddBoxZone("beanmachine"..k, vec3(v.coords.x, v.coords.y, v.coords.z-1), 0.7, 0.7, { name="beanmachine"..k, heading = v.coords.w, debugPoly=Config.Debug, minZ = v.coords.z-1.2, maxZ = v.coords.z+0.1, },
{ options = { { event = "jim-beanmachine:Chair", icon = "fas fa-chair", label = Loc[Config.Lan].targetinfo["sit_down"], loc = v.coords, stand = v.stand }, },
distance = 2.2 })
end
end)
RegisterNetEvent('jim-beanmachine:Chair', function(data)
local canSit = true
local sitting = false
local ped = PlayerPedId()
for _, v in pairs(QBCore.Functions.GetPlayersFromCoords(data.loc.xyz, 0.6)) do
local dist = #(GetEntityCoords(GetPlayerPed(v)) - data.loc.xyz)
if dist <= 0.4 then triggerNotify(nil, Loc[Config.Lan].error["someone_already_sitting"]) canSit = false end
end
if canSit then
if not IsPedHeadingTowardsPosition(ped, data.loc.xyz, 20.0) then TaskTurnPedToFaceCoord(ped, data.loc.xyz, 1500) Wait(1500) end
if #(data.loc.xyz - GetEntityCoords(PlayerPedId())) > 1.5 then TaskGoStraightToCoord(ped, data.loc.xyz, 0.5, 1000, 0.0, 0) Wait(1100) end
TaskStartScenarioAtPosition(PlayerPedId(), "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER", data.loc.x, data.loc.y, data.loc.z-0.5, data.loc[4], 0, 1, true)
beanseat = data.stand
sitting = true
end
while sitting do
if sitting then
if IsControlJustReleased(0, 202) and IsPedUsingScenario(ped, "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER") then
sitting = false
ClearPedTasks(ped)
if beanseat then SetEntityCoords(ped, beanseat) end
beanseat = nil
end
end
Wait(5) if not IsPedUsingScenario(ped, "PROP_HUMAN_SEAT_CHAIR_MP_PLAYER") then sitting = false end
end
end)
Config.Chairs = {}
for k, v in pairs(Config.Locations) do
if v.zoneEnable and k == "beangabzlegion" then
--STOOLS
Config.Chairs[#Config.Chairs+1] = { coords = vec4(124.5, -1032.41, 29.27+0.3, 160.0), stand = vec3(124.7, -1031.93, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(122.95, -1032.34, 29.28+0.3, 205.0), stand = vec3(122.79, -1031.92, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(122.19, -1033.4, 29.28+0.3, 250.0), stand = vec3(121.61, -1033.26, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(121.76, -1034.44, 29.28+0.3, 250.0), stand = vec3(121.31, -1034.31, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(119.12, -1041.75, 29.28+0.3, 250.0), stand = vec3(118.58, -1041.51, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(118.74, -1042.75, 29.28+0.3, 235.0), stand = vec3(118.33, -1042.5, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(118.71, -1044.01, 29.28+0.3, 295.0), stand = vec3(118.27, -1044.17, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(119.9, -1044.98, 29.28+0.3, 340.0), stand = vec3(119.69, -1045.51, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(122.45, -1046.87, 29.28+0.3, 86.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(121.74, -1048.66, 29.28+0.3, 18.0), stand = vec3(121.35, -1048.28, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(117.91, -1047.8, 29.28+0.3, 18.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(116.28, -1047.11, 29.28+0.3, 317.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(125.15, -1029.07, 29.28+0.3, 118.0), stand = vec3(125.21, -1029.64, 29.28-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(123.61, -1028.61, 29.28+0.3, 188.0), stand = vec3(123.35, -1029.11, 29.28-0.5) }
--SEATING
Config.Chairs[#Config.Chairs+1] = { coords = vec4(120.67, -1027.8, 29.28, 165.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(119.02, -1028.51, 29.28, 245.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(119.15, -1031.58, 29.28, 175.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(117.74, -1032.55, 29.28, 250.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(117.39, -1033.47, 29.28, 250.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(117.04, -1034.42, 29.28, 250.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(117.66, -1035.97, 29.28, 335.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(113.5, -1044.37, 28.92, 265.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(114.34, -1045.99, 28.92, 325.0), }
end
if v.zoneEnable and k == "beanunclejust" then
--STOOLS
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-633.64, 234.67, 81.88+0.2, 92.0), stand = vec3(-633.09, 234.73, 81.88-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-633.75, 235.63, 81.88+0.2, 92.0), stand = vec3(-633.33, 235.65, 81.88-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-634.03, 236.75, 81.88+0.2, 182.0), stand = vec3(-633.18, 236.42, 81.88-0.5) }
--Downstairs
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-631.62, 236.27, 81.88, 275.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-628.6, 236.73, 81.88, 95.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-626.22, 235.22, 81.88, 300.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-624.47, 234.09, 81.88, 355.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-622.67, 235.11, 81.88, 55.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-626.85, 229.71, 81.88, 0.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-625.7, 229.69, 81.88, 0.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-624.6, 231.15, 81.88, 90.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-618.61, 232.89, 81.88, 145.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-620.51, 230.18, 81.88, 320.0), }
--Upstairs
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-623.7, 233.62, 86.33, 130.0), stand = vec3(-623.28, 234.17, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-625.21, 233.45, 86.33, 220.0), stand = vec3(-625.67, 233.94, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-625.64, 232.02, 86.33, 310.0), stand = vec3(-626.22, 231.48, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-626.82, 231.09, 86.33, 130.0), stand = vec3(-626.22, 231.48, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-628.39, 230.97, 86.33, 220.0), stand = vec3(-628.8, 231.53, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-628.82, 229.51, 86.33, 310.0), stand = vec3(-629.37, 229.13, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-624.59, 237.18, 86.33, 120.0), stand = vec3(-624.06, 237.63, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-625.31, 235.85, 86.33, 30.0), stand = vec3(-624.83, 235.34, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-626.81, 236.01, 86.33, 300.0), stand = vec3(-627.37, 235.66, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-633.4, 232.16, 86.33, 150.0), stand = vec3(-632.88, 231.85, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-633.4, 230.55, 86.33, 60.0), stand = vec3(-632.9, 230.19, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-634.76, 229.97, 86.33, 330.0), stand = vec3(-634.17, 229.69, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-633.4, 227.58, 86.33, 85.0), stand = vec3(-632.73, 227.6, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-634.7, 226.83, 86.33, 355.0), stand = vec3(-634.97, 226.02, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-635.75, 227.74, 86.33, 265.0), stand = vec3(-634.97, 226.02, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-635.67, 225.62, 86.33, 180.0), stand = vec3(-634.97, 226.02, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-634.9, 224.33, 86.33, 90.0), stand = vec3(-634.29, 224.31, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-635.68, 223.1, 86.33, 0.0), stand = vec3(-634.99, 223.01, 86.33-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-633.07, 222.61, 86.33, 0.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-631.93, 222.61, 86.33, 0.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-630.54, 223.7, 86.33, 75.0), }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-630.29, 224.82, 86.33, 75.0), }
--UPPER BALCONY
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-636.72, 231.8, 86.08, 4.0), stand = vec3(-635.86, 231.68, 86.07-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-636.77, 233.65, 86.06, 184.0), stand = vec3(-635.83, 233.58, 86.06-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-634.11, 237.08, 86.06, 300.0), stand = vec3(-633.6, 236.49, 86.06-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-632.86, 236.82, 86.06, 30.0), stand = vec3(-633.6, 236.49, 86.06-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-632.59, 238.04, 86.05, 120.0), stand = vec3(-633.11, 238.64, 86.06-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-633.83, 238.35, 86.06, 210.0), stand = vec3(-633.11, 238.64, 86.06-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-630.14, 240.02, 86.04, 15.0), stand = vec3(-630.85, 239.81, 86.05-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-631.28, 240.54, 86.05, 285.0), stand = vec3(-630.85, 239.81, 86.05-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-629.63, 241.17, 86.04, 105.0), stand = vec3(-629.39, 240.55, 86.05-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-627.25, 241.95, 86.04, 285.0), stand = vec3(-626.88, 241.24, 86.03-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-626.05, 241.39, 86.03, 15.0), stand = vec3(-626.88, 241.24, 86.03-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-625.36, 242.58, 86.01, 105.0), stand = vec3(-625.29, 241.79, 86.01-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-622.15, 242.37, 86.01, 260.0), stand = vec3(-622.11, 241.57, 86.01-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-621.37, 241.42, 86.01, 350.0), stand = vec3(-622.11, 241.57, 86.01-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-620.39, 242.2, 86.01, 80.0), stand = vec3(-620.5, 241.43, 86.01-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-624.04, 238.96, 86.01, 120.0), stand = vec3(-624.4, 239.71, 86.01-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-625.59, 238.09, 86.02, 300.0), stand = vec3(-626.01, 238.73, 86.02-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-627.76, 237.1, 86.03, 120.0), stand = vec3(-628.09, 237.79, 86.03-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-629.32, 236.25, 86.04, 300.0), stand = vec3(-629.75, 236.94, 86.03-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-627.61, 227.61, 81.88, 180.0), stand = vec3(-628.61, 227.59, 81.88-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-628.46, 226.75, 81.88, 270.0), stand = vec3(-628.61, 227.59, 81.88-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-628.45, 225.91, 81.88, 270.0), stand = vec3(-628.31, 225.18, 81.88-0.5) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(-627.53, 224.93, 81.88, 0.0), stand = vec3(-628.31, 225.18, 81.88-0.5) }
end
if v.zoneEnable and k == "beanrflx" then
Config.Chairs[#Config.Chairs+1] = { coords = vec4(279.53, -969.18, 29.42, 0.0), stand = vec3(280.63, -969.1, 29.42) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(279.51, -966.31, 29.42, 200.0) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(283.36, -971.17, 29.42, 0.0), stand = vec3(282.39, -970.87, 29.42) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(283.31, -968.57, 29.42, 180.0), stand = vec3(282.11, -968.12, 29.42) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(283.3, -967.58, 29.42, 0.0), stand = vec3(282.11, -968.12, 29.42) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(283.31, -964.97, 29.42, 180.0), stand = vec3(282.05, -965.13, 29.42) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(287.83, -974.44, 29.42, 90.0) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(287.83, -975.25, 29.42, 90.0) }
Config.Chairs[#Config.Chairs+1] = { coords = vec4(287.83, -976.10, 29.42, 90.0) }
end
end
AddEventHandler('onResourceStop', function(r) if r ~= GetCurrentResourceName() then return end
if GetResourceState("qb-target") == "started" or GetResourceState("ox_target") == "started" then
for k, v in pairs(Chairs) do exports['qb-target']:RemoveZone(k) end
end
end)

View File

@ -0,0 +1,343 @@
local QBCore = exports[Config.Core]:GetCoreObject()
PlayerJob = {}
local Targets, Props, CraftLock = {}, {}, false
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
QBCore.Functions.GetPlayerData(function(PlayerData) PlayerJob = PlayerData.job if PlayerJob.onduty then for k, v in pairs(Config.Locations) do if PlayerData.job.name == v.job then TriggerServerEvent("QBCore:ToggleDuty") end end end end)
end)
RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) PlayerJob = JobInfo onDuty = PlayerJob.onduty end)
RegisterNetEvent('QBCore:Client:SetDuty', function(duty) onDuty = duty end)
AddEventHandler('onResourceStart', function(r) if GetCurrentResourceName() ~= r then return end
QBCore.Functions.GetPlayerData(function(PlayerData) PlayerJob = PlayerData.job for k, v in pairs(Config.Locations) do if PlayerData.job.name == v.job then onDuty = PlayerJob.onduty end end end)
end)
CreateThread(function()
for k, v in pairs(Config.Locations) do
local bossroles = {}
for grade, info in pairs(QBCore.Shared.Jobs[v.job].grades) do
if info.isboss then
if bossroles[v.job] then
if bossroles[v.job] > tonumber(grade) then bossroles[v.job] = tonumber(grade) end
else bossroles[v.job] = tonumber(grade) end
end
end
if v.zoneEnable then
makeBlip({ coords = v.blip, sprite = v.blipsprite, col = v.blipcolor, scale = v.blipscale, disp = v.blipdisp, category = v.blipcat, name = v.label })
if v.zones then
JobLocation = PolyZone:Create(v.zones, { name = v.label, debugPoly = Config.Debug })
JobLocation:onPlayerInOut(function(isPointInside)
if PlayerJob.name == v.job then
if v.autoClock and v.autoClock.enter then if isPointInside and not onDuty then TriggerServerEvent("QBCore:ToggleDuty") end end
if v.autoClock and v.autoClock.exit then if not isPointInside and onDuty then TriggerServerEvent("QBCore:ToggleDuty") end end
end
end)
end
local propTable = {}
if k == "beangabzlegion" then
Targets["BeanWash"] =
exports['qb-target']:AddBoxZone("BeanWash", vec3(123.73, -1039.24, 29.28-0.5), 1.2, 0.6, { name="BeanWash", heading = 340.0, debugPoly=Config.Debug, minZ=28.88, maxZ=29.68 },
{ options = { { event = "jim-beanmachine:washHands", icon = "fas fa-hand-holding-water", label = Loc[Config.Lan].targetinfo["wash_hands"], job = v.job, coords = vec3(123.73, -1039.24, 29.28) }, }, distance = 1.5 })
Targets["BeanCounter"] =
exports['qb-target']:AddBoxZone("BeanCounter", vec3(120.54, -1040.74, 29.28-1), 0.6, 0.4, { name="BeanCounter", heading = 340.0, debugPoly=Config.Debug, minZ=29.08, maxZ=30.08 },
{ options = { { event = "jim-beanmachine:Stash", icon = "fas fa-mug-saucer", label = Loc[Config.Lan].targetinfo["open_counter"], stash = "Counter", coords = vec3(120.54, -1040.74, 29.28) }, }, distance = 2.0 })
Targets["BeanCounter2"] =
exports['qb-target']:AddBoxZone("BeanCounter2", vec3(121.85, -1037.08, 29.28-1), 0.6, 0.4, { name="BeanCounter2", heading = 340.0, debugPoly=Config.Debug, minZ=29.08, maxZ=30.08},
{ options = { { event = "jim-beanmachine:Stash", icon = "fas fa-mug-saucer", label = Loc[Config.Lan].targetinfo["open_counter"], stash = "Counter2", coords = vec3(121.85, -1037.08, 29.28) }, }, distance = 2.0 })
Targets["BeanReceipt"] =
exports['qb-target']:AddBoxZone("BeanReceipt", vec3(120.73, -1040.09, 29.28), 0.6, 0.6, { name="BeanReceipt", heading = 340.0, debugPoly=Config.Debug, minZ=29.08, maxZ=29.88 },
{ options = { { event = "jim-payments:client:Charge", icon = "fas fa-credit-card", label = Loc[Config.Lan].targetinfo["charge_customer"], job = v.job,
img = "<center><p><img src=https://static.wikia.nocookie.net/gtawiki/images/f/fc/TheBeanMachine-GTA4-logo.png width=100px></p>"
} }, distance = 2.0 })
Targets["BeanReceipt2"] =
exports['qb-target']:AddBoxZone("BeanReceipt2",vec3(122.0, -1036.51, 29.28), 0.6, 0.6, { name="BeanReceipt2", heading = 340.0, debugPoly=Config.Debug, minZ=29.08, maxZ=29.88 },
{ options = { { event = "jim-payments:client:Charge", icon = "fas fa-credit-card", label = Loc[Config.Lan].targetinfo["charge_customer"], job = v.job,
img = "<center><p><img src=https://static.wikia.nocookie.net/gtawiki/images/f/fc/TheBeanMachine-GTA4-logo.png width=100px></p>"
} }, distance = 2.0 })
Targets["BeanFridge"] =
exports['qb-target']:AddBoxZone("BeanFridge", vec3(124.51, -1037.97, 29.28), 0.85, 0.6, { name="BeanFridge", heading = 340.0, debugPoly=Config.Debug, minZ=29.28, maxZ=30.08 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["open_storage"], job = v.job, shop = Config.FoodItems, shopname = "BeanFridge", coords = vec3(124.51, -1037.97, 29.28) }, }, distance = 1.5 })
Targets["BeanFridge2"] =
exports['qb-target']:AddBoxZone("BeanFridge2", vec3(123.5, -1040.74, 29.28), 0.9, 0.6, { name="BeanFridge2", heading = 340.0, debugPoly=Config.Debug, minZ=29.28, maxZ=30.08 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["open_storage"], job = v.job, shop = Config.FoodItems, shopname = "BeanFridge2", coords = vec3(123.5, -1040.74, 29.28) }, }, distance = 1.5 })
Targets["BeanDrink"] =
exports['qb-target']:AddBoxZone("BeanDrink", vec3(124.56, -1036.88, 29.28), 0.7, 0.4, { name="BeanDrink", heading = 340.0, debugPoly=Config.Debug, minZ=29.08, maxZ=29.88 },
{ options = { { event = "jim-beanmachine:Crafting", icon = "fas fa-mug-hot", label = Loc[Config.Lan].targetinfo["prepare_coffee"], job = v.job, craftable = Crafting.Drinks, header = Loc[Config.Lan].menu["drink_menu"], coords = vec3(124.56, -1036.88, 29.28) }, }, distance = 2.0 })
Targets["BeanDrink2"] =
exports['qb-target']:AddBoxZone("BeanDrink2", vec3(122.93, -1041.68, 29.28), 0.7, 0.6, { name="BeanDrink2", heading = 340.0, debugPoly=Config.Debug, minZ=29.08, maxZ=29.88 },
{ options = { { event = "jim-beanmachine:Crafting", icon = "fas fa-mug-hot", label = Loc[Config.Lan].targetinfo["prepare_coffee"], job = v.job, craftable = Crafting.Drinks, header = Loc[Config.Lan].menu["drink_menu"], coords = vec3(122.93, -1041.68, 29.28) }, }, distance = 2.0 })
Targets["BeanSlush"] =x
exports['qb-target']:AddBoxZone("BeanSlush", vec3(126.07, -1036.59, 29.28), 0.6, 0.6, { name="BeanSlush", heading = 340.0, debugPoly=Config.Debug, minZ=29.08, maxZ=30.08 },
{ options = { { event = "jim-beanmachine:Crafting", icon = "fas fa-cocktail", label = Loc[Config.Lan].targetinfo["prepare_slush"], job = v.job, craftable = Crafting.Slush, header = Loc[Config.Lan].menu["slush_menu"], coords = vec3(126.07, -1036.59, 29.28) }, }, distance = 2.0 })
Targets["BeanSoda"] =
exports['qb-target']:AddBoxZone("BeanSoda", vec3(123.56, -1042.75, 29.28), 0.6, 1.4, { name="BeanSoda", heading = 340.0, debugPoly=Config.Debug, minZ=29.08, maxZ=29.88 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["grab_soda"], job = v.job, shop = Config.SodaItems, shopname = "BeanSoda", coords = vec3(123.56, -1042.75, 29.28) }, }, distance = 2.0 })
Targets["BeanDonut"] =
exports['qb-target']:AddBoxZone("BeanDonut", vec3(121.4, -1038.43, 29.28), 1.45, 0.6, { name="BeanDonut", heading = 340.0, debugPoly=Config.Debug, minZ=29.28, maxZ=29.88 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-circle-dot", label = Loc[Config.Lan].targetinfo["grab_food"], job = v.job, shop = Config.DesertItems, shopname = "BeanDonut", coords = vec3(121.4, -1038.43, 29.28) },
{ event = "jim-beanmachine:Stash", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["open_stash"], job = v.job, stash = "LegionStash", coords = vec3(121.4, -1038.43, 29.28) }, }, distance = 2.0 })
Targets["BeanClockin"] =
exports['qb-target']:AddBoxZone("BeanClockin", vec3(126.94, -1035.84, 29.28), 1.2, 0.2, { name="BeanClockin", heading = 340.0, debugPoly=Config.Debug, minZ=29.28, maxZ=31.28 },
{ options = { { type = "server", event = "QBCore:ToggleDuty", icon = "fas fa-user-check", label = Loc[Config.Lan].targetinfo["toggle_duty"], job = v.job },
{ event = "qb-bossmenu:client:OpenMenu", icon = "fas fa-list", label = Loc[Config.Lan].targetinfo["open_bossmenu"], job = bossroles, },
}, distance = 2.0 })
end
if k == "beanunclejust" then
--Cereal Boxes
CreateModelHide(vec3(-636.56, 235.83, 82.43), 1.0, -598185919, true) CreateModelHide(vec3(-636.56, 235.83, 82.43), 1.0, 714696561, true)
CreateModelHide(vec3(-636.56, 235.83, 82.43), 1.0, 1241647493, true) CreateModelHide(vec3(-636.56, 235.83, 82.43), 1.0, 2141353157, true)
CreateModelHide(vec3(-634.28, 235.29, 83.12), 1.5, 270388964, true) CreateModelHide(vec3(-634.28, 235.29, 83.12), 1.5, 974883178, true)
CreateModelHide(vec3(-634.28, 235.29, 83.12), 1.5, -85890288, true) CreateModelHide(vec3(-635.73, 235.3, 81.88), 1.5, -1281587804, true)
CreateModelHide(vec3(-633.47, 233.44, 81.88+1.0), 1.5, 1503218008, true)
propTable[#propTable+1] = { prop = "prop_till_03", coords = vec4(-634.38, 235.29, 83.12, 285.0)}
propTable[#propTable+1] = { prop = "prop_food_tray_01", coords = vec4(-634.28, 234.69, 83.12, 95.0)}
propTable[#propTable+1] = { prop = "prop_slush_dispenser", coords = vec4(-636.56, 235.83, 82.43+0.91, 90.0)}
Targets["UBeanCounter"] =
exports['qb-target']:AddBoxZone("UBeanCounter", vec3(-634.27, 234.69, 81.88), 0.55, 0.4, { name="UBeanCounter", heading = 5.0, debugPoly=Config.Debug, minZ=81.93, maxZ=82.48 },
{ options = { { event = "jim-beanmachine:Stash", icon = "fas fa-mug-saucer", label = Loc[Config.Lan].targetinfo["open_counter"], stash = "UCounter", coords = vec3(-634.27, 234.69, 81.88) }, }, distance = 2.0 })
Targets["UBeanReceipt"] =
exports['qb-target']:AddBoxZone("UBeanReceipt", vec3(-634.38, 235.3, 81.88), 0.4, 0.5, { name="UBeanReceipt", heading = 15.0, debugPoly=Config.Debug, minZ=82.08, maxZ=82.68 },
{ options = { { event = "jim-payments:client:Charge", icon = "fas fa-credit-card", label = Loc[Config.Lan].targetinfo["charge_customer"], job = v.job,
img = "<center><p><img src=https://static.wikia.nocookie.net/gtawiki/images/f/fc/TheBeanMachine-GTA4-logo.png width=100px></p>"
} }, distance = 2.0 })
Targets["UBeanDrink"] =
exports['qb-target']:AddBoxZone("UBeanDrink", vec3(-635.7, 236.55, 81.88), 0.6, 1.0, { name="UBeanDrink", heading = 1.0, debugPoly=Config.Debug, minZ=81.88, maxZ=82.68 },
{ options = { { event = "jim-beanmachine:Crafting", icon = "fas fa-mug-hot", label = Loc[Config.Lan].targetinfo["prepare_coffee"], job = v.job, craftable = Crafting.Drinks, header = Loc[Config.Lan].menu["drink_menu"], coords = vec3(-635.7, 236.55, 81.88) }, }, distance = 2.0 })
Targets["UBeanDrink2"] =
exports['qb-target']:AddBoxZone("UBeanDrink2", vec3(-627.68, 222.8, 81.88), 0.6, 0.8, { name="UBeanDrink2", heading = 0.0, debugPoly=Config.Debug, minZ=81.88, maxZ=82.68 },
{ options = { { event = "jim-beanmachine:Crafting", icon = "fas fa-mug-hot", label = Loc[Config.Lan].targetinfo["prepare_coffee"], job = v.job, craftable = Crafting.Drinks, header = Loc[Config.Lan].menu["drink_menu"], coords = vec3(-627.68, 222.8, 81.88) }, }, distance = 2.0 })
Targets["UBeanFridge"] =
exports['qb-target']:AddBoxZone("UBeanFridge", vec3(-635.51, 233.23, 81.88-1), 0.4, 1.6, { name="UBeanFridge", heading = 339.0, debugPoly=Config.Debug, minZ=80.88, maxZ=83.08 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["open_shop"], job = v.job, shop = Config.FoodItems, coords = vec3(-635.51, 233.23, 81.88), shopname = "UBeanFridge", },
{ event = "jim-beanmachine:Stash", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["open_stash"], job = v.job, stash = "UStash", coords = vec3(-635.51, 233.23, 81.88) }, }, distance = 1.5 })
Targets["UBeanSlush"] =
exports['qb-target']:AddBoxZone("UBeanSlush", vec3(-636.52, 235.81, 81.88), 0.5, 0.5, { name="UBeanSlush", heading = 1.0, debugPoly=Config.Debug, minZ=82.28, maxZ=83.08 },
{ options = { { event = "jim-beanmachine:Crafting", icon = "fas fa-cocktail", label = Loc[Config.Lan].targetinfo["prepare_slush"], job = v.job, craftable = Crafting.Slush, header = Loc[Config.Lan].menu["slush_menu"], coords = vec3(-636.52, 235.81, 81.88) }, }, distance = 2.0 })
Targets["UBeanSoda"] =
exports['qb-target']:AddBoxZone("UBeanSoda", vec3(-636.74, 234.86, 81.88-1), 2.6, 0.4, { name="UBeanSoda", heading = 1.0, debugPoly=Config.Debug, minZ=80.88, maxZ=82.08 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["grab_soda"], job = v.job, shop = Config.SodaItems, shopname = "UBeanSoda", coords = vec3(-636.74, 234.86, 81.88) }, }, distance = 2.0 })
Targets["UBeanDonut"] =
exports['qb-target']:AddBoxZone("UBeanDonut", vec3(-636.62, 234.98, 81.88), 0.6, 0.6, { name="UBeanDonut", heading = 1.0, debugPoly=Config.Debug, minZ=82.28, maxZ=82.88 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-circle-dot", label = Loc[Config.Lan].targetinfo["grab_food"], job = v.job, shop = Config.DesertItems, shopname = "UBeanDonut", coords = vec3(-636.62, 234.98, 81.88) }, }, distance = 2.0 })
Targets["UBeanWash"] =
exports['qb-target']:AddBoxZone("UBeanWash", vec3(-621.67, 226.77, 81.88-1), 1.6, 0.6, { name="BeanWash", heading = 0.0, debugPoly=Config.Debug, minZ=80.88, maxZ=82.68 },
{ options = { { event = "jim-beanmachine:washHands", icon = "fas fa-hand-holding-water", label = Loc[Config.Lan].targetinfo["wash_hands"], job = v.job, coords = vec3(-621.67, 226.77, 81.88) }, }, distance = 1.5 })
Targets["UBeanWash2"] =
exports['qb-target']:AddBoxZone("UBeanWash2", vec3(-630.71, 222.83, 81.88-1), 0.7, 1.4, { name="UBeanWash2", heading = 0.0, debugPoly=Config.Debug, minZ=80.88, maxZ=82.68 },
{ options = { { event = "jim-beanmachine:washHands", icon = "fas fa-hand-holding-water", label = Loc[Config.Lan].targetinfo["wash_hands"], job = v.job, coords = vec3(-630.71, 222.83, 81.88) }, }, distance = 1.5 })
Targets["UBeanClockin"] =
exports['qb-target']:AddBoxZone("UBeanClockin", vec3(-635.89, 227.12, 81.88), 1.6, 0.2, { name="UBeanClockin", heading = 0.0, debugPoly=Config.Debug, minZ=81.68, maxZ=82.93 },
{ options = { { type = "server", event = "QBCore:ToggleDuty", icon = "fas fa-user-check", label = Loc[Config.Lan].targetinfo["toggle_duty"], job = v.job },
{ event = "qb-bossmenu:client:OpenMenu", icon = "fas fa-list", label = "Åben ledermenu", job = bossroles, },
}, distance = 2.0 })
end
if k == "beanrflx" then
Targets["flxClockin"] =
exports['qb-target']:AddBoxZone("flxClockin", vec3(284.84, -978.75, 29.42-0.3), 0.5, 0.5, { name="flxClockin", heading = 343.0, debugPoly=Config.Debug, minZ=29.02, maxZ=29.62 },
{ options = { { type = "server", event = "QBCore:ToggleDuty", icon = "fas fa-user-check", label = Loc[Config.Lan].targetinfo["toggle_duty"], job = v.job },
{ event = "qb-bossmenu:client:OpenMenu", icon = "fas fa-list", label = "Åben ledermenu", job = bossroles, },
}, distance = 2.0 })
Targets["flxReceipt"] =
exports['qb-target']:AddBoxZone("flxReceipt", vec3(280.99, -972.57, 29.42), 0.4, 0.5, { name="flxReceipt", heading = 90.0, debugPoly=Config.Debug, minZ=29.42, maxZ=30.22 },
{ options = { { event = "jim-payments:client:Charge", icon = "fas fa-credit-card", label = Loc[Config.Lan].targetinfo["charge_customer"], job = v.job,
img = "<center><p><img src=https://static.wikia.nocookie.net/gtawiki/images/f/fc/TheBeanMachine-GTA4-logo.png width=100px></p>"
} }, distance = 2.0 })
Targets["flxCounter"] =
exports['qb-target']:AddBoxZone("flxCounter", vec3(280.50, -972.54, 30.29-1), 0.55, 0.7, { name="flxCounter", heading = 90.0, debugPoly=Config.Debug, minZ=29.42, maxZ=30.02 },
{ options = { { event = "jim-beanmachine:Stash", icon = "fas fa-mug-saucer", label = Loc[Config.Lan].targetinfo["open_counter"], stash = "flxCounter", coords = vec3(280.50, -972.54, 30.29) }, }, distance = 2.0 })
Targets["flxBeanSlush"] =
exports['qb-target']:AddBoxZone("flxBeanSlush", vec3(278.92, -972.6, 30.62-1), 0.5, 0.5, { name="flxBeanSlush", heading = 90.0, debugPoly=Config.Debug, minZ=29.42, maxZ=30.52 },
{ options = { { event = "jim-beanmachine:Crafting", icon = "fas fa-cocktail", label = Loc[Config.Lan].targetinfo["prepare_slush"], job = v.job, craftable = Crafting.Slush, header = "Slush Menu", coords = vec3(278.92, -972.6, 30.62) }, }, distance = 2.0 })
Targets["flxBeanSoda"] =
exports['qb-target']:AddBoxZone("flxBeanSoda", vec3(278.87, -973.37, 30.62-1), 0.5, 0.7, { name="flxBeanSoda", heading = 90.0, debugPoly=Config.Debug, minZ=29.42, maxZ=30.22 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["grab_soda"], job = v.job, shop = Config.SodaItems, shopname = "flxBeanSoda", coords = vec3(278.87, -973.37, 30.62) }, }, distance = 2.0 })
Targets["flxBeanDonut"] =
exports['qb-target']:AddBoxZone("flxBeanDonut", vec3(279.62, -972.55, 30.62-1), 0.5, 0.7, { name="flxBeanDonut", heading = 90.0, debugPoly=Config.Debug, minZ=29.42, maxZ=30.02 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-circle-dot", label = Loc[Config.Lan].targetinfo["grab_food"], job = v.job, shop = Config.DesertItems, shopname = "flxBeanDonut", coords = vec3(279.62, -972.55, 30.62) },
{ event = "jim-beanmachine:Stash", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["open_stash"], job = v.job, stash = "flxStash", coords = vec3(279.62, -972.55, 30.62) }, }, distance = 2.0 })
Targets["flxBeanDrink"] =
exports['qb-target']:AddBoxZone("flxBeanDrink", vec3(279.54, -975.12, 29.42), 0.7, 0.5, { name="flxBeanDrink", heading = 90.0, debugPoly=Config.Debug, minZ=29.42, maxZ=30.22 },
{ options = { { event = "jim-beanmachine:Crafting", icon = "fas fa-mug-hot", label = Loc[Config.Lan].targetinfo["prepare_coffee"], job = v.job, craftable = Crafting.Drinks, header = "Drinks Menu", coords = vec3(279.54, -975.12, 29.42) }, }, distance = 2.0 })
Targets["flxBeanFridge"] =
exports['qb-target']:AddBoxZone("flxBeanFridge", vec3(278.94, -971.89, 29.42-1), 0.85, 0.7, { name="flxBeanFridge", heading = 0.0, debugPoly=Config.Debug, minZ=28.62, maxZ=30.62 },
{ options = { { event = "jim-beanmachine:Shop", icon = "fas fa-archive", label = Loc[Config.Lan].targetinfo["open_storage"], job = v.job, shop = Config.FoodItems, shopname = "flxBeanFridge", coords = vec3(278.94, -971.89, 29.42) }, }, distance = 1.5 })
propTable[#propTable+1] = { prop = "v_res_cakedome", coords = vec4(279.62, -972.55, 30.62, 90.0) }
propTable[#propTable+1] = { prop = "prop_slush_dispenser", coords = vec4(278.92, -972.6, 30.62, 90.0) }
propTable[#propTable+1] = { prop = "prop_food_bs_soda_01", coords = vec4(278.87, -973.37, 30.62, 90.0) }
propTable[#propTable+1] = { prop = "prop_food_tray_01", coords = vec4(280.50, -972.54, 30.625, 0.0) }
end
for _, prop in pairs(propTable) do makeProp(prop, true, false) end
end
end
end)
RegisterNetEvent('jim-beanmachine:washHands', function(data)
QBCore.Functions.Progressbar('hand_washing', Loc[Config.Lan].progressbar["washing_hands"], 5000, false, true, { disableMovement = true, disableCarMovement = false, disableMouse = false, disableCombat = false, },
{ animDict = "mp_arresting", anim = "a_uncuff", flags = 8, }, {}, {}, function()
triggerNotify(nil, Loc[Config.Lan].success["success_washed_hands"], 'success')
end, function() -- Cancel
TriggerEvent('inventory:client:busy:status', false) triggerNotify(nil, Loc[Config.Lan].error["cancelled"], 'error')
end, "fas fa-hand-holding-droplet")
end)
--[[CRAFTING]]--
RegisterNetEvent('jim-beanmachine:Crafting:MakeItem', function(data)
if not CraftLock then CraftLock = true else return end
local bartime = 5000
if (data.amount and data.amount ~= 1) then data.craft["amount"] = data.amount
for k, v in pairs(data.craft[data.item]) do data.craft[data.item][k] *= data.amount end
bartime *= data.amount bartime *= 0.9
end
QBCore.Functions.Progressbar('making_food', Loc[Config.Lan].progressbar["making"]..QBCore.Shared.Items[data.item].label, bartime, false, true, { disableMovement = true, disableCarMovement = false, disableMouse = false, disableCombat = false, },
{ animDict = "mp_ped_interaction", anim = "handshake_guy_a", flags = 8, }, {}, {}, function()
CraftLock = false
TriggerServerEvent('jim-beanmachine:Crafting:GetItem', data.item, data.craft)
Wait(500)
TriggerEvent("jim-beanmachine:Crafting", data)
end, function() -- Cancel
CraftLock = false
TriggerEvent('inventory:client:busy:status', false)
end, data.item)
end)
RegisterNetEvent('jim-beanmachine:Crafting', function(data)
if CraftLock then return end
--if not jobCheck() then return end
local Menu = {}
if Config.Menu == "qb" then
Menu[#Menu + 1] = { header = data.header, txt = "", isMenuHeader = true }
Menu[#Menu + 1] = { icon = "fas fa-circle-xmark", header = "", txt = Loc[Config.Lan].menu["close"], params = { event = "" } }
end
for i = 1, #data.craftable do
for k, v in pairs(data.craftable[i]) do
if k ~= "amount" then
local text = ""
setheader = QBCore.Shared.Items[tostring(k)].label
if data.craftable[i]["amount"] ~= nil then setheader = setheader.." x"..data.craftable[i]["amount"] end
local disable = false
local checktable = {}
for l, b in pairs(data.craftable[i][tostring(k)]) do
if b == 0 or b == 1 then number = "" else number = " x"..b end
if not QBCore.Shared.Items[l] then
print("^3Error^7: ^2Script can't find ingredient item in QB-Core items.lua - ^1"..l.."^7")
return
end
if Config.Menu == "ox" then text = text..QBCore.Shared.Items[l].label..number.."\n" end
if Config.Menu == "qb" then text = text.."- "..QBCore.Shared.Items[l].label..number.."<br>" end
settext = text
checktable[l] = HasItem(l, b)
end
for _, v in pairs(checktable) do if v == false then disable = true break end end
if not disable then setheader = setheader.." ✔️" end
local event = "jim-beanmachine:Crafting:MakeItem"
if Config.MultiCraft then event = "jim-beanmachine:Crafting:MultiCraft" end
Menu[#Menu + 1] = {
disabled = disable,
icon = "nui://"..Config.img..QBCore.Shared.Items[tostring(k)].image,
header = setheader, txt = settext, --qb-menu
params = { event = event, args = { item = k, craft = data.craftable[i], craftable = data.craftable, header = data.header } }, -- qb-menu
title = setheader, description = settext, -- ox_lib
event = event, args = { item = k, craft = data.craftable[i], craftable = data.craftable, header = data.header }, -- ox_lib
}
settext, setheader = nil
end
end
end
if Config.Menu == "ox" then exports.ox_lib:registerContext({id = 'Crafting', title = data.header, position = 'top-right', options = Menu }) exports.ox_lib:showContext("Crafting")
elseif Config.Menu == "qb" then exports['qb-menu']:openMenu(Menu) end
lookEnt(data.coords)
end)
RegisterNetEvent('jim-beanmachine:Crafting:MultiCraft', function(data)
local success = Config.MultiCraftAmounts local Menu = {}
for k in pairs(success) do success[k] = true
for l, b in pairs(data.craft[data.item]) do
local has = HasItem(l, (b * k)) if not has then success[k] = false break else success[k] = true end
end
end
if Config.Menu == "qb" then Menu[#Menu + 1] = { header = data.header, txt = "", isMenuHeader = true } end
Menu[#Menu + 1] = { icon = "fas fa-arrow-left", title = Loc[Config.Lan].menu["back"], header = "", txt = Loc[Config.Lan].menu["back"], params = { event = "jim-beanmachine:Crafting", args = data }, event = "jim-beanmachine:Crafting", args = data }
for k in pairsByKeys(success) do
Menu[#Menu + 1] = {
disabled = not success[k],
icon = "nui://"..Config.img..QBCore.Shared.Items[data.item].image, header = QBCore.Shared.Items[data.item].label.." [x"..k.."]", title = QBCore.Shared.Items[data.item].label.." [x"..k.."]",
event = "jim-beanmachine:Crafting:MakeItem", args = { item = data.item, craft = data.craft, craftable = data.craftable, header = data.header, anim = data.anim, amount = k },
params = { event = "jim-beanmachine:Crafting:MakeItem", args = { item = data.item, craft = data.craft, craftable = data.craftable, header = data.header, anim = data.anim, amount = k } }
}
end
if Config.Menu == "ox" then exports.ox_lib:registerContext({id = 'Crafting', title = data.header, position = 'top-right', options = Menu }) exports.ox_lib:showContext("Crafting")
elseif Config.Menu == "qb" then exports['qb-menu']:openMenu(Menu) end
end)
--[[STASH SHOPS]]--
RegisterNetEvent('jim-beanmachine:Stash', function(data)
--if data.job and not jobCheck() then return end
if Config.Inv == "ox" then exports.ox_inventory:openInventory('stash', "beanmachine_"..data.stash) else
TriggerEvent("inventory:client:SetCurrentStash", "beanmachine_"..data.stash)
TriggerServerEvent("inventory:server:OpenInventory", "stash", "beanmachine_"..data.stash) end
lookEnt(data.coords)
end)
RegisterNetEvent('jim-beanmachine:Shop', function(data)
--if not jobCheck() then return end
local event = "inventory:server:OpenInventory"
if Config.JimShop then event = "jim-shops:ShopOpen"
elseif Config.Inv == "ox" then exports.ox_inventory:openInventory('shop', { type = data.shopname }) end
TriggerServerEvent(event, "shop", "beanmachine", data.shop)
lookEnt(data.coords)
end)
-- [[CONSUME]] --
local function ConsumeSuccess(itemName, type)
ExecuteCommand("e c")
toggleItem(false, itemName, 1)
if QBCore.Shared.Items[itemName].hunger then
TriggerServerEvent("QBCore:Server:SetMetaData", "hunger", QBCore.Functions.GetPlayerData().metadata["hunger"] + QBCore.Shared.Items[itemName].hunger)
--TriggerServerEvent("consumables:server:addHunger", QBCore.Functions.GetPlayerData().metadata["hunger"] + QBCore.Shared.Items[itemName].hunger)
end
if QBCore.Shared.Items[itemName].thirst then
TriggerServerEvent("QBCore:Server:SetMetaData", "thirst", QBCore.Functions.GetPlayerData().metadata["thirst"] + QBCore.Shared.Items[itemName].thirst)
--TriggerServerEvent("consumables:server:addThirst", QBCore.Functions.GetPlayerData().metadata["thirst"] + QBCore.Shared.Items[itemName].thirst)
end
if type == "alcohol" then alcoholCount += 1
if alcoholCount > 1 and alcoholCount < 4 then TriggerEvent("evidence:client:SetStatus", "alcohol", 200) elseif alcoholCount >= 4 then TriggerEvent("evidence:client:SetStatus", "heavyalcohol", 200) AlienEffect() end
end
if Config.RewardItem == itemName then toggleItem(true, Config.RewardPool[math.random(1, #Config.RewardPool)], 1) end
end
RegisterNetEvent('jim-beanmachine:client:Consume', function(itemName, type)
local emoteTable = {
["sprunk"] = "sprunk", ["sprunklight"] = "sprunk", ["ecola"] = "ecola", ["ecolalight"] = "ecola",
["ecocoffee"] = "bmcoffee1", ["flusher"] = "bmcoffee2", ["caffeagra"] = "bmcoffee3", ["crisps"] = "crisps",
["beandonut"] = "donut2", ["chocolate"] = "egobar",
}
local progstring, defaultemote = Loc[Config.Lan].progressbar["drinking"], "drink"
if type == "food" then progstring = Loc[Config.Lan].progressbar["eating"] defaultemote = "burger" end
ExecuteCommand("e "..(emoteTable[itemName] or defaultemote))
QBCore.Functions.Progressbar('making_food', progstring..QBCore.Shared.Items[itemName].label.."..", math.random(3000, 6000), false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, },
{ animDict = animDictNow, anim = animNow, flags = 8, }, {}, {}, function()
ConsumeSuccess(itemName, type)
end, function() -- Cancel
ExecuteCommand("e c")
end, itemName)
end)
AddEventHandler('onResourceStop', function(r) if r ~= GetCurrentResourceName() then return end
if GetResourceState("qb-target") == "started" or GetResourceState("ox_target") == "started" then
for k in pairs(Targets) do exports['qb-target']:RemoveZone(k) end
for k in pairs(Props) do DeleteEntity(Props[k]) end
exports['qb-menu']:closeMenu()
end
end)

View File

@ -0,0 +1,133 @@
-- If you need support I now have a discord available, it helps me keep track of issues and give better support.
-- https://discord.gg/xKgQZ6wZvS
Config = {
Debug = false, -- false to remove green boxes
Lan = "da", -- change the language
img = "ps-inventory/html/images/", -- Change this to your inventory's name and image folder
Core = "qb-core", -- set this to your core folder
Inv = "qb", -- set to "ox" if using OX Inventory
Menu = "qb", -- set to "ox" if using ox_lib context menus
Notify = "qb",
ProgressBar = "qb", -- set to "ox" if using ox_lib progressbar
JimConsumables = false, -- Enable this to disable this scripts control of food/drink items
JimShop = false, -- If true shops will open in jim-shops
MultiCraft = true,
MultiCraftAmounts = { [1], [5], [10] },
--Simple Toy Reward Support - disabled if JimConsumables are true
RewardItem = "", --Set this to the name of an item eg "bento"
RewardPool = { -- Set this to the list of items to be given out as random prizes when the item is used - can be more or less items in the list
"",
"",
"",
},
FoodItems = {
label = "Køleskab",
slots = 8,
items = {
{ name = "water_bottle", price = 0, amount = 50, info = {}, type = "item", slot = 1, },
{ name = "orange", price = 0, amount = 50, info = {}, type = "item", slot = 2, },
{ name = "sugar", price = 0, amount = 50, info = {}, type = "item", slot = 3, },
{ name = "chickenbreast", price = 0, amount = 50, info = {}, type = "item", slot = 4, },
{ name = "rhinohorn", price = 0, amount = 50, info = {}, type = "item", slot = 5, },
{ name = "oystershell", price = 0, amount = 50, info = {}, type = "item", slot = 6, },
{ name = "milk", price = 0, amount = 50, info = {}, type = "item", slot = 7, },
{ name = "beancoffee", price = 0, amount = 50, info = {}, type = "item", slot = 8, },
},
},
DesertItems = {
label = "Desert-skab",
slots = 6,
items = {
{ name = "beandonut", price = 0, amount = 50, info = {}, type = "item", slot = 1, },
{ name = "chocolate", price = 0, amount = 50, info = {}, type = "item", slot = 2, },
{ name = "crisps", price = 0, amount = 50, info = {}, type = "item", slot = 3, },
{ name = "watermelon", price = 0, amount = 50, info = {}, type = "item", slot = 4, },
{ name = "cheesecake", price = 0, amount = 50, info = {}, type = "item", slot = 5, },
{ name = "tosti", price = 0, amount = 50, info = {}, type = "item", slot = 6, },
},
},
SodaItems = {
label = "Sodavand",
slots = 4,
items = {
{ name = "ecola", price = 0, amount = 50, info = {}, type = "item", slot = 1, },
{ name = "ecolalight", price = 0, amount = 50, info = {}, type = "item", slot = 2, },
{ name = "sprunk", price = 0, amount = 50, info = {}, type = "item", slot = 3, },
{ name = "sprunklight", price = 0, amount = 50, info = {}, type = "item", slot = 4, },
},
},
Locations = {
["beangabzlegion"] = {
zoneEnable = false,
label = "Bean Machine (Legion)",
job = "beanmachine",
autoClock = { enter = false, exit = true, },
zones = {
vec2(137.44329833984, -1019.5242919922),
vec2(122.99235534668, -1058.451171875),
vec2(101.35326385498, -1048.4799804688),
vec2(115.27521514893, -1011.9081420898)
},
garage = { spawn = vec4(130.93, -1032.04, 28.76, 340.2),
out = vec4(129.41, -1031.15, 29.43, 253.32),
list = { "panto", } },
blip = vec3(120.27, -1038.09, 29.28), blipcolor = 56, blipsprite = 106, blipdisp = 6, blipscale = 0.7, blipcat = nil,
},
["beanunclejust"] = {
zoneEnable = true,
label = "Bean Machine (Vinewood)",
job = "beanmachine",
autoClock = { enter = false, exit = true, },
zones = {
vec2(-649.89886474609, 259.69918823242),
vec2(-611.98547363281, 257.3210144043),
vec2(-614.646484375, 203.47846984863),
vec2(-649.29040527344, 203.21409606934)
},
garage = { spawn = vec4(-631.99, 207.03, 73.31, 93.9),
out = vec4(-632.39, 209.14, 74.32, 179.94),
list = { "panto", } },
blip = vec3(-629.63, 234.39, 81.88), blipcolor = 56, blipsprite = 106, blipdisp = 6, blipscale = 0.7, blipcat = nil,
},
["beanrflx"] = {
zoneEnable = true,
label = "Bean Machine (Legion)",
job = "beanmachine",
autoClock = { enter = false, exit = true, },
zones = {
vec2(271.67, -955.77),
vec2(291.9, -954.33),
vec2(291.61, -986.73),
vec2(261.14, -973.50)
},
garage = { spawn = vec4(278.17, -956.56, 28.6, 269.1),
out = vec4(277.52, -958.44, 29.4, 356.56),
list = { "panto", } },
blip = vec3(281.54, -965.68, 29.42), blipcolor = 56, blipsprite = 106, blipdisp = 6, blipscale = 0.7, blipcat = nil,
},
},
}
Crafting = {
Slush = {
{ ['bigfruit'] = { ['watermelon'] = 1, ['water_bottle'] = 1, ['orange'] = 1, ['sugar'] = 1, }, },
},
Drinks = {
{ ['highnoon'] = { ['beancoffee'] = 1, ['water_bottle'] = 1, ['orange'] = 1, }, },
{ ['speedball'] = { ['beancoffee'] = 3, ['sugar'] = 1, }, },
{ ['gunkaccino'] = { ['beancoffee'] = 1, ['sugar'] = 2, ['cheesecake'] = 1, }, },
{ ['bratte'] = { ['beancoffee'] = 2, ['sugar'] = 5, }, },
{ ['flusher'] = { ['beancoffee'] = 1, ['water_bottle'] = 1, ['orange'] = 1, ['chickenbreast'] = 1, }, },
{ ['ecocoffee'] = { ['beancoffee'] = 1, ['water_bottle'] = 1, ['milk'] = 1, ['plastic'] = 1, }, },
{ ['caffeagra'] = { ['beancoffee'] = 1, ['rhinohorn'] = 2, ['oystershell'] = 1, }, },
},
}
Loc = {}

View File

@ -0,0 +1,11 @@
name "Jim-BeanMachine"
author "Jimathy"
version "1.4.4"
description "BeanMachine Job Script By Jimathy"
fx_version 'cerulean'
game "gta5"
lua54 'yes'
server_scripts { 'server/*.lua' }
shared_scripts { 'config.lua', 'locales/da.lua', 'shared/*.lua' }
client_scripts { '@PolyZone/client.lua', '@PolyZone/BoxZone.lua', '@PolyZone/EntityZone.lua', '@PolyZone/CircleZone.lua', '@PolyZone/ComboZone.lua', 'client/*.lua', }

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,229 @@
Thank you for your purchase <3 I hope you have fun with this script and that it brings jobs and RP to your server
If you need support I now have a discord available, it helps me keep track of issues and give better support.
https://discord.gg/xKgQZ6wZvS
-------------------------------------------------------------------------------------------------
# INSTALLATION
Check the `Config.lua` for the settings you need to change
To enable or disable a location, toggle `zoneEnable` in their Locations table
## Inventory Images
Add the item images to your inventory script
`[qb]` > `qb-inventory` > `html` > `images`
## Items.lua
Under the QBShared.Items = {
--Jim-BeanMachine
["beancoffee"] = {["name"] = "beancoffee", ["label"] = "Coffe Beans", ["weight"] = 100, ["type"] = "item", ["image"] = "beancoffee.png", ["unique"] = false, ["useable"] = false, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['hunger'] = math.random(20, 30) },
["beandonut"] = {["name"] = "beandonut", ["label"] = "Donut", ["weight"] = 100, ["type"] = "item", ["image"] = "popdonut.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['hunger'] = math.random(20, 30) },
["rhinohorn"] = {["name"] = "rhinohorn", ["label"] = "Rhino Horn", ["weight"] = 100, ["type"] = "item", ["image"] = "rhinohorn.png", ["unique"] = false, ["useable"] = false, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "" },
["oystershell"] = {["name"] = "oystershell", ["label"] = "Oyster Shell", ["weight"] = 100, ["type"] = "item", ["image"] = "oyster.png", ["unique"] = false, ["useable"] = false, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "" },
["watermelon"] = {["name"] = "watermelon", ["label"] = "WaterMelon Slice", ["weight"] = 100, ["type"] = "item", ["image"] = "watermelon.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['hunger'] = math.random(20, 30), ['thirst'] = math.random(20, 30) },
["bigfruit"] = {["name"] = "bigfruit", ["label"] = "The Big Fruit", ["weight"] = 100, ["type"] = "item", ["image"] = "bigfruit.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["highnoon"] = {["name"] = "highnoon", ["label"] = "Highnoon", ["weight"] = 100, ["type"] = "item", ["image"] = "highnoon.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["speedball"] = {["name"] = "speedball", ["label"] = "The SpeedBall", ["weight"] = 100, ["type"] = "item", ["image"] = "speedball.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["gunkaccino"] = {["name"] = "gunkaccino", ["label"] = "The Gunkaccino", ["weight"] = 100, ["type"] = "item", ["image"] = "gunkaccino.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["bratte"] = {["name"] = "bratte", ["label"] = "The Bratte", ["weight"] = 100, ["type"] = "item", ["image"] = "bratte.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["flusher"] = {["name"] = "flusher", ["label"] = "The Flusher", ["weight"] = 100, ["type"] = "item", ["image"] = "flusher.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["ecocoffee"] = {["name"] = "ecocoffee", ["label"] = "The Eco-ffee", ["weight"] = 100, ["type"] = "item", ["image"] = "ecoffee.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["caffeagra"] = {["name"] = "caffeagra", ["label"] = "Caffeagra", ["weight"] = 100, ["type"] = "item", ["image"] = "caffeagra.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["chocolate"] = {["name"] = "chocolate", ["label"] = "Chocolate", ["weight"] = 200, ["type"] = "item", ["image"] = "chocolate.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = false, ["combinable"] = nil, ["description"] = "Chocolate Bar", ['hunger'] = math.random(10, 20) },
["cheesecake"] = {["name"] = "cheesecake", ["label"] = "Cheese Cake", ["weight"] = 100, ["type"] = "item", ["image"] = "cheesecake.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['hunger'] = math.random(20, 30) },
["crisps"] = {["name"] = "crisps", ["label"] = "Crisps", ["weight"] = 100, ["type"] = "item", ["image"] = "chips.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['hunger'] = math.random(20, 30) },
["sugar"] = {["name"] = "sugar", ["label"] = "Sugar", ["weight"] = 100, ["type"] = "item", ["image"] = "sugar.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = false, ["combinable"] = nil, ["description"] = "", ['hunger'] = math.random(10, 20) },
["orange"] = {["name"] = "orange", ["label"] = "Orange", ["weight"] = 200, ["type"] = "item", ["image"] = "orange.png", ["unique"] = false, ["useable"] = false, ["shouldClose"] = false, ["combinable"] = nil, ["description"] = "An Orange." },
["milk"] = {["name"] = "milk", ["label"] = "Milk", ["weight"] = 300, ["type"] = "item", ["image"] = "burger-milk.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "Carton of Milk", ['thirst'] = math.random(10, 20) },
["chickenbreast"] = {["name"] = "chickenbreast", ["label"] = "Chicken Breast", ["weight"] = 100, ["type"] = "item", ["image"] = "chickenbreast.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = false, ["combinable"] = nil, ["description"] = "", ['hunger'] = math.random(10, 20) },
["sprunk"] = {["name"] = "sprunk", ["label"] = "Sprunk", ["weight"] = 100, ["type"] = "item", ["image"] = "sprunk.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["sprunklight"] = {["name"] = "sprunklight", ["label"] = "Sprunk Light", ["weight"] = 100, ["type"] = "item", ["image"] = "sprunklight.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["ecola"] = {["name"] = "ecola", ["label"] = "eCola", ["weight"] = 100, ["type"] = "item", ["image"] = "ecola.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
["ecolalight"] = {["name"] = "ecolalight", ["label"] = "eCola Light", ["weight"] = 100, ["type"] = "item", ["image"] = "ecolalight.png", ["unique"] = false, ["useable"] = true, ["shouldClose"] = true, ["combinable"] = nil, ["description"] = "", ['thirst'] = math.random(20, 30) },
## Jobs
Under the QBShared.Jobs = {
['beanmachine'] = {
label = 'Bean Machine',
defaultDuty = true,
grades = {
['0'] = { name = 'Recruit', payment = 50 },
['1'] = { name = 'Novice', payment = 75 },
['2'] = { name = 'Experienced', payment = 100 },
['3'] = { name = 'Advanced', payment = 125 },
['4'] = { name = 'Manager', isboss = true, payment = 150 },
},
},
## Payment Systems
The payment system that is being used is my free script, jim-payments
This system supports receipts being handed out to wokers who are clocked in and working
They can then trade this in at the bank for rewards
Grab it at: https://github.com/jimathy/jim-payments
# qb-management:
Update to the latest github version
Make sure the job "beanmachine" has been added to the database
The menu's targets should be accessible to bosses at clockin areas
## Emotes
Custom emotes currently run through dpemotes, its the easier option and adds extra emotes for you to use :)
These go in your [standalone] > dpemotes > client > AnimationList.lua
At about line 1666, place these under DP.PropEmotes = {
["ecola"] = {"mp_player_intdrink", "loop_bottle", "E-cola", AnimationOptions =
{ Prop = "prop_ecola_can", PropBone = 18905, PropPlacement = {0.12, 0.008, 0.03, 240.0, -60.0},
EmoteMoving = true, EmoteLoop = true, }},
["sprunk"] = {"mp_player_intdrink", "loop_bottle", "Sprunk", AnimationOptions =
{ Prop = "v_res_tt_can03", PropBone = 18905, PropPlacement = {0.12, 0.008, 0.03, 240.0, -60.0},
EmoteMoving = true, EmoteLoop = true, }},
["crisps"] = {"amb@world_human_drinking@coffee@male@idle_a", "idle_c", "Crisps", AnimationOptions =
{ Prop = 'v_ret_ml_chips2', PropBone = 28422, PropPlacement = {0.01, -0.05, -0.1, 0.0, 0.0, 90.0},
EmoteLoop = true, EmoteMoving = true, }},
["bmcoffee1"] = {"amb@world_human_drinking@coffee@male@idle_a", "idle_c", "Coffee2", AnimationOptions =
{ Prop = 'prop_fib_coffee', PropBone = 28422, PropPlacement = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
EmoteLoop = true, EmoteMoving = true, }},
["bmcoffee2"] = {"amb@world_human_drinking@coffee@male@idle_a", "idle_c", "Coffee3", AnimationOptions =
{ Prop = 'ng_proc_coffee_01a', PropBone = 28422, PropPlacement = {0.0, 0.0, -0.06, 0.0, 0.0, 0.0},
EmoteLoop = true, EmoteMoving = true, }},
["bmcoffee3"] = {"amb@world_human_drinking@coffee@male@idle_a", "idle_c", "Coffee3", AnimationOptions =
{ Prop = 'v_club_vu_coffeecup', PropBone = 28422, PropPlacement = {0.0, 0.0, -0.06, 0.0, 0.0, 0.0},
EmoteLoop = true, EmoteMoving = true, }},
["milk"] = {"mp_player_intdrink", "loop_bottle", "Milk", AnimationOptions =
{ Prop = "v_res_tt_milk", PropBone = 18905, PropPlacement = {0.10, 0.008, 0.07, 240.0, -60.0},
EmoteMoving = true, EmoteLoop = true, }},
["donut2"] = {"mp_player_inteat@burger", "mp_player_int_eat_burger", "Donut2", AnimationOptions =
{ Prop = 'prop_donut_02', PropBone = 18905, PropPlacement = {0.13, 0.05, 0.02, -50.0, 100.0, 270.0},
EmoteMoving = true, EmoteLoop = true, }},
--------------------------------------------------------------------------------------------------
## Jim-Consumables item setup - (Optional) - (https://github.com/jimathy/jim-consumables)
- Support for new `Jim-Consumables` automated adding of items and emotes
- Start `jim-consumables` BEFORE this script and set `Config.JimConsumables` to `true` for it to work
- In Jim-Beanmachine's `config.lua` set `JimConsumables` to true
- Restart/Ensure Jim-Beanmachine
- Restart Jim-Consumables
--------------------------------------------------------------------------------------------------
## Changelog
### v1.4.4:
- Add "Multi-Craft" option in the config.lua
- Split-Stack item exploit fix
- Optional: Support for new `Jim-Consumables` automated item adding events
- (Start `jim-consumables` BEFORE this script and set `Config.JimConsumables` to `true` for it to work)
- (https://github.com/jimathy/jim-consumables)
- Change/Add support for creating jobGarages in (https://github.com/jimathy/jim-jobgarage)
### v1.4.3
- Added image icons to ox_lib menus
- Added Version Update check
### v1.4.2
- Improved `OX_Lib` Context support (better layout for ingredients)
- Improved `OX_Inv` support
- Added `OX_Lib` Progressbar suppport
- *Basic* `OX_Lib` notification support (Set `Config.Notify = "ox"`)
- Improved script stopping/restarting events
- Added more options to blip creation
- Locale fixes
- Updated shared functions to give more info and be more optimized
- Merged built-in eating events into one optimized event
- Fix emote references for jim-consumables and built in events ("bmcoffee1" not "bmcoffee")
### v1.4.1
- Add support for OX Lib's Context menu
- Updated emote code in built-in consumable events
### v1.4
- Support for changing Core name
- Support added for OX_Target
- Support added for OX_Inventory
- Added autoClock variable to locations config
- This helps define if leaving or entering the zone clocks in or out
### v1.3.4
- Updated install.md
- Included instructions to make use of Jim-Consuambles if wanted
- Add de.lua locale
- Updated built-in client and server Hasitem events to be more accurate
- Support for different job roles at each location
- Fix for built in job garages letting anyone grab a vehicle
### v1.3.3
- Locale support thanks to Dafke
### v1.3.2
- Workaround for the `HasItem()` allowing crafting when items aren't there
### v1.3.1
- Made the `HasItem` functions built in, so no edits to core needed
- This allows optimizations + makes crafting menus open/load much faster
- Add item duping protection to item crafting
### v1.3
- New Location supported, the free MLO - https://www.gta5-mods.com/maps/mlo-bean-machine-fivem-sp
- Rewrote the locations and how they are handled in the client to allow easier additions of new locations
- Rewrote the chair code to be more optimzed and handle multiple locations better
- Rewrote the garage.lua to support new functions
- Added shared function file to optimize code and improve loading of props and blips
- Made use of new custom lookEnt() event to make player look towards points of interactions.
### v1.2.1
- Added support ps-progressbar
### v1.2
- Added Support for Crafting CheckMarks
- This is a toggle in the config, if it causes issues(like lag) disable it
- Added Support for Jim-Shops
- Added Support for new qb-menu icons
- Added Job Garages for deliveries to both default locations
- QoL fixes
- Improved and optimized loading of targets and props
- UncleJust changes
- Slushie machine prop now added to separate the Soda's and Slushie targets
- Added Prepared Food Stash to the fridge
- Gabz Changes
- Added Prepared Food Stash to the Donut Counter
- Upgraded Crafting systems to be more optimised
- Changed "Donut" menu to be a shop instead of just grabbing them
- Added simple support for Toys/Prizes
- BossMenu is now accessible from the same areas as clock in targets
### v1.1.1
- Fixed typo breaking coffee menu
- Fixed type making "crisps" unusable
### v1.1
- Fix for item check callback
- Added support for grabbing images from inventory script
- You need to set your inventory image folder link in the config if not using qb-inventory
### v1.0.2
- Added config option for zones
- zoneEable, enables or disables the chosen zone and its map blip
- Added Debug messages to server.lua
- helps..debug..any issues with the script
- Missed chocolate completely from the list
- Even the item check in client.lua was missing it.
- Added more Custom Emotes
- Didn't know there were acutal extra BM Coffee Props

View File

@ -0,0 +1,56 @@
Loc["da"] = {
error = {
["someone_already_sitting"] = "Der sidder allerede nogen..",
["not_clocked_in"] = "Du er ikke på arbejde!",
["cancelled"] = "Afbrudt",
["in_the_way"] = " er i vejen",
},
success = {
["success_washed_hands"] = "Du vaskede hænder!",
["retrieved"] = "Hentede ",
["job_vehicle_map"] = "Firmabil markeret på kortet",
},
info = {
},
targetinfo = {
["sit_down"] = "Sid ned",
["wash_hands"] = "Vask dine hænder",
["open_counter"] = "Åben",
["charge_customer"] = "Opkræv kunde",
["open_storage"] = "Åben Lager",
["prepare_coffee"] = "Lav Kaffe",
["prepare_slush"] = "Lav Slush",
["grab_soda"] = "Tag Sodavand",
["grab_food"] = "Tag Mad",
["open_stash"] = "Åben Stash",
["toggle_duty"] = "Skift Vagt",
["open_bossmenu"] = "Åben Bossmenu",
["open_shop"] = "Åben Butik",
},
email = {
},
menu = {
["close"] = "Luk",
["back"] = "Tilbage",
["slush_menu"] = "Slush Menu",
["drink_menu"] = "Drinks Menu",
["job_vehicles"] = "Firmabiler",
["job_garage"] = "Firmagarage",
["vehicle"] = "Køretøj: ",
["plate"] = "<br> Nummerplade: [",
["remove_vehicle"] = "Fjern køretøj",
["vehicle_out_of_garage"] = "Køretøj ude af garage",
},
commands = {
},
progressbar = {
["washing_hands"] = 'Vasker hænder',
["making"] = "Laver ",
["drinking"] = "Drikker ",
["eating"] = "Spiser ",
},
warning = {},
}

View File

@ -0,0 +1,161 @@
local QBCore = exports[Config.Core]:GetCoreObject()
AddEventHandler('onResourceStart', function(r) if GetCurrentResourceName() ~= r then return end
for k, v in pairs(Crafting) do for i = 1, #v do
for l, b in pairs(v[i]) do if not QBCore.Shared.Items[l] then print("Crafting: Missing Item from QBCore.Shared.Items: '"..l.."'") end
for j, c in pairs(b) do if not QBCore.Shared.Items[j] then print("Crafting: Missing Item from QBCore.Shared.Items: '"..j.."'") end end end end end
for i = 1, #Config.FoodItems.items do
if not QBCore.Shared.Items[Config.FoodItems.items[i].name] then print("Store: Missing Item from QBCore.Shared.Items: '"..Config.FoodItems.items[i].name.."'") end
end
for i = 1, #Config.DesertItems.items do
if not QBCore.Shared.Items[Config.DesertItems.items[i].name] then print("Store: Missing Item from QBCore.Shared.Items: '"..Config.DesertItems.items[i].name.."'") end
end
for i = 1, #Config.SodaItems.items do
if not QBCore.Shared.Items[Config.SodaItems.items[i].name] then print("Store: Missing Item from QBCore.Shared.Items: '"..Config.SodaItems.items[i].name.."'") end
end
for k, v in pairs(Config.Locations) do
if not QBCore.Shared.Jobs[v.job] then print("Error: Job role not found - '"..v.job.."'") end
if v.garage then
TriggerEvent("jim-jobgarage:server:syncAddLocations", { job = v.job, garage = v.garage }) -- Job Garage creation
end
end
end)
if not Config.JimConsumables then
CreateThread(function()
local drinks = { "bigfruit", "highnoon", "speedball", "gunkaccino", "bratte", "flusher", "ecocoffee", "caffeagra", "ecola", "ecolalight", "sprunk", "sprunklight" }
for k,v in pairs(drinks) do QBCore.Functions.CreateUseableItem(v, function(source, item) TriggerClientEvent('jim-beanmachine:client:Consume', source, item.name, "drink") end) end
local food = { "cheesecake", "watermelon", "beandonut", "chocolate", "crisps", }
for k,v in pairs(food) do QBCore.Functions.CreateUseableItem(v, function(source, item) TriggerClientEvent('jim-beanmachine:client:Consume', source, item.name, "food") end) end
end)
else
local foodTable = {
["bigfruit"] = { emote = "bmcoffee1", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20, 30), }},
["highnoon"] = { emote = "bmcoffee1", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20, 30), }},
["speedball"] = { emote = "bmcoffee1", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20, 30), }},
["gunkaccino"] = { emote = "bmcoffee1", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20, 30), }},
["bratte"] = { emote = "bmcoffee1", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20, 30), }},
["flusher"] = { emote = "bmcoffee2", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20, 30), }},
["ecocoffee"] = { emote = "bmcoffee", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20, 30), }},
["caffeagra"] = { emote = "bmcoffee3", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20, 30), }},
["cheesecake"] = { emote = "sandwich", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "food", stats = { hunger = math.random(20, 30), }},
["watermelon"] = { emote = "sandwich", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "food", stats = { hunger = math.random(20, 30), thirst = math.random(20, 30) }},
["beandonut"] = { emote = "donut2", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "food", stats = { hunger = math.random(20, 30), }},
["chocolate"] = { emote = "egobar", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "food", stats = { hunger = math.random(10, 20), }},
["crisps"] = { emote = "crisps", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "food", stats = { hunger = math.random(20, 30), }},
["ecola"] = { emote = "ecola", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20,30), }},
["ecolalight"] = { emote = "ecola", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20,30), }},
["sprunk"] = { emote = "sprunk", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20,30), }},
["sprunklight"] = { emote = "sprunk", canRun = false, time = math.random(5000, 6000), stress = math.random(2, 4), heal = 0, armor = 0, type = "drink", stats = { thirst = math.random(20,30), }},
}
local emoteTable = {
["ecola"] = {"mp_player_intdrink", "loop_bottle", "E-cola", AnimationOptions = { Prop = "prop_ecola_can", PropBone = 18905, PropPlacement = {0.12, 0.008, 0.03, 240.0, -60.0}, EmoteMoving = true, EmoteLoop = true, }},
["sprunk"] = {"mp_player_intdrink", "loop_bottle", "Sprunk", AnimationOptions = { Prop = "v_res_tt_can03", PropBone = 18905, PropPlacement = {0.12, 0.008, 0.03, 240.0, -60.0}, EmoteMoving = true, EmoteLoop = true, }},
["crisps"] = {"amb@world_human_drinking@coffee@male@idle_a", "idle_c", "Crisps", AnimationOptions = { Prop = 'v_ret_ml_chips2', PropBone = 28422, PropPlacement = {0.01, -0.05, -0.1, 0.0, 0.0, 90.0}, EmoteLoop = true, EmoteMoving = true, }},
["bmcoffee1"] = {"amb@world_human_drinking@coffee@male@idle_a", "idle_c", "Coffee2", AnimationOptions = { Prop = 'prop_fib_coffee', PropBone = 28422, PropPlacement = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, EmoteLoop = true, EmoteMoving = true, }},
["bmcoffee2"] = {"amb@world_human_drinking@coffee@male@idle_a", "idle_c", "Coffee3", AnimationOptions = { Prop = 'ng_proc_coffee_01a', PropBone = 28422, PropPlacement = {0.0, 0.0, -0.06, 0.0, 0.0, 0.0}, EmoteLoop = true, EmoteMoving = true, }},
["bmcoffee3"] = {"amb@world_human_drinking@coffee@male@idle_a", "idle_c", "Coffee3", AnimationOptions = { Prop = 'v_club_vu_coffeecup', PropBone = 28422, PropPlacement = {0.0, 0.0, -0.06, 0.0, 0.0, 0.0}, EmoteLoop = true, EmoteMoving = true, }},
["milk"] = {"mp_player_intdrink", "loop_bottle", "Milk", AnimationOptions = { Prop = "v_res_tt_milk", PropBone = 18905, PropPlacement = {0.10, 0.008, 0.07, 240.0, -60.0}, EmoteMoving = true, EmoteLoop = true, }},
["donut2"] = {"mp_player_inteat@burger", "mp_player_int_eat_burger", "Donut2", AnimationOptions = { Prop = 'prop_donut_02', PropBone = 18905, PropPlacement = {0.13, 0.05, 0.02, -50.0, 100.0, 270.0}, EmoteMoving = true, EmoteLoop = true, }},
["ecola"] = {"mp_player_intdrink", "loop_bottle", "e-cola", AnimationOptions = { Prop = "prop_ecola_can", PropBone = 18905, PropPlacement = {0.12, 0.008, 0.03, 240.0, -60.0}, EmoteMoving = true, EmoteLoop = true, }},
["sprunk"] = {"mp_player_intdrink", "loop_bottle", "sprunk", AnimationOptions = { Prop = "v_res_tt_can03", PropBone = 18905, PropPlacement = {0.12, 0.008, 0.03, 240.0, -60.0}, EmoteMoving = true, EmoteLoop = true, }},
}
for k, v in pairs(foodTable) do TriggerEvent("jim-consumables:server:syncAddItem", k, v) end
for k, v in pairs(emoteTable) do TriggerEvent("jim-consumables:server:syncAddEmote", k, v) end
end
---Crafting
RegisterServerEvent('jim-beanmachine:Crafting:GetItem', function(ItemMake, craftable)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
--This grabs the table from client and removes the item requirements
local amount = craftable["amount"] or 1
if craftable then
--if craftable["amount"] then amount = craftable["amount"] end
for k, v in pairs(craftable[ItemMake]) do TriggerEvent("jim-beanmachine:server:toggleItem", false, tostring(k), v, src) end
end
--This should give the item, while the rest removes the requirements
TriggerEvent("jim-beanmachine:server:toggleItem", true, ItemMake, amount, src)
end)
local function dupeWarn(src, item)
local P = QBCore.Functions.GetPlayer(src)
print("^5DupeWarn: ^1"..P.PlayerData.charinfo.firstname.." "..P.PlayerData.charinfo.lastname.."^7(^1"..tostring(src).."^7) ^2Tried to remove item ^7('^3"..item.."^7')^2 but it wasn't there^7")
-- if not Config.Debug then DropPlayer(src, "^1Kicked for attempting to duplicate items") end
print("^5DupeWarn: ^1"..P.PlayerData.charinfo.firstname.." "..P.PlayerData.charinfo.lastname.."^7(^1"..tostring(src).."^7) ^2Dropped from server for item duplicating^7")
end
RegisterNetEvent('jim-beanmachine:server:toggleItem', function(give, item, amount, newsrc)
local src = newsrc or source
local Player = QBCore.Functions.GetPlayer(src)
local remamount = (amount or 1)
if give == 0 or give == false then
if HasItem(src, item, amount or 1) then -- check if you still have the item
while remamount > 0 do if Player.Functions.RemoveItem(item, 1) then end remamount -= 1 end
TriggerClientEvent('inventory:client:ItemBox', src, QBCore.Shared.Items[item], "remove", amount or 1)
if Config.Debug then print("^5Debug^7: ^1Removing ^2from Player^7(^2"..src.."^7) '^6"..QBCore.Shared.Items[item].label.."^7(^2x^6"..(amount or "1").."^7)'") end
else dupeWarn(src, item) end -- if not boot the player
else
if Player.Functions.AddItem(item, amount or 1) then
TriggerClientEvent('inventory:client:ItemBox', src, QBCore.Shared.Items[item], "add", amount or 1)
if Config.Debug then print("^5Debug^7: ^4Giving ^2Player^7(^2"..src.."^7) '^6"..QBCore.Shared.Items[item].label.."^7(^2x^6"..(amount or "1").."^7)'") end
end
end
end)
if Config.Inv == "ox" then
--Unclejust--
exports.ox_inventory:RegisterStash("beanmachine_UCounter", "Beanmachine Counter", 20, 400000)
exports.ox_inventory:RegisterStash("beanmachine_UStash", "Beanmachine Stash", 20, 400000)
exports.ox_inventory:RegisterShop("UBeanFridge", { name = Config.FoodItems.label, inventory = Config.FoodItems.items })
exports.ox_inventory:RegisterShop("UBeanSoda", { name = Config.SodaItems.label, inventory = Config.SodaItems.items })
exports.ox_inventory:RegisterShop("UBeanDonut", { name = Config.DesertItems.label, inventory = Config.DesertItems.items })
--Gabz--
exports.ox_inventory:RegisterStash("beanmachine_Counter", "Beanmachine Counter", 20, 400000)
exports.ox_inventory:RegisterStash("beanmachine_Counter2", "Beanmachine Counter 2", 20, 400000)
exports.ox_inventory:RegisterStash("beanmachine_LegionStash", "Beanmachine Legion Stash", 20, 400000)
exports.ox_inventory:RegisterShop("BeanFridge", { name = Config.FoodItems.label, inventory = Config.FoodItems.items})
exports.ox_inventory:RegisterShop("BeanFridge2", { name = Config.FoodItems.label, inventory = Config.FoodItems.items})
exports.ox_inventory:RegisterShop("BeanSoda", { name = Config.SodaItems.label, inventory = Config.SodaItems.items})
exports.ox_inventory:RegisterShop("BeanDonut", { name = Config.DesertItems.label, inventory = Config.DesertItems.items})
--flx--
exports.ox_inventory:RegisterStash("beanmachine_flxCounter", "Beanmachine Counter", 20, 400000)
exports.ox_inventory:RegisterStash("beanmachine_flxStash", "Beanmachine Stash", 20, 400000)
exports.ox_inventory:RegisterShop("flxBeanFridge", { name = Config.FoodItems.label, inventory = Config.FoodItems.items })
exports.ox_inventory:RegisterShop("flxBeanSoda", { name = Config.SodaItems.label, inventory = Config.SodaItems.items })
exports.ox_inventory:RegisterShop("flxBeanDonut", { name = Config.DesertItems.label, inventory = Config.DesertItems.items })
function HasItem(src, items, amount)
local count = exports.ox_inventory:Search(src, 'count', items)
if exports.ox_inventory:Search(src, 'count', items) >= (amount or 1) then if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^5FOUND^7 x^3"..count.."^7 ^3"..tostring(items)) end return true
else if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Items ^1NOT FOUND^7") end return false end
end
else
function HasItem(source, items, amount)
local amount, count = amount or 1, 0
local Player = QBCore.Functions.GetPlayer(source)
if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Checking if player has required item^7 '^3"..tostring(items).."^7'") end
for _, itemData in pairs(Player.PlayerData.items) do
if itemData and (itemData.name == items) then
if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Item^7: '^3"..tostring(items).."^7' ^2Slot^7: ^3"..itemData.slot.." ^7x(^3"..tostring(itemData.amount).."^7)") end
count += itemData.amount
end
end
if count >= amount then if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Items ^5FOUND^7 x^3"..count.."^7") end return true end
if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Items ^1NOT FOUND^7") end return false
end
end
local function CheckVersion()
PerformHttpRequest('https://raw.githubusercontent.com/jimathy/UpdateVersions/master/jim-beanmachine.txt', function(err, newestVersion, headers)
local currentVersion = GetResourceMetadata(GetCurrentResourceName(), 'version')
if not newestVersion then print("Currently unable to run a version check.") return end
local advice = "^1You are currently running an outdated version^7, ^1please update^7"
if newestVersion:gsub("%s+", "") == currentVersion:gsub("%s+", "") then advice = '^6You are running the latest version.^7'
else
-- print("^3Version Check^7: ^2Current^7: "..currentVersion.." ^2Latest^7: "..newestVersion)
end
-- print(advice)
end)
end
CheckVersion()

View File

@ -0,0 +1,391 @@
local QBCore = exports['qb-core']:GetCoreObject()
RegisterNetEvent('QBCore:Client:UpdateObject', function() QBCore = exports['qb-core']:GetCoreObject() end)
local time = 1000
function loadModel(model) if not HasModelLoaded(model) then
if Config.Debug then print("^5Debug^7: ^2Loading Model^7: '^6"..model.."^7'") end
while not HasModelLoaded(model) do
if time > 0 then time -= 1 RequestModel(model)
else time = 1000 print("^5Debug^7: ^3LoadModel^7: ^2Timed out loading model ^7'^6"..model.."^7'") break
end
Wait(10)
end
end end
function unloadModel(model) if Config.Debug then print("^5Debug^7: ^2Removing Model^7: '^6"..model.."^7'") end SetModelAsNoLongerNeeded(model) end
function loadAnimDict(dict) if not HasAnimDictLoaded(dict) then if Config.Debug then print("^5Debug^7: ^2Loading Anim Dictionary^7: '^6"..dict.."^7'") end while not HasAnimDictLoaded(dict) do RequestAnimDict(dict) Wait(5) end end end
function unloadAnimDict(dict) if Config.Debug then print("^5Debug^7: ^2Removing Anim Dictionary^7: '^6"..dict.."^7'") end RemoveAnimDict(dict) end
function loadPtfxDict(dict) if not HasNamedPtfxAssetLoaded(dict) then if Config.Debug then print("^5Debug^7: ^2Loading Ptfx Dictionary^7: '^6"..dict.."^7'") end while not HasNamedPtfxAssetLoaded(dict) do RequestNamedPtfxAsset(dict) Wait(5) end end end
function unloadPtfxDict(dict) if Config.Debug then print("^5Debug^7: ^2Removing Ptfx Dictionary^7: '^6"..dict.."^7'") end RemoveNamedPtfxAsset(dict) end
function destroyProp(entity)
if Config.Debug then print("^5Debug^7: ^2Destroying Prop^7: '^6"..entity.."^7'") end
SetEntityAsMissionEntity(entity) Wait(5)
--DetachEntity(entity, true, true) Wait(5)
DeleteObject(entity)
end
function makeProp(data, freeze, synced)
loadModel(data.prop)
local prop = CreateObject(data.prop, 0.0, 0.0, 0.0, synced or false, synced or false, false)
SetEntityHeading(prop, data.coords.w+180.0)
--FreezeEntityPosition(prop, freeze or false)
SetEntityCompletelyDisableCollision(prop, true, false)
DisableCamCollisionForEntity(prop)
DisableCamCollisionForObject(prop)
if Config.Debug then print("^5Debug^7: ^6Prop ^2Created ^7: '^6"..prop.."^7'") end
return prop
end
function triggerNotify(title, message, type, src)
if Config.Notify == "okok" then
if not src then exports['okokNotify']:Alert(title, message, 6000, type)
else TriggerClientEvent('okokNotify:Alert', src, title, message, 6000, type) end
elseif Config.Notify == "qb" then
if not src then TriggerEvent("QBCore:Notify", message, type)
else TriggerClientEvent("QBCore:Notify", src, message, type) end
elseif Config.Notify == "t" then
if not src then exports['t-notify']:Custom({title = title, style = type, message = message, sound = true})
else TriggerClientEvent('t-notify:client:Custom', src, { style = type, duration = 6000, title = title, message = message, sound = true, custom = true}) end
elseif Config.Notify == "infinity" then
if not src then TriggerEvent('infinity-notify:sendNotify', message, type)
else TriggerClientEvent('infinity-notify:sendNotify', src, message, type) end
elseif Config.Notify == "rr" then
if not src then exports.rr_uilib:Notify({msg = message, type = type, style = "dark", duration = 6000, position = "top-right", })
else TriggerClientEvent("rr_uilib:Notify", src, {msg = message, type = type, style = "dark", duration = 6000, position = "top-right", }) end
end
end
function countTable(table) local i = 0 for keys in pairs(table) do i += 1 end return i end
function toggleItem(give, item, amount) TriggerServerEvent("ns-bars:server:toggleItem", give, item, amount) end
--Screen Effects
local alienEffect = false
function AlienEffect()
if alienEffect then return else alienEffect = true end
if Config.Debug then print("^5Debug^7: ^3AlienEffect^7() ^2activated") end
AnimpostfxPlay("DrugsMichaelAliensFightIn", 3.0, 0)
Wait(math.random(5000, 8000))
local Ped = PlayerPedId()
local animDict = "MOVE_M@DRUNK@VERYDRUNK"
loadAnimDict(animDict)
SetPedCanRagdoll(Ped, true)
ShakeGameplayCam('DRUNK_SHAKE', 2.80)
SetTimecycleModifier("Drunk")
SetPedMovementClipset(Ped, animDict, 1)
SetPedMotionBlur(Ped, true)
SetPedIsDrunk(Ped, true)
Wait(1500)
SetPedToRagdoll(Ped, 5000, 1000, 1, 0, 0, 0)
Wait(13500)
SetPedToRagdoll(Ped, 5000, 1000, 1, 0, 0, 0)
Wait(120500)
ClearTimecycleModifier()
ResetScenarioTypesEnabled()
ResetPedMovementClipset(Ped, 0)
SetPedIsDrunk(Ped, false)
SetPedMotionBlur(Ped, false)
AnimpostfxStopAll()
ShakeGameplayCam('DRUNK_SHAKE', 0.0)
AnimpostfxPlay("DrugsMichaelAliensFight", 3.0, 0)
Wait(math.random(45000, 60000))
AnimpostfxPlay("DrugsMichaelAliensFightOut", 3.0, 0)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
alienEffect = false
if Config.Debug then print("^5Debug^7: ^3AlienEffect^7() ^2stopped") end
end
local weedEffect = false
function WeedEffect()
if weedEffect then return else weedEffect = true end
if Config.Debug then print("^5Debug^7: ^3WeedEffect^7() ^2activated") end
AnimpostfxPlay("DrugsMichaelAliensFightIn", 3.0, 0)
Wait(math.random(3000, 20000))
AnimpostfxPlay("DrugsMichaelAliensFight", 3.0, 0)
Wait(math.random(15000, 20000))
AnimpostfxPlay("DrugsMichaelAliensFightOut", 3.0, 0)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
weedEffect = false
if Config.Debug then print("^5Debug^7: ^3WeedEffect^7() ^2stopped") end
end
local trevorEffect = false
function TrevorEffect()
if trevorEffect then return else trevorEffect = true end
if Config.Debug then print("^5Debug^7: ^3TrevorEffect^7() ^2activated") end
AnimpostfxPlay("DrugsTrevorClownsFightIn", 3.0, 0)
Wait(3000)
AnimpostfxPlay("DrugsTrevorClownsFight", 3.0, 0)
Wait(30000)
AnimpostfxPlay("DrugsTrevorClownsFightOut", 3.0, 0)
AnimpostfxStop("DrugsTrevorClownsFight")
AnimpostfxStop("DrugsTrevorClownsFightIn")
AnimpostfxStop("DrugsTrevorClownsFightOut")
trevorEffect = false
if Config.Debug then print("^5Debug^7: ^3TrevorEffect^7() ^2stopped") end
end
local turboEffect = false
function TurboEffect()
if turboEffect then return else turboEffect = true end
if Config.Debug then print("^5Debug^7: ^3TurboEffect^7() ^2activated") end
AnimpostfxPlay('RaceTurbo', 0, true)
SetTimecycleModifier('rply_motionblur')
ShakeGameplayCam('SKY_DIVING_SHAKE', 0.25)
Wait(30000)
StopGameplayCamShaking(true)
SetTransitionTimecycleModifier('default', 0.35)
Wait(1000)
ClearTimecycleModifier()
AnimpostfxStop('RaceTurbo')
turboEffect = false
if Config.Debug then print("^5Debug^7: ^3TurboEffect^7() ^2stopped") end
end
local rampageEffect = false
function RampageEffect()
if rampageEffect then return else rampageEffect = true end
if Config.Debug then print("^5Debug^7: ^3RampageEffect^7() ^2activated") end
AnimpostfxPlay('Rampage', 0, true)
SetTimecycleModifier('rply_motionblur')
ShakeGameplayCam('SKY_DIVING_SHAKE', 0.25)
Wait(30000)
StopGameplayCamShaking(true)
SetTransitionTimecycleModifier('default', 0.35)
Wait(1000)
ClearTimecycleModifier()
AnimpostfxStop('Rampage')
rampageEffect = false
if Config.Debug then print("^5Debug^7: ^3RampageEffect^7() ^2stopped") end
end
local focusEffect = false
function FocusEffect()
if focusEffect then return else focusEffect = true end
if Config.Debug then print("^5Debug^7: ^3FocusEffect^7() ^2activated") end
Wait(1000)
AnimpostfxPlay('FocusIn', 0, true)
Wait(30000)
AnimpostfxStop('FocusIn')
focusEffect = false
if Config.Debug then print("^5Debug^7: ^3FocusEffect^7() ^2stopped") end
end
local NightVisionEffect = false
function NightVisionEffect()
if NightVisionEffect then return else NightVisionEffect = true end
if Config.Debug then print("^5Debug^7: ^3NightVisionEffect^7() ^2activated") end
SetNightvision(true)
Wait(math.random(3000, 4000)) -- FEEL FREE TO CHANGE THIS
SetNightvision(false)
SetSeethrough(false)
NightVisionEffect = false
if Config.Debug then print("^5Debug^7: ^3NightVisionEffect^7() ^2stopped") end
end
local thermalEffect = false
function ThermalEffect()
if thermalEffect then return else thermalEffect = true end
if Config.Debug then print("^5Debug^7: ^3ThermalEffect^7() ^2activated") end
SetNightvision(true)
SetSeethrough(true)
Wait(math.random(2000, 3000)) -- FEEL FREE TO CHANGE THIS
SetNightvision(false)
SetSeethrough(false)
thermalEffect = false
if Config.Debug then print("^5Debug^7: ^3ThermalEffect^7() ^2stopped") end
end
--Built-in Buff effects
local healEffect = false
function HealEffect(data)
if healEffect then return end
if Config.Debug then print("^5Debug^7: ^3HealEffect^7() ^2activated") end
healEffect = true
local count = (data[1] / 1000)
while count > 0 do
Wait(1000)
count -= 1
SetEntityHealth(PlayerPedId(), GetEntityHealth(PlayerPedId()) + data[2])
end
healEffect = false
if Config.Debug then print("^5Debug^7: ^3HealEffect^7() ^2stopped") end
end
local staminaEffect = false
function StaminaEffect(data)
if staminaEffect then return end
if Config.Debug then print("^5Debug^7: ^3StaminaEffect^7() ^2activated") end
staminaEffect = true
local startStamina = (data[1] / 1000)
SetRunSprintMultiplierForPlayer(PlayerId(), 1.49)
while startStamina > 0 do
Wait(1000)
if math.random(5, 100) < 10 then RestorePlayerStamina(PlayerId(), data[2]) end
startStamina -= 1
if math.random(5, 100) < 51 then end
end
startStamina = 0
SetRunSprintMultiplierForPlayer(PlayerId(), 1.0)
staminaEffect = false
if Config.Debug then print("^5Debug^7: ^3StaminaEffect^7() ^2stopped") end
end
function StopEffects() -- Used to clear up any effects stuck on screen
if Config.Debug then print("^5Debug^7: ^2All screen effects stopped") end
ShakeGameplayCam('DRUNK_SHAKE', 0.0)
SetPedToRagdoll(PlayerPedId(), 5000, 1000, 1, 0, 0, 0)
ClearTimecycleModifier()
ResetScenarioTypesEnabled()
ResetPedMovementClipset(PlayerPedId(), 0)
SetPedIsDrunk(PlayerPedId(), false)
SetPedMotionBlur(PlayerPedId(), false)
SetNightvision(false)
SetSeethrough(false)
AnimpostfxStop("DrugsMichaelAliensFightIn")
AnimpostfxStop("DrugsMichaelAliensFight")
AnimpostfxStop("DrugsMichaelAliensFightOut")
AnimpostfxStop("DrugsTrevorClownsFight")
AnimpostfxStop("DrugsTrevorClownsFightIn")
AnimpostfxStop("DrugsTrevorClownsFightOut")
AnimpostfxStop('RaceTurbo')
AnimpostfxStop('FocusIn')
AnimpostfxStop('Rampage')
end
local time = 1000
function loadModel(model) if not HasModelLoaded(model) then
if Config.Debug then print("^5Debug^7: ^3loadModel^7(): ^2Loading Model^7: '^6"..model.."^7'") end
while not HasModelLoaded(model) do
if time > 0 then time = time - 1 RequestModel(model)
else time = 1000 print("^5Debug^7: ^3loadModel^7(): ^2Timed out loading model ^7'^6"..model.."^7'") break
end
Wait(10)
end
end
end
function unloadModel(model) if Config.Debug then print("^5Debug^7: ^2Removing Model^7: '^6"..model.."^7'") end SetModelAsNoLongerNeeded(model) end
function loadAnimDict(dict) if Config.Debug then print("^5Debug^7: ^2Loading Anim Dictionary^7: '^6"..dict.."^7'") end while not HasAnimDictLoaded(dict) do RequestAnimDict(dict) Wait(5) end end
function unloadAnimDict(dict) if Config.Debug then print("^5Debug^7: ^2Removing Anim Dictionary^7: '^6"..dict.."^7'") end RemoveAnimDict(dict) end
function loadPtfxDict(dict) if Config.Debug then print("^5Debug^7: ^2Loading Ptfx Dictionary^7: '^6"..dict.."^7'") end while not HasNamedPtfxAssetLoaded(dict) do RequestNamedPtfxAsset(dict) Wait(5) end end
function unloadPtfxDict(dict) if Config.Debug then print("^5Debug^7: ^2Removing Ptfx Dictionary^7: '^6"..dict.."^7'") end RemoveNamedPtfxAsset(dict) end
function lookEnt(entity)
if type(entity) == "vector3" then
if not IsPedHeadingTowardsPosition(PlayerPedId(), entity, 10.0) then
TaskTurnPedToFaceCoord(PlayerPedId(), entity, 1500)
if Config.Debug then print("^5Debug^7: ^3lookEnt^7(): ^2Turning Player to^7: '^6"..json.encode(entity).."^7'") end
Wait(1500)
end
else
if DoesEntityExist(entity) then
if not IsPedHeadingTowardsPosition(PlayerPedId(), GetEntityCoords(entity), 30.0) then
TaskTurnPedToFaceCoord(PlayerPedId(), GetEntityCoords(entity), 1500)
if Config.Debug then print("^5Debug^7: ^3lookEnt^7(): ^2Turning Player to^7: '^6"..entity.."^7'") end
Wait(1500)
end
end
end
end
function makeProp(data, freeze, synced)
loadModel(data.prop)
local prop = CreateObject(data.prop, data.coords.x, data.coords.y, data.coords.z-1.03, synced or false, synced or false, false)
SetEntityHeading(prop, data.coords.w)
FreezeEntityPosition(prop, freeze or 0)
if Config.Debug then
local coords = { string.format("%.2f", data.coords.x), string.format("%.2f", data.coords.y), string.format("%.2f", data.coords.z), (string.format("%.2f", data.coords.w or 0.0)) }
print("^5Debug^7: ^1Prop ^2Created^7: '^6"..prop.."^7' | ^2Hash^7: ^7'^6"..(data.prop).."^7' | ^2Coord^7: ^5vec4^7(^6"..(coords[1]).."^7, ^6"..(coords[2]).."^7, ^6"..(coords[3]).."^7, ^6"..(coords[4]).."^7)")
end
return prop
end
function makePed(model, coords, freeze, collision, scenario, anim)
loadModel(model)
local ped = CreatePed(0, model, coords.x, coords.y, coords.z-1.03, coords.w, false, false)
SetEntityInvincible(ped, true)
SetBlockingOfNonTemporaryEvents(ped, true)
FreezeEntityPosition(ped, freeze or true)
if collision then SetEntityNoCollisionEntity(ped, PlayerPedId(), false) end
if scenario then TaskStartScenarioInPlace(ped, scenario, 0, true) end
if anim then
loadAnimDict(anim[1])
TaskPlayAnim(ped, anim[1], anim[2], 1.0, 1.0, -1, 1, 0.2, 0, 0, 0)
end
if Config.Debug then
local coords = { string.format("%.2f", coords.x), string.format("%.2f", coords.y), string.format("%.2f", coords.z), (string.format("%.2f", coords.w or 0.0)) }
print("^5Debug^7: ^1Ped ^2Created^7: '^6"..ped.."^7' | ^2Hash^7: ^7'^6"..(model).."^7' | ^2Coord^7: ^5vec4^7(^6"..(coords[1]).."^7, ^6"..(coords[2]).."^7, ^6"..(coords[3]).."^7, ^6"..(coords[4]).."^7)")
end
return ped
end
function makeBlip(data)
local blip = AddBlipForCoord(data.coords)
SetBlipAsShortRange(blip, true)
SetBlipSprite(blip, data.sprite or 1)
SetBlipColour(blip, data.col or 0)
SetBlipScale(blip, data.scale or 0.7)
SetBlipDisplay(blip, (data.disp or 6))
if data.category then SetBlipCategory(blip, data.category) end
BeginTextCommandSetBlipName('STRING')
AddTextComponentString(tostring(data.name))
EndTextCommandSetBlipName(blip)
if Config.Debug then print("^5Debug^7: ^6Blip ^2created for location^7: '^6"..data.name.."^7'") end
return blip
end
function triggerNotify(title, message, type, src)
if not src then TriggerEvent("QBCore:Notify", message, type)
else TriggerClientEvent("QBCore:Notify", src, message, type) end
end
function pairsByKeys(t) local a = {} for n in pairs(t) do a[#a+1] = n end table.sort(a) local i = 0 local iter = function() i += 1 if a[i] == nil then return nil else return a[i], t[a[i]] end end return iter end
function searchCar(vehicle)
local newName = nil
for k, v in pairs(QBCore.Shared.Vehicles) do
if tonumber(v.hash) == GetHashKey(vehicle) then
if Config.Debug then print("^5Debug^7: ^2Vehicle info found in^7 ^4vehicles^7.^4lua^7: ^6"..v.hash.. " ^7(^6"..QBCore.Shared.Vehicles[k].name.."^7)") end
newName = QBCore.Shared.Vehicles[k].name.." "..QBCore.Shared.Vehicles[k].brand
end
end
if Config.Debug then
if not newName then print("^5Debug^7: ^2Vehicle ^1not ^2found in ^4vehicles^7.^4lua^7: ^6"..vehicle.." ^7(^6"..GetDisplayNameFromVehicleModel(vehicle):lower().."^7)") end
end
if not newName then
local name = GetDisplayNameFromVehicleModel(vehicle):lower()
local brand = GetMakeNameFromVehicleModel(vehicle):lower()
newName = name:sub(1,1):upper()..name:sub(2).." "..brand:sub(1,1):upper()..brand:sub(2)
end
return newName
end
function HasItem(items, amount)
local isTable = type(items) == 'table'
local isArray = isTable and table.type(items) == 'array' or false
local totalItems = #items
local count = 0
local kvIndex = 2
if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Checking if player has required items") end
if isTable and not isArray then totalItems = 0
for _ in pairs(items) do totalItems += 1 end
kvIndex = 1
end
for _, itemData in pairs(QBCore.Functions.GetPlayerData().items) do
if isTable then
for k, v in pairs(items) do
local itemKV = {k, v}
if itemData and itemData.name == itemKV[kvIndex] and ((amount and itemData.amount >= amount) or (not isArray and itemData.amount >= v) or (not amount and isArray)) then
count += 1
end
end
if count == totalItems then
if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Items ^2FOUND^7") end
return true
end
else -- Single item as string
if itemData and itemData.name == items and (not amount or (itemData and amount and itemData.amount >= amount)) then
if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Item^7: '^3"..tostring(items).."^7' ^2FOUND^7") end
return true
end
end
end
if Config.Debug then print("^5Debug^7: ^3HasItem^7: ^2Items ^1NOT FOUND^7") end
return false
end

View File

@ -0,0 +1,82 @@
# jim-jobgarage
Job garage for grabbing job related vehicles
---
# What is this?
- This is a dynamic system for pulling out job and grade locked vehicles
- **It's not for parking and saving your vehicles, that is done in your garage scripts**
![](https://i.imgur.com/JhGaGMS.jpeg)
![](https://i.imgur.com/ycXPTj1.jpeg)
### If you need support I have a discord server available, it helps me keep track of issues and give better support.
https://discord.gg/xKgQZ6wZvS
### If you think I did a good job here, consider donating as it keeps by lights on and my cat fat/floofy:
https://ko-fi.com/jixelpatterns
---
# Installation
- I always recommend starting my scripts **AFTER** `[qb]` not inside it as it can mess with any dependancies on server load
- I have a separate folder called `[jim]` (that is also in the resources folder) that starts WAY after everything else.
- This ensure's it has everything it requires before trying to load
- Example of my load order:
```CSS
# QBCore & Extra stuff
ensure qb-core
ensure [qb]
ensure [standalone]
ensure [voice]
ensure [defaultmaps]
ensure [vehicles]
# Extra Jim Stuff
ensure [jim]
```
---
## Location Setup
- Vehicle Modifiers - These can be used for making sure the vehicles comes out exactly how you want it to
- `CustomName = "Police Car",` this will show a custom override name for your vehicles so you don't need to add them to your vehicles.lua
- `rank = { 2, 4 },` This specifes which grades can see it, and only these grades
- `grade = 4,` This specifies the lowest grade and above that can see the vehicle
- `colors = { 136, 137 },` This is the colour index id of the vehicle, Primary and Secondary in that order
- `bulletproof = true,` This determines if the tyres are bullet proof (don't ask me why, I was asked to add this)
- `livery = 1,` This sets the livery id of the vehicle, (most mod menus would number them or have them in number order) 0 = stock
- `extras = { 1, 5 },` -- This enables the selected extras on the vehicle
- `performance = "max",` -- This sets the car upgrades all to MAX levels
- `performance = { 2, 3, 3, 2, 4, true },` -- This allows more specific settings for each upgrade level, in order: engine, brakes, suspension, transmission, armour, turbo
- `trunkItems = { },` -- Use this to add items to the trunk of the vehicle when it is spawned
- Example:
```lua
Locations = {
{ zoneEnable = true, -- disable if you want to hide this temporarily
job = "mechanic", -- set this to required job grade
garage = {
spawn = vector4(-179.34, -1285.27, 30.83, 89.24), -- Where the vehicle will spawn
out = vector4(-177.1, -1282.25, 31.3, 179.01), -- Where you select the vehicles from
ped = { -- Add a "ped" table to make a ped spawn instead of the parking meter
model = `G_M_M_ChemWork_01`, -- set ped model here
scenario = "WORLD_HUMAN_CLIPBOARD" -- set scenario here
},
list = { -- The list of cars that will spawn
["cheburek"] = {
CustomName = "Police Car",
colors = { 136, 137 }, -- Color index numbers { primary, secondary },
grade = 4, -- Job Grade Required to access this vehicle
performance = { 2, 3, 3, 2, 4, true },
trunkItems = {
{ name = "heavyarmor", amount = 2, info = {}, type = "item", slot = 1, },
{ name = "empty_evidence_bag", amount = 10, info = {}, type = "item", slot = 2, },
{ name = "police_stormram", amount = 1, info = {}, type = "item", slot = 3, },
},
},
["burrito3"] = { }, -- Don't need to add any modifiers/restrictions
},
},
},
},
```

View File

@ -0,0 +1,216 @@
local QBCore = exports['qb-core']:GetCoreObject()
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
QBCore.Functions.GetPlayerData(function(PlayerData) PlayerJob = PlayerData.job end)
end)
RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) PlayerJob = JobInfo end)
AddEventHandler('onResourceStart', function(r) if GetCurrentResourceName() ~= r then return end
QBCore.Functions.GetPlayerData(function(PlayerData) PlayerJob = PlayerData.job end)
end)
for i = 1, #Config.Locations do -- Convert trunk items to usable stashes
for k, v in pairs(Config.Locations[i].garage.list) do
if v.trunkItems then
local items = {}
for _, item in pairs(v.trunkItems) do
local itemInfo = QBCore.Shared.Items[item.name:lower()]
items[item.slot] = {
name = itemInfo["name"],
amount = tonumber(item.amount),
info = item.info,
label = itemInfo["label"],
description = itemInfo["description"] and itemInfo["description"] or "",
weight = itemInfo["weight"],
type = itemInfo["type"],
unique = itemInfo["unique"],
useable = itemInfo["useable"],
image = itemInfo["image"],
slot = item.slot,
}
end
Config.Locations[i].garage.list[k].trunkItems = items
end
end
end
local Targets = {}
local Parking = {}
--Garage Locations
CreateThread(function()
for k, v in pairs(Config.Locations) do
if v.garage then
local out = v.garage.out
if v.garage.ped then Parking[#Parking+1] = makePed(v.garage.ped.model, out, 1, 1, v.garage.ped.scenario)
else Parking[#Parking+1] = makeProp({prop = `prop_parkingpay`, coords = vector4(out.x, out.y, out.z, out.w-180.0)}, 1, false) end
Targets["JobGarage: "..k] =
exports['qb-target']:AddBoxZone("JobGarage: "..k, vector3(out.x, out.y, out.z-1.03), 0.8, 0.5, { name="JobGarage: "..k, heading = out.w+180.0, debugPoly=Config.Debug, minZ=out.z-1.05, maxZ=out.z+0.80 },
{ options = { { event = "jim-jobgarage:client:Garage:Menu", icon = "fas fa-clipboard", label = "Firmabiler", job = v.job, coords = v.garage.spawn, list = v.garage.list, prop = Parking[#Parking] }, },
distance = 2.0 })
end
end
end)
local currentVeh = { out = false, current = nil }
RegisterNetEvent('jim-jobgarage:client:Garage:Menu', function(data)
if not IsPedInAnyVehicle(PlayerPedId()) then
lookEnt(data.prop)
loadAnimDict("amb@prop_human_atm@male@enter")
TaskPlayAnim(PlayerPedId(), 'amb@prop_human_atm@male@enter', "enter", 1.0,-1.0, 1500, 1, 1, true, true, true)
end
local vehicleMenu = { { icon = "fas fa-car-tunnel", header = PlayerJob.label.." ".."Firmagarage", isMenuHeader = true } }
vehicleMenu[#vehicleMenu+1] = { icon = "fas fa-circle-xmark", header = "", txt = "Luk", params = { event = "jim-jobgarage:client:Menu:Close" } }
if currentVeh.out and DoesEntityExist(currentVeh.current) then
vehicleMenu[#vehicleMenu+1] = { icon = "fas fa-clipboard-list", header = "Køretøj ude af garage",
txt = "Køretøj: "..currentVeh.name.."<br> Plate: ["..GetVehicleNumberPlateText(currentVeh.current).."]",
params = { event = "jim-jobgarage:client:Garage:Blip", }, }
vehicleMenu[#vehicleMenu+1] = { icon = "fas fa-car-burst", header = "Fjern køretøj", params = { event = "jim-jobgarage:client:RemSpawn" } }
else
currentVeh = { out = false, current = nil }
table.sort(data.list, function(a, b) return a:lower() < b:lower() end)
for k, v in pairsByKeys(data.list) do
local showButton = false
if v.grade then if v.grade <= PlayerJob.grade.level then showButton = true end end
if v.rank then for _, b in pairs(v.rank) do if b == PlayerJob.grade.level then showButton = true end end end
if not v.grade and not v.rank then showButton = true end
if showButton == true then
local spawnName = k local spawnHash = GetHashKey(spawnName)
--k = data.list[spawnName].CustomName or searchCar(spawnName)
local classtable = {
[8] = "fas fa-motorcycle", -- Motorcycle icon
[9] = "fas fa-truck-monster", -- Off Road icon
[10] = "fas fa-truck-front", -- Van / Truck icon
[11] = "fas fa-truck-front", -- Van / Truck icon
[12] = "fas fa-truck-front", -- Van / Truck icon
[13] = "fas fa-bicycle", -- Bike
[14] = "fas fa-ship", -- Boats
[15] = "fas fa-helicopter",-- Helicopter
[16] = "fas fa-plane", -- Planes
[18] = "fas fa-kit-medical", -- Emergency
}
local seticon = classtable[GetVehicleClassFromName(spawnHash)] or "fas fa-car"
vehicleMenu[#vehicleMenu+1] = { icon = seticon, header = (data.list[k].CustomName or searchCar(k)), params = { event = "jim-jobgarage:client:SpawnList", args = { spawnName = spawnName, coords = data.coords, list = v } } }
end
end
end
showButton = nil
if #vehicleMenu <= 2 then triggerNotify(nil, "Ingen køretøjer tilgængelig") return end
exports['qb-menu']:openMenu(vehicleMenu)
unloadAnimDict("amb@prop_human_atm@male@enter")
end)
RegisterNetEvent("jim-jobgarage:client:SpawnList", function(data)
local oldveh = GetClosestVehicle(data.coords.x, data.coords.y, data.coords.z, 2.5, 0, 71)
local name = ""
if oldveh ~= 0 then
name = searchCar(GetEntityModel(oldveh))
triggerNotify(nil, name.." er i vejen", "error")
else
QBCore.Functions.SpawnVehicle(data.spawnName, function(veh)
local name = data.list.CustomName or searchCar(data.spawnName)
currentVeh = { out = true, current = veh, name = name }
SetVehicleModKit(veh, 0)
NetworkRequestControlOfEntity(veh)
SetVehicleNumberPlateText(veh, string.sub(PlayerJob.label, 1, 5)..tostring(math.random(100, 999)))
--SetVehicleNumberPlateText(veh, "PD-"..QBCore.Functions.GetPlayerData().metadata.callsign)
SetEntityHeading(veh, data.coords.w)
TaskWarpPedIntoVehicle(PlayerPedId(), veh, -1)
if data.list.colors then SetVehicleColours(veh, data.list.colors[1], data.list.colors[2]) end
if data.list.bulletproof then SetVehicleTyresCanBurst(veh, false) end
if data.list.extras then
for _, v in pairs(data.list.extras) do SetVehicleExtra(veh, v, 0) end
end
if data.list.livery then
if GetNumVehicleMods(veh, 48) == 0 and GetVehicleLiveryCount(veh) ~= 0 then
SetVehicleLivery(veh, data.list.livery)
SetVehicleMod(veh, 48, -1, false)
else
SetVehicleMod(veh, 48, (data.list.livery - 1), false)
SetVehicleLivery(veh, -1)
end
end
if data.list.turbo then ToggleVehicleMod(veh, 18, data.list.turbo) end
if data.list.xenon then ToggleVehicleMod(veh, 22, data.list.xenon) end
if data.list.mod42 then SetVehicleMod(veh, 42, (data.list.mod42-1), false) end
if data.list.mod43 then SetVehicleMod(veh, 43, (data.list.mod43-1), false) end
if data.list.mod44 then SetVehicleMod(veh, 44, (data.list.mod44-1), false) end
if data.list.mod45 then SetVehicleMod(veh, 45, (data.list.mod45-1), false) end
if data.list.mod49 then SetVehicleMod(veh, 49, (data.list.mod49-1), false) end
if data.list.performance then
if type(data.list.performance) ~= "table" then
SetVehicleMod(veh, 11, GetNumVehicleMods(veh, 11)-1) -- Engine
SetVehicleMod(veh, 12, GetNumVehicleMods(veh, 12)-1) -- Brakes
SetVehicleMod(veh, 15, GetNumVehicleMods(veh, 15)-1) -- Suspension
SetVehicleMod(veh, 13, GetNumVehicleMods(veh, 13)-1) -- Transmission
SetVehicleMod(veh, 16, GetNumVehicleMods(veh, 16)-1) -- Armour
ToggleVehicleMod(veh, 18, true) -- Turbo
else
SetVehicleMod(veh, 11, data.list.performance[1]-1) -- Engine
SetVehicleMod(veh, 12, data.list.performance[2]-1) -- Brakes
SetVehicleMod(veh, 15, data.list.performance[3]-1) -- Suspension
SetVehicleMod(veh, 13, data.list.performance[4]-1) -- Transmission
SetVehicleMod(veh, 16, data.list.performance[5]-1) -- Armour
ToggleVehicleMod(veh, 18, data.list.performance[6]) -- Turbo
end
end
if data.list.trunkItems then TriggerServerEvent("jim-jobgarage:server:addTrunkItems", QBCore.Functions.GetPlate(veh), data.list.trunkItems) end
TriggerEvent("vehiclekeys:client:SetOwner", QBCore.Functions.GetPlate(veh))
exports['qb-fuel']:SetFuel(veh, 100.0)
SetVehicleEngineOn(veh, true, true)
Wait(250)
SetVehicleDirtLevel(veh, 0.0)
triggerNotify(nil, "Tog "..name.." ["..GetVehicleNumberPlateText(currentVeh.current).."]", "success")
end, data.coords, true)
end
end)
RegisterNetEvent("jim-jobgarage:client:RemSpawn", function(data)
if Config.CarDespawn then
SetVehicleEngineHealth(currentVeh.current, 200.0)
SetVehicleBodyHealth(currentVeh.current, 200.0)
for i = 0, 7 do SmashVehicleWindow(currentVeh.current, i) Wait(150) end PopOutVehicleWindscreen(currentVeh.current)
for i = 0, 5 do SetVehicleTyreBurst(currentVeh.current, i, true, 0) Wait(150) end
for i = 0, 5 do SetVehicleDoorBroken(currentVeh.current, i, false) Wait(150) end
Wait(800)
end
QBCore.Functions.DeleteVehicle(currentVeh.current) currentVeh = { out = false, current = nil }
end)
local markerOn = false
RegisterNetEvent("jim-jobgarage:client:Garage:Blip", function(data)
triggerNotify(nil, "Firmabil markeret på kortet")
if markerOn then markerOn = not markerOn end
markerOn = true
if not DoesBlipExist(garageBlip) then
garageBlip = makeBlip({ coords = GetEntityCoords(currentVeh.current), sprite = 85, col = 8, name = "Firmabil" })
SetBlipRoute(garageBlip, true)
SetBlipRouteColour(garageBlip, 3)
end
while markerOn do
local time = 5000
local carLoc = GetEntityCoords(currentVeh.current)
local playerLoc = GetEntityCoords(PlayerPedId())
if DoesEntityExist(currentVeh.current) then
local dist = #(carLoc - playerLoc)
if dist <= 30.0 and dist > 1.5 then time = 1000
elseif dist <= 1.5 then
RemoveBlip(garageBlip)
garageBlip = nil
markerOn = not markerOn
else time = 5000 end
else
RemoveBlip(garageBlip)
garageBlip = nil
markerOn = not markerOn
end
Wait(time)
end
end)
AddEventHandler('onResourceStop', function(r) if r ~= GetCurrentResourceName() then return end
for k in pairs(Targets) do exports['qb-target']:RemoveZone(k) end
for i = 1, #Parking do unloadModel(GetEntityModel(Parking[i])) DeleteEntity(Parking[i]) end
end)

View File

@ -0,0 +1,128 @@
--[[ LIST OF POSSIBLE VEHICLE MODIFIERS ]]--
-- Using these will change how each vehicle spawns
-- This can be used for making sure the vehicles comes out exactly how you want it to
-- CustomName = "Police Car", this will show a custom override name for your vehicles so you don't need to add them to your vehicles.lua
-- rank = { 2, 4 }, -- This specifes which grades can see it, and only these grades
-- grade = 4, -- This specifies the lowest grade and above that can see the vehicle
-- colors = { 136, 137 }, -- This is the colour index id of the vehicle, Primary and Secondary in that order
-- bulletproof = true, -- This determines if the tyres are bullet proof (don't ask me why, I was asked to add this)
-- livery = 1, -- This sets the livery id of the vehicle, (most mod menus would number them or have them in number order) 0 = stock
-- extras = { 1, 5 }, -- This enables the selected extras on the vehicle
-- performance = "max", this sets the stats to max if available
-- performance = { 2, 3, 3, 2, 4, true }, -- This allows more specific settings for each upgrade level, in order: engine, brakes, suspension, transmission, armour, turbo
-- trunkItems = { }, -- Use this to add items to the trunk of the vehicle when it is spawned
-- ANY VEHICLE, BOATS, POLICE CARS, EMS VEHICLES OR EVEN PLANES CAN BE ADDED.
Config = {
Debug = false, -- Enable to use debug features
Notify = "qb",
CarDespawn = true, -- Sends the vehicle to hell
Locations = {
{
zoneEnable = true,
job = "police",
garage = {
spawn = vector4(430.77, -980.06, 25.14, 183.81),
out = vector4(427.75, -988.33, 25.14, 273.73),
list = {
["polgauntlet"] = {
CustomName = "Gauntlet Interceptor",
grade = 0,
colors = { 91, 132 },
livery = 19,
extras = { 3 },
turbo = true,
xenon = true,
mod42 = 1,
mod43 = 7,
performance = "max",
trunkItems = {
{ name = "heavyarmor", amount = 2, info = {}, type = "item", slot = 1, },
{ name = "empty_evidence_bag", amount = 10, info = {}, type = "item", slot = 2, },
{ name = "police_stormram", amount = 1, info = {}, type = "item", slot = 3, },
},
},
},
},
},
{
zoneEnable = true, -- disable if you want to hide this temporarily
job = "police", -- set this to required job grade
garage = {
spawn = vector4(447.65, -981.21, 43.69, 93.61), -- Where the vehicle will spawn
out = vector4(463.93, -980.53, 43.69, 97.02), -- Where you select the vehicles from
list = { -- The list of cars that will spawn
["polmav"] = {
CustomName = "Politihelikopter",
livery = 0,
extras = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 },
performance = "max",
}
},
},
},
{
zoneEnable = true, -- disable if you want to hide this temporarily
job = "ambulance", -- set this to required job grade
garage = {
spawn = vector4(351.58, -587.45, 74.16, 160.5), -- Where the vehicle will spawn
out = vector4(337.3927, -586.7730, 74.1617, 250.08), -- Where you select the vehicles from
list = { -- The list of cars that will spawn
["polmav"] = {
CustomName = "Lægehelikopter",
livery = 1,
extras = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 },
performance = "max",
}
},
},
},
{
zoneEnable = true, -- disable if you want to hide this temporarily
job = "ambulance", -- set this to required job grade
garage = {
spawn = vector4(290.21, -572.13, 43.2, 75.99), -- Where the vehicle will spawn
out = vector4(293.33, -567.13, 43.26, 157.69), -- Where you select the vehicles from
list = { -- The list of cars that will spawn
["ambulance"] = {
CustomName = "Ambulance",
livery = 1,
extras = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 },
performance = "max",
},
["21yuk"] = {
CustomName = "EMS SUV",
livery = 1,
extras = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 },
performance = "max",
}
},
},
},
{
zoneEnable = true,
job = "gardener",
garage = {
spawn = vector4(967.95, -1019.72, 40.85, 271.94),
out = vector4(963.88, -1016.92, 40.85, 270.38),
list = {
["bison3"] = {
CustomName = "Redskabsvogn",
grade = 0,
colors = { 131, 131 },
livery = 19,
extras = { 3 },
turbo = true,
xenon = true,
performance = "max",
trunkItems = {
{ name = "shears", amount = 1, info = {}, type = "item", slot = 1, },
},
},
},
},
},
},
}

View File

@ -0,0 +1,130 @@
local QBCore = exports['qb-core']:GetCoreObject()
RegisterNetEvent('QBCore:Client:UpdateObject', function() QBCore = exports['qb-core']:GetCoreObject() end)
local time = 1000
function loadModel(model) if not HasModelLoaded(model) then
if Config.Debug then print("^5Debug^7: ^2Loading Model^7: '^6"..model.."^7'") end
while not HasModelLoaded(model) do
if time > 0 then time -= 1 RequestModel(model)
else time = 1000 print("^5Debug^7: ^3LoadModel^7: ^2Timed out loading model ^7'^6"..model.."^7'") break
end
Wait(10)
end
end end
function unloadModel(model) if Config.Debug then print("^5Debug^7: ^2Removing Model^7: '^6"..model.."^7'") end SetModelAsNoLongerNeeded(model) end
function loadAnimDict(dict) if not HasAnimDictLoaded(dict) then if Config.Debug then print("^5Debug^7: ^2Loading Anim Dictionary^7: '^6"..dict.."^7'") end while not HasAnimDictLoaded(dict) do RequestAnimDict(dict) Wait(5) end end end
function unloadAnimDict(dict) if Config.Debug then print("^5Debug^7: ^2Removing Anim Dictionary^7: '^6"..dict.."^7'") end RemoveAnimDict(dict) end
function loadPtfxDict(dict) if not HasNamedPtfxAssetLoaded(dict) then if Config.Debug then print("^5Debug^7: ^2Loading Ptfx Dictionary^7: '^6"..dict.."^7'") end while not HasNamedPtfxAssetLoaded(dict) do RequestNamedPtfxAsset(dict) Wait(5) end end end
function unloadPtfxDict(dict) if Config.Debug then print("^5Debug^7: ^2Removing Ptfx Dictionary^7: '^6"..dict.."^7'") end RemoveNamedPtfxAsset(dict) end
function destroyProp(entity)
if Config.Debug then print("^5Debug^7: ^2Destroying Prop^7: '^6"..entity.."^7'") end
SetEntityAsMissionEntity(entity) Wait(5)
DetachEntity(entity, true, true) Wait(5)
DeleteEntity(entity)
end
function makeProp(data, freeze, synced)
loadModel(data.prop)
local prop = CreateObject(data.prop, data.coords.x, data.coords.y, data.coords.z-1.03, synced or 0, synced or false, false)
SetEntityHeading(prop, data.coords.w)
FreezeEntityPosition(prop, freeze or 0)
if Config.Debug then print("^5Debug^7: ^6Prop ^2Created ^7: '^6"..prop.."^7'") end
return prop
end
function makePed(model, coords, freeze, collision, scenario, anim)
loadModel(model)
local ped = CreatePed(0, model, coords.x, coords.y, coords.z-1.03, coords.w, false, false)
SetEntityInvincible(ped, true)
SetBlockingOfNonTemporaryEvents(ped, true)
FreezeEntityPosition(ped, freeze or true)
if collision then SetEntityNoCollisionEntity(ped, PlayerPedId(), false) end
if scenario then TaskStartScenarioInPlace(ped, scenario, 0, true) end
if anim then
loadAnimDict(anim[1])
TaskPlayAnim(ped, anim[1], anim[2], 1.0, 1.0, -1, 1, 0.2, 0, 0, 0)
end
if Config.Debug then print("^5Debug^7: ^6Ped ^2Created for location^7: '^6"..model.."^7'") end
return ped
end
function makeBlip(data)
local blip = AddBlipForCoord(data.coords)
SetBlipAsShortRange(blip, true)
SetBlipSprite(blip, data.sprite or 1)
SetBlipColour(blip, data.col or 0)
SetBlipScale(blip, data.scale or 0.7)
SetBlipDisplay(blip, (data.disp or 6))
BeginTextCommandSetBlipName('STRING')
AddTextComponentString(tostring(data.name))
EndTextCommandSetBlipName(blip)
if Config.Debug then print("^5Debug^7: ^6Blip ^2created for location^7: '^6"..data.name.."^7'") end
return blip
end
function lookEnt(entity)
if type(entity) == "vector3" then
if not IsPedHeadingTowardsPosition(PlayerPedId(), entity, 10.0) then
TaskTurnPedToFaceCoord(PlayerPedId(), entity, 1500)
if Config.Debug then print("^5Debug^7: ^2Turning Player to^7: '^6"..json.encode(entity).."^7'") end
Wait(1500)
end
else
if DoesEntityExist(entity) then
if not IsPedHeadingTowardsPosition(PlayerPedId(), GetEntityCoords(entity), 30.0) then
TaskTurnPedToFaceCoord(PlayerPedId(), GetEntityCoords(entity), 1500)
if Config.Debug then print("^5Debug^7: ^2Turning Player to^7: '^6"..entity.."^7'") end
Wait(1500)
end
end
end
end
function triggerNotify(title, message, type, src)
if Config.Notify == "okok" then
if not src then exports['okokNotify']:Alert(title, message, 6000, type or 'info')
else TriggerClientEvent('okokNotify:Alert', src, title, message, 6000, type or 'info') end
elseif Config.Notify == "qb" then
if not src then TriggerEvent("QBCore:Notify", message, type)
else TriggerClientEvent("QBCore:Notify", src, message, type) end
elseif Config.Notify == "t" then
if not src then exports['t-notify']:Custom({title = title, style = type, message = message, sound = true})
else TriggerClientEvent('t-notify:client:Custom', src, { style = type, duration = 6000, title = title, message = message, sound = true, custom = true}) end
elseif Config.Notify == "infinity" then
if not src then TriggerEvent('infinity-notify:sendNotify', message, type)
else TriggerClientEvent('infinity-notify:sendNotify', src, message, type) end
elseif Config.Notify == "rr" then
if not src then exports.rr_uilib:Notify({msg = message, type = type, style = "dark", duration = 6000, position = "top-right", })
else TriggerClientEvent("rr_uilib:Notify", src, {msg = message, type = type, style = "dark", duration = 6000, position = "top-right", }) end
end
end
function pairsByKeys(t)
local a = {}
for n in pairs(t) do a[#a+1] = n end
table.sort(a)
local i = 0
local iter = function() i += 1 if a[i] == nil then return nil else return a[i], t[a[i]] end end
return iter
end
function searchCar(vehicle)
newName = nil
for k, v in pairs(QBCore.Shared.Vehicles) do
if tonumber(v.hash) == vehicle then
if Config.Debug then print("^5Debug^7: ^2Vehicle info found in^7 ^4vehicles^7.^4lua^7: ^6"..v.hash.. " ^7(^6"..QBCore.Shared.Vehicles[k].name.."^7)") end
newName = QBCore.Shared.Vehicles[k].name.." "..QBCore.Shared.Vehicles[k].brand
end
end
if Config.Debug then
if not newName then print("^5Debug^7: ^2Vehicle ^1not ^2found in ^4vehicles^7.^4lua^7: ^6"..vehicle.." ^7(^6"..GetDisplayNameFromVehicleModel(vehicle):lower().."^7)") end
end
if not newName then
local name = GetDisplayNameFromVehicleModel(vehicle):lower()
local brand = GetMakeNameFromVehicleModel(vehicle):lower()
newName = name:sub(1,1):upper()..name:sub(2).." "..brand:sub(1,1):upper()..brand:sub(2)
end
return newName
end

View File

@ -0,0 +1,12 @@
name "Jim-JobGarage"
author "Jimathy"
version "v1.3.2"
description "Job Garage Script By Jimathy"
fx_version 'cerulean'
game "gta5"
client_scripts { 'client.lua', }
server_scripts { 'server.lua'}
shared_scripts { 'config.lua', 'functions.lua' }
lua54 'yes'

View File

@ -0,0 +1 @@
RegisterNetEvent("jim-jobgarage:server:addTrunkItems", function(plate, items) exports["ps-inventory"]:addTrunkItems(plate, items) end)

Some files were not shown because too many files have changed in this diff Show More