1
0
Fork 0
forked from Simnation/Main
Main/resources/[standalone]/nordi_tdm/client.lua
2025-07-26 07:01:07 +02:00

640 lines
19 KiB
Lua

local QBCore = exports['qb-core']:GetCoreObject()
local inTDM = false
local currentTeam = nil
local currentGameId = nil
local currentField = nil
local currentLobbyField = nil -- Neue Variable für aktuelle Lobby
local tdmBlips = {} -- Mehrere Blips
local teamZoneBlips = {}
local isHit = false
local activeGames = {}
local spawnedNPCs = {} -- Gespawnte NPCs verwalten
-- Spieler Statistiken
local playerStats = {
hits = 0,
deaths = 0,
gamesPlayed = 0
}
-- Events (gleich wie vorher, nur leaveGame angepasst)
RegisterNetEvent('tdm:updateGamesList', function(games)
activeGames = games
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'
})
end)
RegisterNetEvent('tdm:leaveGame', function()
inTDM = false
local previousField = currentField
currentTeam = nil
currentGameId = nil
currentField = nil
isHit = false
-- Zurück zur entsprechenden Lobby (falls vorher in einem Spiel)
if previousField and Config.gameFields[previousField] then
local lobbyPos = Config.gameFields[previousField].lobby.pos
SetEntityCoords(PlayerPedId(), lobbyPos.x, lobbyPos.y, lobbyPos.z)
elseif currentLobbyField and Config.gameFields[currentLobbyField] then
-- Zurück zur aktuellen Lobby
local lobbyPos = Config.gameFields[currentLobbyField].lobby.pos
SetEntityCoords(PlayerPedId(), lobbyPos.x, lobbyPos.y, lobbyPos.z)
else
-- Fallback zur ersten Lobby
local firstField = next(Config.gameFields)
if firstField then
local lobbyPos = Config.gameFields[firstField].lobby.pos
SetEntityCoords(PlayerPedId(), lobbyPos.x, lobbyPos.y, lobbyPos.z)
end
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'
})
end)
-- Alle anderen Events bleiben gleich...
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 or isHit then return end
isHit = true
local ped = PlayerPedId()
RequestAnimDict("random@mugging3")
while not HasAnimDictLoaded("random@mugging3") do
Wait(1)
end
TaskPlayAnim(ped, "random@mugging3", "handsup_standing_base", 8.0, -8.0, -1, 50, 0, false, false, false)
lib.notify({
title = 'TeamDeathmatch',
description = 'Du wurdest getroffen! Gehe zurück zu deiner Team Zone!',
type = 'error'
})
highlightTeamZone(currentTeam)
end)
RegisterNetEvent('tdm:updateScore', function(team1Score, team2Score, gameStats)
local displayText = string.format(
'[Team 1: %d] VS [Team 2: %d] | Deine Treffer: %d | Tode: %d',
team1Score,
team2Score,
playerStats.hits,
playerStats.deaths
)
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
})
showHitMarker()
end)
RegisterNetEvent('tdm:deathRegistered', function()
playerStats.deaths = playerStats.deaths + 1
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')
end)
-- Angepasste setTeamMask Funktion
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)
end
end
end
-- Alternative Methode über QBCore Player Data
function setTeamMaskQB(team)
local ped = PlayerPedId()
local Player = QBCore.Functions.GetPlayerData()
local maskData = Config.teamMasks[team]
if maskData and Player.charinfo then
-- Geschlecht aus QBCore Charinfo
local playerGender = Player.charinfo.gender == 1 and "female" or "male"
-- Entsprechende Maske setzen
local genderMask = maskData[playerGender]
if genderMask then
SetPedComponentVariation(ped, genderMask.component, genderMask.drawable, genderMask.texture, 0)
end
end
end
-- Erweiterte Funktion mit Fallback
function setTeamMaskAdvanced(team)
local ped = PlayerPedId()
local maskData = Config.teamMasks[team]
if not maskData then return end
local playerGender = "male" -- Default
-- Methode 1: Über Ped Model
if GetEntityModel(ped) == GetHashKey("mp_f_freemode_01") then
playerGender = "female"
end
-- Methode 2: Über QBCore (Fallback)
if playerGender == "male" then
local Player = QBCore.Functions.GetPlayerData()
if Player.charinfo and Player.charinfo.gender == 1 then
playerGender = "female"
end
end
-- Maske setzen
local genderMask = maskData[playerGender]
if genderMask then
SetPedComponentVariation(ped, genderMask.component, genderMask.drawable, genderMask.texture, 0)
lib.notify({
title = 'TeamDeathmatch',
description = 'Team-Maske (' .. playerGender .. ') angelegt!',
type = 'info',
duration = 2000
})
end
end
-- Angepasste Menü-Funktionen
function openMainMenu(fieldId)
currentLobbyField = fieldId -- Aktuelle Lobby merken
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)
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)
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
-- Alle Threads bleiben gleich...
CreateThread(function()
while true do
Wait(500)
if inTDM and isHit and currentTeam and currentField then
local ped = PlayerPedId()
local playerPos = GetEntityCoords(ped)
local zone = Config.gameFields[currentField].teamZones[currentTeam]
local distance = #(playerPos - zone.center)
if distance <= zone.radius then
isHit = false
ClearPedTasks(ped)
if teamZoneBlips[currentTeam] and DoesBlipExist(teamZoneBlips[currentTeam]) then
SetBlipFlashes(teamZoneBlips[currentTeam], false)
end
lib.notify({
title = 'TeamDeathmatch',
description = 'Du bist wieder im Spiel!',
type = 'success'
})
end
end
end
end)
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)
CreateThread(function()
while true do
Wait(100)
if inTDM and not isHit then
local ped = PlayerPedId()
if HasEntityBeenDamagedByAnyPed(ped) then
local damager = GetPedSourceOfDeath(ped)
local damagerPlayer = nil
for _, player in pairs(GetActivePlayers()) do
if GetPlayerPed(player) == damager then
damagerPlayer = GetPlayerServerId(player)
break
end
end
ClearEntityLastDamageEntity(ped)
TriggerEvent('tdm:playerHit')
TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, damagerPlayer)
end
end
end
end)
CreateThread(function()
while true do
Wait(1000)
if inTDM then
local ped = PlayerPedId()
if IsEntityDead(ped) then
TriggerServerEvent('tdm:playerDied', currentGameId)
lib.notify({
title = 'TeamDeathmatch',
description = 'Du bist ausgeschieden!',
type = 'error'
})
Wait(3000)
TriggerServerEvent('tdm:leaveGame')
end
end
end
end)
-- Angepasstes NPC Setup für alle Felder
CreateThread(function()
-- Für jedes Spielfeld Blip und NPC erstellen
for fieldId, fieldData in pairs(Config.gameFields) do
local lobbyPos = fieldData.lobby.pos
local npcData = fieldData.lobby.npc
-- Blip erstellen
local blip = AddBlipForCoord(lobbyPos.x, lobbyPos.y, lobbyPos.z)
SetBlipSprite(blip, 432)
SetBlipDisplay(blip, 4)
SetBlipScale(blip, 0.8)
SetBlipColour(blip, 1)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("TDM - " .. fieldData.name)
EndTextCommandSetBlipName(blip)
tdmBlips[fieldId] = blip
-- NPC erstellen
RequestModel(GetHashKey(npcData.model))
while not HasModelLoaded(GetHashKey(npcData.model)) do
Wait(1)
end
local npc = CreatePed(4, GetHashKey(npcData.model), npcData.coords.x, npcData.coords.y, npcData.coords.z, npcData.coords.w, false, true)
SetEntityInvincible(npc, true)
FreezeEntityPosition(npc, true)
SetBlockingOfNonTemporaryEvents(npc, true)
spawnedNPCs[fieldId] = npc
-- Target für diesen NPC
exports['qb-target']:AddTargetEntity(npc, {
options = {
{
type = "client",
event = "tdm:openFieldMenu",
icon = "fas fa-crosshairs",
label = "TeamDeathmatch - " .. fieldData.name,
fieldId = fieldId
},
},
distance = 2.5
})
end
end)
-- Event für Feld-spezifisches Menü
RegisterNetEvent('tdm:openFieldMenu', function(data)
openMainMenu(data.fieldId)
end)
-- Chat Command zum Spiel verlassen
RegisterCommand('leavetdm', function()
if inTDM then
TriggerServerEvent('tdm:leaveGame')
lib.notify({
title = 'TeamDeathmatch',
description = 'Du hast das Spiel über Command verlassen!',
type = 'info'
})
else
lib.notify({
title = 'TeamDeathmatch',
description = 'Du bist in keinem Spiel!',
type = 'error'
})
end
end, false)
-- Keybind zum Spiel verlassen
RegisterKeyMapping('leavetdm', 'TeamDeathmatch verlassen', 'keyboard', 'F7')