164 lines
5.1 KiB
Lua
164 lines
5.1 KiB
Lua
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|||
|
|
-- Interaction system – proximity detection, key prompt
|
|||
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
Interaction = {}
|
|||
|
|
|
|||
|
|
local _nearbyPC = nil -- current nearby PC config or nil
|
|||
|
|
local _inSession = false
|
|||
|
|
local _camCfg = nil -- aktive PC-Kamera-Config (coords/heading) oder nil (mobil)
|
|||
|
|
local _pcs = {} -- keyed by id, populated from server
|
|||
|
|
|
|||
|
|
-- Sync-Liste vom Server empfangen
|
|||
|
|
RegisterNetEvent('pc-live:client:syncDevices', function(list)
|
|||
|
|
_pcs = {}
|
|||
|
|
for _, pc in ipairs(list) do
|
|||
|
|
pc.coords = vector3(pc.coords.x, pc.coords.y, pc.coords.z)
|
|||
|
|
_pcs[pc.id] = pc
|
|||
|
|
end
|
|||
|
|
end)
|
|||
|
|
|
|||
|
|
-- Device-Liste beim Start anfordern
|
|||
|
|
AddEventHandler('onClientResourceStart', function(res)
|
|||
|
|
if res == GetCurrentResourceName() then
|
|||
|
|
TriggerServerEvent('pc-live:server:requestDevices')
|
|||
|
|
end
|
|||
|
|
end)
|
|||
|
|
|
|||
|
|
local function DrawText3D(x, y, z, text)
|
|||
|
|
local onScreen, sx, sy = World3dToScreen2d(x, y, z)
|
|||
|
|
if not onScreen then return end
|
|||
|
|
SetTextScale(0.0, 0.45)
|
|||
|
|
SetTextFont(4)
|
|||
|
|
SetTextProportional(1)
|
|||
|
|
SetTextColour(255, 255, 255, 215)
|
|||
|
|
SetTextEntry("STRING")
|
|||
|
|
SetTextCentre(true)
|
|||
|
|
AddTextComponentString(text)
|
|||
|
|
DrawText(sx, sy)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
local function DrawRect2D(x, y, width, height, r, g, b, a)
|
|||
|
|
DrawRect(x, y, width, height, r, g, b, a)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- Main proximity loop.
|
|||
|
|
function Interaction.StartLoop()
|
|||
|
|
CreateThread(function()
|
|||
|
|
while true do
|
|||
|
|
local sleep = 1000
|
|||
|
|
if not _inSession then
|
|||
|
|
local ped = PlayerPedId()
|
|||
|
|
local coords = GetEntityCoords(ped)
|
|||
|
|
|
|||
|
|
local found = nil
|
|||
|
|
for _, pc in pairs(_pcs) do
|
|||
|
|
local dist = #(coords - pc.coords)
|
|||
|
|
if dist < Config.InteractRange + 8.0 then
|
|||
|
|
sleep = 0
|
|||
|
|
if dist < Config.InteractRange then
|
|||
|
|
found = pc
|
|||
|
|
break
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
_nearbyPC = found
|
|||
|
|
else
|
|||
|
|
_nearbyPC = nil
|
|||
|
|
end
|
|||
|
|
Wait(sleep)
|
|||
|
|
end
|
|||
|
|
end)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- Draw prompt and handle key.
|
|||
|
|
function Interaction.StartPromptLoop()
|
|||
|
|
CreateThread(function()
|
|||
|
|
while true do
|
|||
|
|
Wait(0)
|
|||
|
|
if _nearbyPC and not _inSession then
|
|||
|
|
-- Draw 3D label
|
|||
|
|
DrawText3D(
|
|||
|
|
_nearbyPC.coords.x,
|
|||
|
|
_nearbyPC.coords.y,
|
|||
|
|
_nearbyPC.coords.z + 1.1,
|
|||
|
|
("[E] %s"):format(_nearbyPC.label)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if IsControlJustReleased(0, Config.InteractKey) then
|
|||
|
|
Interaction.Open(_nearbyPC)
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
end)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- Open PC – request session from server.
|
|||
|
|
---@param pc table
|
|||
|
|
function Interaction.Open(pc)
|
|||
|
|
if _inSession then return end
|
|||
|
|
TriggerServerEvent('pc-live:server:openPC', pc.id)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- Called when the session starts (from server confirmation).
|
|||
|
|
---@param data table
|
|||
|
|
function Interaction.OnSessionStart(data)
|
|||
|
|
_inSession = true
|
|||
|
|
_nearbyPC = nil
|
|||
|
|
|
|||
|
|
-- PC-Config finden; mobile Terminals (Streifenwagen) haben keinen Standort/Kamera.
|
|||
|
|
local pcCfg
|
|||
|
|
for _, pc in ipairs(Config.PCs) do
|
|||
|
|
if pc.id == data.pcId then pcCfg = pc; break end
|
|||
|
|
end
|
|||
|
|
local isMobile = pcCfg and pcCfg.mobile
|
|||
|
|
|
|||
|
|
if Config.FreezeOnOpen and not isMobile then
|
|||
|
|
FreezeEntityPosition(PlayerPedId(), true)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
if pcCfg and not isMobile then
|
|||
|
|
_camCfg = { coords = pcCfg.coords, heading = pcCfg.heading }
|
|||
|
|
Camera.FocusPC(pcCfg.coords, pcCfg.heading)
|
|||
|
|
else
|
|||
|
|
_camCfg = nil
|
|||
|
|
end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- Interaktionsmodus: PC-Kamera lösen (Gameplay-Kamera zurück -> umsehen möglich)
|
|||
|
|
-- bzw. wieder auf den PC richten, wenn man zurückkehrt.
|
|||
|
|
function Interaction.EnterInteract()
|
|||
|
|
if _camCfg then Camera.Restore() end
|
|||
|
|
end
|
|||
|
|
function Interaction.ExitInteract()
|
|||
|
|
if _camCfg then Camera.FocusPC(_camCfg.coords, _camCfg.heading) end
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
--- Öffnet ein mobiles Terminal (z.B. Streifenwagen-PC) – kein Standort/Distanz nötig.
|
|||
|
|
---@param pcId string|nil
|
|||
|
|
function Interaction.OpenMobile(pcId)
|
|||
|
|
if _inSession then return end
|
|||
|
|
TriggerServerEvent('pc-live:server:openPC', pcId or 'police_mobile')
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
-- Wird vom M-Menü (core-interaction) im Einsatzfahrzeug getriggert.
|
|||
|
|
AddEventHandler('pc-live:openMobileTerminal', function(pcId)
|
|||
|
|
Interaction.OpenMobile(pcId)
|
|||
|
|
end)
|
|||
|
|
|
|||
|
|
--- Called when closing the session.
|
|||
|
|
function Interaction.OnSessionClose()
|
|||
|
|
_inSession = false
|
|||
|
|
|
|||
|
|
if Config.FreezeOnOpen then
|
|||
|
|
FreezeEntityPosition(PlayerPedId(), false)
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
Camera.Restore()
|
|||
|
|
_camCfg = nil
|
|||
|
|
TriggerServerEvent('pc-live:server:closePC')
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
Interaction.StartLoop()
|
|||
|
|
Interaction.StartPromptLoop()
|