1
0
Fork 0
forked from Simnation/Main
Main/resources/[standalone]/nordi_tdm/client.lua

1361 lines
45 KiB
Lua
Raw Normal View History

2025-07-26 06:21:34 +02:00
local QBCore = exports['qb-core']:GetCoreObject()
local inTDM = false
local currentTeam = nil
local currentGameId = nil
local currentField = nil
2025-07-26 07:24:05 +02:00
local currentLobbyField = nil
local tdmBlips = {}
2025-07-26 06:21:34 +02:00
local teamZoneBlips = {}
local isHit = false
local activeGames = {}
2025-07-26 07:24:05 +02:00
local spawnedNPCs = {}
2025-07-27 23:47:36 +02:00
local lastDamager = nil
local lastDamageTime = 0
local lastDamageWeapon = 0
2025-07-26 06:21:34 +02:00
2025-07-26 06:36:06 +02:00
-- Spieler Statistiken
local playerStats = {
hits = 0,
deaths = 0,
gamesPlayed = 0
}
2025-07-26 22:13:11 +02:00
-- Debug-Funktion für Konsole
local function debugPrint(message)
print("^2[TDM DEBUG]^7 " .. message)
end
2025-07-27 02:52:57 +02:00
-- Funktion zum Prüfen, ob eine Waffe eine Airsoft-Waffe ist
function isAirsoftWeapon(weaponHash)
return Config.airsoftWeapons[weaponHash] or Config.treatAllWeaponsAsAirsoft
end
2025-07-27 23:47:36 +02:00
-- Enhanced function to check if a player is in ragdoll state
2025-07-27 02:52:57 +02:00
function isPedInRagdoll(ped)
2025-07-27 23:47:36 +02:00
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
2025-07-27 02:52:57 +02:00
end
2025-07-26 07:24:05 +02:00
-- Events
2025-07-26 06:21:34 +02:00
RegisterNetEvent('tdm:updateGamesList', function(games)
activeGames = games
2025-07-26 23:28:00 +02:00
debugPrint("Spieleliste aktualisiert: " .. (games and table.count(games) or 0) .. " aktive Spiele")
2025-07-26 06:21:34 +02:00
end)
RegisterNetEvent('tdm:joinGame', function(gameId, team, fieldId)
currentGameId = gameId
currentTeam = team
currentField = fieldId
inTDM = true
isHit = false
2025-07-26 06:36:06 +02:00
-- Stats zurücksetzen
playerStats.hits = 0
playerStats.deaths = 0
playerStats.gamesPlayed = playerStats.gamesPlayed + 1
2025-07-26 06:21:34 +02:00
local fieldConfig = Config.gameFields[fieldId]
-- Teleport zu Team Spawn
local spawnPoints = fieldConfig.teamSpawns[team]
local randomSpawn = spawnPoints[math.random(#spawnPoints)]
SetEntityCoords(PlayerPedId(), randomSpawn.x, randomSpawn.y, randomSpawn.z)
-- Team Maske setzen
setTeamMask(team)
-- Team Zone Blip erstellen
createTeamZoneBlip(team, fieldConfig)
lib.notify({
title = 'TeamDeathmatch',
description = 'Du bist dem Spiel beigetreten! Team: ' .. team,
type = 'success'
})
2025-07-26 22:13:11 +02:00
debugPrint("Spiel beigetreten: " .. gameId .. ", Team: " .. team .. ", Feld: " .. fieldId)
2025-07-26 06:21:34 +02:00
end)
RegisterNetEvent('tdm:leaveGame', function()
inTDM = false
2025-07-26 07:01:07 +02:00
local previousField = currentField
2025-07-26 06:21:34 +02:00
currentTeam = nil
currentGameId = nil
currentField = nil
isHit = false
2025-07-27 23:47:36 +02:00
lastDamager = nil
lastDamageTime = 0
lastDamageWeapon = 0
2025-07-26 06:21:34 +02:00
2025-07-26 07:24:05 +02:00
-- Sichere Rückkehr zur Lobby
local lobbyPos = nil
-- Versuche zuerst die vorherige Feld-Lobby
if previousField and Config.gameFields[previousField] and Config.gameFields[previousField].lobby then
lobbyPos = Config.gameFields[previousField].lobby.pos
-- Dann die aktuelle Lobby-Feld
elseif currentLobbyField and Config.gameFields[currentLobbyField] and Config.gameFields[currentLobbyField].lobby then
lobbyPos = Config.gameFields[currentLobbyField].lobby.pos
-- Fallback zur ersten verfügbaren Lobby
2025-07-26 07:01:07 +02:00
else
2025-07-26 07:24:05 +02:00
for fieldId, fieldData in pairs(Config.gameFields) do
if fieldData.lobby and fieldData.lobby.pos then
lobbyPos = fieldData.lobby.pos
break
end
2025-07-26 07:01:07 +02:00
end
end
2025-07-26 06:21:34 +02:00
2025-07-26 07:24:05 +02:00
-- Teleport zur Lobby (mit Fallback-Position)
if lobbyPos then
SetEntityCoords(PlayerPedId(), lobbyPos.x, lobbyPos.y, lobbyPos.z)
else
2025-07-26 23:28:00 +02:00
-- Notfall-Fallback Position (anpassen an deine Map)
2025-07-26 07:24:05 +02:00
SetEntityCoords(PlayerPedId(), -1042.4, -2745.8, 21.4)
2025-07-26 23:28:00 +02:00
debugPrint("WARNUNG: Keine Lobby gefunden, Fallback-Position verwendet!")
2025-07-26 07:24:05 +02:00
end
2025-07-26 06:21:34 +02:00
-- Maske entfernen
SetPedComponentVariation(PlayerPedId(), 1, 0, 0, 0)
-- Zone Blips entfernen
removeTeamZoneBlips()
2025-07-26 06:36:06 +02:00
lib.hideTextUI()
2025-07-26 06:21:34 +02:00
lib.notify({
title = 'TeamDeathmatch',
description = 'Du hast das Spiel verlassen!',
type = 'error'
})
2025-07-26 22:13:11 +02:00
2025-07-26 23:28:00 +02:00
debugPrint("Spiel verlassen")
2025-07-26 06:21:34 +02:00
end)
RegisterNetEvent('tdm:joinRequest', function(gameId, playerName, playerId)
local alert = lib.alertDialog({
header = 'Join Anfrage',
content = playerName .. ' möchte deinem Spiel beitreten.\n\nErlauben?',
centered = true,
cancel = true,
labels = {
cancel = 'Ablehnen',
confirm = 'Erlauben'
}
})
if alert == 'confirm' then
TriggerServerEvent('tdm:approveJoinRequest', gameId, playerId, true)
else
TriggerServerEvent('tdm:approveJoinRequest', gameId, playerId, false)
end
end)
RegisterNetEvent('tdm:joinRequestResult', function(approved, gameName)
if approved then
lib.notify({
title = 'TeamDeathmatch',
description = 'Deine Anfrage wurde angenommen!',
type = 'success'
})
else
lib.notify({
title = 'TeamDeathmatch',
description = 'Deine Anfrage für "' .. gameName .. '" wurde abgelehnt!',
type = 'error'
})
end
end)
RegisterNetEvent('tdm:playerHit', function()
2025-07-26 22:13:11 +02:00
if not inTDM then
2025-07-26 22:42:38 +02:00
debugPrint("WARNUNG: Hit-Event empfangen, aber nicht im TDM!")
2025-07-26 22:13:11 +02:00
return
end
2025-07-26 06:21:34 +02:00
2025-07-26 22:13:11 +02:00
if isHit then
2025-07-26 22:42:38 +02:00
debugPrint("WARNUNG: Hit-Event empfangen, aber bereits getroffen!")
2025-07-26 22:13:11 +02:00
return
2025-07-26 06:21:34 +02:00
end
2025-07-26 22:42:38 +02:00
debugPrint("Spieler wurde getroffen - Starte Respawn-Sequenz")
2025-07-26 22:13:11 +02:00
isHit = true
local ped = PlayerPedId()
2025-07-26 06:21:34 +02:00
2025-07-26 22:13:11 +02:00
-- Benachrichtigung
2025-07-26 06:21:34 +02:00
lib.notify({
title = 'TeamDeathmatch',
2025-07-27 02:52:57 +02:00
description = 'Du wurdest getroffen! Respawn in ' .. (Config.respawnDelay / 1000) .. ' Sekunden...',
2025-07-26 06:21:34 +02:00
type = 'error'
})
2025-07-27 02:52:57 +02:00
-- Optional: Ragdoll aktivieren
SetPedToRagdoll(ped, 2000, 2000, 0, true, true, false)
-- Verbesserte Respawn-Logik mit automatischem Teleport
SetTimeout(Config.respawnDelay, function()
2025-07-26 22:42:38 +02:00
if not inTDM then
debugPrint("Respawn abgebrochen - nicht mehr im TDM")
return
end
2025-07-26 21:25:55 +02:00
2025-07-26 22:13:11 +02:00
debugPrint("Respawn wird ausgeführt...")
-- Respawn zum Team Spawn
local fieldConfig = Config.gameFields[currentField]
if not fieldConfig then
2025-07-26 22:42:38 +02:00
debugPrint("FEHLER: Feldkonfiguration nicht gefunden für Respawn!")
2025-07-26 22:13:11 +02:00
return
end
local spawnPoints = fieldConfig.teamSpawns[currentTeam]
if not spawnPoints or #spawnPoints == 0 then
2025-07-26 22:42:38 +02:00
debugPrint("FEHLER: Keine Spawn-Punkte gefunden für Respawn!")
2025-07-26 22:13:11 +02:00
return
2025-07-26 21:25:55 +02:00
end
2025-07-26 22:13:11 +02:00
local randomSpawn = spawnPoints[math.random(#spawnPoints)]
2025-07-26 22:42:38 +02:00
debugPrint("Respawn-Position: " .. randomSpawn.x .. ", " .. randomSpawn.y .. ", " .. randomSpawn.z)
2025-07-26 22:13:11 +02:00
-- Teleport zum Spawn mit Fade
DoScreenFadeOut(500)
Wait(600)
2025-07-26 22:42:38 +02:00
-- Stellen Sie sicher, dass der Spieler lebt
NetworkResurrectLocalPlayer(randomSpawn.x, randomSpawn.y, randomSpawn.z, 0.0, true, false)
debugPrint("Spieler wiederbelebt")
2025-07-26 22:13:11 +02:00
-- Alle Animationen stoppen
ClearPedTasksImmediately(ped)
2025-07-27 02:52:57 +02:00
-- Teleport direkt zum Spawn-Punkt
2025-07-26 22:13:11 +02:00
SetEntityCoords(ped, randomSpawn.x, randomSpawn.y, randomSpawn.z)
2025-07-26 22:42:38 +02:00
SetEntityHealth(ped, GetEntityMaxHealth(ped))
2025-07-26 22:13:11 +02:00
-- Spieler ist wieder aktiv
isHit = false
2025-07-26 22:42:38 +02:00
debugPrint("Respawn abgeschlossen - Spieler ist wieder aktiv")
2025-07-26 22:13:11 +02:00
Wait(100)
DoScreenFadeIn(500)
lib.notify({
title = 'TeamDeathmatch',
description = 'Du bist wieder im Spiel!',
type = 'success'
})
2025-07-26 21:25:55 +02:00
end)
2025-07-26 06:21:34 +02:00
end)
2025-07-26 06:36:06 +02:00
RegisterNetEvent('tdm:updateScore', function(team1Score, team2Score, gameStats)
2025-07-27 23:47:36 +02:00
-- Enhanced debug output
2025-07-26 22:13:11 +02:00
debugPrint("Score Update empfangen: Team1=" .. team1Score .. ", Team2=" .. team2Score)
if gameStats then
2025-07-27 23:47:36 +02:00
debugPrint("GameStats received: ")
dumpTable(gameStats)
else
debugPrint("WARNING: No gameStats received from server!")
2025-07-26 22:13:11 +02:00
end
2025-07-27 23:47:36 +02:00
-- More robust handling of stats
local myHits = 0
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
2025-07-26 21:25:55 +02:00
2025-07-26 06:36:06 +02:00
local displayText = string.format(
'[Team 1: %d] VS [Team 2: %d] | Deine Treffer: %d | Tode: %d',
team1Score,
team2Score,
2025-07-26 21:25:55 +02:00
myHits,
myDeaths
2025-07-26 06:36:06 +02:00
)
lib.showTextUI(displayText, {
2025-07-26 06:21:34 +02:00
position = "top-center",
icon = 'crosshairs'
})
end)
2025-07-26 06:36:06 +02:00
RegisterNetEvent('tdm:hitRegistered', function()
playerStats.hits = playerStats.hits + 1
lib.notify({
title = 'Treffer!',
description = 'Du hast einen Gegner getroffen! (+1 Punkt)',
type = 'success',
duration = 2000
})
2025-07-26 22:42:38 +02:00
-- Spiele einen Sound ab
PlaySoundFrontend(-1, "WEAPON_PURCHASE", "HUD_AMMO_SHOP_SOUNDSET", true)
2025-07-26 06:36:06 +02:00
showHitMarker()
2025-07-26 21:25:55 +02:00
-- Score sofort aktualisieren
2025-07-26 22:13:11 +02:00
if currentGameId then
TriggerServerEvent('tdm:requestScoreUpdate', currentGameId)
end
debugPrint("Treffer registriert! Neue Hits: " .. playerStats.hits)
2025-07-26 06:36:06 +02:00
end)
RegisterNetEvent('tdm:deathRegistered', function()
playerStats.deaths = playerStats.deaths + 1
2025-07-26 21:25:55 +02:00
-- Score sofort aktualisieren
2025-07-26 22:13:11 +02:00
if currentGameId then
TriggerServerEvent('tdm:requestScoreUpdate', currentGameId)
end
debugPrint("Tod registriert! Neue Deaths: " .. playerStats.deaths)
2025-07-26 06:36:06 +02:00
end)
2025-07-26 06:21:34 +02:00
RegisterNetEvent('tdm:gameEnded', function(winnerTeam, team1Score, team2Score)
lib.hideTextUI()
2025-07-26 06:36:06 +02:00
local wonGame = (currentTeam == 'team1' and winnerTeam == 'team1') or (currentTeam == 'team2' and winnerTeam == 'team2')
local resultText = wonGame and '🏆 GEWONNEN!' or '💀 VERLOREN!'
local statsText = string.format(
'%s\n\n' ..
'Endergebnis:\n' ..
'Team 1: %d Punkte\n' ..
'Team 2: %d Punkte\n\n' ..
'Deine Statistiken:\n' ..
'🎯 Treffer: %d\n' ..
'💀 Tode: %d\n' ..
'📊 K/D: %.2f',
resultText,
team1Score,
team2Score,
playerStats.hits,
playerStats.deaths,
playerStats.deaths > 0 and (playerStats.hits / playerStats.deaths) or playerStats.hits
)
2025-07-26 06:21:34 +02:00
local alert = lib.alertDialog({
header = 'Spiel beendet!',
2025-07-26 06:36:06 +02:00
content = statsText,
2025-07-26 06:21:34 +02:00
centered = true,
cancel = false
})
Wait(5000)
TriggerServerEvent('tdm:leaveGame')
2025-07-26 23:28:00 +02:00
debugPrint("Spiel beendet! Gewinner: " .. (winnerTeam or "Unentschieden"))
2025-07-26 06:21:34 +02:00
end)
2025-07-26 07:24:05 +02:00
-- Funktionen
2025-07-26 06:21:34 +02:00
function setTeamMask(team)
local ped = PlayerPedId()
local maskData = Config.teamMasks[team]
if maskData then
2025-07-26 07:01:07 +02:00
-- Geschlecht des Spielers ermitteln
local playerGender = GetEntityModel(ped) == GetHashKey("mp_f_freemode_01") and "female" or "male"
-- Entsprechende Maske setzen
local genderMask = maskData[playerGender]
if genderMask then
SetPedComponentVariation(ped, genderMask.component, genderMask.drawable, genderMask.texture, 0)
2025-07-26 22:13:11 +02:00
debugPrint("Maske gesetzt: " .. team .. " (" .. playerGender .. ")")
else
debugPrint("Keine Maske für " .. team .. " (" .. playerGender .. ") gefunden!")
2025-07-26 07:01:07 +02:00
end
2025-07-26 22:13:11 +02:00
else
debugPrint("Keine Masken-Daten für Team " .. team .. " gefunden!")
2025-07-26 06:21:34 +02:00
end
end
2025-07-26 07:24:05 +02:00
function createTeamZoneBlip(team, fieldConfig)
local zone = fieldConfig.teamZones[team]
2025-07-26 06:21:34 +02:00
2025-07-26 07:24:05 +02:00
local blip = AddBlipForRadius(zone.center.x, zone.center.y, zone.center.z, zone.radius)
SetBlipHighDetail(blip, true)
SetBlipColour(blip, team == 'team1' and 1 or 3)
SetBlipAlpha(blip, 128)
teamZoneBlips[team] = blip
2025-07-26 22:13:11 +02:00
debugPrint("Team Zone Blip erstellt für " .. team)
2025-07-26 06:21:34 +02:00
end
2025-07-26 07:24:05 +02:00
function removeTeamZoneBlips()
for team, blip in pairs(teamZoneBlips) do
if DoesBlipExist(blip) then
RemoveBlip(blip)
2025-07-26 06:36:06 +02:00
end
2025-07-26 07:01:07 +02:00
end
2025-07-26 07:24:05 +02:00
teamZoneBlips = {}
2025-07-26 22:13:11 +02:00
debugPrint("Team Zone Blips entfernt")
2025-07-26 07:24:05 +02:00
end
function highlightTeamZone(team)
if teamZoneBlips[team] and DoesBlipExist(teamZoneBlips[team]) then
SetBlipFlashes(teamZoneBlips[team], true)
2025-07-26 07:01:07 +02:00
end
2025-07-26 06:36:06 +02:00
end
2025-07-26 07:24:05 +02:00
function showHitMarker()
2025-07-26 22:42:38 +02:00
debugPrint("Zeige Hit-Marker an")
2025-07-26 07:24:05 +02:00
CreateThread(function()
local startTime = GetGameTimer()
2025-07-26 22:42:38 +02:00
local duration = 500 -- 500ms display
2025-07-26 07:24:05 +02:00
2025-07-26 22:42:38 +02:00
while GetGameTimer() - startTime < duration do
2025-07-26 07:24:05 +02:00
Wait(0)
2025-07-26 22:42:38 +02:00
-- Draw hit marker
2025-07-26 07:24:05 +02:00
DrawRect(0.5, 0.5, 0.02, 0.002, 255, 0, 0, 255)
DrawRect(0.5, 0.5, 0.002, 0.02, 255, 0, 0, 255)
2025-07-26 22:42:38 +02:00
-- Draw hit text
2025-07-26 07:24:05 +02:00
SetTextFont(4)
SetTextProportional(1)
SetTextScale(0.5, 0.5)
SetTextColour(255, 0, 0, 255)
SetTextEntry("STRING")
AddTextComponentString("TREFFER!")
SetTextCentre(true)
DrawText(0.5, 0.45)
end
end)
end
2025-07-26 07:01:07 +02:00
function openMainMenu(fieldId)
2025-07-26 07:24:05 +02:00
-- Sicherheitscheck
if not fieldId or not Config.gameFields[fieldId] then
lib.notify({
title = 'Fehler',
description = 'Ungültiges Spielfeld!',
type = 'error'
})
return
end
currentLobbyField = fieldId
2025-07-26 06:21:34 +02:00
TriggerServerEvent('tdm:requestGamesList')
2025-07-26 06:36:06 +02:00
Wait(100)
2025-07-26 06:21:34 +02:00
2025-07-26 07:01:07 +02:00
local fieldName = Config.gameFields[fieldId].name
2025-07-26 06:21:34 +02:00
local options = {
{
title = 'Neues Spiel erstellen',
2025-07-26 07:01:07 +02:00
description = 'Erstelle ein neues Spiel für ' .. fieldName,
2025-07-26 06:21:34 +02:00
icon = 'plus',
onSelect = function()
2025-07-26 07:01:07 +02:00
openCreateGameMenu(fieldId)
2025-07-26 06:21:34 +02:00
end
},
{
title = 'Spiel beitreten',
description = 'Trete einem laufenden Spiel bei',
icon = 'users',
onSelect = function()
2025-07-26 07:01:07 +02:00
openJoinGameMenu(fieldId)
2025-07-26 06:21:34 +02:00
end
}
}
if inTDM then
table.insert(options, {
title = 'Aktuelles Spiel verlassen',
description = 'Verlasse das laufende Spiel',
icon = 'door-open',
iconColor = 'red',
onSelect = function()
TriggerServerEvent('tdm:leaveGame')
end
})
end
lib.registerContext({
2025-07-26 07:01:07 +02:00
id = 'tdm_main_menu_' .. fieldId,
title = 'TeamDeathmatch - ' .. fieldName,
2025-07-26 06:21:34 +02:00
options = options
})
2025-07-26 07:01:07 +02:00
lib.showContext('tdm_main_menu_' .. fieldId)
2025-07-26 06:21:34 +02:00
end
2025-07-26 07:01:07 +02:00
function openCreateGameMenu(fieldId)
2025-07-26 07:24:05 +02:00
if not fieldId or not Config.gameFields[fieldId] then
lib.notify({
title = 'Fehler',
description = 'Ungültiges Spielfeld!',
type = 'error'
})
return
end
2025-07-26 07:01:07 +02:00
local fieldData = Config.gameFields[fieldId]
2025-07-26 06:21:34 +02:00
2025-07-26 07:01:07 +02:00
local input = lib.inputDialog('Neues Spiel erstellen - ' .. fieldData.name, {
2025-07-26 06:21:34 +02:00
{
type = 'input',
label = 'Spiel Name',
description = 'Gib deinem Spiel einen Namen',
required = true,
max = 30
},
{
type = 'select',
label = 'Spiel Typ',
description = 'Wähle den Spiel Typ',
required = true,
options = {
{value = 'public', label = 'Öffentlich (Jeder kann beitreten)'},
{value = 'private', label = 'Privat (Nur mit Genehmigung)'}
}
},
{
type = 'input',
label = 'Passwort (Optional)',
description = 'Passwort für das Spiel (leer lassen für kein Passwort)',
password = true
}
})
2025-07-26 23:28:00 +02:00
if not input then return end
2025-07-26 06:21:34 +02:00
local gameName = input[1]
2025-07-26 07:01:07 +02:00
local gameType = input[2]
local password = input[3] and input[3] ~= '' and input[3] or nil
2025-07-26 06:21:34 +02:00
2025-07-26 07:01:07 +02:00
if gameName and gameType then
2025-07-26 06:21:34 +02:00
TriggerServerEvent('tdm:createGame', gameName, fieldId, gameType, password)
end
end
2025-07-26 07:01:07 +02:00
function openJoinGameMenu(fieldId)
2025-07-26 07:24:05 +02:00
if not fieldId or not Config.gameFields[fieldId] then
lib.notify({
title = 'Fehler',
description = 'Ungültiges Spielfeld!',
type = 'error'
})
return
end
2025-07-26 22:13:11 +02:00
2025-07-26 06:21:34 +02:00
TriggerServerEvent('tdm:requestGamesList')
Wait(200)
local options = {}
2025-07-26 07:01:07 +02:00
local fieldName = Config.gameFields[fieldId].name
2025-07-26 06:21:34 +02:00
2025-07-26 07:01:07 +02:00
-- Nur Spiele für dieses Feld anzeigen
2025-07-26 06:21:34 +02:00
for gameId, gameData in pairs(activeGames) do
2025-07-26 07:01:07 +02:00
if gameData.fieldId == fieldId then
local playerCount = #gameData.team1 + #gameData.team2
local maxPlayers = Config.gameFields[gameData.fieldId].maxPlayers
local statusText = gameData.status == 'waiting' and 'Wartend' or 'Läuft'
local typeText = gameData.gameType == 'public' and '🌐 Öffentlich' or '🔒 Privat'
local passwordIcon = gameData.hasPassword and ' 🔑' or ''
table.insert(options, {
title = gameData.name .. passwordIcon,
description = typeText .. ' | Spieler: ' .. playerCount .. '/' .. maxPlayers .. ' | Status: ' .. statusText,
icon = gameData.gameType == 'public' and 'globe' or 'lock',
iconColor = gameData.gameType == 'public' and 'green' or 'orange',
args = {
gameId = gameId,
hasPassword = gameData.hasPassword,
gameType = gameData.gameType
},
onSelect = function(args)
if args.hasPassword then
local input = lib.inputDialog('Passwort eingeben', {
{
type = 'input',
label = 'Passwort',
description = 'Gib das Spiel-Passwort ein',
required = true,
password = true
}
})
if input and input[1] then
TriggerServerEvent('tdm:requestJoinGame', args.gameId, input[1])
end
else
TriggerServerEvent('tdm:requestJoinGame', args.gameId)
2025-07-26 06:21:34 +02:00
end
end
2025-07-26 07:01:07 +02:00
})
end
2025-07-26 06:21:34 +02:00
end
if #options == 0 then
table.insert(options, {
title = 'Keine Spiele verfügbar',
2025-07-26 07:01:07 +02:00
description = 'Erstelle ein neues Spiel für ' .. fieldName,
2025-07-26 06:21:34 +02:00
icon = 'info',
disabled = true
})
end
lib.registerContext({
2025-07-26 07:01:07 +02:00
id = 'tdm_join_menu_' .. fieldId,
title = 'Spiel beitreten - ' .. fieldName,
menu = 'tdm_main_menu_' .. fieldId,
2025-07-26 06:21:34 +02:00
options = options
})
2025-07-26 07:01:07 +02:00
lib.showContext('tdm_join_menu_' .. fieldId)
2025-07-26 06:21:34 +02:00
end
2025-07-26 07:24:05 +02:00
-- Zone Marker Renderer
2025-07-26 06:21:34 +02:00
CreateThread(function()
while true do
Wait(0)
if inTDM and currentTeam and currentField then
local zone = Config.gameFields[currentField].teamZones[currentTeam]
local color = zone.color
DrawMarker(
1,
zone.center.x, zone.center.y, zone.center.z - 1.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
zone.radius * 2, zone.radius * 2, 1.0,
color.r, color.g, color.b, color.a,
false, true, 2, false, nil, nil, false
)
if isHit then
DrawMarker(
2,
zone.center.x, zone.center.y, zone.center.z + 5.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
1.0, 1.0, 1.0,
255, 255, 0, 200,
true, true, 2, false, nil, nil, false
)
end
else
Wait(1000)
end
end
end)
2025-07-27 02:52:57 +02:00
-- Verbesserte Damage Handler für Airsoft-Waffen
2025-07-26 06:21:34 +02:00
CreateThread(function()
while true do
if inTDM and not isHit then
local ped = PlayerPedId()
2025-07-26 23:28:00 +02:00
-- Prüfe, ob der Spieler Schaden genommen hat
2025-07-26 06:21:34 +02:00
if HasEntityBeenDamagedByAnyPed(ped) then
2025-07-27 02:52:57 +02:00
debugPrint("Schaden erkannt - Identifiziere Angreifer und Waffe")
2025-07-26 22:42:38 +02:00
local damager = nil
2025-07-26 06:36:06 +02:00
local damagerPlayer = nil
2025-07-27 02:52:57 +02:00
local weaponHash = 0
2025-07-26 06:36:06 +02:00
2025-07-27 02:52:57 +02:00
-- Versuche den Angreifer und die Waffe zu identifizieren
2025-07-26 22:13:11 +02:00
for _, player in ipairs(GetActivePlayers()) do
2025-07-26 22:42:38 +02:00
local playerPed = GetPlayerPed(player)
if HasPedBeenDamagedBy(ped, playerPed) then
damager = playerPed
2025-07-26 22:13:11 +02:00
damagerPlayer = GetPlayerServerId(player)
2025-07-27 02:52:57 +02:00
-- Versuche die Waffe zu ermitteln
weaponHash = GetSelectedPedWeapon(playerPed)
debugPrint("Angreifer identifiziert: " .. damagerPlayer .. " mit Waffe: " .. weaponHash)
2025-07-27 23:47:36 +02:00
-- Update last damager info
lastDamager = damagerPlayer
lastDamageTime = GetGameTimer()
lastDamageWeapon = weaponHash
2025-07-26 22:13:11 +02:00
break
2025-07-26 06:36:06 +02:00
end
end
2025-07-27 02:52:57 +02:00
-- Prüfe, ob es eine Airsoft-Waffe ist oder alle Waffen als Airsoft behandelt werden
if isAirsoftWeapon(weaponHash) then
debugPrint("Airsoft-Treffer erkannt mit Waffe: " .. weaponHash)
-- Schaden zurücksetzen
ClearEntityLastDamageEntity(ped)
ClearPedLastWeaponDamage(ped)
SetEntityHealth(ped, GetEntityMaxHealth(ped))
-- Lokale Stats sofort updaten
playerStats.deaths = playerStats.deaths + 1
debugPrint("Getroffen von: " .. (damagerPlayer or "Unbekannt"))
-- Treffer-Events auslösen
TriggerEvent('tdm:playerHit')
TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, damagerPlayer)
-- Warten um mehrfache Auslösung zu verhindern
Wait(500)
else
debugPrint("Keine Airsoft-Waffe erkannt: " .. weaponHash)
-- Schaden trotzdem zurücksetzen
ClearEntityLastDamageEntity(ped)
SetEntityHealth(ped, GetEntityMaxHealth(ped))
end
2025-07-26 06:21:34 +02:00
end
2025-07-26 22:42:38 +02:00
Wait(0)
else
Wait(500)
2025-07-26 06:21:34 +02:00
end
end
end)
2025-07-27 23:47:36 +02:00
-- Enhanced damage detection for ragdoll kills
2025-07-26 23:28:00 +02:00
AddEventHandler('gameEventTriggered', function(name, args)
if name == "CEventNetworkEntityDamage" then
local victimId = args[1]
local attackerId = args[2]
local isDead = args[4] == 1
local weaponHash = args[5]
2025-07-27 02:52:57 +02:00
local isMelee = args[10] == 1
2025-07-26 23:28:00 +02:00
if inTDM and not isHit and victimId == PlayerPedId() then
local attackerServerId = nil
-- Versuche den Angreifer zu identifizieren
for _, player in ipairs(GetActivePlayers()) do
if GetPlayerPed(player) == attackerId then
attackerServerId = GetPlayerServerId(player)
break
end
end
debugPrint("Schaden-Event erkannt: Angreifer=" .. (attackerServerId or "NPC/Unbekannt") .. ", Waffe=" .. weaponHash)
2025-07-27 23:47:36 +02:00
-- Update last damager info
if attackerServerId then
lastDamager = attackerServerId
lastDamageTime = GetGameTimer()
lastDamageWeapon = weaponHash
debugPrint("Last damager updated to " .. attackerServerId .. " with weapon " .. weaponHash)
end
2025-07-27 02:52:57 +02:00
-- Prüfe, ob es eine Airsoft-Waffe ist oder alle Waffen als Airsoft behandelt werden
if isAirsoftWeapon(weaponHash) and attackerServerId then
2025-07-26 23:28:00 +02:00
-- Lokale Stats sofort updaten
playerStats.deaths = playerStats.deaths + 1
-- Verhindern, dass der Spieler stirbt
SetEntityHealth(PlayerPedId(), GetEntityMaxHealth(PlayerPedId()))
-- Treffer-Events auslösen
TriggerEvent('tdm:playerHit')
TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, attackerServerId)
end
end
end
end)
2025-07-27 23:47:36 +02:00
-- Improved Ragdoll-Erkennung Thread
2025-07-26 06:21:34 +02:00
CreateThread(function()
while true do
2025-07-27 23:47:36 +02:00
Wait(50) -- Check more frequently
2025-07-27 02:52:57 +02:00
if inTDM and not isHit then
2025-07-26 06:21:34 +02:00
local ped = PlayerPedId()
2025-07-27 23:47:36 +02:00
-- Prüfe, ob der Spieler im Ragdoll-Zustand ist
if isPedInRagdoll(ped) then
-- Bestimme den Angreifer (verwende den letzten Angreifer, wenn innerhalb von 3 Sekunden)
local attacker = nil
if lastDamager and (GetGameTimer() -
local QBCore = exports['qb-core']:GetCoreObject()
-- Game Management
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
-- 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
2025-07-26 06:21:34 +02:00
end
end
2025-07-27 23:47:36 +02:00
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 .. ")")
2025-07-26 06:21:34 +02:00
end)
2025-07-27 23:47:36 +02:00
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)
2025-07-27 02:52:57 +02:00
2025-07-27 23:47:36 +02:00
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)
2025-07-26 07:24:05 +02:00
else
2025-07-27 23:47:36 +02:00
game.score.team1 = game.score.team1 + 1
debugPrint("Punkt für Team 1 - Neuer Score: " .. game.score.team1)
2025-07-26 07:24:05 +02:00
end
2025-07-27 23:47:36 +02:00
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)
2025-07-26 06:21:34 +02:00
end
end)
2025-07-27 23:47:36 +02:00
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
2025-07-27 22:47:15 +02:00
end
2025-07-27 23:47:36 +02:00
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
2025-07-27 22:47:15 +02:00
2025-07-27 23:47:36 +02:00
-- 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')
2025-07-27 22:47:15 +02:00
end
2025-07-27 23:47:36 +02:00
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)")
2025-07-27 22:47:15 +02:00
end
end
2025-07-27 23:47:36 +02:00
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
})
2025-07-27 22:47:15 +02:00
end
2025-07-27 23:47:36 +02:00
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")
2025-07-27 22:47:15 +02:00
end)
2025-07-27 23:47:36 +02:00
-- Server Start - Games Liste leeren
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() == resourceName then
activeGames = {}
gameIdCounter = 1
debugPrint("TeamDeathmatch System gestartet!")
end
2025-07-27 22:47:15 +02:00
end)
2025-07-27 23:47:36 +02:00
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
-- 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
2025-07-27 22:47:15 +02:00
2025-07-27 23:47:36 +02:00
for _, playerId in ipairs(allPlayers) do
TriggerClientEvent('tdm:leaveGame', playerId)
end
2025-07-27 22:47:15 +02:00
end
2025-07-27 23:47:36 +02:00
activeGames = {}
debugPrint("TeamDeathmatch System gestoppt!")
2025-07-27 22:47:15 +02:00
end
end)
2025-07-27 23:47:36 +02:00
-- 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
2025-07-27 22:47:15 +02:00
end
end
2025-07-27 23:47:36 +02:00
-- 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)
2025-07-27 22:47:15 +02:00
end
end
2025-07-27 23:47:36 +02:00
activeGames = {}
gameIdCounter = 1
debugPrint("TeamDeathmatch System zurückgesetzt!")
if src > 0 then
TriggerClientEvent('QBCore:Notify', src, 'TDM System zurückgesetzt!', 'success')
end
end, true)
2025-07-27 22:47:15 +02:00
2025-07-27 23:47:36 +02:00
-- 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
2025-07-27 22:47:15 +02:00
end
2025-07-27 23:47:36 +02:00
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