175 lines
5.0 KiB
Lua
175 lines
5.0 KiB
Lua
local interactions = require 'client.interactions'
|
|
local utils = require 'client.utils'
|
|
local settings = require 'shared.settings'
|
|
local CURRENT_SELECTION = 1
|
|
local CURRENT_OPTION = 0
|
|
|
|
-- CACHE
|
|
local Wait = Wait
|
|
local IsControlJustPressed = IsControlJustPressed
|
|
local IsNuiFocused = IsNuiFocused
|
|
local IsPedDeadOrDying = IsPedDeadOrDying
|
|
local IsPedCuffed = IsPedCuffed
|
|
|
|
local visibleOptions = {}
|
|
|
|
-- Collects the options whose canInteract passes into visibleOptions and returns how many there are.
|
|
local function getVisibleOptions(interaction)
|
|
table.wipe(visibleOptions)
|
|
local amount = 0
|
|
local options = interaction.options
|
|
for i = 1, #options do
|
|
local option = options[i]
|
|
local ok = true
|
|
if option.canInteract then
|
|
ok = false
|
|
pcall(function() ok = option.canInteract(interaction.entity, interaction.coords, option.args) end)
|
|
end
|
|
if ok then
|
|
amount += 1
|
|
visibleOptions[amount] = option
|
|
end
|
|
end
|
|
return amount
|
|
end
|
|
|
|
local function triggerOption(interaction, option)
|
|
if option.action then
|
|
pcall(function() option.action(interaction.entity, interaction.coords, option.args) end)
|
|
elseif option.serverEvent then
|
|
TriggerServerEvent(option.serverEvent, option.args)
|
|
elseif option.event then
|
|
TriggerEvent(option.event, option)
|
|
end
|
|
end
|
|
|
|
local nearby = {}
|
|
local screenCoords, screenCount = {}, 0
|
|
local hoverRadius = settings.Prompt.hoverRadius
|
|
local GetAspectRatio = GetAspectRatio
|
|
local math_sqrt = math.sqrt
|
|
|
|
local function CreateInteractions()
|
|
local aspect = GetAspectRatio(false)
|
|
local active, activeCoords, activeAmount, activeIndex
|
|
local bestDist = hoverRadius
|
|
screenCount = 0
|
|
|
|
-- Pass 1: find every on-screen point and pick the in-range one closest to the crosshair.
|
|
for i = 1, #nearby do
|
|
local interaction = nearby[i]
|
|
local coords = interaction.coords or utils.getCoordsFromInteract(interaction)
|
|
local onScreen, sx, sy = GetScreenCoordFromWorldCoord(coords.x, coords.y, coords.z)
|
|
|
|
if onScreen then
|
|
local amount = getVisibleOptions(interaction)
|
|
|
|
if amount > 0 then
|
|
screenCount += 1
|
|
screenCoords[screenCount] = coords
|
|
|
|
if interaction.curDist <= interaction.interactDst then
|
|
local dx, dy = (sx - 0.5) * aspect, sy - 0.5
|
|
local dist = math_sqrt(dx * dx + dy * dy)
|
|
if dist < bestDist then
|
|
bestDist = dist
|
|
active, activeCoords, activeAmount, activeIndex = interaction, coords, amount, screenCount
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if screenCount == 0 then
|
|
CURRENT_OPTION = 0
|
|
return
|
|
end
|
|
|
|
-- Pass 2: dots for everything except the hovered point, which gets the prompt instead.
|
|
for i = 1, screenCount do
|
|
if i ~= activeIndex then
|
|
utils.drawDot(screenCoords[i])
|
|
end
|
|
end
|
|
|
|
if settings.Prompt.crosshair then
|
|
utils.drawCrosshair()
|
|
end
|
|
|
|
if not active then
|
|
CURRENT_OPTION = 0
|
|
return
|
|
end
|
|
|
|
-- Rebuild the option list for the hovered point (pass 1 overwrote it while scanning).
|
|
getVisibleOptions(active)
|
|
|
|
-- Bone and model interactions have no id, so fall back to the entity.
|
|
local key = active.id or active.entity
|
|
if key ~= CURRENT_OPTION then
|
|
CURRENT_OPTION = key
|
|
CURRENT_SELECTION = 1
|
|
end
|
|
|
|
if CURRENT_SELECTION > activeAmount then
|
|
CURRENT_SELECTION = activeAmount
|
|
end
|
|
|
|
if CURRENT_SELECTION > 1 and (IsControlJustPressed(0, 172) or IsControlJustPressed(0, 15)) then
|
|
CURRENT_SELECTION -= 1
|
|
elseif CURRENT_SELECTION < activeAmount and (IsControlJustPressed(0, 173) or IsControlJustPressed(0, 14)) then
|
|
CURRENT_SELECTION += 1
|
|
end
|
|
|
|
utils.drawPrompt(activeCoords, visibleOptions, CURRENT_SELECTION)
|
|
|
|
if IsControlJustPressed(0, 38) then
|
|
triggerOption(active, visibleOptions[CURRENT_SELECTION])
|
|
end
|
|
end
|
|
|
|
local function isDisabled()
|
|
if LocalPlayer.state.interactionsDisabled then
|
|
return true
|
|
end
|
|
|
|
if settings.Disable.onDeath and (IsPedDeadOrDying(cache.ped) or LocalPlayer.state.isDead) then
|
|
return true
|
|
end
|
|
|
|
if settings.Disable.onNuiFocus and IsNuiFocused() then
|
|
return true
|
|
end
|
|
|
|
if settings.Disable.onVehicle and cache.vehicle then
|
|
return true
|
|
end
|
|
|
|
if settings.Disable.onHandCuff and IsPedCuffed(cache.ped) then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Fast thread
|
|
CreateThread(function ()
|
|
lib.requestStreamedTextureDict('interactions_txd')
|
|
while true do
|
|
local wait = 500
|
|
if next(nearby) and not isDisabled() then
|
|
wait = 0
|
|
CreateInteractions()
|
|
end
|
|
Wait(wait)
|
|
end
|
|
end)
|
|
|
|
-- Slow checker thread
|
|
CreateThread(function()
|
|
while true do
|
|
nearby = interactions.getNearbyInteractions()
|
|
Wait(500)
|
|
end
|
|
end)
|