From efaef09a83e4de13718ffd946af13e4c0c088aad Mon Sep 17 00:00:00 2001 From: Nordi98 Date: Sun, 27 Jul 2025 02:52:57 +0200 Subject: [PATCH] ed --- resources/[standalone]/nordi_tdm/client.lua | 456 ++++++-------------- resources/[standalone]/nordi_tdm/config.lua | 29 +- 2 files changed, 152 insertions(+), 333 deletions(-) diff --git a/resources/[standalone]/nordi_tdm/client.lua b/resources/[standalone]/nordi_tdm/client.lua index 22a9dacd9..e3bc6d0b0 100644 --- a/resources/[standalone]/nordi_tdm/client.lua +++ b/resources/[standalone]/nordi_tdm/client.lua @@ -22,6 +22,16 @@ 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 @@ -169,12 +179,15 @@ RegisterNetEvent('tdm:playerHit', function() -- Benachrichtigung lib.notify({ title = 'TeamDeathmatch', - description = 'Du wurdest getroffen! Respawn in 3 Sekunden...', + description = 'Du wurdest getroffen! Respawn in ' .. (Config.respawnDelay / 1000) .. ' Sekunden...', type = 'error' }) - -- Verbesserte Respawn-Logik - SetTimeout(3000, function() + -- 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 @@ -209,7 +222,7 @@ RegisterNetEvent('tdm:playerHit', function() -- Alle Animationen stoppen ClearPedTasksImmediately(ped) - -- Teleport + -- Teleport direkt zum Spawn-Punkt SetEntityCoords(ped, randomSpawn.x, randomSpawn.y, randomSpawn.z) SetEntityHealth(ped, GetEntityMaxHealth(ped)) @@ -621,7 +634,7 @@ CreateThread(function() end end) --- Verbesserte Damage Handler für korrekte Treffer-Registrierung +-- Verbesserte Damage Handler für Airsoft-Waffen CreateThread(function() while true do if inTDM and not isHit then @@ -629,38 +642,52 @@ CreateThread(function() -- Prüfe, ob der Spieler Schaden genommen hat if HasEntityBeenDamagedByAnyPed(ped) then - debugPrint("Schaden erkannt - Identifiziere Angreifer") + debugPrint("Schaden erkannt - Identifiziere Angreifer und Waffe") local damager = nil local damagerPlayer = nil + local weaponHash = 0 - -- Versuche den Angreifer zu identifizieren + -- 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) - debugPrint("Angreifer identifiziert: " .. damagerPlayer) + + -- Versuche die Waffe zu ermitteln + weaponHash = GetSelectedPedWeapon(playerPed) + debugPrint("Angreifer identifiziert: " .. damagerPlayer .. " mit Waffe: " .. weaponHash) break end end - -- 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) + -- 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 @@ -676,6 +703,7 @@ AddEventHandler('gameEventTriggered', function(name, args) 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 @@ -690,7 +718,8 @@ AddEventHandler('gameEventTriggered', function(name, args) debugPrint("Schaden-Event erkannt: Angreifer=" .. (attackerServerId or "NPC/Unbekannt") .. ", Waffe=" .. weaponHash) - if attackerServerId then + -- 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 @@ -705,328 +734,91 @@ AddEventHandler('gameEventTriggered', function(name, args) end end) --- Death Handler +-- Direkter Waffen-Schaden Monitor für zusätzliche Zuverlässigkeit CreateThread(function() while true do - Wait(1000) - - if inTDM then + Wait(0) + if inTDM and not isHit then local ped = PlayerPedId() - if IsEntityDead(ped) then - debugPrint("Spieler ist tot!") + -- Prüfe auf Projektil-Treffer + if HasEntityBeenDamagedByWeapon(ped, 0, 2) then -- 2 = Projektilwaffen + debugPrint("Projektil-Treffer erkannt") - TriggerServerEvent('tdm:playerDied', currentGameId) + -- Schaden zurücksetzen + ClearEntityLastDamageEntity(ped) + ClearPedLastWeaponDamage(ped) + SetEntityHealth(ped, GetEntityMaxHealth(ped)) - lib.notify({ - title = 'TeamDeathmatch', - description = 'Du bist ausgeschieden!', - type = 'error' - }) + -- Lokale Stats sofort updaten + playerStats.deaths = playerStats.deaths + 1 - Wait(3000) - TriggerServerEvent('tdm:leaveGame') + -- Treffer-Events auslösen + TriggerEvent('tdm:playerHit') + TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam) + + -- Warten um mehrfache Auslösung zu verhindern + Wait(500) end - end - end -end) - --- NPC Setup für alle Felder -CreateThread(function() - -- Für jedes Spielfeld Blip und NPC erstellen - for fieldId, fieldData in pairs(Config.gameFields) do - if fieldData.lobby and fieldData.lobby.pos and fieldData.lobby.npc then - 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 - }) - - debugPrint("NPC und Blip für Feld " .. fieldId .. " erstellt") else - debugPrint("WARNUNG: Feld " .. fieldId .. " hat keine vollständige Lobby-Konfiguration!") + Wait(500) end end end) --- Event für Feld-spezifisches Menü -RegisterNetEvent('tdm:openFieldMenu', function(data) - if data and data.fieldId then - openMainMenu(data.fieldId) - else - lib.notify({ - title = 'Fehler', - description = 'Keine Feld-ID übertragen!', - type = 'error' - }) - end -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') - --- Debug Command zum Testen der Config -RegisterCommand('debugtdm', function() - debugPrint("Aktuelle Werte:") - debugPrint("inTDM: " .. tostring(inTDM)) - debugPrint("currentField: " .. tostring(currentField)) - debugPrint("currentLobbyField: " .. tostring(currentLobbyField)) - debugPrint("currentTeam: " .. tostring(currentTeam)) - debugPrint("currentGameId: " .. tostring(currentGameId)) - debugPrint("isHit: " .. tostring(isHit)) - debugPrint("Hits: " .. playerStats.hits) - debugPrint("Deaths: " .. playerStats.deaths) +-- Ragdoll-Erkennung Thread +CreateThread(function() + local lastDamager = nil + local lastDamageTime = 0 - debugPrint("Verfügbare Felder:") - for fieldId, fieldData in pairs(Config.gameFields) do - local hasLobby = fieldData.lobby and fieldData.lobby.pos and "✅" or "❌" - debugPrint("- " .. fieldId .. ": " .. fieldData.name .. " " .. hasLobby) - end -end, false) - --- Debug Commands für Masken -RegisterCommand('testmask', function(source, args) - if not args[1] or not args[2] then - lib.notify({ - title = 'Debug', - description = 'Verwendung: /testmask [team1/team2] [male/female]', - type = 'error' - }) - return - end - - local team = args[1] - local gender = args[2] - - if Config.teamMasks[team] and Config.teamMasks[team][gender] then - local maskData = Config.teamMasks[team][gender] - local ped = PlayerPedId() - - SetPedComponentVariation(ped, maskData.component, maskData.drawable, maskData.texture, 0) - - lib.notify({ - title = 'Debug', - description = 'Maske gesetzt: ' .. team .. ' (' .. gender .. ')', - type = 'success' - }) - else - lib.notify({ - title = 'Debug', - description = 'Maske nicht gefunden!', - type = 'error' - }) - end -end, false) - --- Command zum Entfernen der Maske -RegisterCommand('removemask', function() - SetPedComponentVariation(PlayerPedId(), 1, 0, 0, 0) - lib.notify({ - title = 'Debug', - description = 'Maske entfernt!', - type = 'info' - }) -end, false) - --- Debug-Funktion für Respawn -RegisterCommand('forcetdmrespawn', function() - if inTDM and currentTeam and currentField then - local ped = PlayerPedId() - local fieldConfig = Config.gameFields[currentField] - local spawnPoints = fieldConfig.teamSpawns[currentTeam] - local randomSpawn = spawnPoints[math.random(#spawnPoints)] - - DoScreenFadeOut(500) - Wait(600) - - ClearPedTasksImmediately(ped) - NetworkResurrectLocalPlayer(randomSpawn.x, randomSpawn.y, randomSpawn.z, 0.0, true, false) - SetEntityCoords(ped, randomSpawn.x, randomSpawn.y, randomSpawn.z) - SetEntityHealth(ped, GetEntityMaxHealth(ped)) - isHit = false - + while true do Wait(100) - DoScreenFadeIn(500) - - lib.notify({ - title = 'Debug', - description = 'Manueller Respawn durchgeführt!', - type = 'success' - }) - else - lib.notify({ - title = 'Debug', - description = 'Du bist nicht in einem TDM-Spiel!', - type = 'error' - }) - end -end, false) - --- Debug-Funktion für Stats -RegisterCommand('showstats', function() - if inTDM then - debugPrint("Lokale Stats:") - debugPrint("Hits: " .. playerStats.hits) - debugPrint("Deaths: " .. playerStats.deaths) - - if currentGameId then - TriggerServerEvent('tdm:debugPlayerStats', currentGameId) + 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 - - lib.notify({ - title = 'Debug', - description = 'Lokale Stats - Hits: ' .. playerStats.hits .. ', Deaths: ' .. playerStats.deaths, - type = 'info' - }) - else - lib.notify({ - title = 'Debug', - description = 'Du bist nicht in einem TDM-Spiel!', - type = 'error' - }) end -end, false) - --- Debug-Funktion für manuellen Hit -RegisterCommand('testhit', function() - if inTDM then - TriggerEvent('tdm:playerHit') - lib.notify({ - title = 'Debug', - description = 'Test-Hit ausgelöst!', - type = 'info' - }) - else - lib.notify({ - title = 'Debug', - description = 'Du bist nicht in einem TDM-Spiel!', - type = 'error' - }) - end -end, false) - --- Debug-Funktion für manuellen Treffer -RegisterCommand('testtreff', function() - if inTDM then - TriggerEvent('tdm:hitRegistered') - lib.notify({ - title = 'Debug', - description = 'Test-Treffer registriert!', - type = 'info' - }) - else - lib.notify({ - title = 'Debug', - description = 'Du bist nicht in einem TDM-Spiel!', - type = 'error' - }) - end -end, false) - --- Debug-Befehl zum Testen der Treffer-Registrierung -RegisterCommand('testtreffer', function() - if inTDM and currentGameId and currentTeam then - debugPrint("Teste Treffer-Registrierung") - - -- Simuliere einen Treffer gegen sich selbst - local targetTeam = currentTeam == 'team1' and 'team2' or 'team1' - TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam, GetPlayerServerId(PlayerId())) - - lib.notify({ - title = 'Debug', - description = 'Test-Treffer gesendet!', - type = 'info' - }) - else - lib.notify({ - title = 'Debug', - description = 'Du musst in einem TDM-Spiel sein!', - type = 'error' - }) - end -end, false) - --- Debug-Befehl zum Testen des Respawns -RegisterCommand('testrespawn', function() - if inTDM then - debugPrint("Teste Respawn-Funktion") - TriggerEvent('tdm:playerHit') - - lib.notify({ - title = 'Debug', - description = 'Test-Respawn ausgelöst!', - type = 'info' - }) - else - lib.notify({ - title = 'Debug', - description = 'Du musst in einem TDM-Spiel sein!', - type = 'error' - }) - end -end, false) - --- Hilfsfunktion für table.count -table.count = function(tbl) - local count = 0 - for _, _ in pairs(tbl) do - count = count + 1 - end - return count -end +end) diff --git a/resources/[standalone]/nordi_tdm/config.lua b/resources/[standalone]/nordi_tdm/config.lua index 3cf4a16d7..3a923132e 100644 --- a/resources/[standalone]/nordi_tdm/config.lua +++ b/resources/[standalone]/nordi_tdm/config.lua @@ -156,6 +156,33 @@ Config.teamMasks = { -- Game Settings Config.maxGameTime = 600 -- 10 Minuten Config.maxHits = 30 -- Spiel endet bei 30 Treffern +Config.respawnDelay = 3000 -- 3 Sekunden Respawn-Verzögerung + +-- Airsoft-Einstellungen +Config.treatAllWeaponsAsAirsoft = true -- Alle Waffen als Airsoft behandeln +Config.airsoftDamageMultiplier = 0.1 -- Reduzierter Schaden für Airsoft-Waffen + +-- Airsoft-Waffen (Hash-Werte) +Config.airsoftWeapons = { + -- Pistolen + [`WEAPON_AIRSOFTGLOCK20`] = true, + + -- SMGs + [`WEAPON_AIRSFOTMIRCOUZI`] = true, + [`WEAPON_AIRSOFTMP5`] = true, + + -- Gewehre + [`WEAPON_AIRSOFTM4`] = true, + [`WEAPON_AIRSOFTAK47`] = true, + [`WEAPON_AIRSOFTM249`] = true, + [`WEAPON_AIRSOFTG46C`] = true, + [`WEAPON_AIRSOFTR870`] = true, + + -- Scharfschützengewehre + [`WEAPON_AIRSOFTR700`] = true, + + +} -- Debug Settings -Config.debugMode = true -- Set to false in production +Config.debugMode = true -- Auf false setzen für Produktion