- aether-medical: Stationen/Belegung, Intensivstation, Blutbank, Fuhrpark, Nachrichten/Aushänge, Berichte und Textbausteine (server + NUI) - aether-admin: vollständiges Server-Admin-Panel als echtes FiveM-Resource, an aether-core gekoppelt (Rechte über aether_users.admin_group), mit WebRTC-Live-Kameras (Bildschirmfreigabe) und separatem Media-Server (SFU) - Prototypen admin/ vervollständigt - SQL-Schema aether_admin.sql - Docker-Compose mit MariaDB und idempotenter Auto-Migration (schema_migrations) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
496 lines
20 KiB
Lua
496 lines
20 KiB
Lua
-- =====================================================================
|
|
-- aether-police / Modul: Einsatzverwaltung & Dispatch
|
|
--
|
|
-- Einsätze entstehen entweder durch einen Beamten oder durch einen
|
|
-- eingehenden Notruf. Sie werden NIE automatisch zugewiesen,
|
|
-- bearbeitet oder geschlossen — das entscheidet die Leitstelle
|
|
-- beziehungsweise der jeweilige Beamte.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereModul({
|
|
id = 'einsatz', name = 'Einsätze', icon = '🚨',
|
|
reihenfolge = 3, recht = 'einsatz.erstellen',
|
|
beschreibung = 'Dispatch und Einsatzverwaltung',
|
|
})
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Hilfsfunktionen
|
|
-- ---------------------------------------------------------------------
|
|
|
|
--- Prüft, ob eine Einsatzkategorie bekannt ist
|
|
local function KategorieGueltig(kategorie)
|
|
for _, eintrag in ipairs(Polizei.Config.Einsatzkategorien) do
|
|
if eintrag.id == kategorie then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- Lädt einen Einsatz samt beteiligten Beamten und Notizen
|
|
--- @param einsatzId number
|
|
--- @return table|nil
|
|
local function LadeEinsatz(einsatzId)
|
|
local einsatz = MySQL.query.await([[
|
|
SELECT e.*, c.firstname AS ersteller_vorname, c.lastname AS ersteller_nachname
|
|
FROM police_einsatz e
|
|
LEFT JOIN aether_characters c ON c.id = e.erstellt_von
|
|
WHERE e.id = ?
|
|
]], { einsatzId })
|
|
|
|
if not einsatz or not einsatz[1] then return nil end
|
|
local eintrag = einsatz[1]
|
|
|
|
eintrag.beamte = MySQL.query.await([[
|
|
SELECT b.character, b.beigetreten, b.verlassen,
|
|
c.firstname, c.lastname, p.dienstnummer
|
|
FROM police_einsatz_beamter b
|
|
LEFT JOIN aether_characters c ON c.id = b.character
|
|
LEFT JOIN police_personal p ON p.`character` = b.character
|
|
WHERE b.einsatz = ?
|
|
ORDER BY b.id ASC
|
|
]], { einsatzId }) or {}
|
|
|
|
eintrag.notizen = MySQL.query.await([[
|
|
SELECT n.id, n.text, n.am, c.firstname, c.lastname, p.dienstnummer
|
|
FROM police_einsatz_notiz n
|
|
LEFT JOIN aether_characters c ON c.id = n.von
|
|
LEFT JOIN police_personal p ON p.`character` = n.von
|
|
WHERE n.einsatz = ?
|
|
ORDER BY n.id ASC
|
|
]], { einsatzId }) or {}
|
|
|
|
return eintrag
|
|
end
|
|
|
|
--- Informiert alle Beamten im Dienst über eine Änderung an den Einsätzen
|
|
local function VerteileEinsaetze()
|
|
for quelle in pairs(Dienst.AlleImDienst()) do
|
|
TriggerClientEvent('aether:police:einsatzAenderung', quelle)
|
|
end
|
|
end
|
|
|
|
--- Zählt, wie viele Beamte einem Einsatz aktuell zugeordnet sind
|
|
local function AnzahlBeamte(einsatzId)
|
|
return MySQL.scalar.await(
|
|
'SELECT COUNT(*) FROM police_einsatz_beamter WHERE einsatz = ? AND verlassen IS NULL',
|
|
{ einsatzId }) or 0
|
|
end
|
|
|
|
-- =====================================================================
|
|
-- ÜBERSICHT
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('einsatz', 'liste', nil, function(quelle, beamter, daten)
|
|
local nurOffene = daten.nurOffene ~= false
|
|
|
|
local einsaetze = MySQL.query.await(([[
|
|
SELECT e.id, e.nummer, e.titel, e.kategorie, e.prioritaet, e.ort,
|
|
e.pos_x, e.pos_y, e.pos_z, e.status, e.quelle, e.erstellt_am,
|
|
e.beschreibung,
|
|
(SELECT COUNT(*) FROM police_einsatz_beamter b
|
|
WHERE b.einsatz = e.id AND b.verlassen IS NULL) AS einheiten,
|
|
(SELECT COUNT(*) FROM police_einsatz_beamter b2
|
|
WHERE b2.einsatz = e.id AND b2.character = ? AND b2.verlassen IS NULL) AS ichDabei
|
|
FROM police_einsatz e
|
|
%s
|
|
ORDER BY
|
|
CASE WHEN e.status = 'offen' THEN 0 ELSE 1 END,
|
|
e.prioritaet ASC, e.id DESC
|
|
LIMIT 60
|
|
]]):format(nurOffene and "WHERE e.status <> 'abgeschlossen'" or ''),
|
|
{ beamter.charakterId }) or {}
|
|
|
|
return {
|
|
einsaetze = einsaetze,
|
|
kategorien = Polizei.Config.Einsatzkategorien,
|
|
istLeitstelle = Dienst.Leitstelle() == quelle,
|
|
beamteImDienst = Dienst.BaueListe(),
|
|
rechte = {
|
|
zuweisen = Rechte.Hat(beamter, 'einsatz.zuweisen'),
|
|
schliessen = Rechte.Hat(beamter, 'einsatz.schliessen'),
|
|
bericht = Rechte.Hat(beamter, 'bericht.erstellen'),
|
|
},
|
|
}
|
|
end)
|
|
|
|
Kern.RegistriereAktion('einsatz', 'oeffnen', nil, function(quelle, beamter, daten)
|
|
local einsatzId = tonumber(daten.id)
|
|
if not einsatzId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local einsatz = LadeEinsatz(einsatzId)
|
|
if not einsatz then return nil, 'Einsatz nicht gefunden.' end
|
|
|
|
-- Bin ich selbst beteiligt?
|
|
einsatz.ichDabei = false
|
|
for _, eintrag in ipairs(einsatz.beamte) do
|
|
if eintrag.character == beamter.charakterId and not eintrag.verlassen then
|
|
einsatz.ichDabei = true
|
|
break
|
|
end
|
|
end
|
|
|
|
return {
|
|
einsatz = einsatz,
|
|
istLeitstelle = Dienst.Leitstelle() == quelle,
|
|
beamteImDienst = Dienst.BaueListe(),
|
|
rechte = {
|
|
zuweisen = Rechte.Hat(beamter, 'einsatz.zuweisen'),
|
|
schliessen = Rechte.Hat(beamter, 'einsatz.schliessen'),
|
|
bericht = Rechte.Hat(beamter, 'bericht.erstellen'),
|
|
},
|
|
}
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- ANLEGEN
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('einsatz', 'erstellen', 'einsatz.erstellen', function(quelle, beamter, daten)
|
|
local titel = type(daten.titel) == 'string' and daten.titel:gsub('^%s+', ''):gsub('%s+$', '') or ''
|
|
if #titel < 3 or #titel > 160 then
|
|
return nil, 'Der Titel muss 3 bis 160 Zeichen lang sein.'
|
|
end
|
|
|
|
if not KategorieGueltig(daten.kategorie) then
|
|
return nil, 'Unbekannte Kategorie.'
|
|
end
|
|
|
|
local prioritaet = math.max(1, math.min(4, tonumber(daten.prioritaet) or 3))
|
|
local nummer = Kern.ErzeugeKennung('EIN', 'police_einsatz', 'nummer')
|
|
|
|
-- Position: entweder mitgeschickt oder die des erstellenden Beamten
|
|
local position = daten.position
|
|
if daten.eigenePosition then
|
|
local koordinaten = GetEntityCoords(GetPlayerPed(quelle))
|
|
position = { x = koordinaten.x, y = koordinaten.y, z = koordinaten.z }
|
|
end
|
|
|
|
local einsatzId = MySQL.insert.await([[
|
|
INSERT INTO police_einsatz
|
|
(nummer, titel, kategorie, prioritaet, ort, pos_x, pos_y, pos_z,
|
|
beschreibung, status, quelle, erstellt_von)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'offen', 'manuell', ?)
|
|
]], {
|
|
nummer, titel, daten.kategorie, prioritaet,
|
|
type(daten.ort) == 'string' and daten.ort:sub(1, 160) or nil,
|
|
position and position.x, position and position.y, position and position.z,
|
|
type(daten.beschreibung) == 'string' and daten.beschreibung or nil,
|
|
beamter.charakterId,
|
|
})
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Einsatz angelegt',
|
|
{ typ = 'einsatz', id = einsatzId }, { nummer = nummer, kategorie = daten.kategorie })
|
|
|
|
-- Alle Beamten im Dienst informieren — zugewiesen wird nichts
|
|
Hinweise.Erstelle(nil, {
|
|
kategorie = 'einsatz',
|
|
prioritaet = prioritaet == 1 and 'kritisch' or prioritaet == 2 and 'hoch' or 'normal',
|
|
titel = 'Neuer Einsatz: ' .. titel,
|
|
text = (daten.ort and ('Ort: ' .. daten.ort .. ' · ') or '') .. 'Noch keine Einheit zugeordnet.',
|
|
zielTyp = 'einsatz', zielId = einsatzId,
|
|
})
|
|
|
|
Kern.Loese('einsatz.erstellt', einsatzId, beamter)
|
|
VerteileEinsaetze()
|
|
|
|
return { id = einsatzId, nummer = nummer }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- BEITRETEN / VERLASSEN — immer freiwillig
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('einsatz', 'beitreten', nil, function(quelle, beamter, daten)
|
|
local einsatzId = tonumber(daten.id)
|
|
if not einsatzId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local einsatz = MySQL.query.await('SELECT status FROM police_einsatz WHERE id = ?', { einsatzId })
|
|
if not einsatz or not einsatz[1] then return nil, 'Einsatz nicht gefunden.' end
|
|
if einsatz[1].status == 'abgeschlossen' then
|
|
return nil, 'Dieser Einsatz ist bereits abgeschlossen.'
|
|
end
|
|
|
|
-- Bereits dabei? Dann nichts tun
|
|
local vorhanden = MySQL.scalar.await([[
|
|
SELECT id FROM police_einsatz_beamter
|
|
WHERE einsatz = ? AND `character` = ? AND verlassen IS NULL
|
|
]], { einsatzId, beamter.charakterId })
|
|
if vorhanden then return { ok = true } end
|
|
|
|
MySQL.insert.await('INSERT INTO police_einsatz_beamter (einsatz, `character`) VALUES (?, ?)',
|
|
{ einsatzId, beamter.charakterId })
|
|
|
|
-- Sobald jemand dabei ist, gilt der Einsatz als zugewiesen
|
|
MySQL.update("UPDATE police_einsatz SET status = 'zugewiesen' WHERE id = ? AND status = 'offen'",
|
|
{ einsatzId })
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Einsatz beigetreten', { typ = 'einsatz', id = einsatzId })
|
|
Kern.Loese('einsatz.beigetreten', einsatzId, beamter)
|
|
VerteileEinsaetze()
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
Kern.RegistriereAktion('einsatz', 'verlassen', nil, function(quelle, beamter, daten)
|
|
local einsatzId = tonumber(daten.id)
|
|
if not einsatzId then return nil, 'Ungültige Kennung.' end
|
|
|
|
MySQL.update.await([[
|
|
UPDATE police_einsatz_beamter SET verlassen = NOW()
|
|
WHERE einsatz = ? AND `character` = ? AND verlassen IS NULL
|
|
]], { einsatzId, beamter.charakterId })
|
|
|
|
-- Verlässt der letzte Beamte den Einsatz, geht er zurück auf „offen"
|
|
if AnzahlBeamte(einsatzId) == 0 then
|
|
MySQL.update("UPDATE police_einsatz SET status = 'offen' WHERE id = ? AND status <> 'abgeschlossen'",
|
|
{ einsatzId })
|
|
|
|
Hinweise.Erstelle(nil, {
|
|
kategorie = 'einsatz', prioritaet = 'normal',
|
|
titel = 'Einsatz ohne Einheit',
|
|
text = 'Der Einsatz hat keine zugeordnete Einheit mehr.',
|
|
zielTyp = 'einsatz', zielId = einsatzId,
|
|
})
|
|
end
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Einsatz verlassen', { typ = 'einsatz', id = einsatzId })
|
|
VerteileEinsaetze()
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- ZUWEISUNG DURCH DIE LEITSTELLE
|
|
--
|
|
-- Die Leitstelle spricht eine Zuweisung AUS — sie zwingt niemanden.
|
|
-- Der angesprochene Beamte entscheidet selbst, ob er beitritt.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('einsatz', 'zuweisen', 'einsatz.zuweisen', function(quelle, beamter, daten)
|
|
local einsatzId = tonumber(daten.id)
|
|
local zielQuelle = tonumber(daten.quelle)
|
|
if not einsatzId or not zielQuelle then return nil, 'Unvollständige Angaben.' end
|
|
|
|
local ziel = Dienst.Hole(zielQuelle)
|
|
if not ziel then return nil, 'Diese Einheit ist nicht im Dienst.' end
|
|
|
|
local einsatz = MySQL.query.await(
|
|
'SELECT nummer, titel, ort, pos_x, pos_y, pos_z FROM police_einsatz WHERE id = ?',
|
|
{ einsatzId })
|
|
if not einsatz or not einsatz[1] then return nil, 'Einsatz nicht gefunden.' end
|
|
|
|
-- Aufforderung an die Einheit — kein automatischer Beitritt
|
|
TriggerClientEvent('aether:police:einsatzZuweisung', zielQuelle, {
|
|
id = einsatzId,
|
|
nummer = einsatz[1].nummer,
|
|
titel = einsatz[1].titel,
|
|
ort = einsatz[1].ort,
|
|
von = beamter.name .. ' (' .. beamter.dienstnummer .. ')',
|
|
position = einsatz[1].pos_x and {
|
|
x = einsatz[1].pos_x, y = einsatz[1].pos_y, z = einsatz[1].pos_z,
|
|
} or nil,
|
|
})
|
|
|
|
Hinweise.Erstelle({ charakterId = ziel.charakterId }, {
|
|
kategorie = 'einsatz', prioritaet = 'hoch',
|
|
titel = 'Zuweisung: ' .. einsatz[1].titel,
|
|
text = ('%s bittet dich, den Einsatz zu übernehmen.'):format(beamter.dienstnummer),
|
|
zielTyp = 'einsatz', zielId = einsatzId,
|
|
})
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Einheit zugewiesen',
|
|
{ typ = 'einsatz', id = einsatzId }, { einheit = ziel.dienstnummer })
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- STATUS UND NOTIZEN
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('einsatz', 'status', nil, function(quelle, beamter, daten)
|
|
local einsatzId = tonumber(daten.id)
|
|
local status = tostring(daten.status or '')
|
|
if not einsatzId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local erlaubt = { offen = true, zugewiesen = true, vorort = true }
|
|
if not erlaubt[status] then return nil, 'Unbekannter Status.' end
|
|
|
|
MySQL.update.await("UPDATE police_einsatz SET status = ? WHERE id = ? AND status <> 'abgeschlossen'",
|
|
{ status, einsatzId })
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Status geändert',
|
|
{ typ = 'einsatz', id = einsatzId }, { status = status })
|
|
VerteileEinsaetze()
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
Kern.RegistriereAktion('einsatz', 'notiz', nil, function(quelle, beamter, daten)
|
|
local einsatzId = tonumber(daten.id)
|
|
local text = type(daten.text) == 'string' and daten.text:gsub('^%s+', ''):gsub('%s+$', '') or ''
|
|
|
|
if not einsatzId then return nil, 'Ungültige Kennung.' end
|
|
if #text < 2 or #text > 1000 then return nil, 'Die Notiz muss 2 bis 1000 Zeichen lang sein.' end
|
|
|
|
MySQL.insert.await('INSERT INTO police_einsatz_notiz (einsatz, text, von) VALUES (?, ?, ?)',
|
|
{ einsatzId, text, beamter.charakterId })
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Notiz ergänzt', { typ = 'einsatz', id = einsatzId })
|
|
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- ABSCHLIESSEN
|
|
--
|
|
-- Beim Abschluss wird KEIN Bericht automatisch erzeugt. Das System
|
|
-- bietet lediglich an, einen vorbereiteten Entwurf anzulegen.
|
|
-- =====================================================================
|
|
|
|
Kern.RegistriereAktion('einsatz', 'abschliessen', 'einsatz.schliessen', function(quelle, beamter, daten)
|
|
local einsatzId = tonumber(daten.id)
|
|
if not einsatzId then return nil, 'Ungültige Kennung.' end
|
|
|
|
local einsatz = LadeEinsatz(einsatzId)
|
|
if not einsatz then return nil, 'Einsatz nicht gefunden.' end
|
|
if einsatz.status == 'abgeschlossen' then return nil, 'Der Einsatz ist bereits abgeschlossen.' end
|
|
|
|
MySQL.update.await([[
|
|
UPDATE police_einsatz
|
|
SET status = 'abgeschlossen', geschlossen_von = ?, geschlossen_am = NOW()
|
|
WHERE id = ?
|
|
]], { beamter.charakterId, einsatzId })
|
|
|
|
-- Alle noch offenen Beteiligungen beenden
|
|
MySQL.update('UPDATE police_einsatz_beamter SET verlassen = NOW() WHERE einsatz = ? AND verlassen IS NULL',
|
|
{ einsatzId })
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Einsatz abgeschlossen',
|
|
{ typ = 'einsatz', id = einsatzId }, nil, daten.grund)
|
|
|
|
local ergebnis = { ok = true }
|
|
|
|
-- Nur auf ausdrücklichen Wunsch einen Berichtsentwurf vorbereiten
|
|
if daten.berichtAnlegen and Rechte.Hat(beamter, 'bericht.erstellen') then
|
|
local nummer = Kern.ErzeugeKennung('BER', 'police_bericht', 'nummer')
|
|
|
|
-- Beteiligte aus dem Einsatz übernehmen
|
|
local beteiligte = {}
|
|
for _, eintrag in ipairs(einsatz.beamte) do
|
|
beteiligte[#beteiligte + 1] = {
|
|
dienstnummer = eintrag.dienstnummer,
|
|
name = (eintrag.firstname or '') .. ' ' .. (eintrag.lastname or ''),
|
|
}
|
|
end
|
|
|
|
-- Notizen als Ausgangstext, damit nichts abgetippt werden muss
|
|
local zeilen = {}
|
|
for _, notiz in ipairs(einsatz.notizen) do
|
|
zeilen[#zeilen + 1] = ('[%s] %s'):format(
|
|
tostring(notiz.am):sub(12, 16), notiz.text)
|
|
end
|
|
|
|
local berichtId = MySQL.insert.await([[
|
|
INSERT INTO police_bericht
|
|
(nummer, einsatz, titel, beschreibung, beteiligte, status, erstellt_von)
|
|
VALUES (?, ?, ?, ?, ?, 'entwurf', ?)
|
|
]], {
|
|
nummer, einsatzId, einsatz.titel,
|
|
(einsatz.beschreibung and (einsatz.beschreibung .. '\n\n') or '') ..
|
|
(#zeilen > 0 and ('Einsatzverlauf:\n' .. table.concat(zeilen, '\n')) or ''),
|
|
json.encode(beteiligte), beamter.charakterId,
|
|
})
|
|
|
|
Hinweise.Erstelle({ charakterId = beamter.charakterId }, {
|
|
kategorie = 'bericht', prioritaet = 'normal',
|
|
titel = 'Berichtsentwurf angelegt',
|
|
text = ('%s wurde als Entwurf vorbereitet und wartet auf deine Ausarbeitung.'):format(nummer),
|
|
zielTyp = 'bericht', zielId = berichtId,
|
|
})
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Berichtsentwurf angelegt',
|
|
{ typ = 'bericht', id = berichtId }, { einsatz = einsatzId })
|
|
|
|
ergebnis.berichtId = berichtId
|
|
ergebnis.berichtNummer = nummer
|
|
end
|
|
|
|
Kern.Loese('einsatz.abgeschlossen', einsatzId, beamter)
|
|
VerteileEinsaetze()
|
|
|
|
return ergebnis
|
|
end)
|
|
|
|
-- =====================================================================
|
|
-- EINGEHENDE MELDUNGEN (Dispatch)
|
|
-- =====================================================================
|
|
|
|
--- Legt einen eingehenden Einsatz an, ohne ihn zuzuweisen
|
|
--- @param daten table { titel, kategorie, prioritaet, ort, beschreibung, position, quelle }
|
|
--- @return number Einsatz-ID
|
|
function EinsatzEingang(daten)
|
|
local nummer = Kern.ErzeugeKennung('EIN', 'police_einsatz', 'nummer')
|
|
local prioritaet = math.max(1, math.min(4, tonumber(daten.prioritaet) or 2))
|
|
|
|
local einsatzId = MySQL.insert.await([[
|
|
INSERT INTO police_einsatz
|
|
(nummer, titel, kategorie, prioritaet, ort, pos_x, pos_y, pos_z,
|
|
beschreibung, status, quelle)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'offen', ?)
|
|
]], {
|
|
nummer, daten.titel, KategorieGueltig(daten.kategorie) and daten.kategorie or 'notruf',
|
|
prioritaet, daten.ort,
|
|
daten.position and daten.position.x, daten.position and daten.position.y,
|
|
daten.position and daten.position.z,
|
|
daten.beschreibung, daten.quelle or 'notruf',
|
|
})
|
|
|
|
Hinweise.Erstelle(nil, {
|
|
kategorie = 'einsatz',
|
|
prioritaet = prioritaet == 1 and 'kritisch' or 'hoch',
|
|
titel = daten.titel,
|
|
text = (daten.ort and ('Ort: ' .. daten.ort .. ' · ') or '') ..
|
|
'Eingegangen — Zuweisung durch die Leitstelle.',
|
|
zielTyp = 'einsatz', zielId = einsatzId,
|
|
})
|
|
|
|
Kern.Loese('einsatz.eingegangen', einsatzId, daten)
|
|
VerteileEinsaetze()
|
|
|
|
return einsatzId
|
|
end
|
|
|
|
--- Panikknopf eines Beamten (10-13)
|
|
RegisterNetEvent('aether:police:panik', function()
|
|
local quelle = source
|
|
local beamter = Dienst.Hole(quelle)
|
|
if not beamter then return end
|
|
|
|
local koordinaten = GetEntityCoords(GetPlayerPed(quelle))
|
|
|
|
local einsatzId = EinsatzEingang({
|
|
titel = '10-13 — Beamter in Not',
|
|
kategorie = 'panik',
|
|
prioritaet = 1,
|
|
ort = 'Position der Einheit',
|
|
beschreibung = ('Panikknopf ausgelöst von %s (%s).')
|
|
:format(beamter.name, beamter.dienstnummer),
|
|
position = { x = koordinaten.x, y = koordinaten.y, z = koordinaten.z },
|
|
quelle = 'panik',
|
|
})
|
|
|
|
-- Alle Beamten im Dienst mit Ton und Wegpunkt alarmieren
|
|
for andereQuelle in pairs(Dienst.AlleImDienst()) do
|
|
if andereQuelle ~= quelle then
|
|
TriggerClientEvent('aether:police:panikAlarm', andereQuelle, {
|
|
name = beamter.name, dienstnummer = beamter.dienstnummer,
|
|
position = { x = koordinaten.x, y = koordinaten.y, z = koordinaten.z },
|
|
einsatzId = einsatzId,
|
|
})
|
|
end
|
|
end
|
|
|
|
Protokoll.Schreibe(beamter, 'einsatz', 'Panikknopf ausgelöst',
|
|
{ typ = 'einsatz', id = einsatzId })
|
|
end)
|