2026-04-14 15:54:53 +02:00

203 lines
6.4 KiB
Lua

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)