824 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			824 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local QBCore = exports['qb-core']:GetCoreObject()
 | 
						|
local inTDM = false
 | 
						|
local currentTeam = nil
 | 
						|
local currentGameId = nil
 | 
						|
local currentField = nil
 | 
						|
local currentLobbyField = nil
 | 
						|
local tdmBlips = {}
 | 
						|
local teamZoneBlips = {}
 | 
						|
local isHit = false
 | 
						|
local activeGames = {}
 | 
						|
local spawnedNPCs = {}
 | 
						|
 | 
						|
-- Spieler Statistiken
 | 
						|
local playerStats = {
 | 
						|
    hits = 0,
 | 
						|
    deaths = 0,
 | 
						|
    gamesPlayed = 0
 | 
						|
}
 | 
						|
 | 
						|
-- Debug-Funktion für Konsole
 | 
						|
local function debugPrint(message)
 | 
						|
    print("^2[TDM DEBUG]^7 " .. message)
 | 
						|
end
 | 
						|
 | 
						|
-- Funktion zum Prüfen, ob eine Waffe eine Airsoft-Waffe ist
 | 
						|
function isAirsoftWeapon(weaponHash)
 | 
						|
    return Config.airsoftWeapons[weaponHash] or Config.treatAllWeaponsAsAirsoft
 | 
						|
end
 | 
						|
 | 
						|
-- Funktion zum Prüfen, ob der Spieler im Ragdoll-Zustand ist
 | 
						|
function isPedInRagdoll(ped)
 | 
						|
    return IsPedRagdoll(ped) or IsPedFalling(ped) or IsPedDiving(ped)
 | 
						|
end
 | 
						|
 | 
						|
-- Events
 | 
						|
