ed
This commit is contained in:
		
							parent
							
								
									b743002467
								
							
						
					
					
						commit
						f192af7af6
					
				
					 3 changed files with 941 additions and 0 deletions
				
			
		
							
								
								
									
										499
									
								
								resources/[standalone]/nordi_tdm/client.lua
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										499
									
								
								resources/[standalone]/nordi_tdm/client.lua
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,499 @@ | |||
| local QBCore = exports['qb-core']:GetCoreObject() | ||||
| local inTDM = false | ||||
| local currentTeam = nil | ||||
| local currentGameId = nil | ||||
| local currentField = nil | ||||
| local tdmBlip = nil | ||||
| local teamZoneBlips = {} | ||||
| local isHit = false | ||||
| local activeGames = {} | ||||
|  | ||||
| -- Events | ||||
| 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 | ||||
|      | ||||
|     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 | ||||
|     currentTeam = nil | ||||
|     currentGameId = nil | ||||
|     currentField = nil | ||||
|     isHit = false | ||||
|      | ||||
|     -- Zurück zur Lobby | ||||
|     SetEntityCoords(PlayerPedId(), Config.lobbyPos.x, Config.lobbyPos.y, Config.lobbyPos.z) | ||||
|      | ||||
|     -- Maske entfernen | ||||
|     SetPedComponentVariation(PlayerPedId(), 1, 0, 0, 0) | ||||
|      | ||||
|     -- Zone Blips entfernen | ||||
|     removeTeamZoneBlips() | ||||
|      | ||||
|     lib.notify({ | ||||
|         title = 'TeamDeathmatch', | ||||
|         description = 'Du hast das Spiel verlassen!', | ||||
|         type = 'error' | ||||
|     }) | ||||
| 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 or isHit then return end | ||||
|      | ||||
|     isHit = true | ||||
|     local ped = PlayerPedId() | ||||
|      | ||||
|     -- Arme hochnehmen Animation | ||||
|     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' | ||||
|     }) | ||||
|      | ||||
|     -- Zone Marker hervorheben | ||||
|     highlightTeamZone(currentTeam) | ||||
| end) | ||||
|  | ||||
| RegisterNetEvent('tdm:updateScore', function(team1Score, team2Score) | ||||
|     lib.showTextUI('[Team 1: ' .. team1Score .. '] VS [Team 2: ' .. team2Score .. ']', { | ||||
|         position = "top-center", | ||||
|         icon = 'crosshairs' | ||||
|     }) | ||||
| end) | ||||
|  | ||||
| RegisterNetEvent('tdm:gameEnded', function(winnerTeam, team1Score, team2Score) | ||||
|     lib.hideTextUI() | ||||
|      | ||||
|     local alert = lib.alertDialog({ | ||||
|         header = 'Spiel beendet!', | ||||
|         content = 'Team ' .. winnerTeam .. ' hat gewonnen!\n\nTeam 1: ' .. team1Score .. '\nTeam 2: ' .. team2Score, | ||||
|         centered = true, | ||||
|         cancel = false | ||||
|     }) | ||||
|      | ||||
|     Wait(5000) | ||||
|     TriggerServerEvent('tdm:leaveGame') | ||||
| end) | ||||
|  | ||||
| -- Funktionen | ||||
| function setTeamMask(team) | ||||
|     local ped = PlayerPedId() | ||||
|     local maskData = Config.teamMasks[team] | ||||
|      | ||||
|     if maskData then | ||||
|         SetPedComponentVariation(ped, maskData.component, maskData.drawable, maskData.texture, 0) | ||||
|     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 | ||||
| end | ||||
|  | ||||
| function removeTeamZoneBlips() | ||||
|     for team, blip in pairs(teamZoneBlips) do | ||||
|         if DoesBlipExist(blip) then | ||||
|             RemoveBlip(blip) | ||||
|         end | ||||
|     end | ||||
|     teamZoneBlips = {} | ||||
| end | ||||
|  | ||||
| function highlightTeamZone(team) | ||||
|     if teamZoneBlips[team] and DoesBlipExist(teamZoneBlips[team]) then | ||||
|         SetBlipFlashes(teamZoneBlips[team], true) | ||||
|     end | ||||
| end | ||||
|  | ||||
| function openMainMenu() | ||||
|     TriggerServerEvent('tdm:requestGamesList') | ||||
|      | ||||
|     Wait(100) -- Kurz warten für Server Response | ||||
|      | ||||
|     local options = { | ||||
|         { | ||||
|             title = 'Neues Spiel erstellen', | ||||
|             description = 'Erstelle ein neues TeamDeathmatch Spiel', | ||||
|             icon = 'plus', | ||||
|             onSelect = function() | ||||
|                 openCreateGameMenu() | ||||
|             end | ||||
|         }, | ||||
|         { | ||||
|             title = 'Spiel beitreten', | ||||
|             description = 'Trete einem laufenden Spiel bei', | ||||
|             icon = 'users', | ||||
|             onSelect = function() | ||||
|                 openJoinGameMenu() | ||||
|             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', | ||||
|         title = 'TeamDeathmatch', | ||||
|         options = options | ||||
|     }) | ||||
|      | ||||
|     lib.showContext('tdm_main_menu') | ||||
| end | ||||
|  | ||||
| function openCreateGameMenu() | ||||
|     local fieldOptions = {} | ||||
|      | ||||
|     -- Spielfelder zu Options hinzufügen | ||||
|     for fieldId, fieldData in pairs(Config.gameFields) do | ||||
|         table.insert(fieldOptions, { | ||||
|             value = fieldId, | ||||
|             label = fieldData.name .. ' (Max: ' .. fieldData.maxPlayers .. ')' | ||||
|         }) | ||||
|     end | ||||
|      | ||||
|     local input = lib.inputDialog('Neues Spiel erstellen', { | ||||
|         { | ||||
|             type = 'input', | ||||
|             label = 'Spiel Name', | ||||
|             description = 'Gib deinem Spiel einen Namen', | ||||
|             required = true, | ||||
|             max = 30 | ||||
|         }, | ||||
|         { | ||||
|             type = 'select', | ||||
|             label = 'Spielfeld', | ||||
|             description = 'Wähle ein Spielfeld', | ||||
|             required = true, | ||||
|             options = fieldOptions | ||||
|         }, | ||||
|         { | ||||
|             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 fieldId = input[2] | ||||
|     local gameType = input[3] -- 'public' oder 'private' | ||||
|     local password = input[4] and input[4] ~= '' and input[4] or nil | ||||
|      | ||||
|     if gameName and fieldId and gameType then | ||||
|         TriggerServerEvent('tdm:createGame', gameName, fieldId, gameType, password) | ||||
|     end | ||||
| end | ||||
|  | ||||
| function openJoinGameMenu() | ||||
|     TriggerServerEvent('tdm:requestGamesList') | ||||
|      | ||||
|     Wait(200) | ||||
|      | ||||
|     local options = {} | ||||
|      | ||||
|     for gameId, gameData in pairs(activeGames) do | ||||
|         local playerCount = #gameData.team1 + #gameData.team2 | ||||
|         local maxPlayers = Config.gameFields[gameData.fieldId].maxPlayers | ||||
|         local fieldName = Config.gameFields[gameData.fieldId].name | ||||
|          | ||||
|         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 .. ' | Feld: ' .. fieldName .. ' | 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 | ||||
|      | ||||
|     if #options == 0 then | ||||
|         table.insert(options, { | ||||
|             title = 'Keine Spiele verfügbar', | ||||
|             description = 'Erstelle ein neues Spiel', | ||||
|             icon = 'info', | ||||
|             disabled = true | ||||
|         }) | ||||
|     end | ||||
|      | ||||
|     lib.registerContext({ | ||||
|         id = 'tdm_join_menu', | ||||
|         title = 'Spiel beitreten', | ||||
|         menu = 'tdm_main_menu', | ||||
|         options = options | ||||
|     }) | ||||
|      | ||||
|     lib.showContext('tdm_join_menu') | ||||
| end | ||||
|  | ||||
|  | ||||
| -- Zone Checker Thread | ||||
| 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) | ||||
|  | ||||
| -- 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) | ||||
|  | ||||
| -- Damage Handler | ||||
| CreateThread(function() | ||||
|     while true do | ||||
|         Wait(100) | ||||
|          | ||||
|         if inTDM and not isHit then | ||||
|             local ped = PlayerPedId() | ||||
|              | ||||
|             if HasEntityBeenDamagedByAnyPed(ped) then | ||||
|                 ClearEntityLastDamageEntity(ped) | ||||
|                 TriggerEvent('tdm:playerHit') | ||||
|                 TriggerServerEvent('tdm:playerWasHit', currentGameId, currentTeam) | ||||
|             end | ||||
|         end | ||||
|     end | ||||
| end) | ||||
|  | ||||
| -- Death Handler | ||||
| 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) | ||||
|  | ||||
| -- NPC Setup | ||||
| CreateThread(function() | ||||
|     -- Blip erstellen | ||||
|     tdmBlip = AddBlipForCoord(Config.lobbyPos.x, Config.lobbyPos.y, Config.lobbyPos.z) | ||||
|     SetBlipSprite(tdmBlip, 432) | ||||
|     SetBlipDisplay(tdmBlip, 4) | ||||
|     SetBlipScale(tdmBlip, 0.8) | ||||
|     SetBlipColour(tdmBlip, 1) | ||||
|     SetBlipAsShortRange(tdmBlip, true) | ||||
|     BeginTextCommandSetBlipName("STRING") | ||||
|     AddTextComponentString("TeamDeathmatch") | ||||
|     EndTextCommandSetBlipName(tdmBlip) | ||||
|      | ||||
|     -- NPC erstellen | ||||
|     RequestModel(GetHashKey(Config.gameNPC.model)) | ||||
|     while not HasModelLoaded(GetHashKey(Config.gameNPC.model)) do | ||||
|         Wait(1) | ||||
|     end | ||||
|      | ||||
|     local npc = CreatePed(4, GetHashKey(Config.gameNPC.model), Config.gameNPC.coords.x, Config.gameNPC.coords.y, Config.gameNPC.coords.z, Config.gameNPC.coords.w, false, true) | ||||
|     SetEntityInvincible(npc, true) | ||||
|     FreezeEntityPosition(npc, true) | ||||
|     SetBlockingOfNonTemporaryEvents(npc, true) | ||||
| end) | ||||
|  | ||||
| -- Target für NPC | ||||
| exports['qb-target']:AddTargetModel(Config.gameNPC.model, { | ||||
|     options = { | ||||
|         { | ||||
|             type = "client", | ||||
|             event = "tdm:openMainMenu", | ||||
|             icon = "fas fa-crosshairs", | ||||
|             label = "TeamDeathmatch", | ||||
|         }, | ||||
|     }, | ||||
|     distance = 2.5 | ||||
| }) | ||||
|  | ||||
| RegisterNetEvent('tdm:openMainMenu', function() | ||||
|     openMainMenu() | ||||
| end) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nordi98
						Nordi98