Zurück zum Blog
Tutorial8 Min. Lesezeit

FiveM-Rettungsdienst-Skript: Erstelle ein realistisches EMS-System

Erstelle einen kompletten Rettungsdienst für deinen FiveM-Server. Mit Wiederbelebung, Klinikabläufen, ESX- und QBCore-Integration sowie starken EMS-Skripten.

Agency Scripts

Gründer und leitender Entwickler bei Agency Scripts

Übersicht über das Krankenwagensystem

Ein Krankenwagen-Job-Script ist für jeden Roleplay-Server unverzichtbar, der ein immersives Spielerlebnis rund um Rettungsdienste bieten möchte. Das EMS-System übernimmt alles, von Dispatch-Meldungen, wenn ein Spieler außer Gefecht gesetzt wird, über Behandlungen am Einsatzort und den Transport ins Krankenhaus bis hin zur medizinischen Abrechnung. Ein gut entwickeltes Krankenwagen-Script bietet EMS-Spielern sinnvolles Gameplay und sorgt gleichzeitig dafür, dass am Boden liegende Spieler nicht endlos warten müssen. Die Architektur gliedert sich in vier Hauptsysteme: das Dispatch- und Benachrichtigungssystem, das EMS über Vorfälle informiert, die Behandlungs- und Stabilisierungsmechaniken, die Sanitätern konkrete Aufgaben geben, die Anmeldung und Genesung im Krankenhaus sowie das Abrechnungssystem, das Verletzungen mit Kosten verbindet. Jedes System muss eigenständig funktionieren, damit automatische Ausweichsysteme die Genesung von Spielern übernehmen, wenn keine EMS-Spieler im Dienst sind.

Job-Konfiguration und Dienstsystem

Der Krankenwagen-Job braucht klar definierte Ränge mit unterschiedlichen Fähigkeiten für jede Stufe. Auszubildende können vielleicht nur Erste Hilfe leisten, während leitende Rettungssanitäter fortgeschrittene Behandlungen durchführen und auf beschränkte medizinische Vorräte zugreifen können. Verwende eine Konfigurationstabelle, die Jobränge den verfügbaren Aktionen, erforderlichen Gegenständen und Vergütungssätzen zuordnet. Das Dienstsystem sollte erfassen, welche EMS-Spieler gerade aktiv sind, damit der Server weiß, ob er die automatische Respawn-Ausweichlösung verwenden soll:

Config.AmbulanceJob = {
    name = 'ambulance',
    grades = {
        [0] = {
            label = 'Trainee',
            treatments = {'bandage', 'painkillers'},
            canRevive = false,
            salary = 250,
        },
        [1] = {
            label = 'EMT',
            treatments = {'bandage', 'painkillers', 'splint', 'iv_drip'},
            canRevive = true,
            salary = 400,
        },
        [2] = {
            label = 'Paramedic',
            treatments = {'bandage', 'painkillers', 'splint', 'iv_drip',
                         'defibrillator', 'blood_transfusion'},
            canRevive = true,
            salary = 550,
        },
        [3] = {
            label = 'Chief',
            treatments = {'all'},
            canRevive = true,
            salary = 750,
        },
    },
    minOnDutyForNoAutoRespawn = 2,
    respawnTimer = 300,  -- seconds before auto-respawn when no EMS
    hospitalCoords = vector3(311.2, -584.3, 43.3),
    bedSpawns = {
        vector4(309.7, -583.8, 43.3, 70.0),
        vector4(313.0, -585.2, 43.3, 70.0),
        vector4(316.3, -586.6, 43.3, 70.0),
    },
}

Die minOnDutyForNoAutoRespawn Die Einstellung steuert, wann der Server zwischen EMS-abhängiger und automatischer Genesung wechselt. Wenn weniger als zwei EMS-Spieler im Dienst sind, erhalten am Boden liegende Spieler nach der konfigurierten Wartezeit eine zeitgesteuerte Respawn-Option. So müssen Spieler nicht auf medizinische Hilfe warten, die niemals eintreffen wird.

Dispatch- und Benachrichtigungssystem

