aether-framework/resources/[aether]/aether-shop/shared/shops.lua
Jerrit Fritzsche e4b82dd685 Aether-Framework: Fachmodule, Admin-Panel und Docker-Datenbank
- 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>
2026-07-27 18:55:32 +02:00

141 lines
9.2 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- =====================================================================
-- Aether Shop — Warenkatalog, Laeden und Standorte
-- Der Server ist die einzige Quelle fuer Preise und Bestand.
-- Der Client bekommt den Katalog nur zum Anzeigen.
-- =====================================================================
Shop = Shop or {}
-- ---------------------------------------------------------------------
-- Grundeinstellungen
-- ---------------------------------------------------------------------
Shop.INTERAKTION_WEITE = 2.0 -- Reichweite zum Oeffnen (Meter)
Shop.MAX_MENGE = 100 -- Hoechstmenge je Position im Warenkorb
-- Wird ein Waffenschein verlangt?
Shop.LizenzPflicht = true
-- Rueckfallregel, falls `aether-documents` nicht laeuft: dann kann niemand
-- einen Waffenschein nachweisen und lizenzpflichtige Waren waeren gesperrt.
-- Seit der Migration von aether-documents greift die echte Pruefung.
Shop.OhneDokumenteErlauben = false
-- ---------------------------------------------------------------------
-- Produkt-Registry
-- item = Kennung im Inventar (aether-inventory)
-- menge = wie viele Stueck ein Kauf liefert (z. B. Munitionspackung)
-- rabatt = 0..1, macht ein Angebot
-- lizenz = Waffenschein noetig
-- ---------------------------------------------------------------------
Shop.Produkte = {
-- --- 24/7 ---
wasser = { name = 'Wasserflasche', kat = 'Getränke', icon = '🥤', preis = 4, item = 'wasser', bestand = 40, beschreibung = 'Löscht den Durst.' },
cola = { name = 'Cola', kat = 'Getränke', icon = '🥫', preis = 6, item = 'cola', bestand = 30, beschreibung = 'Eiskalt, koffeinhaltig.' },
energy = { name = 'Energydrink', kat = 'Getränke', icon = '⚡', preis = 9, item = 'energy', bestand = 24, beschreibung = 'Gibt Extra-Ausdauer.' },
kaffee = { name = 'Kaffee to go', kat = 'Getränke', icon = '☕', preis = 5, item = 'kaffee', bestand = 18, beschreibung = 'Wach werden.' },
sandwich = { name = 'Sandwich', kat = 'Snacks', icon = '🥪', preis = 8, item = 'sandwich', bestand = 25, beschreibung = 'Stillt den Hunger.' },
chips = { name = 'Chips', kat = 'Snacks', icon = '🍟', preis = 5, item = 'chips', bestand = 32, beschreibung = 'Knuspriger Snack.' },
schoko = { name = 'Schokoriegel', kat = 'Snacks', icon = '🍫', preis = 3, item = 'schoko', bestand = 50, beschreibung = 'Süße Energie.' },
burger = { name = 'Burger', kat = 'Snacks', icon = '🍔', preis = 12, item = 'burger', bestand = 15, rabatt = 0.25, beschreibung = 'Deftige Mahlzeit.' },
verband = { name = 'Verband', kat = 'Gesundheit', icon = '🩹', preis = 15, item = 'verband', bestand = 20, beschreibung = 'Stoppt Blutungen.' },
tablette = { name = 'Schmerztabletten', kat = 'Gesundheit', icon = '💊', preis = 20, item = 'tablette', bestand = 16, beschreibung = 'Lindert Schmerzen.' },
medkit = { name = 'Erste-Hilfe-Kit', kat = 'Gesundheit', icon = '🧳', preis = 120, item = 'medkit', bestand = 8, beschreibung = 'Behandelt schwere Wunden.' },
repkit = { name = 'Reparaturkit', kat = 'Werkzeug', icon = '🧰', preis = 250, item = 'reparaturkit', bestand = 10, beschreibung = 'Repariert Fahrzeuge.' },
dietrich = { name = 'Dietrich', kat = 'Werkzeug', icon = '🗝️', preis = 45, item = 'dietrich', bestand = 22, beschreibung = 'Öffnet einfache Schlösser.' },
kabel = { name = 'Kabelbinder', kat = 'Werkzeug', icon = '🔗', preis = 30, item = 'kabel', bestand = 40, beschreibung = 'Vielseitig einsetzbar.' },
lampe = { name = 'Taschenlampe', kat = 'Werkzeug', icon = '🔦', preis = 35, item = 'lampe', bestand = 14, beschreibung = 'Leuchtet den Weg.' },
handy = { name = 'Smartphone', kat = 'Elektronik', icon = '📱', preis = 450, item = 'handy', bestand = 6, beschreibung = 'Modernes Handy.' },
sim = { name = 'SIM-Karte', kat = 'Elektronik', icon = '📶', preis = 60, item = 'sim', bestand = 30, beschreibung = 'Fürs Handy nötig.' },
akku = { name = 'Akku', kat = 'Elektronik', icon = '🔋', preis = 40, item = 'akku', bestand = 45, beschreibung = 'Ersatz-Akku fürs Handy.' },
funk = { name = 'Funkgerät', kat = 'Elektronik', icon = '📻', preis = 180, item = 'funk', bestand = 9, beschreibung = 'Kurzstrecken-Funk.' },
boerse = { name = 'Börse', kat = 'Zubehör', icon = '👛', preis = 90, item = 'boerse', bestand = 12, beschreibung = 'Fasst bis zu 10 Dokumente.' },
rucksack = { name = 'Rucksack', kat = 'Zubehör', icon = '🎒', preis = 300, item = 'rucksack', bestand = 7, rabatt = 0.15, beschreibung = '+ Tragekapazität.' },
-- --- Ammu-Nation ---
w_pistole = { name = 'Pistole P99', kat = 'Waffen', icon = '🔫', preis = 3500, item = 'pistole', bestand = 5, lizenz = true, beschreibung = '9mm Halbautomatik.' },
w_smg = { name = 'MP5 (MPX)', kat = 'Waffen', icon = '🔫', preis = 8500, item = 'smg', bestand = 3, lizenz = true, beschreibung = 'Maschinenpistole.' },
w_flinte = { name = 'Schrotflinte 870', kat = 'Waffen', icon = '🔫', preis = 5200, item = 'flinte', bestand = 4, lizenz = true, beschreibung = 'Pump-Action Shotgun.' },
a_9mm = { name = '9mm Munition (×30)', kat = 'Munition', icon = '🧨', preis = 120, item = 'munition9', menge = 30, bestand = 60, beschreibung = 'Patronen für 9mm.' },
a_12 = { name = 'Schrot 12/70 (×10)', kat = 'Munition', icon = '🧨', preis = 90, item = 'schrot12', menge = 10, bestand = 40, beschreibung = 'Schrotpatronen.' },
at_sd = { name = 'Schalldämpfer', kat = 'Aufsätze', icon = '🔇', preis = 900, item = 'schalldaempfer', bestand = 10, lizenz = true, beschreibung = 'Dämpft den Mündungsknall.' },
at_lampe = { name = 'Waffenlampe', kat = 'Aufsätze', icon = '🔦', preis = 400, item = 'waffenlampe', bestand = 12, beschreibung = 'Taktische Beleuchtung.' },
at_laser = { name = 'Laservisier', kat = 'Aufsätze', icon = '🎯', preis = 650, item = 'laservisier', bestand = 8, beschreibung = 'Bessere Zielerfassung.' },
s_weste = { name = 'Schutzweste', kat = 'Schutz', icon = '🦺', preis = 1200, item = 'weste', bestand = 6, beschreibung = 'Reduziert Schaden.' },
s_helm = { name = 'Ballistikhelm', kat = 'Schutz', icon = '⛑️', preis = 800, item = 'helm', bestand = 8, beschreibung = 'Schützt den Kopf.' },
}
-- ---------------------------------------------------------------------
-- Laeden — nutzen dieselbe Oberflaeche, listen nur andere Produkte
-- ---------------------------------------------------------------------
Shop.Laeden = {
['247'] = {
name = 'LS 24/7', logo = '🛒', tl = 'Alles für den Alltag',
accent = '#4ade80', ink = '#06210f', warnung = nil,
blip = { sprite = 52, farbe = 2 },
produkte = {
'wasser', 'cola', 'energy', 'kaffee', 'sandwich', 'chips', 'schoko', 'burger',
'verband', 'tablette', 'medkit', 'repkit', 'dietrich', 'kabel', 'lampe',
'handy', 'sim', 'akku', 'funk', 'boerse', 'rucksack',
},
-- Standorte der 24/7-Filialen
standorte = {
vector3(25.7, -1347.3, 29.5),
vector3(-3038.9, 585.9, 7.9),
vector3(-3241.9, 1001.4, 12.8),
vector3(1729.2, 6414.1, 35.0),
vector3(1697.9, 4924.4, 42.1),
vector3(547.4, 2671.7, 42.1),
vector3(2679.0, 3280.1, 55.2),
vector3(1961.4, 3740.6, 32.3),
vector3(293.8, -1447.0, 29.9),
vector3(-2968.2, 390.9, 15.0),
},
},
['ammu'] = {
name = 'Ammu-Nation', logo = '🔫', tl = 'Waffen & Ausrüstung',
accent = '#f0684a', ink = '#2a0c05',
warnung = 'Für Waffen & bestimmte Aufsätze ist ein gültiger Waffenschein erforderlich.',
blip = { sprite = 110, farbe = 1 },
produkte = {
'w_pistole', 'w_smg', 'w_flinte', 'a_9mm', 'a_12',
'at_sd', 'at_lampe', 'at_laser', 's_weste', 's_helm',
},
standorte = {
vector3(22.1, -1107.4, 29.8),
vector3(810.2, -2157.3, 29.6),
vector3(1693.4, 3760.2, 34.7),
vector3(-330.2, 6083.9, 31.5),
vector3(252.6, -50.0, 69.9),
vector3(-662.1, -935.3, 21.8),
vector3(-1305.3, -393.5, 36.7),
vector3(-1118.9, 2698.9, 18.6),
vector3(2567.7, 294.4, 108.7),
},
},
}
--- Liefert den effektiven Preis eines Produkts (mit Rabatt)
--- @param produkt table Produktdefinition
--- @return number Preis in Euro
function Shop.Preis(produkt)
if not produkt then return 0 end
local preis = produkt.preis or 0
if produkt.rabatt then
preis = math.floor(preis * (1 - produkt.rabatt) + 0.5)
end
return preis
end
--- Prueft, ob ein Laden ein bestimmtes Produkt fuehrt
--- @param ladenId string Kennung des Ladens
--- @param produktId string Kennung des Produkts
--- @return boolean
function Shop.FuehrtProdukt(ladenId, produktId)
local laden = Shop.Laeden[ladenId]
if not laden then return false end
for _, kennung in ipairs(laden.produkte) do
if kennung == produktId then return true end
end
return false
end