RegisterNetEvent('tdm:updateGamesList', function(games)
 | 
						|
    activeGames = games
 | 
						|
    debugPrint("Spieleliste aktualisiert: " .. (games and table.count(games) or 0) .. " aktive Spiele")
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('tdm:joinGame', function(gameId, team, fieldId)
 | 
						|
    currentGameId = gameId
 | 
						|
    currentTeam = team
 | 
						|
    currentField = fieldId
 | 
						|
    inTDM = true
 | 
						|
    isHit = false
 | 
						|
    
 | 
						|
    -- Stats zurücksetzen
 | 
						|
    playerStats.hits = 0
 | 
						|
    playerStats.deaths = 0
 | 
						|
    playerStats.gamesPlayed = playerStats.gamesPlayed + 1
 | 
						|
    
 | 
						|
    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'
 | 
						|
    })
 | 
						|
    
 | 
						|
    debugPrint("Spiel beigetreten: " .. gameId .. ", Team: " .. team .. ", Feld: " .. fieldId)
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('tdm:leaveGame', function()
 | 
						|
    inTDM = false
 | 
						|
    local previousField = currentField
 | 
						|
    currentTeam = nil
 | 
						|
    currentGameId = nil
 | 
						|
    currentField = nil
 | 
						|
    isHit = false
 | 
						|
    
 | 
						|
    -- 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
 | 
						|
    else
 | 
						|
        for fieldId, fieldData in pairs(Config.gameFields) do
 | 
						|
            if fieldData.lobby and fieldData.lobby.pos then
 | 
						|
                lobbyPos = fieldData.lobby.pos
 | 
						|
                break
 | 
						|
            end
 | 
						|
        end
 | 
						|
    end
 | 
						|
    
 | 
						|
    -- Teleport zur Lobby (mit Fallback-Position)
 | 
						|
    if lobbyPos then
 | 
						|
        SetEntityCoords(PlayerPedId(), lobbyPos.x, lobbyPos.y, lobbyPos.z)
 | 
						|
    else
 | 
						|
        -- Notfall-Fallback Position (anpassen an deine Map)
 | 
						|
        SetEntityCoords(PlayerPedId(), -1042.4, -2745.8, 21.4)
 | 
						|
        debugPrint("WARNUNG: Keine Lobby gefunden, Fallback-Position verwendet!")
 | 
						|
    end
 | 
						|
    
 | 
						|
    -- Maske entfernen
 | 
						|
    SetPedComponentVariation(PlayerPedId(), 1, 0, 0, 0)
 | 
						|
    
 | 
						|
    -- Zone Blips entfernen
 | 
						|
    removeTeamZoneBlips()
 | 
						|
    
 | 
						|
    lib.hideTextUI()
 | 
						|
    
 | 
						|
    lib.notify({
 | 
						|
        title = 'TeamDeathmatch',
 | 
						|
        description = 'Du hast das Spiel verlassen!',
 | 
						|
        type = 'error'
 | 
						|
    })
 | 
						|
    
 | 
						|
    debugPrint("Spiel verlassen")
 | 
						|
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()
 | 
						|
    if not inTDM then 
 | 
						|
        debugPrint("WARNUNG: Hit-Event empfangen, aber nicht im TDM!")
 | 
						|
        return 
 | 
						|
    end
 | 
						|
    
 | 
						|
    if isHit then 
 | 
						|
        debugPrint("WARNUNG: Hit-Event empfangen, aber bereits getroffen!")
 | 
						|
        return 
 | 
						|
    end
 | 
						|
    
 | 
						|
    debugPrint("Spieler wurde getroffen - Starte Respawn-Sequenz")
 | 
						|
    isHit = true
 | 
						|
    local ped = PlayerPedId()
 | 
						|
    
 | 
						|
    -- Benachrichtigung
 | 
						|
    lib.notify({
 | 
						|
        title = 'TeamDeathmatch',
 | 
						|
        description = 'Du wurdest getroffen! Respawn in ' .. (Config.respawnDelay / 1000) .. ' Sekunden...',
 | 
						|
        type = 'error'
 | 
						|
    })
 | 
						|
    
 | 
						|
    -- Optional: Ragdoll aktivieren
 | 
						|
    SetPedToRagdoll(ped, 2000, 2000, 0, true, true, false)
 | 
						|
    
 | 
						|
    -- Verbesserte Respawn-Logik mit automatischem Teleport
 | 
						|
    SetTimeout(Config.respawnDelay, function()
 | 
						|
        if not inTDM then 
 | 
						|
            debugPrint("Respawn abgebrochen - nicht mehr im TDM")
 | 
						|
            return 
 | 
						|
        end
 | 
						|
        
 | 
						|
        debugPrint("Respawn wird ausgeführt...")
 | 
						|
        
 | 
						|
        -- Respawn zum Team Spawn
 | 
						|
        local fieldConfig = Config.gameFields[currentField]
 | 
						|
        if not fieldConfig then
 | 
						|
            debugPrint("FEHLER: Feldkonfiguration nicht gefunden für Respawn!")
 | 
						|
            return
 | 
						|
        end
 | 
						|
        
 | 
						|
        local spawnPoints = fieldConfig.teamSpawns[currentTeam]
 | 
						|
        if not spawnPoints or #spawnPoints == 0 then
 | 
						|
            debugPrint("FEHLER: Keine Spawn-Punkte gefunden für Respawn!")
 | 
						|
            return
 | 
						|
        end
 | 
						|
        
 | 
						|
        local randomSpawn = spawnPoints[math.random(#spawnPoints)]
 | 
						|
        debugPrint("Respawn-Position: " .. randomSpawn.x .. ", " .. randomSpawn.y .. ", " .. randomSpawn.z)
 | 
						|
        
 | 
						|
        -- Teleport zum Spawn mit Fade
 | 
						|
        DoScreenFadeOut(500)
 | 
						|
        Wait(600)
 | 
						|
        
 | 
						|
        -- Stellen Sie sicher, dass der Spieler lebt
 | 
						|
        NetworkResurrectLocalPlayer(randomSpawn.x, randomSpawn.y, randomSpawn.z, 0.0, true, false)
 | 
						|
        debugPrint("Spieler wiederbelebt")
 | 
						|
        
 | 
						|
        -- Alle Animationen stoppen
 | 
						|
        ClearPedTasksImmediately(ped)
 | 
						|
        
 | 
						|
        -- Teleport direkt zum Spawn-Punkt
 | 
						|
        SetEntityCoords(ped, randomSpawn.x, randomSpawn.y, randomSpawn.z)
 | 
						|
        SetEntityHealth(ped, GetEntityMaxHealth(ped))
 | 
						|
        
 | 
						|
        -- Spieler ist wieder aktiv
 | 
						|
        isHit = false
 | 
						|
        debugPrint("Respawn abgeschlossen - Spieler ist wieder aktiv")
 | 
						|
        
 | 
						|
        Wait(100)
 | 
						|
        DoScreenFadeIn(500)
 | 
						|
        
 | 
						|
        lib.notify({
 | 
						|
            title = 'TeamDeathmatch',
 | 
						|
            description = 'Du bist wieder im Spiel!',
 | 
						|
            type = 'success'
 | 
						|
        })
 | 
						|
    end)
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('tdm:updateScore', function(team1Score, team2Score, gameStats)
 | 
						|
    -- Debug-Ausgabe
 | 
						|
    debugPrint("Score Update empfangen: Team1=" .. team1Score .. ", Team2=" .. team2Score)
 | 
						|
    if gameStats then
 | 
						|
        debugPrint("GameStats: Hits=" .. (gameStats.hits or "nil") .. ", Deaths=" .. (gameStats.deaths or "nil"))
 | 
						|
    end
 | 
						|
    
 | 
						|
    -- Verwende gameStats falls verfügbar, sonst lokale Stats
 | 
						|
    local myHits = gameStats and gameStats.hits or playerStats.hits
 | 
						|
    local myDeaths = gameStats and gameStats.deaths or playerStats.deaths
 | 
						|
    
 | 
						|
    local displayText = string.format(
 | 
						|
        '[Team 1: %d] VS [Team 2: %d] | Deine Treffer: %d | Tode: %d',
 | 
						|
        team1Score,
 | 
						|
        team2Score,
 | 
						|
        myHits,
 | 
						|
        myDeaths
 | 
						|
    )
 | 
						|
    
 | 
						|
    lib.showTextUI(displayText, {
 | 
						|
        position = "top-center",
 | 
						|
        icon = 'crosshairs'
 | 
						|
    })
 | 
						|
end)
 | 
						|
 | 
						|
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
 | 
						|
    })
 | 
						|
    
 | 
						|
    -- Spiele einen Sound ab
 | 
						|
    PlaySoundFrontend(-1, "WEAPON_PURCHASE", "HUD_AMMO_SHOP_SOUNDSET", true)
 | 
						|
    
 | 
						|
    showHitMarker()
 | 
						|
    
 | 
						|
    -- Score sofort aktualisieren
 | 
						|
    if currentGameId then
 | 
						|
        TriggerServerEvent('tdm:requestScoreUpdate', currentGameId)
 | 
						|
    end
 | 
						|
    
 | 
						|
    debugPrint("Treffer registriert! Neue Hits: " .. playerStats.hits)
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('tdm:deathRegistered', function()
 | 
						|
    playerStats.deaths = playerStats.deaths + 1
 | 
						|
    
 | 
						|
    -- Score sofort aktualisieren
 | 
						|
    if currentGameId then
 | 
						|
        TriggerServerEvent('tdm:requestScoreUpdate', currentGameId)
 | 
						|
    end
 | 
						|
    
 | 
						|
    debugPrint("Tod registriert! Neue Deaths: " .. playerStats.deaths)
 | 
						|
end)
 | 
						|
 | 
						|
RegisterNetEvent('tdm:gameEnded', function(winnerTeam, team1Score, team2Score)
 | 
						|
    lib.hideTextUI()
 | 
						|
    
 | 
						|
    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
 | 
						|
    )
 | 
						|
    
 | 
						|
    local alert = lib.alertDialog({
 | 
						|
        header = 'Spiel beendet!',
 | 
						|
        content = statsText,
 | 
						|
        centered = true,
 | 
						|
        cancel = false
 | 
						|
    })
 | 
						|
    
 | 
						|
    Wait(5000)
 | 
						|
    TriggerServerEvent('tdm:leaveGame')
 | 
						|
    
 | 
						|
    debugPrint("Spiel beendet! Gewinner: " .. (winnerTeam or "Unentschieden"))
 | 
						|
end)
 | 
						|
 | 
						|
