1
0
Fork 0
forked from Simnation/Main
Main/resources/[tools]/nordi_dj/client/main.lua
2025-08-03 18:30:40 +02:00

665 lines
20 KiB
Lua

local QBCore = exports['qb-core']:GetCoreObject()
local PlayerData = {}
local currentBooth = nil
local isPlaying = false
local currentVolume = Config.DefaultVolume
local currentSong = nil
local activeBooths = {}
local isUIOpen = false
local isDJBooth = false
local nearestBooth = nil
-- Events
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
PlayerData = QBCore.Functions.GetPlayerData()
-- Frage Server nach aktiven DJ-Booths
TriggerServerEvent('dj:server:requestActiveDJs')
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
if not isDJBooth then
lib.notify({
title = 'DJ System',
description = 'Du musst an einem DJ Pult stehen!',
type = 'error'
})
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)
-- NUI Callbacks
RegisterNUICallback('djInterfaceClosed', function(data, cb)
SetNuiFocus(false, false)
isUIOpen = false
-- Kurze Verzögerung, um sicherzustellen, dass alle Animationen abgeschlossen sind
Wait(100)
-- Aktiviere alle Controls wieder
EnableAllControlActions(0)
cb('ok')
end)
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)
end
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')
end)
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)
-- 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
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'
})
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
function PlayMusicAsDJ(title, url, volume)
if not nearestBooth then
lib.notify({
title = 'DJ System',
description = 'Du musst an einem DJ-Pult stehen!',
type = 'error'
})
return
end
-- 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=.-$", "")
end
-- 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
},
range = CalculateRange(volume or Config.DefaultVolume)
})
-- Lokale Variablen aktualisieren
isPlaying = true
currentVolume = volume or Config.DefaultVolume
currentSong = {
title = title,
url = cleanUrl
}
currentBooth = {
name = nearestBooth,
coords = Config.DJBooths[nearestBooth].coords
}
end
function StopMusicAsDJ()
if not nearestBooth then
lib.notify({
title = 'DJ System',
description = 'Du musst an einem DJ-Pult stehen!',
type = 'error'
})
return
end
-- Sende an Server zur Synchronisation
TriggerServerEvent('dj:server:stopMusic', nearestBooth)
-- Lokale Variablen aktualisieren
isPlaying = false
currentSong = nil
currentBooth = nil
end
function SetVolumeAsDJ(volume)
if not nearestBooth then
lib.notify({
title = 'DJ System',
description = 'Du musst an einem DJ-Pult stehen!',
type = 'error'
})
return
end
-- Sende an Server zur Synchronisation
TriggerServerEvent('dj:server:setVolume', {
volume = volume,
booth = {
name = nearestBooth,
coords = Config.DJBooths[nearestBooth].coords
},
range = CalculateRange(volume)
})
-- Lokale Variablen aktualisieren
currentVolume = volume
end
function CalculateRange(volume)
if not nearestBooth or not Config.DJBooths[nearestBooth] then return 30.0 end
local booth = Config.DJBooths[nearestBooth]
local baseRange = booth.range or 30.0
local maxRange = booth.maxRange or 50.0
local volumePercent = volume / 100
return baseRange + ((maxRange - baseRange) * volumePercent)
end
-- Regelmäßige Prüfung der Distanz zu DJ-Booths und aktiven Musiken
CreateThread(function()
while true do
Wait(Config.DistanceVolume and Config.DistanceVolume.updateInterval or 1000)
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)
-- 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
else
-- Booth existiert nicht mehr, stoppe Musik
SendNUIMessage({
type = 'stopMusic'
})
currentBooth = nil
isPlaying = false
currentSong = nil
end
end
end
end)
-- 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)
end
-- 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
end)
-- Event für Target-Integration
RegisterNetEvent('dj:client:openDJMenu', function(data)
nearestBooth = data.boothName
isDJBooth = true
OpenDJInterface()
end)
-- 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)
end
end)
end
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