- aether-medical: Stationen/Belegung, Intensivstation, Blutbank, Fuhrpark, Nachrichten/Aushänge, Berichte und Textbausteine (server + NUI) - aether-admin: vollständiges Server-Admin-Panel als echtes FiveM-Resource, an aether-core gekoppelt (Rechte über aether_users.admin_group), mit WebRTC-Live-Kameras (Bildschirmfreigabe) und separatem Media-Server (SFU) - Prototypen admin/ vervollständigt - SQL-Schema aether_admin.sql - Docker-Compose mit MariaDB und idempotenter Auto-Migration (schema_migrations) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
150 lines
5.1 KiB
Lua
150 lines
5.1 KiB
Lua
-- =====================================================================
|
|
-- Aether Shop — Clientlogik
|
|
-- Setzt Karten-Markierungen, erkennt Filialen und oeffnet die Oberflaeche.
|
|
-- Gekauft wird ausschliesslich ueber den Server.
|
|
-- =====================================================================
|
|
|
|
local istOffen = false
|
|
local istGeladen = false
|
|
|
|
-- Taste E zum Betreten
|
|
local TASTE_BENUTZEN = 38
|
|
|
|
--- Schickt eine Nachricht an die Oberflaeche
|
|
local function SendeAnNui(nachricht)
|
|
SendNuiMessage(json.encode(nachricht))
|
|
end
|
|
|
|
--- Zeigt eine Meldung oben links im Spiel
|
|
local function ZeigeSpielmeldung(text)
|
|
if not text or text == '' then return end
|
|
BeginTextCommandThefeedPost('STRING')
|
|
AddTextComponentSubstringPlayerName(text)
|
|
EndTextCommandThefeedPostTicker(false, true)
|
|
end
|
|
|
|
--- Schliesst den Laden
|
|
local function Schliessen()
|
|
if not istOffen then return end
|
|
istOffen = false
|
|
SetNuiFocus(false, false)
|
|
SendeAnNui({ aktion = 'schliessen' })
|
|
TriggerServerEvent('aether:shop:schliessen')
|
|
pcall(function() exports['aether-hud']:SetzeSichtbar(true) end)
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Karten-Markierungen setzen
|
|
-- ---------------------------------------------------------------------
|
|
CreateThread(function()
|
|
for _, laden in pairs(Shop.Laeden) do
|
|
for _, standort in ipairs(laden.standorte) do
|
|
local blip = AddBlipForCoord(standort.x, standort.y, standort.z)
|
|
SetBlipSprite(blip, laden.blip.sprite)
|
|
SetBlipColour(blip, laden.blip.farbe)
|
|
SetBlipScale(blip, 0.7)
|
|
SetBlipAsShortRange(blip, true)
|
|
BeginTextCommandSetBlipName('STRING')
|
|
AddTextComponentString(laden.name)
|
|
EndTextCommandSetBlipName(blip)
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Ereignisse
|
|
-- ---------------------------------------------------------------------
|
|
AddEventHandler('aether:client:bereit', function()
|
|
istGeladen = true
|
|
end)
|
|
|
|
--- Server erlaubt das Oeffnen und schickt den Katalog
|
|
RegisterNetEvent('aether:shop:zeige', function(katalog, geld)
|
|
istOffen = true
|
|
SetNuiFocus(true, true)
|
|
pcall(function() exports['aether-hud']:SetzeSichtbar(false) end)
|
|
SendeAnNui({ aktion = 'oeffnen', katalog = katalog, geld = geld })
|
|
end)
|
|
|
|
--- Kauf erfolgreich: Beleg und aktualisierter Katalog
|
|
RegisterNetEvent('aether:shop:gekauft', function(ergebnis)
|
|
ergebnis.aktion = 'gekauft'
|
|
SendeAnNui(ergebnis)
|
|
end)
|
|
|
|
--- Ablehnung einer Aktion
|
|
RegisterNetEvent('aether:shop:fehler', function(text)
|
|
if istOffen then
|
|
SendeAnNui({ aktion = 'fehler', text = text })
|
|
else
|
|
ZeigeSpielmeldung(text)
|
|
end
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Filiale in der Naehe erkennen
|
|
-- ---------------------------------------------------------------------
|
|
CreateThread(function()
|
|
while true do
|
|
local pause = 800
|
|
|
|
if istGeladen and not istOffen then
|
|
local position = GetEntityCoords(PlayerPedId())
|
|
|
|
-- Naechstgelegene Filiale suchen
|
|
local trefferId, trefferLaden, trefferPos = nil, nil, nil
|
|
for ladenId, laden in pairs(Shop.Laeden) do
|
|
for _, standort in ipairs(laden.standorte) do
|
|
if #(position - standort) <= Shop.INTERAKTION_WEITE then
|
|
trefferId, trefferLaden, trefferPos = ladenId, laden, standort
|
|
break
|
|
end
|
|
end
|
|
if trefferId then break end
|
|
end
|
|
|
|
if trefferId then
|
|
pause = 0 -- fuer die Textanzeige jeden Frame laufen
|
|
|
|
SetTextFont(4)
|
|
SetTextScale(0.34, 0.34)
|
|
SetTextColour(255, 255, 255, 215)
|
|
SetTextCentre(true)
|
|
SetTextOutline()
|
|
SetTextEntry('STRING')
|
|
AddTextComponentString('[E] ' .. trefferLaden.name)
|
|
SetDrawOrigin(trefferPos.x, trefferPos.y, trefferPos.z + 0.9, 0)
|
|
DrawText(0.0, 0.0)
|
|
ClearDrawOrigin()
|
|
|
|
if IsControlJustReleased(0, TASTE_BENUTZEN) then
|
|
TriggerServerEvent('aether:shop:oeffnen', trefferId)
|
|
end
|
|
end
|
|
end
|
|
|
|
Wait(pause)
|
|
end
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- NUI-Callbacks
|
|
-- ---------------------------------------------------------------------
|
|
RegisterNUICallback('schliessen', function(_, rueckgabe)
|
|
Schliessen()
|
|
rueckgabe({ ok = true })
|
|
end)
|
|
|
|
RegisterNUICallback('kaufen', function(daten, rueckgabe)
|
|
TriggerServerEvent('aether:shop:kaufen', daten.warenkorb or {}, daten.zahlung or 'bargeld')
|
|
rueckgabe({ ok = true })
|
|
end)
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- Aufraeumen
|
|
-- ---------------------------------------------------------------------
|
|
AddEventHandler('onResourceStop', function(ressource)
|
|
if ressource ~= GetCurrentResourceName() then return end
|
|
SetNuiFocus(false, false)
|
|
end)
|