forked from Simnation/Main
437 lines
13 KiB
Lua
437 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`,
|
|
`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)
|