Main/resources/[jobs]/[weapons]/force-sling/client/functions.lua

377 lines
13 KiB
Lua
Raw Normal View History

2025-06-07 08:51:21 +02:00
local POSITION_CLAMP = 0.2
local DEFAULT_SPEED = 0.001
local FAST_SPEED = 0.01
DEFAULT_BONE = 24816
Sling = {
isPreset = false,
cachedPositions = {},
cachedPresets = {},
cachedWeapons = {},
cachedAttachments = {},
currentAttachedAmount = 0,
inPositioning = false,
data = {
object = nil,
}
}
2025-06-12 00:25:14 +02:00
local otherPlayersWeapons = {}
function Sling:SyncWeaponAttachment(weaponName, weaponVal, coords, action)
local weaponData = {
weaponName = weaponName,
weaponVal = weaponVal,
coords = coords
}
TriggerServerEvent('force-sling:server:syncWeapons', weaponData, action)
end
2025-06-07 08:51:21 +02:00
function Sling:InitMain()
Debug("info", "Initializing main thread")
Sling:InitSling()
Sling:InitCommands()
Debug("info", "Main thread initialized")
end
function Sling:InitSling()
local libCallbackAwait = lib.callback.await
Sling.cachedPositions = libCallbackAwait("force-sling:callback:getCachedPositions", false)
Sling.cachedPresets = libCallbackAwait("force-sling:callback:getCachedPresets", false)
Sling.cachedWeapons = Inventory:GetWeapons()
Sling:WeaponThread()
local function loadBoneOptions()
local bones = {}
for boneName, _ in pairs(Config.Bones) do
table.insert(bones, boneName)
end
return bones
end
local function loadWeaponOptions()
local weapons = {}
for weaponName, _ in pairs(Config.Weapons) do
table.insert(weapons, weaponName)
end
return weapons
end
local bones = loadBoneOptions()
local weapons = loadWeaponOptions()
local selectData = {
boneId = DEFAULT_BONE,
weapon = `w_pi_pistol50`,
weaponName = "weapon_pistol50"
}
lib.registerMenu({
id = 'sling_select',
title = locale("slingConfig"),
position = 'top-right',
onSideScroll = function(selected, scrollIndex, args)
if selected == 1 then
selectData.boneId = Config.Bones[args[scrollIndex]]
elseif selected == 2 then
local weapon = Config.Weapons[args[scrollIndex]]
selectData.weapon = weapon.model
selectData.weaponName = args[scrollIndex]
end
end,
onSelected = function(selected, secondary, args)
end,
onClose = function(keyPressed)
Sling.inPositioning = false
end,
options = {
{ label = 'Bone', values = bones, args = bones },
{ label = 'Weapon', values = weapons, args = weapons },
{ label = 'Continue' },
}
}, function(selected, scrollIndex, args)
Debug("info", "Selected weapon: " .. selectData.weapon)
Debug("info", "Selected bone: " .. selectData.boneId)
Sling:StartPositioning(selectData)
end)
end
function Sling:WeaponThread()
local function handleWeaponAttachment(weaponName, weaponVal, playerPed, weapon)
if not Sling.cachedAttachments[weaponName] then
Sling.cachedAttachments[weaponName] = {}
end
2025-06-12 00:07:24 +02:00
-- Check if player is in vehicle
local isInVehicle = IsPedInAnyVehicle(playerPed, false)
if weapon == weaponVal.name or isInVehicle then
2025-06-07 08:51:21 +02:00
if DoesEntityExist(Sling.cachedAttachments[weaponName].obj) then
Utils:DeleteWeapon(weaponName)
end
else
if not DoesEntityExist(Sling.cachedAttachments[weaponName].obj) then
local coords = Sling.cachedPositions[weaponName] or Sling.cachedPresets[weaponName] or
{ coords = { x = 0.0, y = -0.15, z = 0.0 }, rot = { x = 0.0, y = 0.0, z = 0.0 }, boneId = DEFAULT_BONE }
Utils:CreateAndAttachWeapon(weaponName, weaponVal, coords, playerPed)
else
if not IsEntityAttachedToAnyPed(Sling.cachedAttachments[weaponName].placeholder) then
Utils:DeleteWeapon(weaponName)
end
end
end
end
CreateThread(function()
2025-06-12 00:07:24 +02:00
local lastVehicleState = false
2025-06-07 08:51:21 +02:00
while true do
local weapon = GetSelectedPedWeapon(cache.ped)
2025-06-12 00:07:24 +02:00
local isInVehicle = IsPedInAnyVehicle(cache.ped, false)
if lastVehicleState ~= isInVehicle then
lastVehicleState = isInVehicle
if isInVehicle then
for weaponName, _ in pairs(Sling.cachedAttachments) do
if DoesEntityExist(Sling.cachedAttachments[weaponName].obj) then
Utils:DeleteWeapon(weaponName)
2025-06-12 00:25:14 +02:00
Sling:SyncWeaponAttachment(weaponName, nil, nil, 'detach')
2025-06-12 00:07:24 +02:00
end
end
else
for weaponName, weaponVal in pairs(Sling.cachedWeapons) do
if not DoesEntityExist(Sling.cachedAttachments[weaponName]?.obj) then
local coords = Sling.cachedPositions[weaponName] or Sling.cachedPresets[weaponName] or
{ coords = { x = 0.0, y = -0.15, z = 0.0 }, rot = { x = 0.0, y = 0.0, z = 0.0 }, boneId = DEFAULT_BONE }
Utils:CreateAndAttachWeapon(weaponName, weaponVal, coords, cache.ped)
2025-06-12 00:25:14 +02:00
Sling:SyncWeaponAttachment(weaponName, weaponVal, coords, 'attach')
2025-06-12 00:07:24 +02:00
end
end
end
end
2025-06-07 08:51:21 +02:00
while Sling.inPositioning do
Wait(1000)
end
2025-06-12 00:07:24 +02:00
if not isInVehicle then
for weaponName, weaponVal in pairs(Sling.cachedWeapons) do
handleWeaponAttachment(weaponName, weaponVal, cache.ped, weapon)
end
2025-06-07 08:51:21 +02:00
end
Wait(1000)
end
end)
end
function Sling:OnPositioningDone(coords, selectData)
lib.hideTextUI()
Sling.inPositioning = false
local weapon = selectData.weapon
coords.position = vec3(coords.position.x, coords.position.y, coords.position.z)
local distanceFromMiddle = #(coords.position - vec3(0.0, 0.0, 0.0))
local distanceFromMiddle2 = #(coords.position - vec3(0.0, 0.0, -0.2))
local distanceFromMiddle3 = #(coords.position - vec3(0.0, 0.0, 0.2))
if distanceFromMiddle < 0.14 or distanceFromMiddle2 < 0.14 or distanceFromMiddle3 < 0.14 then
coords.position = vec3(coords.position.x, 0.17, coords.position.z)
end
TriggerServerEvent("force-sling:server:saveWeaponPosition", coords.position, coords.rotation, weapon,
selectData.weaponName, selectData.boneId, Sling.isPreset)
Sling.cachedPositions[selectData.weaponName] = {
coords = coords.position,
rot = coords.rotation,
boneId = selectData.boneId
}
if Sling.cachedAttachments[selectData.weaponName] then
if DoesEntityExist(Sling.cachedAttachments[selectData.weaponName].obj) or DoesEntityExist(Sling.cachedAttachments[selectData.weaponName].placeholder) then
DeleteObject(Sling.cachedAttachments[selectData.weaponName].obj)
DeleteObject(Sling.cachedAttachments[selectData.weaponName].placeholder)
end
end
DeleteObject(Sling.object)
SetModelAsNoLongerNeeded(selectData.weapon)
end
local function DisableControls()
local controls = {
25, 44, 45, 51, 140, 141, 143,
263, 264, 24, 96, 97, 47, 74, 177
}
for i = 1, #controls do
DisableControlAction(0, controls[i], true)
end
end
function Sling:StartPositioning(selectData)
if Sling.inPositioning then return end
local coords = {
position = vec3(0.0, 0.0, 0.0),
rotation = vec3(0.0, 0.0, 0.0)
}
if Sling.cachedAttachments[selectData.weaponName] and DoesEntityExist(Sling.cachedAttachments[selectData.weaponName].obj) then
Utils:DeleteWeapon(selectData.weaponName)
end
if Sling.cachedPositions[selectData.weaponName] and selectData.boneId == Sling.cachedPositions[selectData.weaponName].boneId then
coords.position = Sling.cachedPositions[selectData.weaponName].coords
coords.rotation = Sling.cachedPositions[selectData.weaponName].rot
elseif Sling.cachedPresets[selectData.weaponName] and selectData.boneId == Sling.cachedPresets[selectData.weaponName].boneId then
coords.position = Sling.cachedPresets[selectData.weaponName].coords
coords.rotation = Sling.cachedPresets[selectData.weaponName].rot
end
Sling.inPositioning = true
CreateThread(function()
local speed = DEFAULT_SPEED
local function updatePosition(axis, delta)
local x, y, z = coords.position.x, coords.position.y, coords.position.z
if axis == 'x' then
x = lib.math.clamp(x + delta, -POSITION_CLAMP, POSITION_CLAMP)
elseif axis == 'y' then
y = lib.math.clamp(y + delta, -POSITION_CLAMP, POSITION_CLAMP)
elseif axis == 'z' then
z = lib.math.clamp(z + delta, -POSITION_CLAMP, POSITION_CLAMP)
end
coords.position = vec3(x, y, z)
AttachEntityToEntity(Sling.object, cache.ped, GetPedBoneIndex(cache.ped, selectData.boneId),
coords.position.x, coords.position.y, coords.position.z,
coords.rotation.x, coords.rotation.y, coords.rotation.z,
true, true, false, true, 2, true)
end
local function updateRotation(axis, delta)
local x, y, z = coords.rotation.x, coords.rotation.y, coords.rotation.z
if axis == 'x' then
x = x + delta
elseif axis == 'y' then
y = y + delta
elseif axis == 'z' then
z = z + delta
end
coords.rotation = vec3(x, y, z)
AttachEntityToEntity(Sling.object, cache.ped, GetPedBoneIndex(cache.ped, selectData.boneId),
coords.position.x, coords.position.y, coords.position.z,
coords.rotation.x, coords.rotation.y, coords.rotation.z,
true, true, false, true, 2, true)
end
while Sling.inPositioning do
if not DoesEntityExist(Sling.object) then
if not HasModelLoaded(selectData.weapon) then
lib.requestModel(selectData.weapon)
end
Sling.object = CreateObject(selectData.weapon, 0, 0, 0, false, true, false)
AttachEntityToEntity(Sling.object, cache.ped, GetPedBoneIndex(cache.ped, selectData.boneId), coords.position.x,
coords.position.y, coords.position.z, coords.rotation.x, coords.rotation.y, coords.rotation.z, true, true,
false, true, 2, true)
SetEntityCollision(Sling.object, false, false)
end
if IsDisabledControlJustPressed(0, 18) then
Sling:OnPositioningDone(coords, selectData)
break
end
if IsDisabledControlJustPressed(0, 177) then
DeleteObject(Sling.object)
Sling.inPositioning = false
lib.hideTextUI()
SetModelAsNoLongerNeeded(selectData.weapon)
break
end
if IsDisabledControlPressed(0, 21) then
speed = FAST_SPEED
end
if IsDisabledControlReleased(0, 21) then
speed = DEFAULT_SPEED
end
if IsDisabledControlPressed(0, 44) then updatePosition('x', -speed) end
if IsDisabledControlPressed(0, 46) then updatePosition('x', speed) end
if IsDisabledControlPressed(0, 188) then updatePosition('y', speed) end
if IsDisabledControlPressed(0, 187) then updatePosition('y', -speed) end
if IsDisabledControlPressed(0, 189) then updatePosition('z', speed) end
if IsDisabledControlPressed(0, 190) then updatePosition('z', -speed) end
if IsDisabledControlPressed(0, 96) then updateRotation('x', speed + 1.0) end
if IsDisabledControlPressed(0, 97) then updateRotation('x', -(speed + 1.0)) end
if IsDisabledControlPressed(0, 48) then updateRotation('z', speed + 1.0) end
if IsDisabledControlPressed(0, 73) then updateRotation('z', -(speed + 1.0)) end
if IsDisabledControlPressed(0, 47) then updateRotation('y', speed + 1.0) end
if IsDisabledControlPressed(0, 74) then updateRotation('y', -(speed + 1.0)) end
local text = ("pos: (%.2f, %.2f, %.2f) | rot: (%.2f, %.2f, %.2f)"):format(coords.position.x, coords.position.y,
coords.position.z, coords.rotation.x, coords.rotation.y, coords.rotation.z)
lib.showTextUI((locale("currentPosition") .. ": %s"):format(text) ..
' \n ' ..
'[QE] - ' ..
locale("up") ..
'/' ..
locale("down") ..
' \n' ..
'[Arrows] - ' ..
locale("move") ..
', XY \n' ..
'[Scroll]- ' ..
locale("rotate") ..
' \n' ..
'[XZ]- ' ..
locale("rotate") ..
' \n' ..
'[GH] - ' ..
locale("rotate") ..
' Z \n' ..
'[Shift] - ' ..
locale("speed") .. ' \n' .. '[ENTER] - ' .. locale("confirm") .. ' \n' .. '[BACKSPACE] - ' .. locale("cancel"))
DisableControls()
Wait(4)
end
end)
end
function Sling:StartConfiguration(isPreset)
Sling.isPreset = isPreset
lib.showMenu('sling_select')
end
function Sling:InitCommands()
Debug("info", "Initializing commands")
local admin = lib.callback.await("force-sling:callback:isPlayerAdmin", false)
if Config.Debug or admin.isAdmin then
RegisterCommand(Config.Command.name, function(source, args, raw)
if Config.Command.permission ~= "any" and admin ~= Config.Command.permission then return end
Sling:StartConfiguration(false)
end, false)
RegisterCommand(Config.Command.reset, function(source, args, raw)
if Config.Command.permission ~= "any" and admin ~= Config.Command.permission then return end
local weapon = args[1] and args[1]:lower() or GetSelectedPedWeapon(cache.ped)
if type(weapon) == "number" then
for weaponName, weaponVal in pairs(Sling.cachedWeapons) do
if weaponVal.name == weapon then
weapon = weaponName
break
end
end
end
Sling.cachedPositions = lib.callback.await("force-sling:callback:resetWeaponPositions", false, weapon)
end, false)
RegisterCommand(Config.Presets.command, function(source, args, raw)
if Config.Presets.permission ~= "any" and admin ~= Config.Presets.permission then return end
Sling:StartConfiguration(true)
end, false)
end
Debug("info", "Commands initialized")
end