forked from Simnation/Main
621 lines
18 KiB
Lua
621 lines
18 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
|
|
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)
|
|
|
|
-- 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
|
|
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
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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.updateInterval)
|
|
|
|
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
|
|
|
|
-- NUI Callbacks für das DJ-Interface
|
|
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)
|