1
0
Fork 0
forked from Simnation/Main

Update client.lua

This commit is contained in:
Nordi98 2025-07-27 23:47:36 +02:00
parent 2e1498dd79
commit 4485cc31df

View file

@ -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 = {}
end local gameIdCounter = 1
-- Treffer-Events auslösen -- Debug-Funktion für Konsole
TriggerEvent('tdm:playerHit') local function debugPrint(message)
TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, attacker) print("^2[TDM SERVER]^7 " .. message)
end
-- Zurücksetzen des letzten Angreifers
lastDamager = nil -- Events
RegisterNetEvent('tdm:createGame', function(gameName, fieldId, gameType, password)
-- Warten um mehrfache Auslösung zu verhindern local src = source
Wait(500) local Player = QBCore.Functions.GetPlayer(src)
end
if not Player then
debugPrint("Spielerstellung fehlgeschlagen - Spieler nicht gefunden: " .. src)
return
end
-- Prüfen ob Spielfeld bereits belegt
for gameId, gameData in pairs(activeGames) do
if gameData.fieldId == fieldId then
debugPrint("Spielerstellung abgelehnt - Feld bereits belegt: " .. fieldId)
TriggerClientEvent('QBCore:Notify', src, 'Dieses Spielfeld ist bereits belegt!', 'error')
return
end
end
local gameId = 'game_' .. gameIdCounter
gameIdCounter = gameIdCounter + 1
activeGames[gameId] = {
id = gameId,
name = gameName,
fieldId = fieldId,
admin = src,
adminName = Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname,
gameType = gameType,
password = password,
hasPassword = password ~= nil,
status = 'waiting',
team1 = {},
team2 = {},
score = {team1 = 0, team2 = 0},
startTime = nil,
maxTime = Config.maxGameTime,
maxHits = Config.maxHits,
playerStats = {} -- Spieler-Statistiken initialisieren
}
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 else
Wait(500) game.score.team1 = game.score.team1 + 1
debugPrint("Punkt für Team 1 - Neuer Score: " .. game.score.team1)
end 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
end) end)
-- Function to spawn NPCs for all fields RegisterNetEvent('tdm:playerDied', function(gameId)
function spawnFieldNPCs() local src = source
for fieldId, fieldData in pairs(Config.gameFields) do debugPrint("Spieler " .. src .. " ist gestorben in Spiel " .. gameId)
if fieldData.lobby and fieldData.lobby.npc then removePlayerFromGame(src, gameId)
local npcData = fieldData.lobby.npc end)
local model = GetHashKey(npcData.model)
RegisterNetEvent('tdm:requestScoreUpdate', function(gameId)
-- Request the model local src = source
RequestModel(model)
while not HasModelLoaded(model) do if activeGames[gameId] then
Wait(10) 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
-- Create the NPC
local npc = CreatePed(4, model, npcData.coords.x, npcData.coords.y, npcData.coords.z, npcData.coords.w, false, true)
SetEntityAsMissionEntity(npc, true, true)
SetBlockingOfNonTemporaryEvents(npc, true)
FreezeEntityPosition(npc, true)
SetEntityInvincible(npc, true)
-- Add to spawned NPCs table
table.insert(spawnedNPCs, npc)
-- Add target interaction
exports['qb-target']:AddTargetEntity(npc, {
options = {
{
type = "client",
event = "tdm:openMenu",
icon = "fas fa-gamepad",
label = "TeamDeathmatch Menu",
fieldId = fieldId
}
},
distance = 2.0
})
debugPrint("Spawned NPC for field: " .. fieldId)
end end
end end
end
-- Register event handler for NPC interaction
RegisterNetEvent('tdm:openMenu', function(data)
if data.fieldId then
openMainMenu(data.fieldId)
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
-- Call this function when the resource starts
CreateThread(function()
Wait(2000) -- Wait for everything to load
createTDMBlips()
end)
-- Cleanup function
function cleanupResources()
-- Remove NPCs
for _, npc in ipairs(spawnedNPCs) do
if DoesEntityExist(npc) then
DeleteEntity(npc)
end
end
spawnedNPCs = {}
-- Remove blips if not removed then
for _, blip in ipairs(tdmBlips) do debugPrint("Spieler " .. playerId .. " nicht in Spiel " .. gameId .. " gefunden")
if DoesBlipExist(blip) then return
RemoveBlip(blip)
end
end end
tdmBlips = {}
debugPrint("Cleaned up all NPCs and blips") -- 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 end
-- Register cleanup when resource stops 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
})
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
function updateGamesListForAll()
local players = QBCore.Functions.GetPlayers()
for _, playerId in pairs(players) do
TriggerClientEvent('tdm:updateGamesList', playerId, activeGames)
end
end
-- Player Disconnect Handler
AddEventHandler('playerDropped', function()
local src = source
for gameId, game in pairs(activeGames) do
removePlayerFromGame(src, gameId)
end
debugPrint("Spieler " .. src .. " hat den Server verlassen - aus allen Spielen entfernt")
end)
-- Server Start - Games Liste leeren
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() == resourceName then
activeGames = {}
gameIdCounter = 1
debugPrint("TeamDeathmatch System gestartet!")
end
end)
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