local open = false local phoneObject = nil local lastToggleAt = 0 local function loadModel(model) local hash = GetHashKey(model) RequestModel(hash) local deadline = GetGameTimer() + 5000 while not HasModelLoaded(hash) and GetGameTimer() < deadline do Wait(0) end return HasModelLoaded(hash) and hash or nil end local function loadAnim(dict) RequestAnimDict(dict) local deadline = GetGameTimer() + 5000 while not HasAnimDictLoaded(dict) and GetGameTimer() < deadline do Wait(0) end return HasAnimDictLoaded(dict) end local function removePhoneProp() ClearPedSecondaryTask(PlayerPedId()) if phoneObject and DoesEntityExist(phoneObject) then DeleteEntity(phoneObject) end phoneObject = nil end local function createPhoneProp() removePhoneProp() local ped = PlayerPedId() local model = loadModel('prop_npc_phone_02') if model then phoneObject = CreateObject(model, 0.0, 0.0, 0.0, false, false, false) AttachEntityToEntity(phoneObject, ped, GetPedBoneIndex(ped, 28422), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, true, false, true, 1, true) SetModelAsNoLongerNeeded(model) end if loadAnim('cellphone@') then TaskPlayAnim(ped, 'cellphone@', 'cellphone_text_read_base', 3.0, -3.0, -1, 49, 0.0, false, false, false) end end local function sendState() local player = exports.devx_core:GetPlayerData() DevXTriggerCallback('phone:state', {}, function(phoneResult) DevXTriggerCallback('phone:bankOverview', {}, function(bankResult) SendNUIMessage({ action = 'state', player = player, phone = phoneResult.ok and phoneResult or nil, banking = bankResult.ok and bankResult.overview or nil, error = (not phoneResult.ok and phoneResult.error) or (not bankResult.ok and bankResult.error) or nil }) end) end) end local function setOpen(state) if state and not exports.devx_core:IsPlayerLoaded() then return end open = state == true SetNuiFocus(open, open) SendNUIMessage({ action = open and 'open' or 'close' }) if open then createPhoneProp(); sendState() else removePhoneProp() end end local function togglePhone() local now = GetGameTimer() if now - lastToggleAt < 300 then return end lastToggleAt = now setOpen(not open) end RegisterCommand('phone', togglePhone, false) RegisterCommand('+devxphone', togglePhone, false) RegisterCommand('-devxphone', function() end, false) RegisterKeyMapping('+devxphone', 'DevX Smartphone öffnen/schließen', 'keyboard', 'M') -- No raw INPUT_INTERACTION_MENU fallback here. It used to fire again on key -- release and immediately close the phone, which made M behave like hold-to-use. RegisterNUICallback('close', function(_, cb) setOpen(false); cb({ ok = true }) end) RegisterNUICallback('refresh', function(_, cb) sendState(); cb({ ok = true }) end) RegisterNUICallback('bankTransfer', function(data, cb) DevXTriggerCallback('phone:bankTransfer', data, function(result) if result.ok then sendState() end cb(result) end) end) RegisterNUICallback('contactCreate', function(data, cb) DevXTriggerCallback('phone:contactCreate', data, function(result) if result.ok then sendState() end cb(result) end) end) RegisterNUICallback('contactDelete', function(data, cb) DevXTriggerCallback('phone:contactDelete', data, function(result) if result.ok then sendState() end cb(result) end) end) RegisterNUICallback('companionInvite', function(data, cb) if GetResourceState('devx_stripclub') ~= 'started' then cb({ ok = false, error = 'Die Begleiterfunktion ist nicht aktiv.' }) return end DevXTriggerCallback('phone:companionInvite', data, cb) end) RegisterNetEvent('devx_phone:client:refresh', function() if open then sendState() end end) AddEventHandler('onResourceStop', function(resourceName) if resourceName ~= GetCurrentResourceName() then return end removePhoneProp() SetNuiFocus(false, false) end)