ESX = exports['es_extended']:getSharedObject() local spawnedPeds = {} local isNuiOpen = false local currentDealer = nil -- ===================== -- DRAW TEXT 3D -- ===================== function DrawText3D(x, y, z, text) SetTextScale(0.35, 0.35) SetTextFont(4) SetTextProportional(1) SetTextColour(255, 255, 255, 215) SetTextEntry('STRING') SetTextCentre(true) AddTextComponentString(text) SetDrawOrigin(x, y, z, 0) DrawText(0.0, 0.0) local factor = (string.len(text)) / 370 DrawRect(0.0, 0.0 + 0.0125, 0.017 + factor, 0.03, 0, 0, 0, 100) ClearDrawOrigin() end -- ===================== -- NPC SPAWNING -- ===================== Citizen.CreateThread(function() for dealerId, dealer in pairs(Config.Dealers) do local npc = dealer.npc local hash = GetHashKey(npc.model) RequestModel(hash) while not HasModelLoaded(hash) do Citizen.Wait(10) end local ped = CreatePed(4, hash, npc.coords.x, npc.coords.y, npc.coords.z - 1.0, npc.coords.w, false, true) SetEntityHeading(ped, npc.coords.w) FreezeEntityPosition(ped, true) SetEntityInvincible(ped, true) SetBlockingOfNonTemporaryEvents(ped, true) if npc.scenario then TaskStartScenarioInPlace(ped, npc.scenario, 0, true) end spawnedPeds[dealerId] = ped SetModelAsNoLongerNeeded(hash) end end) AddEventHandler('onResourceStop', function(resourceName) if resourceName ~= GetCurrentResourceName() then return end for _, ped in pairs(spawnedPeds) do if DoesEntityExist(ped) then DeleteEntity(ped) end end end) -- ===================== -- NPC INTERACTION -- ===================== Citizen.CreateThread(function() while true do local sleep = 1000 local playerCoords = GetEntityCoords(PlayerPedId()) local closestDealer = nil local closestDist = Config.InteractionDistance + 1 for dealerId, dealer in pairs(Config.Dealers) do local dist = #(playerCoords - vector3(dealer.npc.coords.x, dealer.npc.coords.y, dealer.npc.coords.z)) if dist < closestDist then closestDist = dist closestDealer = dealerId end end if closestDealer and closestDist <= Config.InteractionDistance then sleep = 0 local dealer = Config.Dealers[closestDealer] if not isNuiOpen then local npcCoords = dealer.npc.coords DrawText3D(npcCoords.x, npcCoords.y, npcCoords.z + 1.0, '[E] ' .. dealer.label) if IsControlJustReleased(0, 38) then OpenSellMenu(closestDealer) end end end Citizen.Wait(sleep) end end) -- ===================== -- NUI MANAGEMENT -- ===================== function OpenSellMenu(dealerId) if isNuiOpen then return end currentDealer = dealerId local dealer = Config.Dealers[dealerId] ESX.TriggerServerCallback('mercy-sell:getPlayerItems', function(playerItems) ESX.TriggerServerCallback('mercy-sell:getDealerPrices', function(prices) isNuiOpen = true SetNuiFocus(true, true) SendNUIMessage({ type = 'open', dealerLabel = dealer.label, dealerId = dealerId, dealerImage = dealer.image or '', dealerColor = dealer.color or '', prices = prices, playerItems = playerItems, }) -- Check if there's an active sell or pending collect ESX.TriggerServerCallback('mercy-sell:getSellStatus', function(sellData, collectData) if sellData then SendNUIMessage({ type = 'sellStatus', itemName = sellData.itemName, itemLabel = sellData.itemLabel, amount = sellData.amount, totalMoney = sellData.totalMoney, totalDuration = sellData.totalDuration, elapsed = sellData.elapsed, isComplete = sellData.isComplete, }) elseif collectData then SendNUIMessage({ type = 'sellStatus', itemName = collectData.itemName, itemLabel = collectData.itemLabel, amount = collectData.amount, totalMoney = collectData.totalMoney, totalDuration = 0, elapsed = 0, isComplete = true, }) end end) end, dealerId) end) end function CloseSellMenu() if not isNuiOpen then return end isNuiOpen = false currentDealer = nil SetNuiFocus(false, false) SendNUIMessage({ type = 'close' }) end -- ===================== -- NUI CALLBACKS -- ===================== RegisterNUICallback('close', function(_, cb) CloseSellMenu() cb('ok') end) RegisterNUICallback('sellItems', function(data, cb) if not currentDealer then cb({ success = false }) return end TriggerServerEvent('mercy-sell:sellItems', data.itemName, data.amount, currentDealer) cb('ok') end) RegisterNUICallback('collectMoney', function(_, cb) TriggerServerEvent('mercy-sell:collectMoney') cb('ok') end) -- ===================== -- SERVER EVENT HANDLERS -- ===================== RegisterNetEvent('mercy-sell:sellResult') AddEventHandler('mercy-sell:sellResult', function(success, message) if success then TriggerEvent('hex_4_hud:notify', 'Verkauf', message, 'success', 3000) else TriggerEvent('hex_4_hud:notify', 'Verkauf', message, 'error', 3000) SendNUIMessage({ type = 'sellFailed', message = message }) end end) RegisterNetEvent('mercy-sell:sellStarted') AddEventHandler('mercy-sell:sellStarted', function(data) SendNUIMessage({ type = 'sellStarted', itemName = data.itemName, itemLabel = data.itemLabel, amount = data.amount, totalMoney = data.totalMoney, totalDuration = data.totalDuration, }) -- Refresh inventory and prices if currentDealer then ESX.TriggerServerCallback('mercy-sell:getPlayerItems', function(playerItems) SendNUIMessage({ type = 'updateInventory', playerItems = playerItems }) end) ESX.TriggerServerCallback('mercy-sell:getDealerPrices', function(prices) SendNUIMessage({ type = 'updatePrices', prices = prices }) end, currentDealer) end end) RegisterNetEvent('mercy-sell:sellReady') AddEventHandler('mercy-sell:sellReady', function(data) SendNUIMessage({ type = 'sellStatus', itemName = data.itemName, itemLabel = data.itemLabel, amount = data.amount, totalMoney = data.totalMoney, totalDuration = 0, elapsed = 0, isComplete = true, }) end) RegisterNetEvent('mercy-sell:sellComplete') AddEventHandler('mercy-sell:sellComplete', function(data) TriggerEvent('hex_4_hud:notify', 'Verkauf', '$' .. data.totalMoney .. ' abgeholt!', 'success', 5000) SendNUIMessage({ type = 'sellComplete', totalMoney = data.totalMoney }) -- Refresh inventory if currentDealer then ESX.TriggerServerCallback('mercy-sell:getPlayerItems', function(playerItems) SendNUIMessage({ type = 'updateInventory', playerItems = playerItems }) end) end end) CreateThread(function() for dealerID, data in pairs(Config.Dealers) do -- Prüfen, ob für diesen Dealer ein Blip aktiviert ist if data.blip and data.blip.enabled then local coords = data.npc.coords -- Blip erstellen local blip = AddBlipForCoord(coords.x, coords.y, coords.z) -- Blip optisch anpassen SetBlipSprite(blip, data.blip.sprite or 1) -- Icon SetBlipDisplay(blip, 4) -- Überall anzeigen SetBlipScale(blip, data.blip.scale or 1.0) -- Größe SetBlipColour(blip, data.blip.color or 1) -- Farbe SetBlipAsShortRange(blip, true) -- Nur auf Minimap, wenn nah dran -- Namen setzen BeginTextCommandSetBlipName("STRING") AddTextComponentString(data.label) EndTextCommandSetBlipName(blip) end end end)