Wenn ein Spieler außer Gefecht gesetzt wird, sendet das System eine Dispatch-Meldung an alle EMS-Spieler im Dienst. Sie enthält den Standort, die Art des Notfalls und eine Möglichkeit, den Einsatz anzunehmen. Mehrere EMS-Spieler können die Meldung sehen, aber nur einer sollte den Einsatz übernehmen, um Verwirrung zu vermeiden. Implementiere ein Annahmesystem, bei dem der erste Helfer, der den Einsatz annimmt, zugeteilt wird. Für die anderen EMS-Spieler wird der Einsatz als angenommen angezeigt, zusammen mit der Kennung der antwortenden Einheit:

-- Server: handle player down event
local ActiveCalls = {}

RegisterNetEvent('ambulance:server:playerDown', function(deathCause)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local ped = GetPlayerPed(src)
    local coords = GetEntityCoords(ped)
    local streetHash, _ = GetStreetNameAtCoord(coords.x, coords.y, coords.z)
    local streetName = GetStreetNameFromHashKey(streetHash)

    local callId = 'EMS-' .. os.time() .. '-' .. src
    ActiveCalls[callId] = {
        source = src,
        patient = Player.PlayerData.charinfo.firstname .. ' ' ..
                  Player.PlayerData.charinfo.lastname,
        coords = coords,
        street = streetName,
        cause = deathCause,
        time = os.time(),
        claimed = false,
        responder = nil,
    }

    -- Notify all on-duty EMS
    local emsList = QBCore.Functions.GetPlayersOnDuty('ambulance')
    for _, emsId in ipairs(emsList) do
        TriggerClientEvent('ambulance:client:newCall', emsId, callId, ActiveCalls[callId])
    end
end)

RegisterNetEvent('ambulance:server:claimCall', function(callId)
    local src = source
    local call = ActiveCalls[callId]
    if not call or call.claimed then return end

    local Player = QBCore.Functions.GetPlayer(src)
    if not Player or Player.PlayerData.job.name ~= 'ambulance' then return end

    call.claimed = true
    call.responder = src

    -- Notify all EMS that call is claimed
    local emsList = QBCore.Functions.GetPlayersOnDuty('ambulance')
    for _, emsId in ipairs(emsList) do
        TriggerClientEvent('ambulance:client:callClaimed', emsId, callId, Player.PlayerData.charinfo.firstname)
    end

    -- Set GPS waypoint for responding EMT
    TriggerClientEvent('ambulance:client:setWaypoint', src, call.coords)
end)

Nimm die Todesursache in die Dispatch-Informationen auf, damit sich der eintreffende EMT vorab mit den passenden Behandlungsmitteln ausstatten kann. Häufige Ursachen wie Schusswunden, Verkehrsunfälle, Stürze und Ertrinken erfordern jeweils unterschiedliche Behandlungen. Das sorgt für mehr Tiefe im EMS-Roleplay.

Behandlung und Wiederbelebung

Die Behandlung sollte mehr umfassen, als nur einen Knopf zu drücken, um jemanden wiederzubeleben. Erstelle einen mehrstufigen Ablauf, bei dem der EMT den Patienten zunächst mit grundlegenden Maßnahmen stabilisieren muss, bevor die eigentliche Wiederbelebung erfolgt. Jeder Behandlungsschritt verbraucht einen Gegenstand aus dem Inventar des EMT und dauert eine festgelegte Zeit. Währenddessen wird eine Animation mit Fortschrittsbalken angezeigt. Der Zustand des Patienten lässt sich mit einem einfachen Gesundheitsstatus modellieren, der sich mit jeder durchgeführten Behandlung verbessert:

-- Client: treatment system
local TreatmentSteps = {
    ['bandage'] = {
        label = 'Apply Bandage',
        duration = 5000,
        anim = {dict = 'mini@cpr@char_a@cpr_str', name = 'cpr_pumpchest'},
        healthRestore = 10,
        item = 'bandage',
    },
    ['painkillers'] = {
        label = 'Administer Painkillers',
        duration = 3000,
        anim = {dict = 'mp_arresting', name = 'a_uncuff'},
        healthRestore = 15,
        item = 'painkillers',
    },
    ['defibrillator'] = {
        label = 'Use Defibrillator',
        duration = 8000,
        anim = {dict = 'mini@cpr@char_a@cpr_str', name = 'cpr_pumpchest'},
        healthRestore = 0,
        item = 'defibrillator',
        canRevive = true,
    },
}