-- Funktionen
 | 
						|
function setTeamMask(team)
 | 
						|
    local ped = PlayerPedId()
 | 
						|
    local maskData = Config.teamMasks[team]
 | 
						|
    
 | 
						|
    if maskData then
 | 
						|
        -- 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)
 | 
						|
            debugPrint("Maske gesetzt: " .. team .. " (" .. playerGender .. ")")
 | 
						|
        else
 | 
						|
            debugPrint("Keine Maske für " .. team .. " (" .. playerGender .. ") gefunden!")
 | 
						|
        end
 | 
						|
    else
 | 
						|
        debugPrint("Keine Masken-Daten für Team " .. team .. " gefunden!")
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
function createTeamZoneBlip(team, fieldConfig)
 | 
						|
    local zone = fieldConfig.teamZones[team]
 | 
						|
    
 | 
						|
    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
 | 
						|
    debugPrint("Team Zone Blip erstellt für " .. team)
 | 
						|
end
 | 
						|
 | 
						|
function removeTeamZoneBlips()
 | 
						|
    for team, blip in pairs(teamZoneBlips) do
 | 
						|
        if DoesBlipExist(blip) then
 | 
						|
            RemoveBlip(blip)
 | 
						|
        end
 | 
						|
    end
 | 
						|
    teamZoneBlips = {}
 | 
						|
    debugPrint("Team Zone Blips entfernt")
 | 
						|
