forked from Simnation/Main
388 lines
14 KiB
Lua
388 lines
14 KiB
Lua
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,
|
|
}
|
|
}
|
|
|
|
function Sling:SyncWeaponAttachment(weaponName, weaponVal, coords, action)
|
|
local weaponData = {
|
|
weaponName = weaponName,
|
|
weaponVal = weaponVal,
|
|
coords = coords
|
|
}
|
|
TriggerServerEvent('force-sling:server:syncWeapons', weaponData, action)
|
|
end
|
|
|
|
|
|
|
|
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
|
|
|
|
local isInVehicle = IsPedInAnyVehicle(playerPed, false)
|
|
local isSitting = IsPedUsingScenario(playerPed, "PROP_HUMAN_SEAT_CHAIR")
|
|
local isRagdoll = IsPedRagdoll(playerPed)
|
|
local shouldHideWeapons = isInVehicle or isSitting or isRagdoll
|
|
|
|
if weapon == weaponVal.name or shouldHideWeapons then
|
|
if DoesEntityExist(Sling.cachedAttachments[weaponName].obj) then
|
|
Utils:DeleteWeapon(weaponName)
|
|
Sling:SyncWeaponAttachment(weaponName, nil, nil, 'detach')
|
|
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, false)
|
|
Sling:SyncWeaponAttachment(weaponName, weaponVal, coords, 'attach')
|
|
end
|
|
end
|
|
end
|
|
|
|
CreateThread(function()
|
|
local lastState = {
|
|
inVehicle = false,
|
|
sitting = false,
|
|
ragdoll = false
|
|
}
|
|
|
|
while true do
|
|
local playerPed = cache.ped
|
|
local weapon = GetSelectedPedWeapon(playerPed)
|
|
local currentState = {
|
|
inVehicle = IsPedInAnyVehicle(playerPed, false),
|
|
sitting = IsPedUsingScenario(playerPed, "PROP_HUMAN_SEAT_CHAIR"),
|
|
ragdoll = IsPedRagdoll(playerPed)
|
|
}
|
|
|
|
local stateChanged = lastState.inVehicle ~= currentState.inVehicle or
|
|
lastState.sitting ~= currentState.sitting or
|
|
lastState.ragdoll ~= currentState.ragdoll
|
|
|
|
if stateChanged then
|
|
lastState = Utils:DeepCopy(currentState)
|
|
local shouldHideWeapons = currentState.inVehicle or currentState.sitting or currentState.ragdoll
|
|
|
|
-- Nur eigene Waffen verarbeiten
|
|
for weaponName, weaponVal in pairs(Sling.cachedWeapons) do
|
|
if shouldHideWeapons then
|
|
if DoesEntityExist(Sling.cachedAttachments[weaponName]?.obj) then
|
|
Utils:DeleteWeapon(weaponName)
|
|
Sling:SyncWeaponAttachment(weaponName, nil, nil, 'detach')
|
|
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, false)
|
|
Sling:SyncWeaponAttachment(weaponName, weaponVal, coords, 'attach')
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if not (currentState.inVehicle or currentState.sitting or currentState.ragdoll) then
|
|
-- Nur eigene Waffen verarbeiten
|
|
for weaponName, weaponVal in pairs(Sling.cachedWeapons) do
|
|
handleWeaponAttachment(weaponName, weaponVal, playerPed, weapon)
|
|
end
|
|
end
|
|
|
|
Wait(500)
|
|
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
|