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