end
 | 
						|
 | 
						|
function highlightTeamZone(team)
 | 
						|
    if teamZoneBlips[team] and DoesBlipExist(teamZoneBlips[team]) then
 | 
						|
        SetBlipFlashes(teamZoneBlips[team], true)
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
function showHitMarker()
 | 
						|
    debugPrint("Zeige Hit-Marker an")
 | 
						|
    CreateThread(function()
 | 
						|
        local startTime = GetGameTimer()
 | 
						|
        local duration = 500 -- 500ms display
 | 
						|
        
 | 
						|
        while GetGameTimer() - startTime < duration do
 | 
						|
            Wait(0)
 | 
						|
            
 | 
						|
            -- Draw hit marker
 | 
						|
            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)
 | 
						|
            
 | 
						|
            -- Draw hit text
 | 
						|
            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
 | 
						|
 | 
						|
function openMainMenu(fieldId)
 | 
						|
    -- Sicherheitscheck
 | 
						|
    if not fieldId or not Config.gameFields[fieldId] then
 | 
						|
        lib.notify({
 | 
						|
            title = 'Fehler',
 | 
						|
            description = 'Ungültiges Spielfeld!',
 | 
						|
            type = 'error'
 | 
						|
        })
 | 
						|
        return
 | 
						|
    end
 | 
						|
    
 | 
						|
    currentLobbyField = fieldId
 | 
						|
    TriggerServerEvent('tdm:requestGamesList')
 | 
						|
    
 | 
						|
    Wait(100)
 | 
						|
    
 | 
						|
    local fieldName = Config.gameFields[fieldId].name
 | 
						|
    
 | 
						|
    local options = {
 | 
						|
        {
 | 
						|
            title = 'Neues Spiel erstellen',
 | 
						|
            description = 'Erstelle ein neues Spiel für ' .. fieldName,
 | 
						|
            icon = 'plus',
 | 
						|
            onSelect = function()
 | 
						|
                openCreateGameMenu(fieldId)
 | 
						|
            end
 | 
						|
        },
 | 
						|
        {
 | 
						|
            title = 'Spiel beitreten',
 | 
						|
            description = 'Trete einem laufenden Spiel bei',
 | 
						|
            icon = 'users',
 | 
						|
            onSelect = function()
 | 
						|
                openJoinGameMenu(fieldId)
 | 
						|
            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({
 | 
						|
        id = 'tdm_main_menu_' .. fieldId,
 | 
						|
        title = 'TeamDeathmatch - ' .. fieldName,
 | 
						|
        options = options
 | 
						|
    })
 | 
						|
    
 | 
						|
    lib.showContext('tdm_main_menu_' .. fieldId)
 | 
						|
end
 | 
						|
 | 
						|
function openCreateGameMenu(fieldId)
 | 
						|
    if not fieldId or not Config.gameFields[fieldId] then
 | 
						|
        lib.notify({
 | 
						|
            title = 'Fehler',
 | 
						|
            description = 'Ungültiges Spielfeld!',
 | 
						|
            type = 'error'
 | 
						|
        })
 | 
						|
        return
 | 
						|
    end
 | 
						|
    
 | 
						|
    local fieldData = Config.gameFields[fieldId]
 | 
						|
    
 | 
						|
    local input = lib.inputDialog('Neues Spiel erstellen - ' .. fieldData.name, {
 | 
						|
        {
 | 
						|
            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
 | 
						|
        }
 | 
						|
    })
 | 
						|
    
 | 
						|
    if not input then return end
 | 
						|
    
 | 
						|
    local gameName = input[1]
 | 
						|
    local gameType = input[2]
 | 
						|
    local password = input[3] and input[3] ~= '' and input[3] or nil
 | 
						|
    
 | 
						|
    if gameName and gameType then
 | 
						|
        TriggerServerEvent('tdm:createGame', gameName, fieldId, gameType, password)
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
function openJoinGameMenu(fieldId)
 | 
						|
    if not fieldId or not Config.gameFields[fieldId] then
 | 
						|
        lib.notify({
 | 
						|
            title = 'Fehler',
 | 
						|
            description = 'Ungültiges Spielfeld!',
 | 
						|
            type = 'error'
 | 
						|
        })
 | 
						|
        return
 | 
						|
    end
 | 
						|
    
 | 
						|
    TriggerServerEvent('tdm:requestGamesList')
 | 
						|
    
 | 
						|
    Wait(200)
 | 
						|
    
 | 
						|
    local options = {}
 | 
						|
    local fieldName = Config.gameFields[fieldId].name
 | 
						|
    
 | 
						|
    -- Nur Spiele für dieses Feld anzeigen
 | 
						|
    for gameId, gameData in pairs(activeGames) do
 | 
						|
        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)
 | 
						|
                    end
 | 
						|
                end
 | 
						|
            })
 | 
						|
        end
 | 
						|
    end
 | 
						|
    
 | 
						|
    if #options == 0 then
 | 
						|
        table.insert(options, {
 | 
						|
            title = 'Keine Spiele verfügbar',
 | 
						|
            description = 'Erstelle ein neues Spiel für ' .. fieldName,
 | 
						|
            icon = 'info',
 | 
						|
            disabled = true
 | 
						|
        })
 | 
						|
    end
 | 
						|
    
 | 
						|
    lib.registerContext({
 | 
						|
        id = 'tdm_join_menu_' .. fieldId,
 | 
						|
        title = 'Spiel beitreten - ' .. fieldName,
 | 
						|
        menu = 'tdm_main_menu_' .. fieldId,
 | 
						|
        options = options
 | 
						|
    })
 | 
						|
    
 | 
						|
    lib.showContext('tdm_join_menu_' .. fieldId)
 | 
						|
