1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_dj/client/main.lua

714 lines
21 KiB
Lua
Raw Normal View History

2025-08-03 16:51:12 +02:00
local QBCore = exports['qb-core']:GetCoreObject()
local PlayerData = {}
2025-08-03 17:36:16 +02:00
local currentBooth = nil
2025-08-03 16:51:12 +02:00
local isPlaying = false
local currentVolume = Config.DefaultVolume
local currentSong = nil
2025-08-03 17:36:16 +02:00
local activeBooths = {}
local isUIOpen = false
local isDJBooth = false
local nearestBooth = nil
2025-08-03 16:51:12 +02:00
-- Events
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
PlayerData = QBCore.Functions.GetPlayerData()
2025-08-03 17:36:16 +02:00
-- Frage Server nach aktiven DJ-Booths
TriggerServerEvent('dj:server:requestActiveDJs')
2025-08-03 16:51:12 +02:00
end)
RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo)
PlayerData.job = JobInfo
end)
-- Key Mapping
RegisterKeyMapping('opendj', 'Open DJ Menu', 'keyboard', Config.OpenMenuKey)
RegisterCommand('opendj', function()
if not CanUseDJScript() then
lib.notify({
title = 'DJ System',
description = 'Du hast keine Berechtigung das DJ System zu nutzen!',
type = 'error'
})
return
end
2025-08-03 17:36:16 +02:00
if not isDJBooth then
2025-08-03 16:51:12 +02:00
lib.notify({
title = 'DJ System',
2025-08-03 17:36:16 +02:00
description = 'Du musst an einem DJ Pult stehen!',
2025-08-03 16:51:12 +02:00
type = 'error'
})
2025-08-03 17:36:16 +02:00
return
end
OpenDJInterface()
end)
-- Musik abspielen (von Server empfangen)
RegisterNetEvent('dj:client:playMusic', function(data)
-- Speichere Booth-Informationen
activeBooths[data.booth.name] = {
url = data.url,
title = data.title,
volume = data.volume,
coords = data.booth.coords,
range = data.range
}
-- Prüfe ob Spieler in Reichweite ist
local playerCoords = GetEntityCoords(PlayerPedId())
local distance = #(playerCoords - data.booth.coords)
if distance <= data.range then
-- Berechne Lautstärke basierend auf Distanz
local volumeMultiplier = 1.0 - (distance / data.range)
local adjustedVolume = math.floor(data.volume * volumeMultiplier)
-- Spiele Musik ab
SendNUIMessage({
type = 'playMusic',
url = data.url,
volume = adjustedVolume,
title = data.title
})
-- Setze lokale Variablen
currentBooth = {
name = data.booth.name,
coords = data.booth.coords
}
isPlaying = true
currentSong = {
title = data.title,
url = data.url
}
-- Zeige Benachrichtigung
lib.notify({
title = 'DJ System',
description = 'Spielt: ' .. data.title,
type = 'info'
})
end
end)
-- Musik stoppen (von Server empfangen)
RegisterNetEvent('dj:client:stopMusic', function(boothName)
-- Entferne Booth-Informationen
if activeBooths[boothName] then
activeBooths[boothName] = nil
end
-- Wenn der Spieler diese Musik hört, stoppe sie
if currentBooth and currentBooth.name == boothName then
SendNUIMessage({
type = 'stopMusic'
})
currentBooth = nil
isPlaying = false
currentSong = nil
lib.notify({
title = 'DJ System',
description = 'Musik gestoppt',
type = 'info'
})
end
end)
-- Lautstärke ändern (von Server empfangen)
RegisterNetEvent('dj:client:setVolume', function(data)
-- Aktualisiere Booth-Informationen
if activeBooths[data.booth.name] then
activeBooths[data.booth.name].volume = data.volume
activeBooths[data.booth.name].range = data.range
end
-- Wenn der Spieler diese Musik hört, ändere die Lautstärke
if currentBooth and currentBooth.name == data.booth.name then
-- Berechne Lautstärke basierend auf Distanz
local playerCoords = GetEntityCoords(PlayerPedId())
local distance = #(playerCoords - data.booth.coords)
local volumeMultiplier = 1.0 - (distance / data.range)
local adjustedVolume = math.floor(data.volume * volumeMultiplier)
SendNUIMessage({
type = 'setVolume',
volume = adjustedVolume
})
currentVolume = data.volume
end
end)
-- Empfange aktive DJs
RegisterNetEvent('dj:client:receiveActiveDJs', function(booths)
activeBooths = booths
-- Prüfe ob Spieler in Reichweite eines aktiven DJ-Booths ist
local playerCoords = GetEntityCoords(PlayerPedId())
for boothName, boothData in pairs(activeBooths) do
local distance = #(playerCoords - boothData.coords)
if distance <= boothData.range then
-- Berechne Lautstärke basierend auf Distanz
local volumeMultiplier = 1.0 - (distance / boothData.range)
local adjustedVolume = math.floor(boothData.volume * volumeMultiplier)
-- Spiele Musik ab
SendNUIMessage({
type = 'playMusic',
url = boothData.url,
volume = adjustedVolume,
title = boothData.title
})
currentBooth = {
name = boothName,
coords = boothData.coords
}
isPlaying = true
currentSong = {
title = boothData.title,
url = boothData.url
}
lib.notify({
title = 'DJ System',
description = 'Spielt: ' .. boothData.title,
type = 'info'
})
break
end
end
end)
RegisterNUICallback('djInterfaceClosed', function(data, cb)
SetNuiFocus(false, false)
isUIOpen = false
2025-08-03 18:15:15 +02:00
-- Kurze Verzögerung, um sicherzustellen, dass alle Animationen abgeschlossen sind
Wait(100)
-- Aktiviere alle Controls wieder
EnableAllControlActions(0)
2025-08-03 19:23:41 +02:00
-- Musik NICHT stoppen, wenn stopMusic nicht explizit true ist
if data.stopMusic then
-- Stoppe Musik nur wenn explizit angefordert
if currentBooth then
TriggerServerEvent('dj:server:stopMusic', currentBooth.name)
end
end
2025-08-03 17:36:16 +02:00
cb('ok')
end)
2025-08-03 19:23:41 +02:00
2025-08-03 17:36:16 +02:00
RegisterNUICallback('deckStateChanged', function(data, cb)
if Config.Debug then
print(string.format('[DJ System] Deck %s %s: %s',
data.deck,
data.isPlaying and 'playing' or 'stopped',
data.track and data.track.title or 'No track'
))
end
-- Wenn Deck A oder B abspielt, sende an Server
if data.isPlaying and data.track then
PlayMusicAsDJ(data.track.title, data.track.url, currentVolume)
2025-08-03 16:51:12 +02:00
end
2025-08-03 17:36:16 +02:00
cb('ok')
end)
RegisterNUICallback('volumeChanged', function(data, cb)
if data.volume then
SetVolumeAsDJ(data.volume)
end
cb('ok')
end)
RegisterNUICallback('stopMusic', function(data, cb)
StopMusicAsDJ()
cb('ok')
end)
RegisterNUICallback('audioError', function(data, cb)
lib.notify({
title = 'DJ System',
description = 'Audio Fehler: ' .. (data.error or 'Unbekannter Fehler'),
type = 'error'
})
cb('ok')
end)
RegisterNUICallback('songEnded', function(data, cb)
-- Wenn ein Song endet, kann hier Playlist-Logik implementiert werden
if Config.Debug then
print('[DJ System] Song ended')
end
cb('ok')
2025-08-03 16:51:12 +02:00
end)
2025-08-03 18:15:15 +02:00
RegisterNUICallback('loadTrack', function(data, cb)
if data.deck and data.track then
-- Lade Track in Deck
if data.deck == "A" or data.deck == "B" then
-- Hier könntest du zusätzliche Logik hinzufügen
cb({success = true})
else
cb({success = false, error = "Ungültiges Deck"})
end
else
cb({success = false, error = "Fehlende Daten"})
end
end)
RegisterNUICallback('playTrack', function(data, cb)
if data.deck and data.track then
-- Spiele Track ab
PlayMusicAsDJ(data.track.title, data.track.url, currentVolume)
cb({success = true})
else
cb({success = false, error = "Fehlende Daten"})
end
end)
RegisterNUICallback('getPlaylists', function(data, cb)
-- Hole Playlists vom Server
QBCore.Functions.TriggerCallback('dj:server:getPlaylists', function(playlists)
cb({success = true, playlists = playlists})
end)
end)
RegisterNUICallback('createPlaylist', function(data, cb)
if data.name then
-- Erstelle Playlist
TriggerServerEvent('dj:server:createPlaylist', data.name, data.description, data.isPublic)
cb({success = true})
else
cb({success = false, error = "Fehlender Name"})
end
end)
RegisterNUICallback('addToPlaylist', function(data, cb)
if data.playlistId and data.track then
-- Füge Track zu Playlist hinzu
TriggerServerEvent('dj:server:addSongToPlaylist', data.playlistId, data.track)
cb({success = true})
else
cb({success = false, error = "Fehlende Daten"})
end
end)
RegisterNUICallback('getSessionHistory', function(data, cb)
-- Hole Session-Historie vom Server
QBCore.Functions.TriggerCallback('dj:server:getSessionHistory', function(history)
cb({success = true, history = history})
end, data.limit)
end)
2025-08-03 16:51:12 +02:00
-- Functions
function CanUseDJScript()
if not Config.UseJobRestriction then
return true
end
if not PlayerData.job then
return false
end
for _, job in pairs(Config.AllowedJobs) do
if PlayerData.job.name == job then
return true
end
end
return false
end
2025-08-03 17:26:20 +02:00
function OpenDJInterface()
if not isDJBooth then
lib.notify({
title = 'DJ System',
description = 'Du musst an einem DJ Pult stehen',
type = 'error'
2025-08-03 16:51:12 +02:00
})
2025-08-03 17:26:20 +02:00
return
2025-08-03 16:51:12 +02:00
end
2025-08-03 17:26:20 +02:00
SetNuiFocus(true, true)
SendNUIMessage({
type = 'showDJInterface'
2025-08-03 16:51:12 +02:00
})
2025-08-03 17:26:20 +02:00
isUIOpen = true
-- Disable controls while UI is open
CreateThread(function()
while isUIOpen do
DisableAllControlActions(0)
EnableControlAction(0, 1, true) -- Mouse look
EnableControlAction(0, 2, true) -- Mouse look
2025-08-03 18:15:15 +02:00
EnableControlAction(0, 3, true) -- Mouse movement
EnableControlAction(0, 4, true) -- Mouse movement
EnableControlAction(0, 5, true) -- Mouse wheel
EnableControlAction(0, 6, true) -- Mouse wheel
2025-08-03 17:26:20 +02:00
Wait(0)
end
end)
2025-08-03 16:51:12 +02:00
end
2025-08-03 17:36:16 +02:00
function PlayMusicAsDJ(title, url, volume)
if not nearestBooth then
2025-08-03 16:51:12 +02:00
lib.notify({
title = 'DJ System',
2025-08-03 17:36:16 +02:00
description = 'Du musst an einem DJ-Pult stehen!',
type = 'error'
2025-08-03 16:51:12 +02:00
})
2025-08-03 17:36:16 +02:00
return
2025-08-03 16:51:12 +02:00
end
2025-08-03 17:36:16 +02:00
-- Bereinige YouTube URL von Playlist-Parametern
local cleanUrl = url
if string.find(url, "youtube") then
cleanUrl = string.gsub(url, "&list=.-$", "")
cleanUrl = string.gsub(cleanUrl, "&start_radio=.-$", "")
cleanUrl = string.gsub(cleanUrl, "&index=.-$", "")
2025-08-03 16:51:12 +02:00
end
2025-08-03 17:36:16 +02:00
-- Sende an Server zur Synchronisation
TriggerServerEvent('dj:server:playMusic', {
title = title,
url = cleanUrl,
volume = volume or Config.DefaultVolume,
booth = {
name = nearestBooth,
coords = Config.DJBooths[nearestBooth].coords
2025-08-03 16:51:12 +02:00
},
2025-08-03 17:36:16 +02:00
range = CalculateRange(volume or Config.DefaultVolume)
2025-08-03 16:51:12 +02:00
})
2025-08-03 17:36:16 +02:00
-- Lokale Variablen aktualisieren
2025-08-03 16:51:12 +02:00
isPlaying = true
2025-08-03 17:36:16 +02:00
currentVolume = volume or Config.DefaultVolume
currentSong = {
2025-08-03 16:51:12 +02:00
title = title,
2025-08-03 17:36:16 +02:00
url = cleanUrl
}
currentBooth = {
name = nearestBooth,
coords = Config.DJBooths[nearestBooth].coords
}
2025-08-03 17:39:18 +02:00
end
2025-08-03 16:51:12 +02:00
2025-08-03 17:36:16 +02:00
function StopMusicAsDJ()
if not nearestBooth then
2025-08-03 16:51:12 +02:00
lib.notify({
title = 'DJ System',
2025-08-03 17:36:16 +02:00
description = 'Du musst an einem DJ-Pult stehen!',
2025-08-03 16:51:12 +02:00
type = 'error'
})
return
end
2025-08-03 17:36:16 +02:00
-- Sende an Server zur Synchronisation
TriggerServerEvent('dj:server:stopMusic', nearestBooth)
-- Lokale Variablen aktualisieren
2025-08-03 16:51:12 +02:00
isPlaying = false
currentSong = nil
2025-08-03 17:36:16 +02:00
currentBooth = nil
2025-08-03 17:39:18 +02:00
end
2025-08-03 16:51:12 +02:00
2025-08-03 17:36:16 +02:00
function SetVolumeAsDJ(volume)
if not nearestBooth then
2025-08-03 16:51:12 +02:00
lib.notify({
title = 'DJ System',
2025-08-03 17:36:16 +02:00
description = 'Du musst an einem DJ-Pult stehen!',
2025-08-03 16:51:12 +02:00
type = 'error'
})
return
end
2025-08-03 17:36:16 +02:00
-- Sende an Server zur Synchronisation
TriggerServerEvent('dj:server:setVolume', {
volume = volume,
booth = {
name = nearestBooth,
coords = Config.DJBooths[nearestBooth].coords
},
range = CalculateRange(volume)
2025-08-03 16:51:12 +02:00
})
2025-08-03 17:36:16 +02:00
-- Lokale Variablen aktualisieren
currentVolume = volume
2025-08-03 17:39:18 +02:00
end
2025-08-03 16:51:12 +02:00
2025-08-03 17:36:16 +02:00
function CalculateRange(volume)
if not nearestBooth or not Config.DJBooths[nearestBooth] then return 30.0 end
2025-08-03 16:51:12 +02:00
2025-08-03 17:36:16 +02:00
local booth = Config.DJBooths[nearestBooth]
local baseRange = booth.range or 30.0
local maxRange = booth.maxRange or 50.0
local volumePercent = volume / 100
2025-08-03 16:51:12 +02:00
2025-08-03 17:36:16 +02:00
return baseRange + ((maxRange - baseRange) * volumePercent)
end
2025-08-03 16:51:12 +02:00
2025-08-03 17:36:16 +02:00
-- Regelmäßige Prüfung der Distanz zu DJ-Booths und aktiven Musiken
2025-08-03 16:51:12 +02:00
CreateThread(function()
while true do
2025-08-03 18:15:15 +02:00
Wait(Config.DistanceVolume and Config.DistanceVolume.updateInterval or 1000)
2025-08-03 16:51:12 +02:00
2025-08-03 17:36:16 +02:00
local playerCoords = GetEntityCoords(PlayerPedId())
local foundBooth = false
nearestBooth = nil
local nearestDistance = 999999.9
-- Prüfe Nähe zu DJ-Booths
for boothName, booth in pairs(Config.DJBooths) do
local distance = #(playerCoords - booth.coords)
2025-08-03 16:51:12 +02:00
2025-08-03 17:36:16 +02:00
-- Finde das nächste DJ-Booth
if distance < nearestDistance then
nearestDistance = distance
nearestBooth = boothName
end
-- Prüfe ob Spieler an einem DJ-Booth steht
if distance <= 2.0 then
foundBooth = true
break
end
end
isDJBooth = foundBooth
-- Prüfe Distanz zu aktiven Musiken
if not isPlaying and next(activeBooths) then
-- Spieler hört keine Musik, prüfe ob er in Reichweite eines aktiven DJ-Booths ist
for boothName, boothData in pairs(activeBooths) do
local distance = #(playerCoords - boothData.coords)
if distance <= boothData.range then
-- Berechne Lautstärke basierend auf Distanz
local volumeMultiplier = 1.0 - (distance / boothData.range)
local adjustedVolume = math.floor(boothData.volume * volumeMultiplier)
-- Spiele Musik ab
SendNUIMessage({
type = 'playMusic',
url = boothData.url,
volume = adjustedVolume,
title = boothData.title
})
currentBooth = {
name = boothName,
coords = boothData.coords
}
isPlaying = true
currentSong = {
title = boothData.title,
url = boothData.url
}
lib.notify({
title = 'DJ System',
description = 'Spielt: ' .. boothData.title,
type = 'info'
})
break
end
end
elseif isPlaying and currentBooth then
-- Spieler hört Musik, prüfe ob er noch in Reichweite ist
local boothData = activeBooths[currentBooth.name]
if boothData then
local distance = #(playerCoords - boothData.coords)
if distance > boothData.range then
-- Spieler hat Reichweite verlassen, stoppe Musik
SendNUIMessage({
type = 'stopMusic'
})
currentBooth = nil
isPlaying = false
currentSong = nil
lib.notify({
title = 'DJ System',
description = 'Musik außer Reichweite',
type = 'info'
})
else
-- Spieler ist noch in Reichweite, passe Lautstärke an
local volumeMultiplier = 1.0 - (distance / boothData.range)
local adjustedVolume = math.floor(boothData.volume * volumeMultiplier)
SendNUIMessage({
type = 'setVolume',
volume = adjustedVolume
})
end
2025-08-03 16:51:12 +02:00
else
2025-08-03 17:36:16 +02:00
-- Booth existiert nicht mehr, stoppe Musik
2025-08-03 16:51:12 +02:00
SendNUIMessage({
2025-08-03 17:36:16 +02:00
type = 'stopMusic'
2025-08-03 16:51:12 +02:00
})
2025-08-03 17:36:16 +02:00
currentBooth = nil
isPlaying = false
currentSong = nil
2025-08-03 16:51:12 +02:00
end
end
end
end)
2025-08-03 17:36:16 +02:00
-- DJ Booth Blips und Interaktionen
CreateThread(function()
-- Erstelle Blips für DJ-Booths
for boothName, booth in pairs(Config.DJBooths) do
local blip = AddBlipForCoord(booth.coords)
SetBlipSprite(blip, 614)
SetBlipDisplay(blip, 4)
SetBlipScale(blip, 0.7)
SetBlipColour(blip, 47)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName("STRING")
AddTextComponentString("DJ Booth - " .. boothName)
EndTextCommandSetBlipName(blip)
2025-08-03 16:51:12 +02:00
end
2025-08-03 17:36:16 +02:00
-- Erstelle Interaktionspunkte für DJ-Booths
for boothName, booth in pairs(Config.DJBooths) do
exports['qb-target']:AddBoxZone("dj_booth_" .. boothName, booth.coords, 1.5, 1.5, {
name = "dj_booth_" .. boothName,
heading = 0,
debugPoly = Config.Debug,
minZ = booth.coords.z - 1.0,
maxZ = booth.coords.z + 1.0
}, {
options = {
{
type = "client",
event = "dj:client:openDJMenu",
icon = "fas fa-music",
label = "DJ System öffnen",
boothName = boothName,
job = Config.UseJobRestriction and Config.AllowedJobs or nil
}
},
distance = 2.0
})
end
2025-08-03 16:51:12 +02:00
end)
2025-08-03 17:36:16 +02:00
-- Event für Target-Integration
RegisterNetEvent('dj:client:openDJMenu', function(data)
nearestBooth = data.boothName
isDJBooth = true
OpenDJInterface()
2025-08-03 17:02:11 +02:00
end)
2025-08-03 17:36:16 +02:00
-- Debug
if Config.Debug then
RegisterCommand('djdebug', function()
print('--- DJ SYSTEM DEBUG ---')
print('isPlaying:', isPlaying)
print('isDJBooth:', isDJBooth)
print('nearestBooth:', nearestBooth)
print('currentBooth:', currentBooth and currentBooth.name or 'nil')
print('currentVolume:', currentVolume)
print('currentSong:', currentSong and currentSong.title or 'nil')
print('activeBooths:')
for boothName, boothData in pairs(activeBooths) do
print(' -', boothName, boothData.title, boothData.volume)
2025-08-03 17:02:11 +02:00
end
2025-08-03 17:36:16 +02:00
end)
2025-08-03 17:02:11 +02:00
end
2025-08-03 18:30:40 +02:00
function OpenDJInterface()
if not isDJBooth then
lib.notify({
title = 'DJ System',
description = 'Du musst an einem DJ Pult stehen',
type = 'error'
})
return
end
SetNuiFocus(true, true)
SendNUIMessage({
type = 'showDJInterface',
center = true, -- Hinzugefügt: Zentriere das Interface
reset = true -- Hinzugefügt: Setze Position zurück
})
isUIOpen = true
-- Disable controls while UI is open
CreateThread(function()
while isUIOpen do
DisableAllControlActions(0)
EnableControlAction(0, 1, true) -- Mouse look
EnableControlAction(0, 2, true) -- Mouse look
EnableControlAction(0, 3, true) -- Mouse movement
EnableControlAction(0, 4, true) -- Mouse movement
EnableControlAction(0, 5, true) -- Mouse wheel
EnableControlAction(0, 6, true) -- Mouse wheel
Wait(0)
end
end)
end
2025-08-03 19:35:29 +02:00
-- Callback-Handler für NUI
RegisterNUICallback('callback', function(data, cb)
if data.callbackId then
SendNUIMessage({
type = 'callbackResponse',
callbackId = data.callbackId,
response = data.response or {}
})
end
cb('ok')
end)
-- Verbesserte Error-Handling
RegisterNUICallback('error', function(data, cb)
print("DJ System Error: " .. (data.error or "Unknown error"))
cb('ok')
-- Benachrichtige Spieler
lib.notify({
title = 'DJ System Error',
description = data.error or "Ein unbekannter Fehler ist aufgetreten",
type = 'error'
})
end)
-- Debug-Funktion für NUI-Status
function DebugNUIStatus()
local nuiFocus = {GetNuiFocus()}
print("NUI Focus:", json.encode(nuiFocus))
print("isUIOpen:", isUIOpen)
print("isDJBooth:", isDJBooth)
print("nearestBooth:", nearestBooth)
end
-- Debug-Befehl
RegisterCommand('djdebug', function()
DebugNUIStatus()
end, false)