diff --git a/resources/[tools]/nordi_infopoint/client/main.lua b/resources/[tools]/nordi_infopoint/client/main.lua new file mode 100644 index 000000000..0386561e7 --- /dev/null +++ b/resources/[tools]/nordi_infopoint/client/main.lua @@ -0,0 +1,489 @@ +local QBCore = exports['qb-core']:GetCoreObject() +local nearInfoPoint = false +local currentInfoPoint = nil + +-- Blips erstellen +CreateThread(function() + for _, point in pairs(Config.InfoPoints) do + if point.blip.display then + local blip = AddBlipForCoord(point.coords.x, point.coords.y, point.coords.z) + SetBlipSprite(blip, point.blip.sprite) + SetBlipColour(blip, point.blip.color) + SetBlipScale(blip, point.blip.scale) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString(point.name .. " - Infopoint") + EndTextCommandSetBlipName(blip) + end + end +end) + +-- Hauptschleife für Nähe-Erkennung +CreateThread(function() + while true do + local sleep = 1000 + local playerPed = PlayerPedId() + local playerCoords = GetEntityCoords(playerPed) + + nearInfoPoint = false + currentInfoPoint = nil + + for _, point in pairs(Config.InfoPoints) do + local distance = #(playerCoords - point.coords) + + if distance < 2.0 then + nearInfoPoint = true + currentInfoPoint = point + sleep = 0 + + -- Draw Text anzeigen + lib.showTextUI('[E] - Infopoint öffnen\n' .. point.name, { + position = "top-center", + icon = 'info-circle' + }) + + -- E-Taste abfragen + if IsControlJustPressed(0, 38) then -- E + openInfoPointMenu() + end + break + end + end + + if not nearInfoPoint then + lib.hideTextUI() + end + + Wait(sleep) + end +end) + +-- Infopoint Menü öffnen +function openInfoPointMenu() + local PlayerData = QBCore.Functions.GetPlayerData() + local job = PlayerData.job.name + local jobPermissions = Config.JobPermissions.jobSpecificInfo[job] or {} + + local menuOptions = { + { + title = 'Events anzeigen', + description = 'Aktuelle Events und Veranstaltungen', + icon = 'calendar', + onSelect = function() + showEvents() + end + } + } + + -- Event erstellen (nur für berechtigte Jobs) + local canCreateEvents = false + for _, allowedJob in pairs(Config.JobPermissions.canCreateEvents) do + if job == allowedJob then + canCreateEvents = true + break + end + end + + if canCreateEvents then + table.insert(menuOptions, { + title = 'Event erstellen', + description = 'Neues Event oder Veranstaltung erstellen', + icon = 'plus', + onSelect = function() + createEventDialog() + end + }) + + table.insert(menuOptions, { + title = 'Job-Angebot erstellen', + description = 'Neues Job-Angebot veröffentlichen', + icon = 'briefcase', + onSelect = function() + createJobOfferDialog() + end + }) + end + + -- Vorfall melden (für alle oder spezifische Jobs) + if Config.JobPermissions.canReportIncidents == 'all' or + (type(Config.JobPermissions.canReportIncidents) == 'table' and + table.contains(Config.JobPermissions.canReportIncidents, job)) then + table.insert(menuOptions, { + title = 'Vorfall melden', + description = 'Einen Vorfall an die Behörden melden', + icon = 'exclamation-triangle', + onSelect = function() + reportIncidentDialog() + end + }) + end + + -- Job-spezifische Optionen + if jobPermissions.showIncidents then + table.insert(menuOptions, { + title = 'Vorfälle anzeigen', + description = 'Gemeldete Vorfälle einsehen', + icon = 'list', + onSelect = function() + showIncidents() + end + }) + end + + if jobPermissions.showJobOffers then + table.insert(menuOptions, { + title = 'Job-Angebote', + description = 'Verfügbare Job-Angebote anzeigen', + icon = 'briefcase', + onSelect = function() + showJobOffers() + end + }) + end + + if jobPermissions.showLicenseInfo then + table.insert(menuOptions, { + title = 'Lizenz Informationen', + description = 'Informationen zu verfügbaren Lizenzen', + icon = 'id-card', + onSelect = function() + showLicenseInfo() + end + }) + end + + lib.registerContext({ + id = 'infopoint_main', + title = currentInfoPoint.name .. ' - Infopoint', + options = menuOptions + }) + + lib.showContext('infopoint_main') +end + +-- Events anzeigen +function showEvents() + QBCore.Functions.TriggerCallback('infopoint:getEvents', function(events) + local eventOptions = {} + + if #events == 0 then + table.insert(eventOptions, { + title = 'Keine Events verfügbar', + description = 'Derzeit sind keine Events geplant', + icon = 'info' + }) + else + for _, event in pairs(events) do + local eventDate = event.event_date and os.date('%d.%m.%Y %H:%M', os.time({ + year = tonumber(string.sub(event.event_date, 1, 4)), + month = tonumber(string.sub(event.event_date, 6, 7)), + day = tonumber(string.sub(event.event_date, 9, 10)), + hour = tonumber(string.sub(event.event_date, 12, 13)), + min = tonumber(string.sub(event.event_date, 15, 16)) + })) or 'Kein Datum' + + table.insert(eventOptions, { + title = event.title, + description = 'Datum: ' .. eventDate .. '\nOrt: ' .. (event.location or 'Nicht angegeben') .. '\nErstellt von: ' .. event.created_by_name, + icon = 'calendar', + onSelect = function() + showEventDetails(event) + end + }) + end + end + + table.insert(eventOptions, { + title = '← Zurück', + icon = 'arrow-left', + onSelect = function() + openInfoPointMenu() + end + }) + + lib.registerContext({ + id = 'infopoint_events', + title = 'Events & Veranstaltungen', + options = eventOptions + }) + + lib.showContext('infopoint_events') + end) +end + +-- Event Details anzeigen +function showEventDetails(event) + local eventDate = event.event_date and os.date('%d.%m.%Y %H:%M', os.time({ + year = tonumber(string.sub(event.event_date, 1, 4)), + month = tonumber(string.sub(event.event_date, 6, 7)), + day = tonumber(string.sub(event.event_date, 9, 10)), + hour = tonumber(string.sub(event.event_date, 12, 13)), + min = tonumber(string.sub(event.event_date, 15, 16)) + })) or 'Kein Datum' + + lib.alertDialog({ + header = event.title, + content = 'Kategorie: ' .. (event.category or 'Keine') .. '\n\n' .. + 'Datum: ' .. eventDate .. '\n\n' .. + 'Ort: ' .. (event.location or 'Nicht angegeben') .. '\n\n' .. + 'Beschreibung:\n' .. (event.description or 'Keine Beschreibung verfügbar') .. '\n\n' .. + 'Erstellt von: ' .. event.created_by_name, + centered = true, + cancel = true + }) +end + +-- Event erstellen Dialog +function createEventDialog() + local input = lib.inputDialog('Event erstellen', { + {type = 'input', label = 'Titel', required = true, max = 100}, + {type = 'textarea', label = 'Beschreibung', max = 500}, + {type = 'select', label = 'Kategorie', options = Config.EventCategories, required = true}, + {type = 'input', label = 'Ort', max = 100}, + {type = 'date', label = 'Datum', required = true}, + {type = 'time', label = 'Uhrzeit', required = true} + }) + + if input then + local eventData = { + title = input[1], + description = input[2], + category = input[3], + location = input[4], + date = input[5] .. ' ' .. input[6] .. ':00' + } + + TriggerServerEvent('infopoint:createEvent', eventData) + end +end + +-- Vorfälle anzeigen +function showIncidents() + QBCore.Functions.TriggerCallback('infopoint:getIncidents', function(incidents) + local incidentOptions = {} + + if #incidents == 0 then + table.insert(incidentOptions, { + title = 'Keine Vorfälle', + description = 'Derzeit sind keine Vorfälle gemeldet', + icon = 'info' + }) + else + for _, incident in pairs(incidents) do + local statusColor = incident.status == 'open' and '🔴' or '🟢' + local createdDate = os.date('%d.%m.%Y %H:%M', os.time({ + year = tonumber(string.sub(incident.created_at, 1, 4)), + month = tonumber(string.sub(incident.created_at, 6, 7)), + day = tonumber(string.sub(incident.created_at, 9, 10)), + hour = tonumber(string.sub(incident.created_at, 12, 13)), + min = tonumber(string.sub(incident.created_at, 15, 16)) + })) + + table.insert(incidentOptions, { + title = statusColor .. ' ' .. incident.title, + description = 'Gemeldet: ' .. createdDate .. '\nOrt: ' .. (incident.location or 'Nicht angegeben') .. '\nStatus: ' .. incident.status, + icon = 'exclamation-triangle', + onSelect = function() + showIncidentDetails(incident) + end + }) + end + end + + table.insert(incidentOptions, { + title = '← Zurück', + icon = 'arrow-left', + onSelect = function() + openInfoPointMenu() + end + }) + + lib.registerContext({ + id = 'infopoint_incidents', + title = 'Gemeldete Vorfälle', + options = incidentOptions + }) + + lib.showContext('infopoint_incidents') + end) +end + +-- Vorfall Details anzeigen +function showIncidentDetails(incident) + local createdDate = os.date('%d.%m.%Y %H:%M', os.time({ + year = tonumber(string.sub(incident.created_at, 1, 4)), + month = tonumber(string.sub(incident.created_at, 6, 7)), + day = tonumber(string.sub(incident.created_at, 9, 10)), + hour = tonumber(string.sub(incident.created_at, 12, 13)), + min = tonumber(string.sub(incident.created_at, 15, 16)) + })) + + lib.alertDialog({ + header = incident.title, + content = 'Kategorie: ' .. (incident.category or 'Keine') .. '\n\n' .. + 'Gemeldet am: ' .. createdDate .. '\n\n' .. + 'Ort: ' .. (incident.location or 'Nicht angegeben') .. '\n\n' .. + 'Status: ' .. incident.status .. '\n\n' .. + 'Beschreibung:\n' .. (incident.description or 'Keine Beschreibung verfügbar') .. '\n\n' .. + 'Gemeldet von: ' .. incident.reported_by_name, + centered = true, + cancel = true + }) +end + +-- Vorfall melden Dialog +function reportIncidentDialog() + local input = lib.inputDialog('Vorfall melden', { + {type = 'input', label = 'Titel', required = true, max = 100}, + {type = 'textarea', label = 'Beschreibung', required = true, max = 500}, + {type = 'select', label = 'Kategorie', options = Config.IncidentCategories, required = true}, + {type = 'input', label = 'Ort', max = 100} + }) + + if input then + local incidentData = { + title = input[1], + description = input[2], + category = input[3], + location = input[4] + } + + TriggerServerEvent('infopoint:reportIncident', incidentData) + end +end + +-- Job-Angebote anzeigen +function showJobOffers() + local PlayerData = QBCore.Functions.GetPlayerData() + local job = PlayerData.job.name + + QBCore.Functions.TriggerCallback('infopoint:getJobOffers', function(offers) + local offerOptions = {} + + if #offers == 0 then + table.insert(offerOptions, { + title = 'Keine Job-Angebote', + description = 'Derzeit sind keine Job-Angebote verfügbar', + icon = 'info' + }) + else + for _, offer in pairs(offers) do + table.insert(offerOptions, { + title = offer.title, + description = 'Job: ' .. offer.job_name .. '\nGehalt: ' .. (offer.salary or 'Verhandelbar') .. '\nKontakt: ' .. (offer.contact or 'Nicht angegeben'), + icon = 'briefcase', + onSelect = function() + showJobOfferDetails(offer) + end + }) + end + end + + table.insert(offerOptions, { + title = '← Zurück', + icon = 'arrow-left', + onSelect = function() + openInfoPointMenu() + end + }) + + lib.registerContext({ + id = 'infopoint_job_offers', + title = 'Job-Angebote', + options = offerOptions + }) + + lib.showContext('infopoint_job_offers') + end, nil) +end + +-- Job-Angebot Details anzeigen +function showJobOfferDetails(offer) + lib.alertDialog({ + header = offer.title, + content = 'Job: ' .. offer.job_name .. '\n\n' .. + 'Gehalt: ' .. (offer.salary or 'Verhandelbar') .. '\n\n' .. + 'Kontakt: ' .. (offer.contact or 'Nicht angegeben') .. '\n\n' .. + 'Anforderungen:\n' .. (offer.requirements or 'Keine besonderen Anforderungen') .. '\n\n' .. + 'Beschreibung:\n' .. (offer.description or 'Keine Beschreibung verfügbar'), + centered = true, + cancel = true + }) +end + +-- Job-Angebot erstellen Dialog +function createJobOfferDialog() + local input = lib.inputDialog('Job-Angebot erstellen', { + {type = 'input', label = 'Titel', required = true, max = 100}, + {type = 'textarea', label = 'Beschreibung', max = 500}, + {type = 'textarea', label = 'Anforderungen', max = 300}, + {type = 'input', label = 'Gehalt', max = 50}, + {type = 'input', label = 'Kontakt', max = 100} + }) + + if input then + local jobData = { + title = input[1], + description = input[2], + requirements = input[3], + salary = input[4], + contact = input[5] + } + + TriggerServerEvent('infopoint:createJobOffer', jobData) + end +end + +-- Lizenz Informationen anzeigen +function showLicenseInfo() + QBCore.Functions.TriggerCallback('infopoint:getLicenseInfo', function(licenses) + local licenseOptions = {} + + if #licenses == 0 then + table.insert(licenseOptions, { + title = 'Keine Informationen verfügbar', + description = 'Du hast keine Berechtigung diese Informationen zu sehen', + icon = 'info' + }) + else + for _, license in pairs(licenses) do + table.insert(licenseOptions, { + title = license.type, + description = 'Preis: ' .. license.price .. '\nAnforderungen: ' .. license.requirements, + icon = 'id-card' + }) + end + end + + table.insert(licenseOptions, { + title = '← Zurück', + icon = 'arrow-left', + onSelect = function() + openInfoPointMenu() + end + }) + + lib.registerContext({ + id = 'infopoint_licenses', + title = 'Lizenz Informationen', + options = licenseOptions + }) + + lib.showContext('infopoint_licenses') + end) +end + +-- Hilfsfunktion für table.contains +function table.contains(table, element) + for _, value in pairs(table) do + if value == element then + return true + end + end + return false +end + +-- Event Listener für Daten-Refresh +RegisterNetEvent('infopoint:refreshData', function() + -- Hier könntest du lokale Daten aktualisieren wenn nötig +end) diff --git a/resources/[tools]/nordi_infopoint/config.lua b/resources/[tools]/nordi_infopoint/config.lua new file mode 100644 index 000000000..7f9ddd996 --- /dev/null +++ b/resources/[tools]/nordi_infopoint/config.lua @@ -0,0 +1,101 @@ +Config = {} + +-- Infopoint Standorte +Config.InfoPoints = { + { + id = 1, + name = "Polizei Station", + coords = vector3(428.23, -984.28, 30.71), + blip = { + sprite = 480, + color = 3, + scale = 0.8, + display = true + } + }, + { + id = 2, + name = "Krankenhaus", + coords = vector3(307.7, -1433.4, 29.8), + blip = { + sprite = 61, + color = 2, + scale = 0.8, + display = true + } + }, + { + id = 3, + name = "Rathaus Sandy", + coords = vector3(1746.17, 3788.09, 35.55), + blip = { + sprite = 419, + color = 5, + scale = 0.8, + display = true + } + } +} + +-- Job Berechtigungen +Config.JobPermissions = { + -- Wer kann Events erstellen + canCreateEvents = { + 'police', 'ambulance', 'mechanic', 'realestate', 'taxi', 'burgershot', 'kayas', + }, + + -- Wer kann Vorfälle melden + canReportIncidents = 'all', -- 'all' für alle, oder spezifische Jobs als Table + + -- Spezielle Job Informationen + jobSpecificInfo = { + ['police'] = { + showIncidents = true, + showJobOffers = true, + showLicenseInfo = true, + showCriminalRecords = true + }, + ['ambulance'] = { + showIncidents = true, + showJobOffers = true, + showMedicalRecords = true + }, + ['mechanic'] = { + showJobOffers = true, + showVehicleInfo = true + }, + ['realestate'] = { + showJobOffers = true, + showPropertyInfo = true + }, + ['taxi'] = { + showJobOffers = true + }, + ['burgershot'] = { + showJobOffers = true + } + } +} + +-- Vorfall Kategorien +Config.IncidentCategories = { + 'Diebstahl', + 'Vandalismus', + 'Ruhestörung', + 'Verkehrsunfall', + 'Verdächtige Aktivität', + 'Drogen', + 'Gewalt', + 'Sonstiges' +} + +-- Event Kategorien +Config.EventCategories = { + 'Öffentliche Veranstaltung', + 'Job Event', + 'Training', + 'Meeting', + 'Party', + 'Rennen', + 'Sonstiges' +} diff --git a/resources/[tools]/nordi_infopoint/server/main.lua b/resources/[tools]/nordi_infopoint/server/main.lua new file mode 100644 index 000000000..2b381f747 --- /dev/null +++ b/resources/[tools]/nordi_infopoint/server/main.lua @@ -0,0 +1,208 @@ +local QBCore = exports['qb-core']:GetCoreObject() + +-- Datenbank Setup +CreateThread(function() + MySQL.query([[ + CREATE TABLE IF NOT EXISTS infopoint_events ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + description TEXT, + category VARCHAR(100), + location VARCHAR(255), + event_date DATETIME, + created_by VARCHAR(50), + created_by_name VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + active BOOLEAN DEFAULT TRUE + ) + ]]) + + MySQL.query([[ + CREATE TABLE IF NOT EXISTS infopoint_incidents ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + description TEXT, + category VARCHAR(100), + location VARCHAR(255), + reported_by VARCHAR(50), + reported_by_name VARCHAR(255), + status VARCHAR(50) DEFAULT 'open', + assigned_to VARCHAR(50), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ]]) + + MySQL.query([[ + CREATE TABLE IF NOT EXISTS infopoint_job_offers ( + id INT AUTO_INCREMENT PRIMARY KEY, + job_name VARCHAR(100), + title VARCHAR(255), + description TEXT, + requirements TEXT, + salary VARCHAR(100), + contact VARCHAR(255), + created_by VARCHAR(50), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + active BOOLEAN DEFAULT TRUE + ) + ]]) +end) + +-- Events abrufen +QBCore.Functions.CreateCallback('infopoint:getEvents', function(source, cb) + MySQL.query('SELECT * FROM infopoint_events WHERE active = 1 ORDER BY event_date ASC', {}, function(result) + cb(result) + end) +end) + +-- Vorfälle abrufen (nur für berechtigte Jobs) +QBCore.Functions.CreateCallback('infopoint:getIncidents', function(source, cb) + local Player = QBCore.Functions.GetPlayer(source) + if not Player then return cb({}) end + + local job = Player.PlayerData.job.name + if Config.JobPermissions.jobSpecificInfo[job] and Config.JobPermissions.jobSpecificInfo[job].showIncidents then + MySQL.query('SELECT * FROM infopoint_incidents ORDER BY created_at DESC', {}, function(result) + cb(result) + end) + else + cb({}) + end +end) + +-- Job Angebote abrufen +QBCore.Functions.CreateCallback('infopoint:getJobOffers', function(source, cb, jobName) + local query = 'SELECT * FROM infopoint_job_offers WHERE active = 1' + local params = {} + + if jobName then + query = query .. ' AND job_name = ?' + params = {jobName} + end + + query = query .. ' ORDER BY created_at DESC' + + MySQL.query(query, params, function(result) + cb(result) + end) +end) + +-- Lizenz Informationen abrufen +QBCore.Functions.CreateCallback('infopoint:getLicenseInfo', function(source, cb) + local Player = QBCore.Functions.GetPlayer(source) + if not Player then return cb({}) end + + local job = Player.PlayerData.job.name + if job == 'police' then + -- Hier könntest du Lizenz-Informationen aus der Datenbank abrufen + local licenseInfo = { + {type = 'Führerschein', price = '$500', requirements = 'Theorieprüfung bestehen'}, + {type = 'Waffenschein', price = '$2000', requirements = 'Saubere Akte, Prüfung'}, + {type = 'Pilotenschein', price = '$10000', requirements = 'Flugstunden nachweisen'} + } + cb(licenseInfo) + else + cb({}) + end +end) + +-- Event erstellen +RegisterNetEvent('infopoint:createEvent', function(eventData) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + if not Player then return end + + local job = Player.PlayerData.job.name + local canCreate = false + + for _, allowedJob in pairs(Config.JobPermissions.canCreateEvents) do + if job == allowedJob then + canCreate = true + break + end + end + + if not canCreate then + TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung Events zu erstellen!', 'error') + return + end + + MySQL.insert('INSERT INTO infopoint_events (title, description, category, location, event_date, created_by, created_by_name) VALUES (?, ?, ?, ?, ?, ?, ?)', { + eventData.title, + eventData.description, + eventData.category, + eventData.location, + eventData.date, + Player.PlayerData.citizenid, + Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname + }, function(insertId) + if insertId then + TriggerClientEvent('QBCore:Notify', src, 'Event erfolgreich erstellt!', 'success') + TriggerClientEvent('infopoint:refreshData', -1) + end + end) +end) + +-- Vorfall melden +RegisterNetEvent('infopoint:reportIncident', function(incidentData) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + if not Player then return end + + MySQL.insert('INSERT INTO infopoint_incidents (title, description, category, location, reported_by, reported_by_name) VALUES (?, ?, ?, ?, ?, ?)', { + incidentData.title, + incidentData.description, + incidentData.category, + incidentData.location, + Player.PlayerData.citizenid, + Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname + }, function(insertId) + if insertId then + TriggerClientEvent('QBCore:Notify', src, 'Vorfall erfolgreich gemeldet!', 'success') + + -- Benachrichtigung an alle Polizisten senden + local Players = QBCore.Functions.GetQBPlayers() + for _, player in pairs(Players) do + if player.PlayerData.job.name == 'police' and player.PlayerData.job.onduty then + TriggerClientEvent('QBCore:Notify', player.PlayerData.source, 'Neuer Vorfall gemeldet: ' .. incidentData.title, 'primary') + end + end + end + end) +end) + +-- Job Angebot erstellen +RegisterNetEvent('infopoint:createJobOffer', function(jobData) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + if not Player then return end + + local job = Player.PlayerData.job.name + local canCreate = false + + for _, allowedJob in pairs(Config.JobPermissions.canCreateEvents) do + if job == allowedJob then + canCreate = true + break + end + end + + if not canCreate then + TriggerClientEvent('QBCore:Notify', src, 'Du hast keine Berechtigung Job-Angebote zu erstellen!', 'error') + return + end + + MySQL.insert('INSERT INTO infopoint_job_offers (job_name, title, description, requirements, salary, contact, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)', { + job, + jobData.title, + jobData.description, + jobData.requirements, + jobData.salary, + jobData.contact, + Player.PlayerData.citizenid + }, function(insertId) + if insertId then + TriggerClientEvent('QBCore:Notify', src, 'Job-Angebot erfolgreich erstellt!', 'success') + end + end) +end)