end
 | 
						|
 | 
						|
-- Zone Marker Renderer
 | 
						|
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)
 | 
						|
 | 
						|
-- Verbesserte Damage Handler für Airsoft-Waffen
 | 
						|
CreateThread(function()
 | 
						|
    while true do
 | 
						|
        if inTDM and not isHit then
 | 
						|
            local ped = PlayerPedId()
 | 
						|
            
 | 
						|
            -- Prüfe, ob der Spieler Schaden genommen hat
 | 
						|
            if HasEntityBeenDamagedByAnyPed(ped) then
 | 
						|
                debugPrint("Schaden erkannt - Identifiziere Angreifer und Waffe")
 | 
						|
                
 | 
						|
                local damager = nil
 | 
						|
                local damagerPlayer = nil
 | 
						|
                local weaponHash = 0
 | 
						|
                
 | 
						|
                -- Versuche den Angreifer und die Waffe zu identifizieren
 | 
						|
                for _, player in ipairs(GetActivePlayers()) do
 | 
						|
                    local playerPed = GetPlayerPed(player)
 | 
						|
                    if HasPedBeenDamagedBy(ped, playerPed) then
 | 
						|
                        damager = playerPed
 | 
						|
                        damagerPlayer = GetPlayerServerId(player)
 | 
						|
                        
 | 
						|
                        -- Versuche die Waffe zu ermitteln
 | 
						|
                        weaponHash = GetSelectedPedWeapon(playerPed)
 | 
						|
                        debugPrint("Angreifer identifiziert: " .. damagerPlayer .. " mit Waffe: " .. weaponHash)
 | 
						|
                        break
 | 
						|
                    end
 | 
						|
                end
 | 
						|
                
 | 
						|
                -- 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
 | 
						|
            end
 | 
						|
            Wait(0)
 | 
						|
        else
 | 
						|
            Wait(500)
 | 
						|
        end
 | 
						|
    end
 | 
						|
end)
 | 
						|
 | 
						|
