-- qb-shootingrange client.lua local QBCore = exports['qb-core']:GetCoreObject() -- Variablen local menuActive = false local menuIndex = 1 local shooting = false local score = 0 local targets = {} local timerEnabled = false local timeLeft = 0 local isTrainer = false local currentRange = nil local isInCompetition = false local competitionHost = false -- Gültige Target Models local validTargetModels = { `prop_target_backboard_b`, `gr_prop_gr_target_05c`, `gr_prop_gr_target_04c`, `gr_prop_gr_target_02b`, `gr_prop_gr_target_02a`, `gr_prop_gr_target_w_02b`, } -- Menüoptionen local menuOptions = { "⏱️ Starte Schießstand mit Timer", "🔫 Starte Schießstand ohne Timer", "🏁 Starte Wettkampfmodus", "👥 Trainingsmodus", "🛑 Beende Schießstand", "📋 Bestenliste" } -- Hilfsfunktionen function DrawTxt(text, x, y, scale) SetTextFont(4) SetTextProportional(0) SetTextScale(scale, scale) SetTextColour(255, 255, 255, 255) SetTextDropShadow() SetTextCentre(true) SetTextEntry("STRING") AddTextComponentString(text) DrawText(x, y) end function PlaySound(name) SendNUIMessage({ action = "play", sound = name }) end function GetNearbyPlayers() local players = {} local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) for _, player in ipairs(GetActivePlayers()) do if player ~= PlayerId() then local targetPed = GetPlayerPed(player) local targetCoords = GetEntityCoords(targetPed) local distance = #(playerCoords - targetCoords) if distance <= 20.0 then local playerName = GetPlayerName(player) local serverId = GetPlayerServerId(player) table.insert(players, { label = playerName, serverId = serverId, distance = math.floor(distance) }) end end end return players end function ShowPlayerSelectionMenu() local nearbyPlayers = GetNearbyPlayers() if #nearbyPlayers == 0 then QBCore.Functions.Notify("Keine Spieler in der Nähe!", "error") return end local elements = { { header = "Spieler zum Wettkampf einladen", isMenuHeader = true } } for _, player in ipairs(nearbyPlayers) do table.insert(elements, { header = player.label, txt = string.format("Entfernung: %dm", player.distance), params = { event = "qb-shootingrange:invitePlayer", args = { playerId = player.serverId } } }) end exports['qb-menu']:openMenu(elements) end function ShowStartCompetitionMenu() local elements = { { header = "Wettkampf Steuerung", isMenuHeader = true }, { header = "🏁 Wettkampf starten", txt = "Startet den Wettkampf für alle Teilnehmer", params = { event = "qb-shootingrange:hostStartCompetition", args = {} } }, { header = "❌ Abbrechen", txt = "Bricht den Wettkampf ab", params = { event = "qb-shootingrange:cancelCompetition", args = {} } } } exports['qb-menu']:openMenu(elements) end function FindNearbyTargets() local foundTargets = {} local playerCoords = GetEntityCoords(PlayerPedId()) local radius = 200.0 for _, model in ipairs(validTargetModels) do local handle, object = FindFirstObject() local success repeat if IsEntityAnObject(object) and GetEntityModel(object) == model then local objCoords = GetEntityCoords(object) if #(playerCoords - objCoords) < radius then table.insert(foundTargets, object) end end success, object = FindNextObject(handle) until not success EndFindObject(handle) end return foundTargets end function PromptPlayerName() AddTextEntry('SHOOTING_NAME', 'Gib deinen Namen ein:') DisplayOnscreenKeyboard(1, "SHOOTING_NAME", "", "", "", "", "", 20) while UpdateOnscreenKeyboard() ~= 1 and UpdateOnscreenKeyboard() ~= 2 do Wait(0) end if UpdateOnscreenKeyboard() ~= 2 then return GetOnscreenKeyboardResult() end return "Unbekannt" end function DrawMenu() DrawTxt("~b~" .. currentRange.label .. "~s~", 0.5, 0.3, 0.6) for i, option in ipairs(menuOptions) do local color = i == menuIndex and "~y~" or "~w~" DrawTxt(color .. option, 0.5, 0.35 + (i * 0.05), 0.4) end DrawTxt("~c~↑/↓ Auswählen | ENTER bestätigen | BACKSPACE zurück", 0.5, 0.7, 0.3) end function StartShooting(useTimer, isComp) if shooting then return end if isComp then competitionHost = true ShowPlayerSelectionMenu() return end targets = FindNearbyTargets() if #targets == 0 then QBCore.Functions.Notify("Keine Ziele im Umkreis gefunden!", "error") return end shooting = true score = 0 timerEnabled = useTimer timeLeft = useTimer and 30 or 0 PlaySound("start") QBCore.Functions.Notify("Schießstand gestartet!", "success") if timerEnabled then CreateThread(function() while timeLeft > 0 and shooting do Wait(1000) timeLeft = timeLeft - 1 end if shooting then StopShooting() end end) end CreateThread(function() while shooting do Wait(0) local hit, endCoords = GetPedLastWeaponImpactCoord(PlayerPedId()) if hit then for _, target in ipairs(targets) do if DoesEntityExist(target) then local targetCoords = GetEntityCoords(target) if #(endCoords - targetCoords) < 1.2 then score = score + 10 QBCore.Functions.Notify("🎯 Treffer! Punkte: " .. score, "success") if isInCompetition then TriggerServerEvent("qb-shootingrange:updateCompetitionScore", score) end Wait(300) break end end end end end end) CreateThread(function() while shooting do Wait(0) DrawTxt("Punkte: " .. score, 0.5, 0.05, 0.5) if timerEnabled then DrawTxt("Zeit: " .. timeLeft .. "s", 0.5, 0.09, 0.4) end end end) end function StopShooting() if not shooting then return end shooting = false targets = {} QBCore.Functions.Notify("Beendet! Gesamtpunkte: " .. score, "success") PlaySound("timeout") if not isInCompetition then local name = PromptPlayerName() TriggerServerEvent("qb-shootingrange:saveScore", name, score) end isInCompetition = false competitionHost = false end function ShowHighscores() QBCore.Functions.TriggerCallback('qb-shootingrange:getHighscores', function(scores) if scores and #scores > 0 then local text = "~y~🏆 Bestenliste~s~\n\n" for i, entry in ipairs(scores) do text = text .. string.format("%d. %s - %d Punkte\n~c~%s~s~\n\n", i, entry.name, entry.score, entry.timestamp ) end SetNotificationTextEntry("STRING") AddTextComponentString(text) DrawNotification(false, true) else QBCore.Functions.Notify("Keine Einträge in der Bestenliste.", "error") end end) end -- Event Handler RegisterNetEvent("qb-shootingrange:invitePlayer") AddEventHandler("qb-shootingrange:invitePlayer", function(data) TriggerServerEvent("qb-shootingrange:sendInvite", data.playerId) QBCore.Functions.Notify("Einladung gesendet!", "success") end) RegisterNetEvent("qb-shootingrange:receiveInvite") AddEventHandler("qb-shootingrange:receiveInvite", function(inviterName, inviterId) local elements = { { header = "Schießstand Einladung", txt = string.format("Von: %s", inviterName), isMenuHeader = true }, { header = "✅ Annehmen", txt = "Der Einladung folgen", params = { event = "qb-shootingrange:acceptInvite", args = { inviterId = inviterId } } }, { header = "❌ Ablehnen", txt = "Einladung ablehnen", params = { event = "qb-shootingrange:declineInvite", args = { inviterId = inviterId } } } } exports['qb-menu']:openMenu(elements) end) RegisterNetEvent("qb-shootingrange:acceptInvite") AddEventHandler("qb-shootingrange:acceptInvite", function(data) TriggerServerEvent("qb-shootingrange:acceptInvite", data.inviterId) isInCompetition = true QBCore.Functions.Notify("Du hast die Einladung angenommen!", "success") end) RegisterNetEvent("qb-shootingrange:declineInvite") AddEventHandler("qb-shootingrange:declineInvite", function(data) TriggerServerEvent("qb-shootingrange:declineInvite", data.inviterId) QBCore.Functions.Notify("Du hast die Einladung abgelehnt.", "error") end) RegisterNetEvent("qb-shootingrange:hostStartCompetition") AddEventHandler("qb-shootingrange:hostStartCompetition", function() TriggerServerEvent("qb-shootingrange:startCompetition") end) RegisterNetEvent("qb-shootingrange:cancelCompetition") AddEventHandler("qb-shootingrange:cancelCompetition", function() TriggerServerEvent("qb-shootingrange:cancelCompetition") isInCompetition = false competitionHost = false QBCore.Functions.Notify("Wettkampf abgebrochen", "error") end) RegisterNetEvent("qb-shootingrange:competitionStarted") AddEventHandler("qb-shootingrange:competitionStarted", function() StartShooting(true, false) QBCore.Functions.Notify("Der Wettkampf beginnt!", "success") end) RegisterNetEvent("qb-shootingrange:playerJoinedCompetition") AddEventHandler("qb-shootingrange:playerJoinedCompetition", function(playerName) QBCore.Functions.Notify(playerName .. " ist dem Wettkampf beigetreten!", "success") if competitionHost then ShowStartCompetitionMenu() end end) RegisterNetEvent("qb-shootingrange:updateCompetition") AddEventHandler("qb-shootingrange:updateCompetition", function(scores) if not isInCompetition then return end local scoreText = "🏁 Wettkampf Punktestand:\n" for name, playerScore in pairs(scores) do scoreText = scoreText .. string.format("%s: %d\n", name, playerScore) end DrawTxt(scoreText, 0.5, 0.15, 0.4) end) -- Hauptthread CreateThread(function() while true do Wait(0) local playerPed = PlayerPedId() local playerCoords = GetEntityCoords(playerPed) local closestRange = nil local closestDist = 1000 for _, range in pairs(Config.ShootingRanges) do local dist = #(playerCoords - range.coords) if dist < closestDist then closestDist = dist closestRange = range end end if closestRange and closestDist < 2.0 then currentRange = closestRange if not menuActive then DrawTxt("Drücke ~g~E~s~ für den " .. closestRange.label, 0.5, 0.9, 0.4) if IsControlJustPressed(0, 38) then -- E menuActive = true menuIndex = 1 end else DrawMenu() if IsControlJustPressed(0, 172) then -- Hoch menuIndex = menuIndex - 1 if menuIndex < 1 then menuIndex = #menuOptions end elseif IsControlJustPressed(0, 173) then -- Runter menuIndex = menuIndex + 1 if menuIndex > #menuOptions then menuIndex = 1 end elseif IsControlJustPressed(0, 201) then -- Enter if menuIndex == 1 then StartShooting(true, false) elseif menuIndex == 2 then StartShooting(false, false) elseif menuIndex == 3 then StartShooting(true, true) elseif menuIndex == 4 then QBCore.Functions.Notify("Trainingsmodus wird bald verfügbar!", "primary") elseif menuIndex == 5 then StopShooting() elseif menuIndex == 6 then ShowHighscores() end menuActive = false elseif IsControlJustPressed(0, 202) then -- Backspace menuActive = false end end else menuActive = false currentRange = nil end end end)