function PerformTreatment(targetId, treatmentType)
    local treatment = TreatmentSteps[treatmentType]
    if not treatment then return end

    -- Check if EMT has required item
    if not HasItem(treatment.item) then
        QBCore.Functions.Notify('Missing: ' .. treatment.label, 'error')
        return
    end

    -- Play animation and progress bar
    TaskPlayAnim(PlayerPedId(), treatment.anim.dict, treatment.anim.name,
        8.0, -8.0, treatment.duration, 1, 0, false, false, false)

    QBCore.Functions.Progressbar('treatment_' .. treatmentType,
        treatment.label, treatment.duration, false, true, {}, {}, {}, {},
        function() -- success
            TriggerServerEvent('ambulance:server:applyTreatment',
                targetId, treatmentType)
            ClearPedTasks(PlayerPedId())
        end,
        function() -- cancel
            ClearPedTasks(PlayerPedId())
            QBCore.Functions.Notify('Treatment cancelled', 'error')
        end
    )
end

Der Fortschrittsbalken mit Animation sorgt für eine realistische Behandlungsszene, die andere Spieler beobachten können und die so die Roleplay-Atmosphäre verstärkt. Die Defibrillation sollte der letzte Schritt sein, der den Patienten tatsächlich wiederbelebt. Dafür muss der EMT den Patienten zuvor mit Verbänden und Schmerzmitteln stabilisieren. Dieser mehrstufige Ablauf macht die EMS-Rolle interessanter als eine einfache Wiederbelebung mit nur einem Klick.

Anmeldung und Genesung im Krankenhaus

Nach der Wiederbelebung am Einsatzort oder dem Transport ins Krankenhaus kann ein EMS-Spieler den Patienten für eine vollständige Genesung in einem Krankenhausbett anmelden. Bei der Anmeldung wird der Patient vollständig geheilt, eine Arztrechnung erstellt und der Besuch protokolliert. Krankenhausbetten sollten erfasst werden, damit nicht mehrere Patienten demselben Bett zugewiesen werden. Erstelle ein Bettenverwaltungssystem, das belegte Betten markiert und sie nach einer konfigurierbaren Genesungszeit wieder freigibt:

-- Server: hospital bed management
local OccupiedBeds = {}

RegisterNetEvent('ambulance:server:checkInPatient', function(patientId)
    local src = source
    local EMT = QBCore.Functions.GetPlayer(src)
    local Patient = QBCore.Functions.GetPlayer(patientId)

    if not EMT or not Patient then return end
    if EMT.PlayerData.job.name ~= 'ambulance' then return end

    -- Find available bed
    local bedIndex = nil
    for i, bed in ipairs(Config.AmbulanceJob.bedSpawns) do
        if not OccupiedBeds[i] then
            bedIndex = i
            break
        end
    end

    if not bedIndex then
        TriggerClientEvent('QBCore:Notify', src, 'No beds available', 'error')
        return
    end

    OccupiedBeds[bedIndex] = patientId

    -- Calculate and charge medical bill
    local bill = CalculateMedicalBill(Patient)
    Patient.Functions.RemoveMoney('bank', bill, 'medical-bill')

    -- Heal patient and teleport to bed
    local bed = Config.AmbulanceJob.bedSpawns[bedIndex]
    TriggerClientEvent('ambulance:client:bedRecovery', patientId, bed, bill)

    -- Pay EMT for service
    local salary = Config.AmbulanceJob.grades[EMT.PlayerData.job.grade.level].salary
    EMT.Functions.AddMoney('bank', salary, 'ems-treatment-pay')

    -- Release bed after recovery
    SetTimeout(60000, function()
        OccupiedBeds[bedIndex] = nil
    end)
end)

function CalculateMedicalBill(Patient)
    local baseCost = 500
    local injuryMultiplier = 1.0
    -- Could factor in injury severity, treatment count, etc.
    return math.floor(baseCost * injuryMultiplier)
