414 lines
14 KiB
Lua
414 lines
14 KiB
Lua
local ESX = exports['es_extended']:getSharedObject()
|
|
|
|
-- ============================================================
|
|
-- STATE TRACKING
|
|
-- ============================================================
|
|
|
|
local PlayerDeathStates = {} -- [serverId] = 'UNCONSCIOUS' | 'BLEEDING_OUT' | 'DEAD'
|
|
|
|
-- Broadcastet die Liste aller bewusstlosen Spieler an alle Clients (für ox_target)
|
|
local function BroadcastDownedPlayers()
|
|
local downedList = {}
|
|
for playerId, _ in pairs(PlayerDeathStates) do
|
|
downedList[playerId] = true
|
|
end
|
|
TriggerClientEvent('mercyv-deathscreen:client:syncDownedPlayers', -1, downedList)
|
|
end
|
|
|
|
-- ============================================================
|
|
-- STATE UPDATE
|
|
-- ============================================================
|
|
|
|
RegisterNetEvent('mercyv-deathscreen:server:updateState')
|
|
AddEventHandler('mercyv-deathscreen:server:updateState', function(state)
|
|
local src = source
|
|
|
|
if state == 'ALIVE' then
|
|
PlayerDeathStates[src] = nil
|
|
else
|
|
PlayerDeathStates[src] = state
|
|
end
|
|
|
|
BroadcastDownedPlayers()
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- RESPAWN (Server-authoritative Verluste)
|
|
-- ============================================================
|
|
|
|
RegisterNetEvent('mercyv-deathscreen:server:respawn')
|
|
AddEventHandler('mercyv-deathscreen:server:respawn', function()
|
|
local src = source
|
|
|
|
-- Validieren dass Spieler bewusstlos/verblutend ist
|
|
if not PlayerDeathStates[src] then return end
|
|
|
|
local xPlayer = ESX.GetPlayerFromId(src)
|
|
if not xPlayer then return end
|
|
|
|
-- Geld entfernen
|
|
if Config.LoseMoney then
|
|
if Config.MoneyLossType == 'money' or Config.MoneyLossType == 'both' then
|
|
local cash = xPlayer.getMoney()
|
|
local loss = math.floor(cash * (Config.MoneyLossPercent / 100))
|
|
if loss > 0 then
|
|
xPlayer.removeMoney(loss)
|
|
end
|
|
end
|
|
|
|
if Config.MoneyLossType == 'black_money' or Config.MoneyLossType == 'both' then
|
|
local blackMoney = xPlayer.getAccount('black_money')
|
|
if blackMoney then
|
|
local loss = math.floor(blackMoney.money * (Config.MoneyLossPercent / 100))
|
|
if loss > 0 then
|
|
xPlayer.removeAccountMoney('black_money', loss)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Items entfernen (codem-inventory)
|
|
if Config.LoseItems then
|
|
local identifier = xPlayer.getIdentifier()
|
|
local inventory = exports['codem-inventory']:GetInventory(identifier, src)
|
|
if inventory then
|
|
if #Config.ItemsToLose == 0 then
|
|
-- Alle Items entfernen
|
|
for _, item in pairs(inventory) do
|
|
if item.name and item.amount and item.amount > 0 then
|
|
exports['codem-inventory']:RemoveItem(src, item.name, item.amount)
|
|
end
|
|
end
|
|
else
|
|
-- Nur bestimmte Items entfernen
|
|
for _, item in pairs(inventory) do
|
|
if item.name and item.amount and item.amount > 0 then
|
|
for _, loseName in ipairs(Config.ItemsToLose) do
|
|
if item.name == loseName then
|
|
exports['codem-inventory']:RemoveItem(src, item.name, item.amount)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Waffen entfernen
|
|
if Config.LoseWeapons then
|
|
local loadout = xPlayer.getLoadout()
|
|
local weaponsToRemove = {}
|
|
|
|
for _, weapon in ipairs(loadout) do
|
|
local keep = false
|
|
for _, keepWeapon in ipairs(Config.WeaponsToKeep) do
|
|
if weapon.name == keepWeapon then
|
|
keep = true
|
|
break
|
|
end
|
|
end
|
|
if not keep then
|
|
weaponsToRemove[#weaponsToRemove + 1] = weapon.name
|
|
end
|
|
end
|
|
|
|
for _, weaponName in ipairs(weaponsToRemove) do
|
|
xPlayer.removeWeapon(weaponName)
|
|
end
|
|
end
|
|
|
|
-- Hunger & Durst auf voll setzen
|
|
TriggerClientEvent('esx_status:set', src, 'hunger', 1000000)
|
|
TriggerClientEvent('esx_status:set', src, 'thirst', 1000000)
|
|
|
|
-- State aufräumen und Client zum Respawn schicken
|
|
PlayerDeathStates[src] = nil
|
|
BroadcastDownedPlayers()
|
|
TriggerClientEvent('mercyv-deathscreen:client:doRespawn', src)
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- REVIVE SYSTEM
|
|
-- ============================================================
|
|
|
|
local function RevivePlayer(targetId)
|
|
targetId = tonumber(targetId)
|
|
if not targetId then return false end
|
|
if not PlayerDeathStates[targetId] then return false end
|
|
|
|
-- Hunger & Durst auf voll setzen
|
|
TriggerClientEvent('esx_status:set', targetId, 'hunger', 1000000)
|
|
TriggerClientEvent('esx_status:set', targetId, 'thirst', 1000000)
|
|
|
|
PlayerDeathStates[targetId] = nil
|
|
BroadcastDownedPlayers()
|
|
TriggerClientEvent('mercyv-deathscreen:client:revived', targetId)
|
|
return true
|
|
end
|
|
|
|
-- EMS / Zivilist belebt jemanden wieder
|
|
RegisterNetEvent('mercyv-deathscreen:server:revivePlayer')
|
|
AddEventHandler('mercyv-deathscreen:server:revivePlayer', function(targetId)
|
|
local src = source
|
|
local xPlayer = ESX.GetPlayerFromId(src)
|
|
if not xPlayer then return end
|
|
|
|
if not PlayerDeathStates[targetId] then return end
|
|
|
|
-- Prüfen ob Spieler EMS ist
|
|
local isEMS = false
|
|
for _, jobName in ipairs(Config.EMSJobNames) do
|
|
if xPlayer.job.name == jobName then
|
|
isEMS = true
|
|
break
|
|
end
|
|
end
|
|
|
|
if isEMS then
|
|
RevivePlayer(targetId)
|
|
return
|
|
end
|
|
|
|
-- Zivilist: Nur wenn CitizenRevive aktiviert ist und Item vorhanden
|
|
if Config.CitizenRevive then
|
|
local item = xPlayer.getInventoryItem(Config.CitizenReviveItem)
|
|
if item and item.count > 0 then
|
|
xPlayer.removeInventoryItem(Config.CitizenReviveItem, 1)
|
|
RevivePlayer(targetId)
|
|
TriggerClientEvent('esx:showNotification', src, Config.Locale.reviveItemUsed or (Config.CitizenReviveItem .. ' benutzt'))
|
|
else
|
|
TriggerClientEvent('esx:showNotification', src, Config.Locale.reviveNoItem or ('Du brauchst ein ' .. Config.CitizenReviveItem))
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Admin-Command: /revive [id]
|
|
RegisterCommand('revive', function(source, args)
|
|
local src = source
|
|
|
|
if src == 0 then
|
|
local targetId = tonumber(args[1])
|
|
if targetId then
|
|
RevivePlayer(targetId)
|
|
print('[mercyv-deathscreen] Spieler ' .. targetId .. ' wiederbelebt')
|
|
else
|
|
print('[mercyv-deathscreen] Benutzung: /revive [spieler-id]')
|
|
end
|
|
return
|
|
end
|
|
|
|
local xPlayer = ESX.GetPlayerFromId(src)
|
|
if not xPlayer then return end
|
|
|
|
if xPlayer.getGroup() ~= 'admin' and xPlayer.getGroup() ~= 'superadmin' then
|
|
TriggerClientEvent('esx:showNotification', src, 'Keine Berechtigung!')
|
|
return
|
|
end
|
|
|
|
local targetId = tonumber(args[1])
|
|
if not targetId then
|
|
targetId = src
|
|
end
|
|
|
|
if RevivePlayer(targetId) then
|
|
TriggerClientEvent('esx:showNotification', src, 'Spieler ' .. targetId .. ' wiederbelebt!')
|
|
else
|
|
TriggerClientEvent('esx:showNotification', src, 'Spieler ist nicht bewusstlos/tot!')
|
|
end
|
|
end, false)
|
|
|
|
exports('RevivePlayer', RevivePlayer)
|
|
|
|
-- ============================================================
|
|
-- WUNDENBEHANDLUNG (auf lebende Spieler)
|
|
-- ============================================================
|
|
|
|
RegisterNetEvent('mercyv-deathscreen:server:healPlayer')
|
|
AddEventHandler('mercyv-deathscreen:server:healPlayer', function(targetId, healType)
|
|
local src = source
|
|
local xPlayer = ESX.GetPlayerFromId(src)
|
|
if not xPlayer then return end
|
|
|
|
-- Nur EMS darf heilen
|
|
local isEMS = false
|
|
for _, jobName in ipairs(Config.EMSJobNames) do
|
|
if xPlayer.job.name == jobName then
|
|
isEMS = true
|
|
break
|
|
end
|
|
end
|
|
if not isEMS then return end
|
|
|
|
-- Ziel-Spieler muss existieren und lebendig sein
|
|
local targetPed = GetPlayerPed(targetId)
|
|
if not targetPed or targetPed <= 0 then return end
|
|
if PlayerDeathStates[targetId] then return end -- Bewusstlose nicht heilen, dafür gibts Revive
|
|
|
|
local currentHealth = GetEntityHealth(targetPed)
|
|
local maxHealth = GetEntityMaxHealth(targetPed)
|
|
local newHealth = currentHealth
|
|
|
|
if healType == 'big' then
|
|
-- Große Wunden: Heilt auf X% der max HP
|
|
newHealth = math.floor(maxHealth * (Config.HealBigPercent or 1.0))
|
|
elseif healType == 'small' then
|
|
-- Kleine Wunden: Heilt um X HP
|
|
newHealth = currentHealth + (Config.HealSmallAmount or 50)
|
|
end
|
|
|
|
-- HP nicht über max
|
|
if newHealth > maxHealth then newHealth = maxHealth end
|
|
|
|
TriggerClientEvent('mercyv-deathscreen:client:healed', targetId, newHealth, healType)
|
|
|
|
-- Notification an den Medic
|
|
if healType == 'big' then
|
|
TriggerClientEvent('esx:showNotification', src, Config.Locale.healBigDone or 'Große Wunden behandelt')
|
|
else
|
|
TriggerClientEvent('esx:showNotification', src, Config.Locale.healSmallDone or 'Kleine Wunden behandelt')
|
|
end
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- SELBSTHEILUNG (Bandage)
|
|
-- ============================================================
|
|
|
|
if Config.SelfHeal then
|
|
ESX.RegisterUsableItem(Config.SelfHealItem or 'bandage', function(playerId)
|
|
-- Nicht wenn bewusstlos/tot
|
|
if PlayerDeathStates[playerId] then return end
|
|
|
|
local xPlayer = ESX.GetPlayerFromId(playerId)
|
|
if not xPlayer then return end
|
|
|
|
xPlayer.removeInventoryItem(Config.SelfHealItem or 'bandage', 1)
|
|
TriggerClientEvent('mercyv-deathscreen:client:selfHeal', playerId)
|
|
end)
|
|
end
|
|
|
|
-- ============================================================
|
|
-- EMS BENACHRICHTIGUNG
|
|
-- ============================================================
|
|
|
|
RegisterNetEvent('mercyv-deathscreen:server:callEMS')
|
|
AddEventHandler('mercyv-deathscreen:server:callEMS', function(coords, streetName)
|
|
local src = source
|
|
|
|
if not PlayerDeathStates[src] then return end
|
|
|
|
local xPlayers = ESX.GetPlayers()
|
|
for _, playerId in ipairs(xPlayers) do
|
|
local xTarget = ESX.GetPlayerFromId(playerId)
|
|
if xTarget then
|
|
for _, jobName in ipairs(Config.EMSJobNames) do
|
|
if xTarget.job.name == jobName then
|
|
TriggerClientEvent('mercyv-deathscreen:client:emsDispatch', playerId, {
|
|
coords = coords,
|
|
street = streetName or 'Unbekannt'
|
|
})
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- CLEANUP
|
|
-- ============================================================
|
|
|
|
AddEventHandler('playerDropped', function()
|
|
PlayerDeathStates[source] = nil
|
|
BroadcastDownedPlayers()
|
|
end)
|
|
|
|
AddEventHandler('onResourceStop', function(resourceName)
|
|
if resourceName ~= GetCurrentResourceName() then return end
|
|
PlayerDeathStates = {}
|
|
end)
|
|
|
|
|
|
-- ============================================================
|
|
-- NPC MEDICAL SERVER LOGIC
|
|
-- ============================================================
|
|
|
|
-- Preise für den NPC-Arzt einstellen
|
|
local NPCCosts = {
|
|
Heal = 1000, -- Kosten für "Sich selbst heilen"
|
|
Revive = 1500 -- Kosten für "Andere Person wiederbeleben"
|
|
}
|
|
|
|
-- Hilfsfunktion: Prüft ob EMS im Dienst ist
|
|
local function IsEMSOnDuty()
|
|
local xPlayers = ESX.GetExtendedPlayers('job', 'ambulance') -- Ersetze 'ambulance' falls dein Job anders heißt
|
|
return #xPlayers > 0
|
|
end
|
|
|
|
-- NPC Selbstheilung
|
|
RegisterNetEvent('mercyv-deathscreen:server:npcHealSelf')
|
|
AddEventHandler('mercyv-deathscreen:server:npcHealSelf', function()
|
|
local src = source
|
|
|
|
if IsEMSOnDuty() then
|
|
TriggerClientEvent('esx:showNotification', src, 'Es sind Sanitäter im Dienst! Bitte rufe den Notruf.')
|
|
return
|
|
end
|
|
|
|
local xPlayer = ESX.GetPlayerFromId(src)
|
|
if xPlayer then
|
|
local cost = NPCCosts.Heal
|
|
|
|
-- Bezahl-Logik (zuerst Bargeld, dann Bank)
|
|
if xPlayer.getMoney() >= cost then
|
|
xPlayer.removeMoney(cost)
|
|
elseif xPlayer.getAccount('bank').money >= cost then
|
|
xPlayer.removeAccountMoney('bank', cost)
|
|
else
|
|
TriggerClientEvent('esx:showNotification', src, 'Du hast nicht genug Geld! (Kosten: $' .. cost .. ')')
|
|
return -- Bricht hier ab, wenn kein Geld da ist
|
|
end
|
|
|
|
TriggerClientEvent('mercyv-deathscreen:client:healed', src, 200, 'big')
|
|
TriggerClientEvent('esx:showNotification', src, 'Der Arzt hat deine Wunden für $' .. cost .. ' versorgt.')
|
|
end
|
|
end)
|
|
|
|
-- NPC Wiederbelebung für andere
|
|
-- NPC Wiederbelebung für andere
|
|
RegisterNetEvent('mercyv-deathscreen:server:npcReviveOther')
|
|
AddEventHandler('mercyv-deathscreen:server:npcReviveOther', function(targetId)
|
|
local src = source
|
|
|
|
if IsEMSOnDuty() then
|
|
TriggerClientEvent('esx:showNotification', src, 'Es sind Sanitäter im Dienst! Bitte rufe den Notruf.')
|
|
return
|
|
end
|
|
|
|
local xPlayer = ESX.GetPlayerFromId(src)
|
|
if xPlayer then
|
|
local cost = NPCCosts.Revive
|
|
|
|
-- Bezahl-Logik (zuerst Bargeld, dann Bank)
|
|
if xPlayer.getMoney() >= cost then
|
|
xPlayer.removeMoney(cost)
|
|
elseif xPlayer.getAccount('bank').money >= cost then
|
|
xPlayer.removeAccountMoney('bank', cost)
|
|
else
|
|
TriggerClientEvent('esx:showNotification', src, 'Du hast nicht genug Geld! (Kosten: $' .. cost .. ')')
|
|
return -- Bricht hier ab, wenn kein Geld da ist
|
|
end
|
|
|
|
if RevivePlayer(targetId) then
|
|
TriggerClientEvent('esx:showNotification', src, 'Du hast die Person für $' .. cost .. ' wiederbelebt.')
|
|
TriggerClientEvent('esx:showNotification', targetId, 'Du wurdest von jemandem beim NPC-Arzt gerettet.')
|
|
|
|
-- HIER IST NEU: Teleportiert den ZIEL-SPIELER (targetId) ins Bett und startet /e sleep
|
|
TriggerClientEvent('mercyv-deathscreen:client:npcReviveTeleport', targetId)
|
|
else
|
|
-- Falls die Wiederbelebung fehlschlägt, geben wir das Geld zurück
|
|
xPlayer.addMoney(cost)
|
|
TriggerClientEvent('esx:showNotification', src, 'Fehler bei der Wiederbelebung. Geld wurde erstattet.')
|
|
end
|
|
end
|
|
end) |