434 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			434 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
-- 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`
 | 
						|
}
 | 
						|
 | 
						|
-- 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)
 |