end

Die Arztrechnung sorgt für wirtschaftliche Folgen beim Tod und trägt dazu bei, die Serverwirtschaft auszubalancieren. Du kannst die Rechnungshöhe davon abhängig machen, wie sich der Spieler verletzt hat: Höhere Kosten bei rücksichtslosem Verhalten wie Hochgeschwindigkeitsunfällen, geringere Kosten, wenn jemand Opfer eines Verbrechens wurde. Das fördert vorsichtiges Fahren und verleiht gefährlichen Situationen mehr Gewicht.

Automatisches Respawn-Ausweichsystem

Wenn keine EMS-Spieler im Dienst sind, muss der Server einen alternativen Weg zur Genesung anbieten, damit am Boden liegende Spieler nicht dauerhaft feststecken. Implementiere einen Countdown, der nach einer konfigurierbaren Verzögerung angezeigt wird und dem Spieler ermöglicht, gegen eine automatisch abgezogene Pauschalgebühr für medizinische Versorgung im Krankenhaus zu respawnen. Der Respawn-Ort sollte der Krankenhauseingang oder ein festgelegter Genesungsbereich sein. Prüfe vor der Anzeige der automatischen Respawn-Option immer die aktuelle Anzahl der EMS-Spieler und blende sie sofort aus, wenn ein EMT den Einsatz während des Countdowns übernimmt. Dieses System mit zwei Wegen stellt sicher, dass Spieler sich immer erholen können, und gibt gleichzeitig dem Roleplay mit EMS-Spielern Vorrang, wenn Sanitäter verfügbar sind.

Medizinische Vorräte und Inventarintegration

EMS-Spieler benötigen über ein jobbeschränktes Lager im Krankenhaus sowie ein Herstellungs- oder Kaufsystem Zugang zu medizinischen Vorräten. Bestücke das Krankenhauslager mit Verbänden, Schmerzmitteln, Schienen, Infusionen, Blutbeuteln und Defibrillator-Ladungen. Bei jeder Behandlung wird der entsprechende Gegenstand verbraucht, wodurch ein laufender Nachschubbedarf entsteht. Erfasse den Verbrauch in der Datenbank, um Nutzungsmuster zu beobachten und Krankenhauslager in konfigurierbaren Abständen automatisch aufzufüllen. Ziehe in Betracht, einige Vorräte von Spielern mit der Rolle Apotheker oder Arzt herstellen zu lassen. So entstehen zusätzliche Roleplay-Möglichkeiten und wirtschaftliche Verbindungen zwischen verschiedenen Jobs auf dem Server.

Protokollierung und Performance-Überwachung

Protokolliere jede EMS-Interaktion zur administrativen Überprüfung, einschließlich der behandelten Person, der durchgeführten Behandlungen, des antwortenden EMT und der endgültigen Arztrechnung. Speichere diese Protokolle in einer eigenen Datenbanktabelle und sende Zusammenfassungen optional über einen Discord-Webhook zur Echtzeitüberwachung an das Staff-Team. Diese Protokolle helfen dabei, Streitfälle zu klären, EMS-Spieler zu identifizieren, die ihren Pflichten nicht nachkommen, und den allgemeinen Systemzustand im Blick zu behalten. Halte die Todesprüfung aus Performance-Sicht schlank, indem du sie nur bei Spielern ausführst, deren Gesundheit auf null gesunken ist, und nicht bei jedem Spieler in jedem Frame. Nutze eine ereignisgesteuerte Architektur, bei der der Todesstatus ein einzelnes Server-Event auslöst, anstatt ihn wiederholt abzufragen. Das Dispatch-Benachrichtigungssystem sollte Meldungen bündeln, statt einzelne Events an jeden EMS-Spieler zu senden. So wird die Netzwerklast bei Massenunfällen reduziert, bei denen mehrere Spieler gleichzeitig außer Gefecht gesetzt werden.

Das passende Script

Fertig für deinen Server, Updates inklusive.

Bereit loszulegen?

Hol dir die Scripts in unserem Shop, oder komm auf den Discord für Support, Updates und einen Blick auf das, was kommt.