Скрипт работы скорой помощи FiveM: создание реалистичной EMS системы
Разработайте полноценную работу скорой помощи для вашего сервера FiveM. Воскрешение пациентов, рабочие процессы в больнице, интеграция с ESX и QBCore, а также лучшие EMS скрипты.
Agency Scripts
Основатель и ведущий разработчик Agency Scripts
Обзор системы работы скорой помощи
Скрипт работы скорой помощи необходим для любого ролевого сервера, стремящегося к реалистичному геймплею экстренных служб. Система EMS обрабатывает всё: от получения вызовов при падении игрока, до механик лечения на месте, транспортировки в больницу и медицинского выставления счетов. Хорошо продуманный скрипт скорой создаёт значимый геймплей для EMS игроков и при этом не оставляет упавших игроков ждать бесконечно. Архитектура делится на четыре основных подсистемы: система диспетчеризации и уведомлений, оповещающая EMS о происшествиях; механики лечения и стабилизации, дающие медикам реальные задачи; процесс регистрации и восстановления в больнице; и система выставления счетов, создающая экономическую стоимость травм. Каждая подсистема должна работать независимо, чтобы при отсутствии EMS игроков на дежурстве автоматические системы обеспечивали восстановление игроков.
Конфигурация работы и система дежурств
Работа скорой помощи требует четко определенных рангов с разными возможностями на каждом уровне. Новички могут выполнять только базовую первую помощь, тогда как старшие парамедики способны проводить сложные процедуры и иметь доступ к ограниченным медицинским запасам. Используйте таблицу конфигурации, которая сопоставляет ранги с доступными действиями, необходимыми предметами и ставками оплаты. Система дежурств должна отслеживать, какие EMS-игроки сейчас активны, чтобы сервер знал, использовать ли автоматический респаун как запасной вариант:
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),
},
}
Параметр minOnDutyForNoAutoRespawn настройка контролирует, когда сервер переключается между зависимым от EMS и автоматическим восстановлением. Когда на дежурстве менее двух EMS, у упавших игроков появляется возможность возрождения по таймеру после ожидания заданного количества секунд. Это предотвращает ситуацию, когда игроки застревают в ожидании медицинской помощи, которая никогда не придет.
Система диспетчеризации и уведомлений
Когда игрок без сознания, система рассылает оповещение диспетчеру всем дежурным EMS с указанием места, характера чрезвычайной ситуации и способом принять вызов. Несколько EMS могут видеть оповещение, но только один должен принять вызов, чтобы избежать путаницы. Реализуйте систему принятия вызова, где первый откликнувшийся получает назначение, а остальные EMS видят, что вызов принят с идентификатором ответившего подразделения:
-- 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)
Включите причину смерти в информацию для диспетчера, чтобы реагирующий EMT мог подготовить соответствующие предметы для лечения до прибытия на место. Распространённые причины, такие как огнестрельные ранения, столкновения транспортных средств, падения и утопления, требуют разных подходов к лечению, что добавляет глубину ролевому опыту EMS.
Механики лечения и оживления
Механика лечения должна быть более сложной, чем просто нажатие кнопки для оживления. Создайте многоэтапный процесс, в котором EMT сначала стабилизирует пациента базовыми процедурами, прежде чем выполнять само оживление. Каждый этап лечения потребляет предмет из инвентаря EMT и занимает определенное время с анимацией прогресс-бара. Состояние пациента можно моделировать простой шкалой здоровья, которая улучшается с каждым примененным лечением:
-- 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
Полоса прогресса с анимацией создаёт реалистичную сцену лечения, которую могут наблюдать другие игроки, усиливая атмосферу ролевой игры. Сделайте дефибриллятор финальным шагом, который действительно выполняет оживление, требуя от EMT сначала стабилизировать пациента бинтами и обезболивающими. Такой многоступенчатый подход делает роль EMS более увлекательной, чем простое оживление одним кликом.
Регистрация и восстановление в больнице
После оживления пациента на месте или транспортировки в больницу игрок EMS может оформить его в больничную койку для полного восстановления. Процесс оформления полностью лечит пациента, выставляет медицинский счет и фиксирует визит. Койки в больнице должны отслеживаться, чтобы избежать назначения нескольких пациентов на одну и ту же койку. Создайте систему управления койками, которая отмечает койки как занятые и освобождает их после настраиваемого времени восстановления:
-- 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
Медицинский счёт создаёт экономические последствия за смерть, балансируя экономику сервера. Рассмотрите масштабирование счёта в зависимости от того, как игрок был ранен, с более высокими затратами за безрассудное поведение, например, аварии на высокой скорости, и меньшими , за жертву преступления. Это стимулирует аккуратное вождение и придаёт вес опасным ситуациям.
Система автоматического возрождения при сбое
Когда на дежурстве нет игроков EMS, сервер должен предоставить альтернативный путь восстановления, чтобы упавшие игроки не застряли навсегда. Реализуйте таймер обратного отсчёта, который появляется после настраиваемой задержки, позволяя игроку возродиться в больнице с автоматическим списанием фиксированного медицинского сбора. Место возрождения должно быть у входа в больницу или в назначенной зоне восстановления. Всегда проверяйте текущее количество EMS перед показом опции авто-возрождения и сразу скрывайте её, если EMT принимает вызов во время отсчёта. Такая двойная система гарантирует игрокам возможность восстановления, при этом приоритет отдаётся ролевому взаимодействию с EMS, когда медики доступны.
Интеграция медицинских запасов и инвентаря
Игроки EMS должны иметь доступ к медицинским расходным материалам через хранилище с ограничением по профессии в больнице и через систему крафта или покупки. Заполните больничное хранилище бинтами, обезболивающими, шинами, капельницами, пакетами крови и зарядками для дефибриллятора. Каждое лечение расходует соответствующий предмет, создавая постоянный спрос на пополнение запасов. Отслеживайте использование запасов в базе данных для мониторинга потребления и автоматически пополняйте больничные хранилища с настраиваемыми интервалами. Рассмотрите возможность создания некоторых расходных материалов игроками с ролью фармацевта или врача, создавая дополнительные возможности для ролевой игры и экономические связи между разными профессиями на сервере.
Логирование и мониторинг производительности
Логировать каждое взаимодействие EMS для административного обзора, включая кого лечили, какие процедуры применялись, ответственного EMT и итоговый медицинский счет. Хранить эти логи в отдельной таблице базы данных и при необходимости отправлять сводки в Discord webhook для мониторинга персонала в реальном времени. Эти логи помогают разрешать споры, выявлять EMS игроков, не выполняющих свои обязанности, и отслеживать общее состояние системы. С точки зрения производительности, держите логику проверки смерти легковесной, выполняя её только для игроков с нулевым здоровьем, а не для каждого игрока на каждом кадре. Используйте событийно-ориентированную архитектуру, где состояние смерти вызывает одно серверное событие вместо опроса. Система уведомлений dispatch должна группировать оповещения, а не отправлять отдельные события каждому EMS игроку, снижая нагрузку на сеть при массовых случаях, когда несколько игроков одновременно падают.