forked from Simnation/Main
Update client.lua
This commit is contained in:
parent
2e1498dd79
commit
4485cc31df
1 changed files with 606 additions and 178 deletions
|
@ -9,6 +9,9 @@ local teamZoneBlips = {}
|
||||||
local isHit = false
|
local isHit = false
|
||||||
local activeGames = {}
|
local activeGames = {}
|
||||||
local spawnedNPCs = {}
|
local spawnedNPCs = {}
|
||||||
|
local lastDamager = nil
|
||||||
|
local lastDamageTime = 0
|
||||||
|
local lastDamageWeapon = 0
|
||||||
|
|
||||||
-- Spieler Statistiken
|
-- Spieler Statistiken
|
||||||
local playerStats = {
|
local playerStats = {
|
||||||
|
@ -27,9 +30,26 @@ function isAirsoftWeapon(weaponHash)
|
||||||
return Config.airsoftWeapons[weaponHash] or Config.treatAllWeaponsAsAirsoft
|
return Config.airsoftWeapons[weaponHash] or Config.treatAllWeaponsAsAirsoft
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Funktion zum Prüfen, ob der Spieler im Ragdoll-Zustand ist
|
-- Enhanced function to check if a player is in ragdoll state
|
||||||
function isPedInRagdoll(ped)
|
function isPedInRagdoll(ped)
|
||||||
return IsPedRagdoll(ped) or IsPedFalling(ped) or IsPedDiving(ped)
|
return IsPedRagdoll(ped) or
|
||||||
|
IsPedFalling(ped) or
|
||||||
|
IsPedDiving(ped) or
|
||||||
|
(not IsPedStill(ped) and IsPedOnFoot(ped) and not IsPedWalking(ped) and not IsPedRunning(ped) and not IsPedSprinting(ped))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper function to dump tables for debugging
|
||||||
|
function dumpTable(table, indent)
|
||||||
|
if not indent then indent = 0 end
|
||||||
|
for k, v in pairs(table) do
|
||||||
|
local formatting = string.rep(" ", indent) .. k .. ": "
|
||||||
|
if type(v) == "table" then
|
||||||
|
print(formatting)
|
||||||
|
dumpTable(v, indent+1)
|
||||||
|
else
|
||||||
|
print(formatting .. tostring(v))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Events
|
-- Events
|
||||||
|
@ -80,6 +100,9 @@ RegisterNetEvent('tdm:leaveGame', function()
|
||||||
currentGameId = nil
|
currentGameId = nil
|
||||||
currentField = nil
|
currentField = nil
|
||||||
isHit = false
|
isHit = false
|
||||||
|
lastDamager = nil
|
||||||
|
lastDamageTime = 0
|
||||||
|
lastDamageWeapon = 0
|
||||||
|
|
||||||
-- Sichere Rückkehr zur Lobby
|
-- Sichere Rückkehr zur Lobby
|
||||||
local lobbyPos = nil
|
local lobbyPos = nil
|
||||||
|
@ -242,15 +265,28 @@ RegisterNetEvent('tdm:playerHit', function()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
RegisterNetEvent('tdm:updateScore', function(team1Score, team2Score, gameStats)
|
RegisterNetEvent('tdm:updateScore', function(team1Score, team2Score, gameStats)
|
||||||
-- Debug-Ausgabe
|
-- Enhanced debug output
|
||||||
debugPrint("Score Update empfangen: Team1=" .. team1Score .. ", Team2=" .. team2Score)
|
debugPrint("Score Update empfangen: Team1=" .. team1Score .. ", Team2=" .. team2Score)
|
||||||
if gameStats then
|
if gameStats then
|
||||||
debugPrint("GameStats: Hits=" .. (gameStats.hits or "nil") .. ", Deaths=" .. (gameStats.deaths or "nil"))
|
debugPrint("GameStats received: ")
|
||||||
|
dumpTable(gameStats)
|
||||||
|
else
|
||||||
|
debugPrint("WARNING: No gameStats received from server!")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Verwende gameStats falls verfügbar, sonst lokale Stats
|
-- More robust handling of stats
|
||||||
local myHits = gameStats and gameStats.hits or playerStats.hits
|
local myHits = 0
|
||||||
local myDeaths = gameStats and gameStats.deaths or playerStats.deaths
|
local myDeaths = 0
|
||||||
|
|
||||||
|
if gameStats and type(gameStats) == "table" then
|
||||||
|
myHits = gameStats.hits or 0
|
||||||
|
myDeaths = gameStats.deaths or 0
|
||||||
|
debugPrint("Using server stats: Hits=" .. myHits .. ", Deaths=" .. myDeaths)
|
||||||
|
else
|
||||||
|
myHits = playerStats.hits
|
||||||
|
myDeaths = playerStats.deaths
|
||||||
|
debugPrint("Using local stats: Hits=" .. myHits .. ", Deaths=" .. myDeaths)
|
||||||
|
end
|
||||||
|
|
||||||
local displayText = string.format(
|
local displayText = string.format(
|
||||||
'[Team 1: %d] VS [Team 2: %d] | Deine Treffer: %d | Tode: %d',
|
'[Team 1: %d] VS [Team 2: %d] | Deine Treffer: %d | Tode: %d',
|
||||||
|
@ -658,6 +694,12 @@ CreateThread(function()
|
||||||
-- Versuche die Waffe zu ermitteln
|
-- Versuche die Waffe zu ermitteln
|
||||||
weaponHash = GetSelectedPedWeapon(playerPed)
|
weaponHash = GetSelectedPedWeapon(playerPed)
|
||||||
debugPrint("Angreifer identifiziert: " .. damagerPlayer .. " mit Waffe: " .. weaponHash)
|
debugPrint("Angreifer identifiziert: " .. damagerPlayer .. " mit Waffe: " .. weaponHash)
|
||||||
|
|
||||||
|
-- Update last damager info
|
||||||
|
lastDamager = damagerPlayer
|
||||||
|
lastDamageTime = GetGameTimer()
|
||||||
|
lastDamageWeapon = weaponHash
|
||||||
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -696,7 +738,7 @@ CreateThread(function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Zusätzlicher Event-Handler für zuverlässigere Treffer-Erkennung
|
-- Enhanced damage detection for ragdoll kills
|
||||||
AddEventHandler('gameEventTriggered', function(name, args)
|
AddEventHandler('gameEventTriggered', function(name, args)
|
||||||
if name == "CEventNetworkEntityDamage" then
|
if name == "CEventNetworkEntityDamage" then
|
||||||
local victimId = args[1]
|
local victimId = args[1]
|
||||||
|
@ -718,6 +760,14 @@ AddEventHandler('gameEventTriggered', function(name, args)
|
||||||
|
|
||||||
debugPrint("Schaden-Event erkannt: Angreifer=" .. (attackerServerId or "NPC/Unbekannt") .. ", Waffe=" .. weaponHash)
|
debugPrint("Schaden-Event erkannt: Angreifer=" .. (attackerServerId or "NPC/Unbekannt") .. ", Waffe=" .. weaponHash)
|
||||||
|
|
||||||
|
-- Update last damager info
|
||||||
|
if attackerServerId then
|
||||||
|
lastDamager = attackerServerId
|
||||||
|
lastDamageTime = GetGameTimer()
|
||||||
|
lastDamageWeapon = weaponHash
|
||||||
|
debugPrint("Last damager updated to " .. attackerServerId .. " with weapon " .. weaponHash)
|
||||||
|
end
|
||||||
|
|
||||||
-- Prüfe, ob es eine Airsoft-Waffe ist oder alle Waffen als Airsoft behandelt werden
|
-- Prüfe, ob es eine Airsoft-Waffe ist oder alle Waffen als Airsoft behandelt werden
|
||||||
if isAirsoftWeapon(weaponHash) and attackerServerId then
|
if isAirsoftWeapon(weaponHash) and attackerServerId then
|
||||||
-- Lokale Stats sofort updaten
|
-- Lokale Stats sofort updaten
|
||||||
|
@ -734,199 +784,577 @@ AddEventHandler('gameEventTriggered', function(name, args)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Direkter Waffen-Schaden Monitor für zusätzliche Zuverlässigkeit
|
-- Improved Ragdoll-Erkennung Thread
|
||||||
CreateThread(function()
|
CreateThread(function()
|
||||||
while true do
|
while true do
|
||||||
Wait(0)
|
Wait(50) -- Check more frequently
|
||||||
if inTDM and not isHit then
|
if inTDM and not isHit then
|
||||||
local ped = PlayerPedId()
|
local ped = PlayerPedId()
|
||||||
|
|
||||||
-- Prüfe auf Projektil-Treffer
|
|
||||||
if HasEntityBeenDamagedByWeapon(ped, 0, 2) then -- 2 = Projektilwaffen
|
|
||||||
debugPrint("Projektil-Treffer erkannt")
|
|
||||||
|
|
||||||
-- Schaden zurücksetzen
|
|
||||||
ClearEntityLastDamageEntity(ped)
|
|
||||||
ClearPedLastWeaponDamage(ped)
|
|
||||||
SetEntityHealth(ped, GetEntityMaxHealth(ped))
|
|
||||||
|
|
||||||
-- Lokale Stats sofort updaten
|
|
||||||
playerStats.deaths = playerStats.deaths + 1
|
|
||||||
|
|
||||||
-- Treffer-Events auslösen
|
|
||||||
TriggerEvent('tdm:playerHit')
|
|
||||||
TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam)
|
|
||||||
|
|
||||||
-- Warten um mehrfache Auslösung zu verhindern
|
|
||||||
Wait(500)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Wait(500)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Ragdoll-Erkennung Thread
|
|
||||||
CreateThread(function()
|
|
||||||
local lastDamager = nil
|
|
||||||
local lastDamageTime = 0
|
|
||||||
|
|
||||||
while true do
|
|
||||||
Wait(100)
|
|
||||||
if inTDM and not isHit then
|
|
||||||
local ped = PlayerPedId()
|
|
||||||
|
|
||||||
-- Speichere den letzten Angreifer, wenn Schaden genommen wurde
|
|
||||||
if HasEntityBeenDamagedByAnyPed(ped) then
|
|
||||||
for _, player in ipairs(GetActivePlayers()) do
|
|
||||||
local playerPed = GetPlayerPed(player)
|
|
||||||
if HasPedBeenDamagedBy(ped, playerPed) then
|
|
||||||
lastDamager = GetPlayerServerId(player)
|
|
||||||
lastDamageTime = GetGameTimer()
|
|
||||||
debugPrint("Letzter Angreifer gespeichert: " .. lastDamager)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
ClearEntityLastDamageEntity(ped)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Prüfe, ob der Spieler im Ragdoll-Zustand ist
|
-- Prüfe, ob der Spieler im Ragdoll-Zustand ist
|
||||||
if isPedInRagdoll(ped) then
|
if isPedInRagdoll(ped) then
|
||||||
debugPrint("Ragdoll-Zustand erkannt - Zählt als Tod")
|
|
||||||
|
|
||||||
-- Lokale Stats sofort updaten
|
|
||||||
playerStats.deaths = playerStats.deaths + 1
|
|
||||||
|
|
||||||
-- Bestimme den Angreifer (verwende den letzten Angreifer, wenn innerhalb von 3 Sekunden)
|
-- Bestimme den Angreifer (verwende den letzten Angreifer, wenn innerhalb von 3 Sekunden)
|
||||||
local attacker = nil
|
local attacker = nil
|
||||||
if lastDamager and (GetGameTimer() - lastDamageTime) < 3000 then
|
if lastDamager and (GetGameTimer() -
|
||||||
attacker = lastDamager
|
local QBCore = exports['qb-core']:GetCoreObject()
|
||||||
debugPrint("Ragdoll-Tod zugeordnet an Angreifer: " .. attacker)
|
|
||||||
else
|
-- Game Management
|
||||||
debugPrint("Kein Angreifer für Ragdoll-Tod gefunden")
|
local activeGames = {}
|
||||||
|
local gameIdCounter = 1
|
||||||
|
|
||||||
|
-- Debug-Funktion für Konsole
|
||||||
|
local function debugPrint(message)
|
||||||
|
print("^2[TDM SERVER]^7 " .. message)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Events
|
||||||
|
RegisterNetEvent('tdm:createGame', function(gameName, fieldId, gameType, password)
|
||||||
|
local src = source
|
||||||
|
local Player = QBCore.Functions.GetPlayer(src)
|
||||||
|
|
||||||
|
if not Player then
|
||||||
|
debugPrint("Spielerstellung fehlgeschlagen - Spieler nicht gefunden: " .. src)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Treffer-Events auslösen
|
-- Prüfen ob Spielfeld bereits belegt
|
||||||
TriggerEvent('tdm:playerHit')
|
for gameId, gameData in pairs(activeGames) do
|
||||||
TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, attacker)
|
if gameData.fieldId == fieldId then
|
||||||
|
debugPrint("Spielerstellung abgelehnt - Feld bereits belegt: " .. fieldId)
|
||||||
-- Zurücksetzen des letzten Angreifers
|
TriggerClientEvent('QBCore:Notify', src, 'Dieses Spielfeld ist bereits belegt!', 'error')
|
||||||
lastDamager = nil
|
return
|
||||||
|
|
||||||
-- Warten um mehrfache Auslösung zu verhindern
|
|
||||||
Wait(500)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
Wait(500)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
|
|
||||||
-- Function to spawn NPCs for all fields
|
local gameId = 'game_' .. gameIdCounter
|
||||||
function spawnFieldNPCs()
|
gameIdCounter = gameIdCounter + 1
|
||||||
for fieldId, fieldData in pairs(Config.gameFields) do
|
|
||||||
if fieldData.lobby and fieldData.lobby.npc then
|
|
||||||
local npcData = fieldData.lobby.npc
|
|
||||||
local model = GetHashKey(npcData.model)
|
|
||||||
|
|
||||||
-- Request the model
|
activeGames[gameId] = {
|
||||||
RequestModel(model)
|
id = gameId,
|
||||||
while not HasModelLoaded(model) do
|
name = gameName,
|
||||||
Wait(10)
|
fieldId = fieldId,
|
||||||
end
|
admin = src,
|
||||||
|
adminName = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname,
|
||||||
-- Create the NPC
|
gameType = gameType,
|
||||||
local npc = CreatePed(4, model, npcData.coords.x, npcData.coords.y, npcData.coords.z, npcData.coords.w, false, true)
|
password = password,
|
||||||
SetEntityAsMissionEntity(npc, true, true)
|
hasPassword = password ~= nil,
|
||||||
SetBlockingOfNonTemporaryEvents(npc, true)
|
status = 'waiting',
|
||||||
FreezeEntityPosition(npc, true)
|
team1 = {},
|
||||||
SetEntityInvincible(npc, true)
|
team2 = {},
|
||||||
|
score = {team1 = 0, team2 = 0},
|
||||||
-- Add to spawned NPCs table
|
startTime = nil,
|
||||||
table.insert(spawnedNPCs, npc)
|
maxTime = Config.maxGameTime,
|
||||||
|
maxHits = Config.maxHits,
|
||||||
-- Add target interaction
|
playerStats = {} -- Spieler-Statistiken initialisieren
|
||||||
exports['qb-target']:AddTargetEntity(npc, {
|
|
||||||
options = {
|
|
||||||
{
|
|
||||||
type = "client",
|
|
||||||
event = "tdm:openMenu",
|
|
||||||
icon = "fas fa-gamepad",
|
|
||||||
label = "TeamDeathmatch Menu",
|
|
||||||
fieldId = fieldId
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
distance = 2.0
|
local typeText = gameType == 'public' and 'öffentliches' or 'privates'
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Dein ' .. typeText .. ' Spiel "' .. gameName .. '" wurde erstellt!', 'success')
|
||||||
|
|
||||||
|
updateGamesListForAll()
|
||||||
|
debugPrint("Spiel erstellt: " .. gameId .. " von " .. Player.PlayerData.name .. " (Feld: " .. fieldId .. ")")
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('tdm:requestGamesList', function()
|
||||||
|
local src = source
|
||||||
|
debugPrint("Spiele-Liste angefordert von: " .. src)
|
||||||
|
TriggerClientEvent('tdm:updateGamesList', src, activeGames)
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('tdm:requestJoinGame', function(gameId, password)
|
||||||
|
local src = source
|
||||||
|
local Player = QBCore.Functions.GetPlayer(src)
|
||||||
|
|
||||||
|
if not Player or not activeGames[gameId] then
|
||||||
|
debugPrint("Spielbeitritt fehlgeschlagen - Spieler oder Spiel nicht gefunden: " .. src .. ", " .. gameId)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
local playerName = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname
|
||||||
|
|
||||||
|
debugPrint("Beitrittsanfrage von " .. playerName .. " (ID: " .. src .. ") für Spiel " .. gameId)
|
||||||
|
|
||||||
|
-- Passwort prüfen falls vorhanden
|
||||||
|
if game.hasPassword and game.password ~= password then
|
||||||
|
debugPrint("Beitritt abgelehnt - Falsches Passwort")
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Falsches Passwort!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Spieler bereits im Spiel?
|
||||||
|
for _, playerId in ipairs(game.team1) do
|
||||||
|
if playerId == src then
|
||||||
|
debugPrint("Beitritt abgelehnt - Spieler bereits in Team 1")
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Du bist bereits in diesem Spiel!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, playerId in ipairs(game.team2) do
|
||||||
|
if playerId == src then
|
||||||
|
debugPrint("Beitritt abgelehnt - Spieler bereits in Team 2")
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Du bist bereits in diesem Spiel!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Max Spieler erreicht?
|
||||||
|
local currentPlayers = #game.team1 + #game.team2
|
||||||
|
local maxPlayers = Config.gameFields[game.fieldId].maxPlayers
|
||||||
|
|
||||||
|
if currentPlayers >= maxPlayers then
|
||||||
|
debugPrint("Beitritt abgelehnt - Spiel ist voll")
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Spiel ist voll!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Prüfen ob Admin online ist (für private Spiele)
|
||||||
|
if game.gameType == 'private' then
|
||||||
|
local AdminPlayer = QBCore.Functions.GetPlayer(game.admin)
|
||||||
|
if not AdminPlayer then
|
||||||
|
debugPrint("Beitritt abgelehnt - Admin nicht online")
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Der Spiel-Admin ist nicht online!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Join Logic basierend auf Spiel Typ
|
||||||
|
if game.gameType == 'public' then
|
||||||
|
debugPrint("Öffentliches Spiel - Direkter Beitritt")
|
||||||
|
joinPlayerToGame(src, gameId)
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Du bist dem öffentlichen Spiel beigetreten!', 'success')
|
||||||
|
else
|
||||||
|
debugPrint("Privates Spiel - Sende Anfrage an Admin")
|
||||||
|
TriggerClientEvent('tdm:joinRequest', game.admin, gameId, playerName, src)
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Join-Anfrage gesendet an ' .. game.adminName, 'info')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('tdm:approveJoinRequest', function(gameId, playerId, approved)
|
||||||
|
local src = source
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
|
||||||
|
if not game or game.admin ~= src then
|
||||||
|
debugPrint("Join-Anfrage Bearbeitung fehlgeschlagen - Ungültiges Spiel oder nicht Admin")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if approved then
|
||||||
|
debugPrint("Join-Anfrage genehmigt für Spieler " .. playerId .. " in Spiel " .. gameId)
|
||||||
|
joinPlayerToGame(playerId, gameId)
|
||||||
|
TriggerClientEvent('tdm:joinRequestResult', playerId, true, game.name)
|
||||||
|
else
|
||||||
|
debugPrint("Join-Anfrage abgelehnt für Spieler " .. playerId .. " in Spiel " .. gameId)
|
||||||
|
TriggerClientEvent('tdm:joinRequestResult', playerId, false, game.name)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('tdm:leaveGame', function()
|
||||||
|
local src = source
|
||||||
|
debugPrint("Spieler " .. src .. " möchte alle Spiele verlassen")
|
||||||
|
|
||||||
|
for gameId, game in pairs(activeGames) do
|
||||||
|
removePlayerFromGame(src, gameId)
|
||||||
|
end
|
||||||
|
|
||||||
|
TriggerClientEvent('tdm:leaveGame', src)
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('tdm:playerWasHit', function(gameId, victimTeam, attackerId)
|
||||||
|
local victim = source
|
||||||
|
|
||||||
|
if not activeGames[gameId] then
|
||||||
|
debugPrint("Hit registriert, aber Spiel " .. gameId .. " existiert nicht!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
|
||||||
|
debugPrint("Hit registriert - Opfer: " .. victim .. " (Team: " .. victimTeam .. "), Angreifer: " .. (attackerId or "Unbekannt"))
|
||||||
|
|
||||||
|
-- Spieler Stats initialisieren falls nicht vorhanden
|
||||||
|
if not game.playerStats then
|
||||||
|
game.playerStats = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if not game.playerStats[victim] then
|
||||||
|
game.playerStats[victim] = {hits = 0, deaths = 0}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wichtig: Nur wenn ein Angreifer identifiziert wurde, zähle den Kill
|
||||||
|
if attackerId then
|
||||||
|
if not game.playerStats[attackerId] then
|
||||||
|
game.playerStats[attackerId] = {hits = 0, deaths = 0}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Stats updaten
|
||||||
|
game.playerStats[victim].deaths = (game.playerStats[victim].deaths or 0) + 1
|
||||||
|
game.playerStats[attackerId].hits = (game.playerStats[attackerId].hits or 0) + 1
|
||||||
|
|
||||||
|
-- Benachrichtigung an den Angreifer senden
|
||||||
|
TriggerClientEvent('tdm:hitRegistered', attackerId)
|
||||||
|
debugPrint("Treffer von " .. attackerId .. " gegen " .. victim .. " registriert")
|
||||||
|
|
||||||
|
-- Team Score erhöhen
|
||||||
|
if victimTeam == 'team1' then
|
||||||
|
game.score.team2 = game.score.team2 + 1
|
||||||
|
debugPrint("Punkt für Team 2 - Neuer Score: " .. game.score.team2)
|
||||||
|
else
|
||||||
|
game.score.team1 = game.score.team1 + 1
|
||||||
|
debugPrint("Punkt für Team 1 - Neuer Score: " .. game.score.team1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
debugPrint("Treffer gegen " .. victim .. " von unbekanntem Angreifer registriert - Kein Punkt vergeben")
|
||||||
|
end
|
||||||
|
|
||||||
|
TriggerClientEvent('tdm:deathRegistered', victim)
|
||||||
|
|
||||||
|
-- Score an alle Spieler senden
|
||||||
|
updateScoreForGame(gameId)
|
||||||
|
|
||||||
|
-- Spiel beenden prüfen
|
||||||
|
if game.score.team1 >= game.maxHits or game.score.team2 >= game.maxHits then
|
||||||
|
local winnerTeam = game.score.team1 >= game.maxHits and 'team1' or 'team2'
|
||||||
|
debugPrint("Max Punkte erreicht - Beende Spiel. Gewinner: " .. winnerTeam)
|
||||||
|
endGame(gameId, winnerTeam)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('tdm:playerDied', function(gameId)
|
||||||
|
local src = source
|
||||||
|
debugPrint("Spieler " .. src .. " ist gestorben in Spiel " .. gameId)
|
||||||
|
removePlayerFromGame(src, gameId)
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('tdm:requestScoreUpdate', function(gameId)
|
||||||
|
local src = source
|
||||||
|
|
||||||
|
if activeGames[gameId] then
|
||||||
|
debugPrint("Score-Update angefordert von " .. src .. " für Spiel " .. gameId)
|
||||||
|
updateScoreForGame(gameId)
|
||||||
|
else
|
||||||
|
debugPrint("Score-Update fehlgeschlagen - Spiel " .. gameId .. " nicht gefunden")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
RegisterNetEvent('tdm:debugPlayerStats', function(gameId)
|
||||||
|
local src = source
|
||||||
|
if activeGames[gameId] and activeGames[gameId].playerStats and activeGames[gameId].playerStats[src] then
|
||||||
|
local stats = activeGames[gameId].playerStats[src]
|
||||||
|
debugPrint("Stats für Spieler " .. src .. ": Hits=" .. (stats.hits or 0) .. ", Deaths=" .. (stats.deaths or 0))
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Server Stats - Hits: ' .. (stats.hits or 0) .. ', Deaths: ' .. (stats.deaths or 0), 'info')
|
||||||
|
else
|
||||||
|
debugPrint("Keine Stats gefunden für Spieler " .. src .. " in Spiel " .. gameId)
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Keine Stats gefunden!', 'error')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Funktionen
|
||||||
|
function joinPlayerToGame(playerId, gameId)
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
if not game then
|
||||||
|
debugPrint("Spielbeitritt fehlgeschlagen - Spiel nicht gefunden: " .. gameId)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Team mit weniger Spielern wählen
|
||||||
|
local team = #game.team1 <= #game.team2 and 'team1' or 'team2'
|
||||||
|
|
||||||
|
table.insert(game[team], playerId)
|
||||||
|
|
||||||
|
-- Spieler-Stats initialisieren
|
||||||
|
if not game.playerStats then
|
||||||
|
game.playerStats = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
game.playerStats[playerId] = {hits = 0, deaths = 0}
|
||||||
|
|
||||||
|
-- Spiel starten wenn mindestens 2 Spieler
|
||||||
|
if #game.team1 + #game.team2 >= 2 and game.status == 'waiting' then
|
||||||
|
game.status = 'active'
|
||||||
|
game.startTime = os.time()
|
||||||
|
debugPrint("Spiel " .. gameId .. " gestartet - Mindestens 2 Spieler erreicht")
|
||||||
|
|
||||||
|
-- Game Timer starten
|
||||||
|
startGameTimer(gameId)
|
||||||
|
end
|
||||||
|
|
||||||
|
TriggerClientEvent('tdm:joinGame', playerId, gameId, team, game.fieldId)
|
||||||
|
updateScoreForGame(gameId)
|
||||||
|
updateGamesListForAll()
|
||||||
|
|
||||||
|
debugPrint("Spieler " .. playerId .. " ist Spiel " .. gameId .. " beigetreten (Team: " .. team .. ")")
|
||||||
|
end
|
||||||
|
|
||||||
|
function removePlayerFromGame(playerId, gameId)
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
if not game then return end
|
||||||
|
|
||||||
|
-- Spieler aus Teams entfernen
|
||||||
|
local removed = false
|
||||||
|
|
||||||
|
for i, id in ipairs(game.team1) do
|
||||||
|
if id == playerId then
|
||||||
|
table.remove(game.team1, i)
|
||||||
|
debugPrint("Spieler " .. playerId .. " aus Team 1 entfernt")
|
||||||
|
removed = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not removed then
|
||||||
|
for i, id in ipairs(game.team2) do
|
||||||
|
if id == playerId then
|
||||||
|
table.remove(game.team2, i)
|
||||||
|
debugPrint("Spieler " .. playerId .. " aus Team 2 entfernt")
|
||||||
|
removed = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not removed then
|
||||||
|
debugPrint("Spieler " .. playerId .. " nicht in Spiel " .. gameId .. " gefunden")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wenn Admin das Spiel verlässt, Spiel beenden
|
||||||
|
if game.admin == playerId then
|
||||||
|
debugPrint("Admin hat das Spiel verlassen - Beende Spiel " .. gameId)
|
||||||
|
endGame(gameId, nil, 'Admin hat das Spiel verlassen')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
checkGameEnd(gameId)
|
||||||
|
updateGamesListForAll()
|
||||||
|
end
|
||||||
|
|
||||||
|
function endGame(gameId, winnerTeam, reason)
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
if not game then
|
||||||
|
debugPrint("Spielende fehlgeschlagen - Spiel nicht gefunden: " .. gameId)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
game.status = 'finished'
|
||||||
|
|
||||||
|
local allPlayers = {}
|
||||||
|
for _, playerId in ipairs(game.team1) do
|
||||||
|
table.insert(allPlayers, playerId)
|
||||||
|
end
|
||||||
|
for _, playerId in ipairs(game.team2) do
|
||||||
|
table.insert(allPlayers, playerId)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Game End Event an alle Spieler
|
||||||
|
for _, playerId in ipairs(allPlayers) do
|
||||||
|
debugPrint("Sende Spielende-Event an Spieler " .. playerId)
|
||||||
|
TriggerClientEvent('tdm:gameEnded', playerId, winnerTeam, game.score.team1, game.score.team2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Spiel nach 10 Sekunden löschen
|
||||||
|
SetTimeout(10000, function()
|
||||||
|
activeGames[gameId] = nil
|
||||||
|
updateGamesListForAll()
|
||||||
|
debugPrint("Spiel " .. gameId .. " aus der Liste entfernt")
|
||||||
|
end)
|
||||||
|
|
||||||
|
if reason then
|
||||||
|
debugPrint("Spiel " .. gameId .. " beendet: " .. reason)
|
||||||
|
else
|
||||||
|
debugPrint("Spiel " .. gameId .. " beendet. Gewinner: " .. (winnerTeam or "Unentschieden"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function startGameTimer(gameId)
|
||||||
|
CreateThread(function()
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
if not game then return end
|
||||||
|
|
||||||
|
local maxTime = game.maxTime
|
||||||
|
local startTime = os.time()
|
||||||
|
|
||||||
|
debugPrint("Timer für Spiel " .. gameId .. " gestartet. Maximale Zeit: " .. maxTime .. " Sekunden")
|
||||||
|
|
||||||
|
while game and game.status == 'active' and (os.time() - startTime) < maxTime do
|
||||||
|
Wait(1000)
|
||||||
|
game = activeGames[gameId] -- Refresh game data
|
||||||
|
|
||||||
|
-- Alle 30 Sekunden Debug-Info
|
||||||
|
if (os.time() - startTime) % 30 == 0 then
|
||||||
|
debugPrint("Spiel " .. gameId .. " läuft seit " .. (os.time() - startTime) .. " Sekunden")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Zeit abgelaufen
|
||||||
|
if game and game.status == 'active' then
|
||||||
|
local winnerTeam = game.score.team1 > game.score.team2 and 'team1' or
|
||||||
|
game.score.team2 > game.score.team1 and 'team2' or nil
|
||||||
|
debugPrint("Spielzeit abgelaufen - Beende Spiel " .. gameId)
|
||||||
|
endGame(gameId, winnerTeam, 'Zeit abgelaufen')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function checkGameEnd(gameId)
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
if not game then return end
|
||||||
|
|
||||||
|
local totalPlayers = #game.team1 + #game.team2
|
||||||
|
|
||||||
|
if totalPlayers < 2 and game.status == 'active' then
|
||||||
|
debugPrint("Zu wenig Spieler - Beende Spiel " .. gameId)
|
||||||
|
endGame(gameId, nil, 'Zu wenig Spieler')
|
||||||
|
elseif totalPlayers == 0 then
|
||||||
|
activeGames[gameId] = nil
|
||||||
|
updateGamesListForAll()
|
||||||
|
debugPrint("Spiel " .. gameId .. " gelöscht (keine Spieler)")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function updateScoreForGame(gameId)
|
||||||
|
local game = activeGames[gameId]
|
||||||
|
if not game then
|
||||||
|
debugPrint("Score-Update fehlgeschlagen - Spiel nicht gefunden: " .. gameId)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
debugPrint("Score Update für Spiel " .. gameId .. ": Team1=" .. game.score.team1 .. ", Team2=" .. game.score.team2)
|
||||||
|
|
||||||
|
for _, playerId in ipairs(game.team1) do
|
||||||
|
local playerStats = game.playerStats[playerId] or {hits = 0, deaths = 0}
|
||||||
|
TriggerClientEvent('tdm:updateScore', playerId, game.score.team1, game.score.team2, {
|
||||||
|
hits = playerStats.hits or 0,
|
||||||
|
deaths = playerStats.deaths or 0
|
||||||
})
|
})
|
||||||
|
|
||||||
debugPrint("Spawned NPC for field: " .. fieldId)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for _, playerId in ipairs(game.team2) do
|
||||||
|
local playerStats = game.playerStats[playerId] or {hits = 0, deaths = 0}
|
||||||
|
TriggerClientEvent('tdm:updateScore', playerId, game.score.team1, game.score.team2, {
|
||||||
|
hits = playerStats.hits or 0,
|
||||||
|
deaths = playerStats.deaths or 0
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Register event handler for NPC interaction
|
function updateGamesListForAll()
|
||||||
RegisterNetEvent('tdm:openMenu', function(data)
|
local players = QBCore.Functions.GetPlayers()
|
||||||
if data.fieldId then
|
for _, playerId in pairs(players) do
|
||||||
openMainMenu(data.fieldId)
|
TriggerClientEvent('tdm:updateGamesList', playerId, activeGames)
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Spawn NPCs when resource starts
|
|
||||||
CreateThread(function()
|
|
||||||
Wait(1000) -- Wait for everything to load
|
|
||||||
spawnFieldNPCs()
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Function to create blips for all TDM lobbies
|
|
||||||
function createTDMBlips()
|
|
||||||
for fieldId, fieldData in pairs(Config.gameFields) do
|
|
||||||
if fieldData.lobby and fieldData.lobby.pos then
|
|
||||||
local blip = AddBlipForCoord(fieldData.lobby.pos.x, fieldData.lobby.pos.y, fieldData.lobby.pos.z)
|
|
||||||
SetBlipSprite(blip, 156) -- You can change this to any appropriate sprite
|
|
||||||
SetBlipDisplay(blip, 4)
|
|
||||||
SetBlipScale(blip, 0.8)
|
|
||||||
SetBlipColour(blip, 3)
|
|
||||||
SetBlipAsShortRange(blip, true)
|
|
||||||
BeginTextCommandSetBlipName("STRING")
|
|
||||||
AddTextComponentString("TDM: " .. fieldData.name)
|
|
||||||
EndTextCommandSetBlipName(blip)
|
|
||||||
|
|
||||||
table.insert(tdmBlips, blip)
|
|
||||||
debugPrint("Created blip for TDM field: " .. fieldId)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Call this function when the resource starts
|
-- Player Disconnect Handler
|
||||||
CreateThread(function()
|
AddEventHandler('playerDropped', function()
|
||||||
Wait(2000) -- Wait for everything to load
|
local src = source
|
||||||
createTDMBlips()
|
|
||||||
|
for gameId, game in pairs(activeGames) do
|
||||||
|
removePlayerFromGame(src, gameId)
|
||||||
|
end
|
||||||
|
|
||||||
|
debugPrint("Spieler " .. src .. " hat den Server verlassen - aus allen Spielen entfernt")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Cleanup function
|
-- Server Start - Games Liste leeren
|
||||||
function cleanupResources()
|
AddEventHandler('onResourceStart', function(resourceName)
|
||||||
-- Remove NPCs
|
if GetCurrentResourceName() == resourceName then
|
||||||
for _, npc in ipairs(spawnedNPCs) do
|
activeGames = {}
|
||||||
if DoesEntityExist(npc) then
|
gameIdCounter = 1
|
||||||
DeleteEntity(npc)
|
debugPrint("TeamDeathmatch System gestartet!")
|
||||||
end
|
end
|
||||||
end
|
end)
|
||||||
spawnedNPCs = {}
|
|
||||||
|
|
||||||
-- Remove blips
|
|
||||||
for _, blip in ipairs(tdmBlips) do
|
|
||||||
if DoesBlipExist(blip) then
|
|
||||||
RemoveBlip(blip)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
tdmBlips = {}
|
|
||||||
|
|
||||||
debugPrint("Cleaned up all NPCs and blips")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Register cleanup when resource stops
|
|
||||||
AddEventHandler('onResourceStop', function(resourceName)
|
AddEventHandler('onResourceStop', function(resourceName)
|
||||||
if GetCurrentResourceName() == resourceName then
|
if GetCurrentResourceName() == resourceName then
|
||||||
cleanupResources()
|
-- Alle Spieler aus TDM entfernen
|
||||||
|
for gameId, game in pairs(activeGames) do
|
||||||
|
local allPlayers = {}
|
||||||
|
for _, playerId in ipairs(game.team1) do
|
||||||
|
table.insert(allPlayers, playerId)
|
||||||
|
end
|
||||||
|
for _, playerId in ipairs(game.team2) do
|
||||||
|
table.insert(allPlayers, playerId)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, playerId in ipairs(allPlayers) do
|
||||||
|
TriggerClientEvent('tdm:leaveGame', playerId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
activeGames = {}
|
||||||
|
debugPrint("TeamDeathmatch System gestoppt!")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- Admin-Befehle
|
||||||
|
RegisterCommand('tdmreset', function(source, args)
|
||||||
|
local src = source
|
||||||
|
if src > 0 then -- Spieler
|
||||||
|
local Player = QBCore.Functions.GetPlayer(src)
|
||||||
|
if not Player or not Player.PlayerData.job or Player.PlayerData.job.name ~= 'admin' then
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Alle Spieler aus TDM entfernen
|
||||||
|
for gameId, game in pairs(activeGames) do
|
||||||
|
local allPlayers = {}
|
||||||
|
for _, playerId in ipairs(game.team1) do
|
||||||
|
table.insert(allPlayers, playerId)
|
||||||
|
end
|
||||||
|
for _, playerId in ipairs(game.team2) do
|
||||||
|
table.insert(allPlayers, playerId)
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, playerId in ipairs(allPlayers) do
|
||||||
|
TriggerClientEvent('tdm:leaveGame', playerId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
activeGames = {}
|
||||||
|
gameIdCounter = 1
|
||||||
|
|
||||||
|
debugPrint("TeamDeathmatch System zurückgesetzt!")
|
||||||
|
|
||||||
|
if src > 0 then
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'TDM System zurückgesetzt!', 'success')
|
||||||
|
end
|
||||||
|
end, true)
|
||||||
|
|
||||||
|
-- Debug-Befehl für Server-Status
|
||||||
|
RegisterCommand('tdmstatus', function(source, args)
|
||||||
|
local src = source
|
||||||
|
if src > 0 then -- Spieler
|
||||||
|
local Player = QBCore.Functions.GetPlayer(src)
|
||||||
|
if not Player or not Player.PlayerData.job or Player.PlayerData.job.name ~= 'admin' then
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung!', 'error')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
debugPrint("=== TDM STATUS ===")
|
||||||
|
debugPrint("Aktive Spiele: " .. table.count(activeGames))
|
||||||
|
|
||||||
|
for gameId, game in pairs(activeGames) do
|
||||||
|
debugPrint("Spiel: " .. gameId .. " - " .. game.name)
|
||||||
|
debugPrint(" Status: " .. game.status)
|
||||||
|
debugPrint(" Feld: " .. game.fieldId)
|
||||||
|
debugPrint(" Team 1: " .. #game.team1 .. " Spieler, Score: " .. game.score.team1)
|
||||||
|
debugPrint(" Team 2: " .. #game.team2 .. " Spieler, Score: " .. game.score.team2)
|
||||||
|
end
|
||||||
|
|
||||||
|
if src > 0 then
|
||||||
|
TriggerClientEvent('QBCore:Notify', src, 'TDM Status in Server-Konsole', 'info')
|
||||||
|
end
|
||||||
|
end, true)
|
||||||
|
|
||||||
|
-- Hilfsfunktion für table.count
|
||||||
|
table.count = function(tbl)
|
||||||
|
local count = 0
|
||||||
|
for _, _ in pairs(tbl) do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue