2026-04-15 22:15:01 +02:00

264 lines
8.7 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- ═══════════════════════════════════════════════════════════════
-- MercyV Bike Client
-- ═══════════════════════════════════════════════════════════════
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']:SendNotification(msg, type or "info")
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)