-- ═══════════════════════════════════════════════════════════════ -- MercyV Bike – Client -- ═══════════════════════════════════════════════════════════════ -- NUI beim Start sicher versteckt halten AddEventHandler('onClientResourceStart', function(res) if res ~= GetCurrentResourceName() then return end SetNuiFocus(false, false) SendNUIMessage({ action = "CLOSE" }) end) local NPCEntity = nil local NPCData = nil local IsAdmin = false local AlreadyClaimed = false local PanelOpen = false -- ── Hilfsfunktionen ──────────────────────────────────────────── local function normPlate(p) return string.lower(string.gsub(p or '', '%s+', '')) end local function ShowHint(text) if Config.TextUIHandler == 'custom' then exports['hex_4_hud']:ShowHelpNotify(text, "E") end end local function HideHint() if Config.TextUIHandler == 'custom' then exports['hex_4_hud']:HideHelpNotify() end end local function Notify(msg, type) exports['hex_4_hud']:Notify("Fahrrad", msg, type or "info", 3000) end -- ── NPC spawnen ──────────────────────────────────────────────── local function SpawnNPC(data) if not data or data.x == 0 then return end -- Alten NPC löschen if NPCEntity and DoesEntityExist(NPCEntity) then DeleteEntity(NPCEntity) NPCEntity = nil end local model = GetHashKey(data.model or 'a_m_m_beach_01') RequestModel(model) local t = GetGameTimer() + 5000 while not HasModelLoaded(model) do if GetGameTimer() > t then return end Citizen.Wait(100) end NPCEntity = CreatePed(4, model, data.x, data.y, data.z - 1.0, data.heading or 0.0, false, true) Citizen.Wait(200) PlaceObjectOnGroundProperly(NPCEntity) SetEntityInvincible(NPCEntity, true) SetBlockingOfNonTemporaryEvents(NPCEntity, true) FreezeEntityPosition(NPCEntity, true) SetPedFleeAttributes(NPCEntity, 0, false) SetModelAsNoLongerNeeded(model) end -- ── NPC-Sync vom Server ──────────────────────────────────────── RegisterNetEvent('mercyv-bike:syncNPC', function(data) NPCData = data SpawnNPC(data) end) RegisterNetEvent('mercyv-bike:setAdminStatus', function(status) IsAdmin = status SendNUIMessage({ action = "SET_ADMIN", isAdmin = IsAdmin }) end) RegisterNetEvent('mercyv-bike:claimStatus', function(claimed, model) AlreadyClaimed = claimed SendNUIMessage({ action = "SET_CLAIM_STATUS", claimed = claimed, model = model }) end) -- ── Benachrichtigung ────────────────────────────────────────── RegisterNetEvent('mercyv-bike:notify', function(msg, type) Notify(msg, type) end) -- ── Panel öffnen/schließen ───────────────────────────────────── local function OpenPanel() if PanelOpen then return end PanelOpen = true -- Claim-Status vom Server holen TriggerServerEvent('mercyv-bike:checkClaim') SetNuiFocus(true, true) exports['hex_4_hud']:HideHud(true) SendNUIMessage({ action = "OPEN", bikes = Config.Bikes, isAdmin = IsAdmin, }) end local function ClosePanel() PanelOpen = false SetNuiFocus(false, false) exports['hex_4_hud']:HideHud(false) SendNUIMessage({ action = "CLOSE" }) end -- ── NUI Callbacks ────────────────────────────────────────────── RegisterNUICallback('close', function(data, cb) ClosePanel() cb({}) end) RegisterNUICallback('claimBike', function(data, cb) if not data.model then cb({}); return end TriggerServerEvent('mercyv-bike:claim', data.model) ClosePanel() cb({}) end) -- Admin: NPC-Position speichern RegisterNUICallback('saveNPC', function(data, cb) TriggerServerEvent('mercyv-bike:saveNPC', data) cb({}) end) -- Admin: Aktuelle Position erfassen RegisterNUICallback('capturePos', function(data, cb) ClosePanel() Citizen.Wait(300) local ped = PlayerPedId() local coords = GetEntityCoords(ped) local heading = GetEntityHeading(ped) Notify("Geh zur NPC-Position und drücke E.", "info") exports['hex_4_hud']:ShowHelpNotify("Position erfassen", "E") Citizen.CreateThread(function() while true do Citizen.Wait(0) if IsControlJustPressed(0, 38) then -- E exports['hex_4_hud']:HideHelpNotify() local c = GetEntityCoords(PlayerPedId()) local h = GetEntityHeading(PlayerPedId()) -- Panel wieder öffnen mit erfassten Koordinaten SetNuiFocus(true, true) exports['hex_4_hud']:HideHud(true) PanelOpen = true SendNUIMessage({ action = "OPEN_ADMIN", isAdmin = IsAdmin, captured = { x = c.x, y = c.y, z = c.z, heading = h }, }) break elseif IsControlJustPressed(0, 322) then -- ESC exports['hex_4_hud']:HideHelpNotify() OpenPanel() break end end end) cb({}) end) -- ── Interaktions-Loop ────────────────────────────────────────── Citizen.CreateThread(function() while not NetworkIsPlayerActive(PlayerId()) do Citizen.Wait(500) end Citizen.Wait(2000) TriggerServerEvent('mercyv-bike:clientReady') end) Citizen.CreateThread(function() local hintShown = false while true do local sleep = 500 if NPCData and NPCData.x ~= 0 then local ped = PlayerPedId() local pos = GetEntityCoords(ped) local dist = #(pos - vector3(NPCData.x, NPCData.y, NPCData.z)) if dist < 8.0 then sleep = 0 if dist < Config.NPC.radius and not PanelOpen then if not hintShown then hintShown = true ShowHint("Fahrrad abholen") end if IsControlJustPressed(0, 38) then OpenPanel() end else if hintShown then HideHint() hintShown = false end end else if hintShown then HideHint() hintShown = false end end end Citizen.Wait(sleep) end end) -- ── Fahrrad spawnen ──────────────────────────────────────────── RegisterNetEvent('mercyv-bike:doSpawn', function(bikeModel) local model = GetHashKey(bikeModel) RequestModel(model) local t = GetGameTimer() + 5000 while not HasModelLoaded(model) do if GetGameTimer() > t then Notify("Fahrrad konnte nicht gespawnt werden.", "error") return end Citizen.Wait(100) end local ped = PlayerPedId() local coords = GetEntityCoords(ped) local heading = GetEntityHeading(ped) local rad = math.rad(heading) local spawnX = coords.x + math.sin(-rad) * Config.SpawnOffset local spawnY = coords.y + math.cos(-rad) * Config.SpawnOffset local bike = CreateVehicle(model, spawnX, spawnY, coords.z, heading, true, false) local tw = GetGameTimer() + 3000 while not DoesEntityExist(bike) do if GetGameTimer() > tw then break end Citizen.Wait(100) end if DoesEntityExist(bike) then SetEntityAsMissionEntity(bike, true, true) SetVehicleEngineOn(bike, true, true, false) SetModelAsNoLongerNeeded(model) -- Schlüssel geben local ks = Config.VehicleKeySystem if ks == 'jaksam' then TriggerServerEvent('vehicles_keys:selfGiveVehicleKeys', GetVehicleNumberPlateText(bike)) end end end) -- ── Admin-Befehl ─────────────────────────────────────────────── RegisterCommand('bikeadmin', function() if not IsAdmin then Notify(Config.Notify.NO_ACCESS, "error") return end OpenPanel() Citizen.Wait(200) SendNUIMessage({ action = "OPEN_ADMIN", isAdmin = true }) end, false)