DRS Advanced Ambulance

v2.2.4 drs_ambulancejob
Ver na loja

Emprego de INEM completo, feito de raiz. Tem ferimentos por zona do corpo com efeitos reais e os estados inconsciente, ferido no chão e morto, cada um com o seu tempo. Tem ainda sinal de socorro e o menu do médico (F6) com painel do corpo, tratamentos, reanimação, desfibrilhador e RCP. Os médicos podem carregar o paciente e metê-lo no veículo, e passar contas. No hospital há check-in com camas. Inclui ainda um INEM por IA (NPC) para quando não há médicos de serviço, maca, colegas no mapa e proteção contra revive. Funciona com Qbox, QBCore (incluindo drs-core) e ESX, e é compatível com os scripts feitos para o qb-ambulancejob.

#Requisitos

RecursoObrigatório
oxmysqlSim
Qbox, QBCore ou ESXSim (deteção automática)
ox_inventory ou qb-inventorySim (itens médicos)
ox_target ou qb-targetNão — dá opções diretas nos jogadores
drs_dispatch ou cd_dispatchNão — sem eles o sinal de socorro usa o alerta próprio

#Instalação

  1. Coloca drs_ambulancejob em resources.
  2. Desliga o qb-ambulancejob (ou o ambulancejob da tua base): os dois não podem estar ligados ao mesmo tempo.
  3. Se usas um ecrã de morte à parte (ex.: drs_deathscreen), desliga-o também: o DRS Advanced Ambulance já tem o seu.
  4. Adiciona ao server.cfg, depois da framework e do inventário:
server.cfg
ensure oxmysql
ensure qb-core
ensure qb-inventory
ensure drs_ambulancejob
  1. Cria os itens (ver abaixo) e copia as imagens de install/images/ para o teu inventário.
  2. Ajusta o config.lua: empregos, tempos, preços e hospitais.

#Itens

QBCore — dentro de qb-core/shared/items.lua:

items_qb-core.lua
-- drs_ambulancejob: cola dentro de qb-core/shared/items.lua (drs-core: drs-core/shared/items.lua)
-- Imagens: copia install/images/*.png para a pasta de imagens do teu inventário (qb-inventory/html/images/)

    bandage = { name = 'bandage', label = 'Ligadura', weight = 100, type = 'item', image = 'bandage.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'Para hemorragias ligeiras' },
    gauze = { name = 'gauze', label = 'Compressas', weight = 80, type = 'item', image = 'gauze.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'Estanca hemorragias e feridas de bala' },
    tourniquet = { name = 'tourniquet', label = 'Garrote', weight = 150, type = 'item', image = 'tourniquet.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'Pára hemorragias graves num braço ou perna' },
    splint = { name = 'splint', label = 'Tala', weight = 400, type = 'item', image = 'splint.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'Imobiliza fraturas' },
    burndressing = { name = 'burndressing', label = 'Penso para queimaduras', weight = 100, type = 'item', image = 'burndressing.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'Trata queimaduras' },
    painkillers = { name = 'painkillers', label = 'Analgésicos', weight = 50, type = 'item', image = 'painkillers.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'Tiram a dor durante uns minutos' },
    medkit = { name = 'medkit', label = 'Kit médico', weight = 1500, type = 'item', image = 'medkit.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'INEM: levanta quem está caído' },
    defibrillator = { name = 'defibrillator', label = 'Desfibrilhador', weight = 4000, type = 'item', image = 'defibrillator.png', unique = true, useable = true, shouldClose = true, combinable = nil, description = 'INEM: reanima quem não tem pulso' },
    firstaid = { name = 'firstaid', label = 'Kit de primeiros socorros', weight = 800, type = 'item', image = 'firstaid.png', unique = false, useable = true, shouldClose = true, combinable = nil, description = 'Levanta alguém que está caído' },

ox_inventory / Qbox — dentro de ox_inventory/data/items.lua:

items_ox_inventory.lua
-- drs_ambulancejob: cola dentro de ox_inventory/data/items.lua
-- Imagens: copia install/images/*.png para ox_inventory/web/images/

    ['bandage'] = { label = 'Ligadura', weight = 100, stack = true, close = true, description = 'Para hemorragias ligeiras', client = { image = 'bandage.png' } },
    ['gauze'] = { label = 'Compressas', weight = 80, stack = true, close = true, description = 'Estanca hemorragias e feridas de bala', client = { image = 'gauze.png' } },
    ['tourniquet'] = { label = 'Garrote', weight = 150, stack = true, close = true, description = 'Pára hemorragias graves num braço ou perna', client = { image = 'tourniquet.png' } },
    ['splint'] = { label = 'Tala', weight = 400, stack = true, close = true, description = 'Imobiliza fraturas', client = { image = 'splint.png' } },
    ['burndressing'] = { label = 'Penso para queimaduras', weight = 100, stack = true, close = true, description = 'Trata queimaduras', client = { image = 'burndressing.png' } },
    ['painkillers'] = { label = 'Analgésicos', weight = 50, stack = true, close = true, description = 'Tiram a dor durante uns minutos', client = { image = 'painkillers.png' } },
    ['medkit'] = { label = 'Kit médico', weight = 1500, stack = true, close = true, description = 'INEM: levanta quem está caído', client = { image = 'medkit.png' } },
    ['defibrillator'] = { label = 'Desfibrilhador', weight = 4000, stack = false, close = true, description = 'INEM: reanima quem não tem pulso', client = { image = 'defibrillator.png' } },
    ['firstaid'] = { label = 'Kit de primeiros socorros', weight = 800, stack = true, close = true, description = 'Levanta alguém que está caído', client = { image = 'firstaid.png' } },

ESX — importa na base de dados:

items_esx.sql
-- drs_ambulancejob: itens para ESX (es_extended)
INSERT IGNORE INTO `items` (`name`, `label`, `weight`) VALUES
    ('bandage', 'Ligadura', 1),
    ('gauze', 'Compressas', 1),
    ('tourniquet', 'Garrote', 1),
    ('splint', 'Tala', 4),
    ('burndressing', 'Penso para queimaduras', 1),
    ('painkillers', 'Analgésicos', 1),
    ('medkit', 'Kit médico', 15),
    ('defibrillator', 'Desfibrilhador', 40),
    ('firstaid', 'Kit de primeiros socorros', 8);
ItemO que trataQuem usa
bandageHemorragia ligeiraQualquer pessoa, em si
gauzeHemorragia e ferida de balaQualquer pessoa, em si
tourniquetHemorragia grave (só braços e pernas)Qualquer pessoa, em si
splintFraturasQualquer pessoa, em si
burndressingQueimadurasQualquer pessoa, em si
painkillersTira os efeitos durante uns minutosQualquer pessoa, em si
firstaidLevanta quem está caídoQualquer pessoa, noutro jogador
medkitLevanta quem está caídoMédicos
defibrillatorReanima um morto (dentro do prazo); não se gastaMédicos

#Como funciona

#Ferimentos

O tipo de ferimento vem do dano do GTA (bala, lâmina, pancada, queda, fogo, explosão, atropelamento, afogamento) e fica na zona do corpo atingida: cabeça, pescoço, peito, abdómen, braços ou pernas. Há hemorragia, ferida de bala, fratura, queimadura e asfixia, em 4 níveis. O colete protege o tronco das balas.

FerimentoEfeito
HemorragiaPerde vida de tempos a tempos, com vinheta vermelha
Pernas ou coluna partidasCoxeia e não corre
Braços partidosA mira treme
CabeçaVisão turva de vez em quando

Os analgésicos tiram os efeitos durante Config.Injuries.painkillerTime segundos. Qualquer pessoa vê os seus ferimentos com /ferimentos.

#Estados

  • Inconsciente: murros e pancadas (as armas de Config.Knockout) só deixam a pessoa inconsciente durante uns segundos.
  • Ferido no chão: tem downTime segundos até morrer a sangrar. Carrega em G para mandar o sinal de socorro aos médicos de serviço, com ponto no mapa.
  • Morto: ao fim de respawnTime segundos pode renascer no hospital mais perto (segurando E), pagando a conta respawnPrice. Quem sai do jogo morto volta morto.

#Ecrã de morte

Em cada estado aparece o ecrã de morte: tempo que falta, monitor cardíaco com o batimento (abranda à medida que o tempo acaba), causa (tiro, queda, atropelamento…), arma, autor, distância e hora, as teclas G (socorro) e E (hospital) e quantos médicos estão de serviço. Tudo se liga e desliga em Config.DeathScreen.

Examinar (abre o painel do corpo, com os ferimentos de cada zona e os itens que os tratam), tratar, reanimar com o kit médico, desfibrilhador, RCP, carregar, meter e tirar do veículo, dar entrada no hospital e passar contas (o paciente aceita ou recusa).

Dica

O desfibrilhador só resulta nos primeiros defibWindow segundos depois da morte. Fazer RCP pára esse relógio enquanto chega o desfibrilhador.

#Hospital

O balcão de check-in trata quem chega só quando há poucos médicos de serviço (Config.CheckIn.maxDoctorsOnline). O jogador fica numa cama durante o tratamento. As camas e os pontos de cada hospital estão em Config.Hospitals.

#INEM por IA (NPC)

Quando não há médicos de serviço (Config.AIMedic.maxEmsOnline), quem está caído ou morto chama o INEM com G no ecrã de morte ou com /inem:

  1. No hospital mais perto aparecem dois paramédicos com as malas na mão, que vão a pé até à ambulância e entram.
  2. A ambulância vem com as luzes e a sirene ligadas até 5 m de ti. Se estiveres longe da estrada (onFootDistance), deixam-na na estrada e vão a pé. Os carros dos NPC à volta travam para a deixar passar e, se ficar presa, aparece na estrada mais perto.
  3. Um faz RCP e o outro fica de pé com a prancheta. O ecrã de morte mostra o estado: a caminho (com a distância), a pé e a tratar.
  4. No fim, o servidor reanima-te e cobra Config.AIMedic.price. Os paramédicos voltam para a ambulância e vão-se embora.

Os pontos de cada hospital estão em Config.Hospitals[].aiMedic: ped (onde aparecem os paramédicos) e vehicle (onde está a ambulância). Com refuseGunshot, o INEM por IA não vai a feridos a tiro. Se um médico ou o staff te reanimar antes, a ambulância vai-se embora.

Nota

O INEM por IA tem ideias do FearX-NpcMedic (AGPL-3.0), usadas com autorização do autor. O código é nosso.

#Maca

O médico de serviço, junto à traseira de uma ambulância (Config.Stretcher.vehicles), carrega H para tirar a maca:

  • E pegar ou largar a maca (a empurrar não se corre nem se entra em carros);
  • G deitar o doente caído ou morto na maca, ou levantá-lo;
  • H junto à traseira da ambulância: guarda a maca e, com doente, põe-no dentro da ambulância.

A maca é a do próprio GTA (prop_ld_binbag_01). As posições da maca e do doente afinam-se em Config.Stretcher.push e lie.

#Mapa e blips

  • Hospitais e pedidos de socorro: ícone, cor e tamanho em Config.Blips.hospital e Config.Blips.distress.
  • Colegas de serviço: cada médico de serviço vê os outros no mapa, mesmo longe (de interval em interval segundos). O ícone muda quando vão num veículo e desaparece quando saem de serviço.

Contratar, despedir, cargos e dinheiro da empresa ficam no teu script de menu do patrão (drs_bossmenu ou qb-management). Em Config.BossMenu.mode:

  • 'bossmenu' — usa o ponto desse script, com as coordenadas dele;
  • 'config' — o ambulance põe os pontos de Config.BossMenu.points e, no E, abre o menu do patrão desse script (que confirma se és chefe).

#Comandos

ComandoQuemO que faz
/ferimentosTodosVer os teus ferimentos
/rcpTodosFazer RCP ao jogador mais perto
/carregarTodosCarregar o jogador mais perto
F6MédicosMenu do paciente
GFeridoSinal de socorro (sem médicos de serviço: chama o INEM por IA)
/inemFeridoChamar o INEM por IA (sem médicos de serviço)
H / E / GMédicosMaca: tirar e guardar, pegar e largar, deitar e levantar o doente
/revive [id]StaffReanimar
/heal [id]StaffCurar
/reviveallStaffReanimar toda a gente

#Opções principais

OpçãoPor defeitoDescrição
Config.Locale'pt'Idioma: pt ou en
Config.Jobs{ 'ambulance' }Empregos que contam como médicos
Config.RequireDutytrueSó de serviço para o menu e os alertas
Config.Death.downTime180Segundos no chão até morrer
Config.Death.respawnTime300Segundos morto até poder renascer
Config.Death.respawnPrice2500Conta do hospital ao renascer
Config.Death.clearInventoryfalseTirar os itens ao renascer
Config.Death.defibWindow120Segundos em que o desfibrilhador resulta
Config.CheckIn.price1500Preço do tratamento no balcão
Config.BillToSocietytrueContas vão para a conta da empresa
Config.AIMedic.enabledtrueINEM por IA quando não há médicos de serviço
Config.AIMedic.price2500Preço do INEM por IA
Config.AIMedic.refuseGunshottrueO INEM por IA não vai a feridos a tiro
Config.Stretcher.enabledtrueMaca nas ambulâncias
Config.Blips.colleagues.enabledtrueMédicos de serviço veem os colegas no mapa
Config.BossMenu.mode'bossmenu'Ponto do menu do patrão: o do teu script ou os do config
Config.AntiCheat.enabledtrueProteção contra revive

#Configuração completa

config.lua
Config = {}

-- ------------------------------------------------------------------ geral / general
Config.Locale = 'pt'            -- 'pt' | 'en'
Config.Framework = 'auto'       -- 'auto' | 'qb' | 'qbx' | 'esx' | 'none'
Config.UseOxNotify = false      -- usar as notificações do ox_lib (se estiver ligado)
Config.Debug = false

-- empregos que contam como médicos (INEM) / jobs that count as EMS
Config.Jobs = { 'ambulance' }
Config.RequireDuty = true       -- os médicos têm de estar de serviço para usar o menu e receber alertas

-- ------------------------------------------------------------------ morte / death
Config.Death = {
    downTime = 180,         -- segundos caído (ferido) até morrer a sangrar
    respawnTime = 300,      -- segundos morto até poder renascer no hospital
    respawnHold = 3,        -- segundos a segurar a tecla para renascer
    respawnKey = 38,        -- E (controlo do GTA)
    distressKey = 47,       -- G (controlo do GTA)
    distressCooldown = 60,  -- segundos entre sinais de socorro
    defibWindow = 120,      -- segundos depois de morrer em que o desfibrilhador ainda resulta (a RCP pára este relógio)
    respawnPrice = 2500,    -- conta do hospital ao renascer (0 = grátis)
    clearInventory = false, -- tirar os itens ao renascer
    keepItems = { 'id_card', 'driver_license', 'phone', 'radio' }, -- itens que nunca se perdem
    downHealth = 150,       -- vida com que fica caído (100 = morto, 200 = cheia)
    reviveHealth = 130,     -- vida depois de ser reanimado no local
}

-- Armas que só deixam a pessoa inconsciente (não a matam) / weapons that only knock out
Config.Knockout = {
    enabled = true,
    seconds = 25,
    weapons = { 'WEAPON_UNARMED', 'WEAPON_BAT', 'WEAPON_GOLFCLUB', 'WEAPON_HAMMER', 'WEAPON_KNUCKLE', 'WEAPON_NIGHTSTICK',
        'WEAPON_WRENCH', 'WEAPON_POOLCUE', 'WEAPON_FLASHLIGHT', 'WEAPON_CANDYCANE', 'WEAPON_SNOWBALL' },
}

-- ------------------------------------------------------------------ ferimentos / injuries
Config.Injuries = {
    minDamage = 6,          -- dano mínimo de um golpe para contar como ferimento
    bleedTick = 20,         -- segundos entre perdas de sangue
    bleedDamage = 3,        -- vida perdida por nível de hemorragia em cada tick
    maxLevel = 4,           -- nível máximo de cada ferimento (1 ligeiro · 2 moderado · 3 grave · 4 crítico)
    limp = true,            -- fraturas nas pernas/coluna: coxeia e não corre
    armShake = true,        -- fraturas nos braços: a mira treme
    headBlur = true,        -- ferimentos na cabeça: visão turva de vez em quando
    painkillerTime = 300,   -- segundos que os analgésicos tiram os efeitos
    saveToDatabase = true,  -- os ferimentos ficam guardados quando sai do servidor (metadata do QBCore)
}

-- ------------------------------------------------------------------ itens / items
-- cure: o que cada item trata. zone = escolhe a zona (no painel); self = pode usar em si próprio
Config.Items = {
    bandage = { cure = { bleeding = 1 }, heal = 15, self = true, time = 4 },
    gauze = { cure = { bleeding = 2, gunshot = 1 }, heal = 10, self = true, time = 5 },
    tourniquet = { cure = { bleeding = 4 }, limbsOnly = true, self = true, time = 5 },
    splint = { cure = { fracture = 4 }, self = true, time = 7 },
    burndressing = { cure = { burn = 3 }, self = true, time = 5 },
    painkillers = { painkiller = true, heal = 5, self = true, time = 3 },
    medkit = { revive = 'down', heal = 40, self = false, time = 8, emsOnly = true },        -- levanta quem está caído
    defibrillator = { revive = 'dead', self = false, time = 6, emsOnly = true, keep = true }, -- reanima um morto (dentro do prazo); não gasta
    firstaid = { revive = 'down', heal = 20, self = false, time = 12, emsOnly = false },    -- kit de primeiros socorros: qualquer pessoa
}

-- ------------------------------------------------------------------ menu dos médicos / EMS menu
Config.MenuKey = 'F6'
Config.InteractDistance = 3.0
Config.BillMax = 50000
Config.BillToSociety = true     -- o dinheiro das contas vai para a conta da empresa (qb-banking / qb-management / Renewed)

-- ------------------------------------------------------------------ hospitais / hospitals
Config.CheckIn = {
    price = 1500,               -- preço do tratamento no balcão
    maxDoctorsOnline = 2,       -- com mais médicos de serviço do que isto, o balcão não trata (vai ter com eles)
    time = 15,                  -- segundos na cama
    key = 38,                   -- E
}

Config.Hospitals = {
    {
        label = 'Pillbox Hill',
        checkin = vector3(308.19, -595.35, 43.29),
        respawn = vector4(298.74, -584.33, 43.26, 70.0),
        blip = vector3(298.74, -584.33, 43.26),
        -- INEM por IA: onde aparecem os paramédicos (vão a pé até à ambulância) e onde está a ambulância
        aiMedic = { ped = vector4(298.74, -584.33, 43.26, 160.0), vehicle = vector4(294.30, -607.90, 43.33, 70.0) },
        beds = {
            vector4(353.10, -584.60, 43.11, 152.08), vector4(356.79, -585.86, 43.11, 152.08), vector4(354.12, -593.12, 43.10, 336.32),
            vector4(350.79, -591.80, 43.10, 336.32), vector4(346.99, -590.48, 43.10, 336.32), vector4(360.32, -587.19, 43.02, 152.08),
        },
    },
    {
        label = 'Paleto Bay',
        checkin = vector3(-254.54, 6331.78, 32.43),
        respawn = vector4(-248.97, 6331.42, 32.43, 225.0),
        blip = vector3(-254.54, 6331.78, 32.43),
        aiMedic = { ped = vector4(-248.97, 6331.42, 32.43, 315.0), vehicle = vector4(-262.10, 6346.00, 32.40, 315.0) },
        beds = {
            vector4(-252.43, 6312.25, 32.34, 313.48), vector4(-247.04, 6317.95, 32.34, 134.64), vector4(-255.98, 6315.67, 32.34, 313.91),
        },
    },
    {
        label = 'Sandy Shores',
        checkin = vector3(1839.36, 3672.90, 34.28),
        respawn = vector4(1836.02, 3670.52, 34.28, 210.0),
        blip = vector3(1839.36, 3672.90, 34.28),
        aiMedic = { ped = vector4(1836.02, 3670.52, 34.28, 30.0), vehicle = vector4(1828.80, 3693.60, 34.22, 30.0) },
        beds = {},
    },
}

-- ------------------------------------------------------------------ alertas / alerts
Config.Alerts = {
    blipTime = 90,              -- segundos que o ponto do pedido de socorro fica no mapa dos médicos
    sound = true,
}

-- ------------------------------------------------------------------ menu do patrão (contratar, despedir, cargos, dinheiro)
-- mode = 'bossmenu' -> usa o ponto do teu script de menu do patrão (drs_bossmenu / qb-management), com as coords DELE
-- mode = 'config'   -> o ambulance põe os pontos de baixo e, no [E], abre o menu do patrão desse script (só para chefes)
Config.BossMenu = {
    mode = 'bossmenu',
    points = { vector3(335.46, -594.52, 43.28) }, -- só com mode = 'config'
    resource = 'auto',      -- 'auto' | 'drs_bossmenu' | 'qb-management'
    key = 38,               -- E
}

-- ------------------------------------------------------------------ blips (mapa)
Config.Blips = {
    hospital = { sprite = 61, color = 2, scale = 0.8, shortRange = true },            -- hospitais (toda a gente)
    distress = { sprite = 153, color = 1, scale = 1.1, flash = true },                -- pedido de socorro (médicos de serviço)
    -- colegas de serviço: cada médico de serviço vê os outros no mapa (mesmo longe)
    colleagues = { enabled = true, sprite = 1, vehicleSprite = 56, color = 1, scale = 0.75, interval = 3, heading = true, label = 'INEM' },
}

-- ------------------------------------------------------------------ maca / stretcher
-- O médico tira a maca da traseira da ambulância, empurra-a até ao doente, deita-o nela e volta a pô-la na ambulância
-- (com o doente lá dentro). Usa a maca do próprio GTA (prop_ld_binbag_01), sem modelos de fora.
Config.Stretcher = {
    enabled = true,
    emsOnly = true,         -- só os médicos de serviço tiram a maca
    model = 'prop_ld_binbag_01',
    vehicles = { 'ambulance' }, -- veículos que trazem maca (modelos)
    rearDistance = 3.0,     -- distância à traseira do veículo para tirar / guardar
    keys = { grab = 38, patient = 47, vehicle = 74 }, -- E pegar/largar · G deitar/levantar o doente · H tirar/guardar na ambulância
    labels = { grab = 'E', patient = 'G', vehicle = 'H' }, -- texto das teclas no ecrã
    -- posição da maca à frente de quem empurra e do doente deitado nela (afinar se precisares)
    push = { offset = vector3(0.0, 1.45, -0.43), rot = vector3(0.0, 0.0, 180.0) },
    lie = { offset = vector3(0.0, 0.0, 1.38), rot = vector3(0.0, 0.0, 180.0) },
}

-- ------------------------------------------------------------------ INEM por IA / AI medic
-- Quando não há médicos de serviço, quem está caído/morto chama uma ambulância do hospital mais perto (Config.Hospitals[].aiMedic):
-- dois paramédicos saem a pé com as malas, entram na ambulância e vão com as luzes ligadas até ti. Se estiveres longe
-- da estrada, deixam a ambulância na estrada e vão a pé. O servidor é que reanima e cobra.
Config.AIMedic = {
    enabled = true,
    maxEmsOnline = 0,       -- só aparece quando os médicos de serviço são no máximo este número
    refuseGunshot = true,   -- não vai a feridos a tiro (ficam para os médicos a sério ou para o hospital)
    onDistress = true,      -- o [G] do ecrã de morte chama o INEM por IA quando não há médicos
    command = 'inem',       -- comando para chamar ('' = sem comando)
    price = 2500,           -- cobrado do banco (ou do dinheiro) quando és reanimado
    pedModel = 's_m_m_paramedic_01',
    vehicleModel = 'ambulance',
    crew = 2,               -- paramédicos na ambulância (1 ou 2)
    speed = 35.0,           -- m/s (~125 km/h) com as luzes e a sirene ligadas
    drivingStyle = 787004,  -- condução de emergência (passa os semáforos, contorna os carros)
    stopDistance = 5.0,     -- pára a esta distância de ti
    onFootDistance = 20.0,  -- mais longe do que isto da estrada: deixa a ambulância na estrada e vai a pé
    maxDriveSeconds = 150,  -- se demorar mais (preso no trânsito), a ambulância aparece na estrada mais perto de ti
    reviveSeconds = 12,     -- tempo do tratamento
    minSeconds = 15,        -- o servidor não aceita uma reanimação antes disto (desde a chamada)
    cooldown = 120,         -- segundos entre chamadas
    -- mala na mão (prop do jogo base); se não existir usa o kit pequeno
    bag = { model = 'xm_prop_x17_bag_med_01a', fallback = 'prop_ld_health_pack', bone = 57005,
        offset = vector3(0.28, 0.0, -0.02), rot = vector3(0.0, 270.0, 70.0) },
    blip = { sprite = 56, color = 1, label = 'INEM (IA)' },
}

-- ------------------------------------------------------------------ ecrã de morte / death screen
Config.DeathScreen = {
    showCause = true,       -- causa (tiro, queda, atropelamento…)
    showWeapon = true,      -- arma que te deitou abaixo
    showKiller = true,      -- id de quem te deitou abaixo (#12)
    showDistance = true,    -- distância a que estava quem te atacou
    showEms = true,         -- quantos médicos estão de serviço
    sound = { enabled = true, volume = 0.25 }, -- bip do monitor quando estás caído (sem ficheiros de som)
    -- batimento cardíaco (bpm) mostrado no monitor
    bpm = { knocked = 64, downStart = 78, downEnd = 36 }, -- caído: começa em downStart e desce até downEnd quando o tempo acaba
    keys = { distress = 'G', respawn = 'E' },             -- só o texto das teclas (as teclas são Config.Death.distressKey/respawnKey)
}

-- ------------------------------------------------------------------ proteção contra revive / anti-revive
-- Caído ou morto, só sais desse estado pelo servidor: /revive (staff), médico com o item, cama do hospital,
-- renascer no hospital quando o tempo acaba, ou o "Heal" do txAdmin. Mod menus que se reanimam sozinhos são apanhados.
Config.AntiCheat = {
    enabled = true,
    -- scripts de outros (TriggerClientEvent('hospital:client:Revive', id)) ainda podem reanimar? false = só os caminhos acima
    externalRevive = false,
    maxMove = 12.0,         -- metros que um jogador caído/morto pode "andar" em 2 s sem estar carregado ou num veículo
    blockShooting = true,   -- o dano feito por quem está caído/morto é anulado
    action = 'log',         -- 'log' = só regista e repõe · 'kick' = expulsa ao fim de kickAfter deteções
    kickAfter = 3,
}

-- Webhook do Discord para registos (reanimações, contas, check-ins). Vazio = desligado.
Config.Webhook = ''

#Compatibilidade com o qb-ambulancejob

O recurso faz provide 'qb-ambulancejob' e responde aos eventos hospital:client:Revive, hospital:client:HealInjuries e hospital:client:KillPlayer, e mantém a metadata isdead e inlaststand. Os scripts feitos para o qb-ambulancejob (HUD, telemóvel, polícia…) funcionam sem mudanças — menos o revive dos menus de admin (ver abaixo).

Importante

O teu menu de admin tem de receber estas linhas para conseguir dar revive. Com a proteção contra revive ligada (Config.AntiCheat), quem está caído ou morto só é reanimado pelo servidor. Os menus de admin que reanimam com TriggerClientEvent('hospital:client:Revive', id) (izzy-admin, ps-adminmenu, qb-adminmenu…) deixam de reanimar quem está caído ou morto, porque um mod menu consegue disparar esse mesmo evento. No ficheiro do servidor do teu menu de admin, troca a linha do revive pela de baixo. O /revive [id] e o "Heal" do txAdmin funcionam sempre, sem mudanças. Se preferires não mexer no menu, põe Config.AntiCheat.externalRevive = true (mas assim um mod menu também se consegue reanimar).

menu de admin (servidor) — antes
TriggerClientEvent('hospital:client:Revive', id)
menu de admin (servidor) — depois
if GetResourceState('drs_ambulancejob') == 'started' then
    TriggerEvent('drs_ambulancejob:revivePlayer', id) -- reanimar um jogador
else
    TriggerClientEvent('hospital:client:Revive', id)
end

Para reanimar todos:

menu de admin (servidor)
for _, p in ipairs(GetPlayers()) do TriggerEvent('drs_ambulancejob:revivePlayer', tonumber(p)) end

#Proteção contra revive

Caído ou morto, só se sai desse estado pelo servidor: /revive do staff, médico com o item, cama do hospital, renascer no hospital quando o tempo acaba ou o "Heal" do txAdmin.

  • Os eventos de reanimação disparados no próprio cliente (mod menus) são ignorados: o cliente só obedece ao estado que o servidor confirma.
  • Quem está caído ou morto e se mexe mais de maxMove metros em 2 segundos (sem estar a ser carregado nem num veículo) volta para onde estava e o estado é reposto.
  • O dano feito por quem está caído ou morto é anulado (blockShooting).
  • Cada deteção fica no webhook e na consola. Com action = 'kick' o jogador é expulso ao fim de kickAfter deteções. O teu anticheat pode ouvir AddEventHandler('drs_ambulancejob:cheatSuspect', function(src, motivo, n) end).

#Para programadores

servidor
exports.drs_ambulancejob:GetState(src)          -- 'alive' | 'knocked' | 'down' | 'dead'
exports.drs_ambulancejob:IsDead(src)
exports.drs_ambulancejob:Revive(src)
exports.drs_ambulancejob:GetInjuries(src)
exports.drs_ambulancejob:SetInjuries(src, lista)
exports.drs_ambulancejob:SendAlert(src, coords, rua)

AddEventHandler('drs_ambulancejob:stateChanged', function(src, estado, anterior) end)
cliente
exports.drs_ambulancejob:IsDead()
exports.drs_ambulancejob:GetState()
exports.drs_ambulancejob:GetInjuries()

#Segurança

O servidor é dono do estado e dos ferimentos. Valida o emprego, a distância, os itens e os tempos de cada ação: ninguém se reanima a si próprio nem trata quem está longe. Ver também a proteção contra revive.

#Resolução de problemas

Aparecem dois ecrãs de morte — tens outro script de morte ligado (qb-ambulancejob, drs_deathscreen…). Desliga-o.

O F6 não abre — tens de ter um emprego de Config.Jobs e, com Config.RequireDuty, estar de serviço.

Os médicos não recebem o sinal de socorro — têm de estar de serviço. Se usas o drs_dispatch ou o cd_dispatch, o alerta aparece lá.

O desfibrilhador não resulta — passaram mais de defibWindow segundos desde a morte. Faz RCP enquanto o desfibrilhador não chega.

O INEM por IA não vem — há médicos de serviço (maxEmsOnline), tens feridas de bala (refuseGunshot), chamaste há pouco (cooldown) ou o hospital não tem aiMedic no config.

A maca não sai da ambulância — o modelo da tua ambulância tem de estar em Config.Stretcher.vehicles e tens de estar de serviço, junto à traseira.