ESX = exports['es_extended']:getSharedObject() local spawnedPeds = {} -- location_id -> ped handle local activeLocations = {} -- location_id -> location config local isNuiOpen = false -- ===================== -- NPC SPAWNING -- ===================== function SpawnNPC(locationId, locationConfig) if spawnedPeds[locationId] then return end local coords = locationConfig.coords local model = locationConfig.npcModel or Config.DefaultNpcModel local scenario = locationConfig.npcScenario or Config.DefaultNpcScenario local hash = GetHashKey(model) RequestModel(hash) while not HasModelLoaded(hash) do Citizen.Wait(10) end local ped = CreatePed(4, hash, coords.x, coords.y, coords.z - 1.0, coords.w, false, true) SetEntityHeading(ped, coords.w) FreezeEntityPosition(ped, true) SetEntityInvincible(ped, true) SetBlockingOfNonTemporaryEvents(ped, true) TaskStartScenarioInPlace(ped, scenario, 0, true) SetModelAsNoLongerNeeded(hash) spawnedPeds[locationId] = ped end function DespawnNPC(locationId) if spawnedPeds[locationId] then if DoesEntityExist(spawnedPeds[locationId]) then DeleteEntity(spawnedPeds[locationId]) end spawnedPeds[locationId] = nil end end function SyncLocations(locations) -- Despawn NPCs die nicht mehr aktiv sind for id, _ in pairs(spawnedPeds) do if not locations[id] then DespawnNPC(id) end end -- Spawn neue NPCs for id, config in pairs(locations) do if not spawnedPeds[id] then SpawnNPC(id, config) end end activeLocations = locations end -- ===================== -- LOCATION SYNC -- ===================== RegisterNetEvent('mercyv-blackmarket:syncLocations') AddEventHandler('mercyv-blackmarket:syncLocations', function(locations) SyncLocations(locations) end) -- Cleanup on resource stop AddEventHandler('onResourceStop', function(resourceName) if resourceName ~= GetCurrentResourceName() then return end for id, ped in pairs(spawnedPeds) do if DoesEntityExist(ped) then DeleteEntity(ped) end end end) -- Spawn NPCs und sync bei Start Citizen.CreateThread(function() Citizen.Wait(2000) TriggerServerEvent('mercyv-blackmarket:requestSync') end) -- ===================== -- NPC INTERACTION -- ===================== Citizen.CreateThread(function() local nearLocation = false local nearbyLocationId = nil local COARSE_DIST = Config.CoarseDistance or 50.0 while true do local sleep = 1000 local playerCoords = GetEntityCoords(PlayerPedId()) local foundNear = false if not isNuiOpen then -- Wenn bereits ein nahegelegener Standort bekannt ist if nearbyLocationId then local locConfig = activeLocations[nearbyLocationId] if locConfig then local dist = #(playerCoords - vector3(locConfig.coords.x, locConfig.coords.y, locConfig.coords.z)) if dist <= Config.InteractionDistance then sleep = 0 foundNear = true exports[Config.HelpNotify.export][Config.HelpNotify.showFunc](locConfig.label or 'Schwarzmarkt', 'E') if IsControlJustReleased(0, 38) then OpenBlackmarket(nearbyLocationId) end elseif dist > COARSE_DIST then nearbyLocationId = nil end else nearbyLocationId = nil end end -- Suche nach nahegelegenen Standorten if not nearbyLocationId and not foundNear then for locId, locConfig in pairs(activeLocations) do local dist = #(playerCoords - vector3(locConfig.coords.x, locConfig.coords.y, locConfig.coords.z)) if dist <= COARSE_DIST then nearbyLocationId = locId if dist <= Config.InteractionDistance then sleep = 0 foundNear = true exports[Config.HelpNotify.export][Config.HelpNotify.showFunc](locConfig.label or 'Schwarzmarkt', 'E') if IsControlJustReleased(0, 38) then OpenBlackmarket(locId) end end break end end end if not foundNear and nearLocation then exports[Config.HelpNotify.export][Config.HelpNotify.hideFunc]() end nearLocation = foundNear end Citizen.Wait(sleep) end end) -- ===================== -- NUI MANAGEMENT -- ===================== function OpenBlackmarket(locationId) if isNuiOpen then return end ESX.TriggerServerCallback('mercyv-blackmarket:getMarketData', function(data) isNuiOpen = true SetNuiFocus(true, true) SendNUIMessage({ type = 'open', locationId = locationId, categories = data.categories, theme = Config.Theme, paymentMethod = Config.PaymentMethod, }) end) end function CloseMenu() if not isNuiOpen then return end isNuiOpen = false SetNuiFocus(false, false) SendNUIMessage({ type = 'close' }) end -- ===================== -- NUI CALLBACKS -- ===================== RegisterNUICallback('close', function(_, cb) CloseMenu() cb('ok') end) RegisterNUICallback('buyItem', function(data, cb) TriggerServerEvent('mercyv-blackmarket:buyItem', data.itemName, data.quantity) cb('ok') end) -- ===================== -- SERVER EVENT HANDLERS -- ===================== RegisterNetEvent('mercyv-blackmarket:notify') AddEventHandler('mercyv-blackmarket:notify', function(message, nType) TriggerEvent(Config.Notification.event, Config.Notification.title, message, nType or 'info', Config.Notification.duration) end) RegisterNetEvent('mercyv-blackmarket:buyResult') AddEventHandler('mercyv-blackmarket:buyResult', function(success, updatedLimits) if success then SendNUIMessage({ type = 'updateLimits', limits = updatedLimits }) end end) RegisterNetEvent('mercyv-blackmarket:forceClose') AddEventHandler('mercyv-blackmarket:forceClose', function() CloseMenu() end)