-- ===================================================================== -- aether-police — Clientlogik -- Öffnet das Tablet und vermittelt zwischen Oberfläche und Server. -- ===================================================================== local istOffen = false local istGeladen = false local imDienst = false local function SendeAnNui(nachricht) SendNuiMessage(json.encode(nachricht)) end local function ZeigeSpielmeldung(text) if not text or text == '' then return end BeginTextCommandThefeedPost('STRING') AddTextComponentSubstringPlayerName(text) EndTextCommandThefeedPostTicker(false, true) end local function Schliessen() if not istOffen then return end istOffen = false SetNuiFocus(false, false) SendeAnNui({ aktion = 'schliessen' }) pcall(function() exports['aether-hud']:SetzeSichtbar(true) end) end -- --------------------------------------------------------------------- -- Ereignisse vom Server -- --------------------------------------------------------------------- AddEventHandler('aether:client:bereit', function() istGeladen = true end) --- Tablet mit vollem Zustand anzeigen RegisterNetEvent('aether:police:zeige', function(daten) istOffen = true imDienst = true SetNuiFocus(true, true) pcall(function() exports['aether-hud']:SetzeSichtbar(false) end) daten.aktion = 'oeffnen' SendeAnNui(daten) end) --- Anmeldebildschirm (noch nicht im Dienst) RegisterNetEvent('aether:police:anmeldung', function(daten) istOffen = true SetNuiFocus(true, true) pcall(function() exports['aether-hud']:SetzeSichtbar(false) end) daten.aktion = 'anmeldung' SendeAnNui(daten) end) RegisterNetEvent('aether:police:schliessen', function() imDienst = false Schliessen() ZeigeSpielmeldung('Dienst beendet.') end) --- Antwort auf einen Modul-Aufruf RegisterNetEvent('aether:police:antwort', function(ruf, ergebnis, fehler) SendeAnNui({ aktion = 'antwort', ruf = ruf, ergebnis = ergebnis, fehler = fehler }) end) --- Dienstliste (Beamte im Dienst, für Dashboard und Karte) RegisterNetEvent('aether:police:dienstliste', function(liste) if istOffen then SendeAnNui({ aktion = 'dienstliste', beamte = liste }) end end) --- Hinweis / Erinnerung RegisterNetEvent('aether:police:hinweis', function(hinweis) if istOffen then SendeAnNui({ aktion = 'hinweis', hinweis = hinweis }) else -- Auch ausserhalb des Tablets erreichbar, damit Fristen nicht untergehen local praefix = hinweis.prioritaet == 'kritisch' and '~r~' or hinweis.prioritaet == 'hoch' and '~o~' or '~b~' ZeigeSpielmeldung(praefix .. hinweis.titel .. '~s~' .. (hinweis.text and ('\n' .. hinweis.text) or '')) end end) RegisterNetEvent('aether:police:fehler', function(text) if istOffen then SendeAnNui({ aktion = 'fehler', text = text }) else ZeigeSpielmeldung(text) end end) -- --------------------------------------------------------------------- -- Fahrzeug-Telemetrie -- -- Meldet Tatsachen über das gefahrene Dienstfahrzeug: Position, -- Tankstand, Zustand. Der Server verwirft die Meldung, wenn das -- Kennzeichen nicht zum Fuhrpark gehört. -- --------------------------------------------------------------------- CreateThread(function() while true do Wait(30000) -- alle 30 Sekunden genügt if imDienst then local ped = PlayerPedId() if IsPedInAnyVehicle(ped, false) then local fahrzeug = GetVehiclePedIsIn(ped, false) -- Nur melden, wenn man selbst am Steuer sitzt if GetPedInVehicleSeat(fahrzeug, -1) == ped then local position = GetEntityCoords(fahrzeug) TriggerServerEvent('aether:police:fahrzeugMelden', { kennzeichen = GetVehicleNumberPlateText(fahrzeug), tank = GetVehicleFuelLevel(fahrzeug), zustand = GetVehicleBodyHealth(fahrzeug), x = position.x, y = position.y, z = position.z, }) end end end end end) -- --------------------------------------------------------------------- -- Gewahrsam -- Der Client führt nur aus, was ein Beamter entschieden hat. -- --------------------------------------------------------------------- --- In eine Zelle verbracht werden RegisterNetEvent('aether:police:gewahrsamVerbringen', function(daten) Schliessen() DoScreenFadeOut(600) Wait(800) local ped = PlayerPedId() SetEntityCoordsNoOffset(ped, daten.x + 0.0, daten.y + 0.0, daten.z + 0.0, false, false, false) SetEntityHeading(ped, daten.w or 0.0) -- Warten, bis die Umgebung geladen ist local versuche = 0 while not HasCollisionLoadedAroundEntity(ped) and versuche < 60 do Wait(50) versuche = versuche + 1 end DoScreenFadeIn(800) if daten.rest then ZeigeSpielmeldung(('~y~Gewahrsam~s~ · %s\n%s\nRestzeit laut Akte: %d Minuten') :format(daten.zelle or '', daten.grund or '', math.max(0, daten.rest))) else ZeigeSpielmeldung(('~y~Du wurdest in Gewahrsam genommen~s~ · %s'):format(daten.zelle or '')) end end) --- Entlassung durch einen Beamten RegisterNetEvent('aether:police:gewahrsamEntlassen', function(daten) DoScreenFadeOut(600) Wait(800) local ped = PlayerPedId() SetEntityCoordsNoOffset(ped, daten.x + 0.0, daten.y + 0.0, daten.z + 0.0, false, false, false) SetEntityHeading(ped, daten.w or 0.0) local versuche = 0 while not HasCollisionLoadedAroundEntity(ped) and versuche < 60 do Wait(50) versuche = versuche + 1 end DoScreenFadeIn(800) ZeigeSpielmeldung('~g~Du wurdest aus dem Gewahrsam entlassen.~s~') end) -- --------------------------------------------------------------------- -- Terminals an den Wachen -- --------------------------------------------------------------------- CreateThread(function() for _, terminal in ipairs(Polizei.Config.Terminals) do local blip = AddBlipForCoord(terminal.pos.x, terminal.pos.y, terminal.pos.z) SetBlipSprite(blip, 60) SetBlipColour(blip, 3) SetBlipScale(blip, 0.8) SetBlipAsShortRange(blip, true) BeginTextCommandSetBlipName('STRING') AddTextComponentString('Polizeiwache ' .. terminal.name) EndTextCommandSetBlipName(blip) end while true do local pause = 800 if istGeladen and not istOffen then local position = GetEntityCoords(PlayerPedId()) for _, terminal in ipairs(Polizei.Config.Terminals) do if #(position - terminal.pos) <= Polizei.Config.TerminalWeite then pause = 0 SetTextFont(4) SetTextScale(0.34, 0.34) SetTextColour(255, 255, 255, 215) SetTextCentre(true) SetTextOutline() SetTextEntry('STRING') AddTextComponentString('[E] Terminal ' .. terminal.name) SetDrawOrigin(terminal.pos.x, terminal.pos.y, terminal.pos.z + 0.9, 0) DrawText(0.0, 0.0) ClearDrawOrigin() if IsControlJustReleased(0, 38) then TriggerServerEvent('aether:police:oeffnen') end break end end end Wait(pause) end end) --- Tablet jederzeit öffnen, sobald man im Dienst ist RegisterCommand('mdt', function() if istGeladen and not istOffen then TriggerServerEvent('aether:police:oeffnen') end end, false) RegisterKeyMapping('mdt', 'Polizei-Tablet öffnen', 'keyboard', 'F5') -- --------------------------------------------------------------------- -- Panikknopf (10-13) -- --------------------------------------------------------------------- local panikGesperrtBis = 0 RegisterCommand('panik', function() if not imDienst then return end -- Kurze Sperre, damit der Alarm nicht versehentlich mehrfach ausgeht if GetGameTimer() < panikGesperrtBis then return end panikGesperrtBis = GetGameTimer() + 10000 TriggerServerEvent('aether:police:panik') ZeigeSpielmeldung('~r~10-13 ausgelöst~s~ — Verstärkung wird alarmiert.') end, false) RegisterKeyMapping('panik', 'Panikknopf (10-13)', 'keyboard', 'F7') -- --------------------------------------------------------------------- -- Einsatzmarkierungen auf der Karte -- --------------------------------------------------------------------- local einsatzBlips = {} --- Entfernt alle Einsatzmarkierungen local function LeereEinsatzBlips() for id, blip in pairs(einsatzBlips) do RemoveBlip(blip) einsatzBlips[id] = nil end end --- Setzt Markierungen für die übergebenen Einsätze local function SetzeEinsatzBlips(einsaetze) LeereEinsatzBlips() for _, einsatz in ipairs(einsaetze or {}) do if einsatz.pos_x then local blip = AddBlipForCoord(einsatz.pos_x + 0.0, einsatz.pos_y + 0.0, einsatz.pos_z + 0.0) SetBlipSprite(blip, 161) -- Offene Einsätze fallen durch Blinken auf SetBlipColour(blip, einsatz.status == 'offen' and 1 or 5) SetBlipScale(blip, einsatz.prioritaet == 1 and 1.0 or 0.8) SetBlipFlashes(blip, einsatz.status == 'offen' and einsatz.prioritaet <= 2) BeginTextCommandSetBlipName('STRING') AddTextComponentString(einsatz.nummer .. ' · ' .. einsatz.titel) EndTextCommandSetBlipName(blip) einsatzBlips[einsatz.id] = blip end end end --- Es hat sich etwas an den Einsätzen geändert RegisterNetEvent('aether:police:einsatzAenderung', function() if istOffen then SendeAnNui({ aktion = 'einsatzAenderung' }) end end) --- Panikalarm einer anderen Einheit RegisterNetEvent('aether:police:panikAlarm', function(daten) PlaySoundFrontend(-1, 'Lose_1st', 'GTAO_FM_Events_Soundset', true) ZeigeSpielmeldung(('~r~10-13~s~ — %s (%s) benötigt Verstärkung!') :format(daten.name, daten.dienstnummer)) if daten.position then SetNewWaypoint(daten.position.x, daten.position.y) end if istOffen then SendeAnNui({ aktion = 'panikAlarm', daten = daten }) end end) --- Die Leitstelle bittet um Übernahme eines Einsatzes RegisterNetEvent('aether:police:einsatzZuweisung', function(daten) PlaySoundFrontend(-1, 'Event_Start_Text', 'GTAO_FM_Events_Soundset', true) if daten.position then SetNewWaypoint(daten.position.x, daten.position.y) end if istOffen then SendeAnNui({ aktion = 'einsatzZuweisung', daten = daten }) else ZeigeSpielmeldung(('~b~Zuweisung von %s~s~\n%s — %s') :format(daten.von, daten.nummer, daten.titel)) end end) -- --------------------------------------------------------------------- -- NUI-Callbacks -- --------------------------------------------------------------------- RegisterNUICallback('schliessen', function(_, rueckgabe) Schliessen() rueckgabe({ ok = true }) end) --- Zentraler Modul-Aufruf: die Oberfläche fragt, der Server antwortet RegisterNUICallback('aufruf', function(daten, rueckgabe) TriggerServerEvent('aether:police:aufruf', tostring(daten.modul or ''), tostring(daten.aktion or ''), daten.daten or {}, daten.ruf) rueckgabe({ ok = true }) end) RegisterNUICallback('dienstAntreten', function(_, rueckgabe) TriggerServerEvent('aether:police:dienstAntreten') rueckgabe({ ok = true }) end) RegisterNUICallback('dienstBeenden', function(_, rueckgabe) TriggerServerEvent('aether:police:dienstBeenden') rueckgabe({ ok = true }) end) RegisterNUICallback('status', function(daten, rueckgabe) TriggerServerEvent('aether:police:status', tostring(daten.status or '')) rueckgabe({ ok = true }) end) RegisterNUICallback('leitstelle', function(daten, rueckgabe) TriggerServerEvent('aether:police:leitstelle', tonumber(daten.quelle)) rueckgabe({ ok = true }) end) --- Einsatzmarkierungen auf der Karte auffrischen RegisterNUICallback('einsatzBlips', function(daten, rueckgabe) SetzeEinsatzBlips(daten.einsaetze or {}) rueckgabe({ ok = true }) end) --- Wegpunkt auf der Karte setzen (z. B. zu einem Einsatzort) RegisterNUICallback('wegpunkt', function(daten, rueckgabe) if daten.x and daten.y then SetNewWaypoint(daten.x + 0.0, daten.y + 0.0) ZeigeSpielmeldung('Wegpunkt gesetzt.') end rueckgabe({ ok = true }) end) -- --------------------------------------------------------------------- -- Aufräumen -- --------------------------------------------------------------------- AddEventHandler('onResourceStop', function(ressource) if ressource ~= GetCurrentResourceName() then return end SetNuiFocus(false, false) LeereEinsatzBlips() end)