-- Zusätzlicher Event-Handler für zuverlässigere Treffer-Erkennung
 | 
						|
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]
 | 
						|
        local isMelee = args[10] == 1
 | 
						|
        
 | 
						|
        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)
 | 
						|
            
 | 
						|
            -- Prüfe, ob es eine Airsoft-Waffe ist oder alle Waffen als Airsoft behandelt werden
 | 
						|
            if isAirsoftWeapon(weaponHash) and attackerServerId then
 | 
						|
                -- 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)
 | 
						|
 | 
						|
-- Direkter Waffen-Schaden Monitor für zusätzliche Zuverlässigkeit
 | 
						|
CreateThread(function()
 | 
						|
    while true do
 | 
						|
        Wait(0)
 | 
						|
        if inTDM and not isHit then
 | 
						|
            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
 | 
						|
            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)
 | 
						|
                local attacker = nil
 | 
						|
                if lastDamager and (GetGameTimer() - lastDamageTime) < 3000 then
 | 
						|
                    attacker = lastDamager
 | 
						|
                    debugPrint("Ragdoll-Tod zugeordnet an Angreifer: " .. attacker)
 | 
						|
                else
 | 
						|
                    debugPrint("Kein Angreifer für Ragdoll-Tod gefunden")
 | 
						|
                end
 | 
						|
                
 | 
						|
                -- Treffer-Events auslösen
 | 
						|
                TriggerEvent('tdm:playerHit')
 | 
						|
                TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, attacker)
 | 
						|
                
 | 
						|
                -- Zurücksetzen des letzten Angreifers
 | 
						|
                lastDamager = nil
 | 
						|
                
 | 
						|
                -- Warten um mehrfache Auslösung zu verhindern
 | 
						|
                Wait(500)
 | 
						|
            end
 | 
						|
        else
 | 
						|
            Wait(500)
 | 
						|
        end
 | 
						|
    end
 | 
						|
end)
 | 
						|
 |