- 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>
205 lines
7.0 KiB
Lua
205 lines
7.0 KiB
Lua
-- =====================================================================
|
||
-- Aether Core — Spielerobjekt
|
||
-- Kapselt die Daten eines eingeloggten Charakters samt Methoden.
|
||
-- =====================================================================
|
||
|
||
Aether = Aether or {}
|
||
|
||
-- Alle aktiven Spieler, indiziert nach Server-ID
|
||
Aether.Spieler = {}
|
||
|
||
-- Methodentabelle (Vorlage fuer jedes Spielerobjekt)
|
||
local SpielerMethoden = {}
|
||
SpielerMethoden.__index = SpielerMethoden
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Erzeugung
|
||
-- ---------------------------------------------------------------------
|
||
|
||
--- Erzeugt ein Spielerobjekt aus einem Charakter-Datensatz
|
||
--- @param quelle number Server-ID des Spielers
|
||
--- @param lizenz string Rockstar-Lizenz
|
||
--- @param charakter table Datensatz aus aether_characters
|
||
--- @return table Spielerobjekt
|
||
function Aether.ErzeugeSpieler(quelle, lizenz, charakter)
|
||
-- Gespeicherte Position einlesen (oder Standard-Spawn nutzen)
|
||
local position = Config.SpawnPunkt
|
||
if charakter.position then
|
||
local erfolg, entschluesselt = pcall(json.decode, charakter.position)
|
||
if erfolg and entschluesselt then position = entschluesselt end
|
||
end
|
||
|
||
-- Aussehen einlesen (JSON aus dem Charakter-Ersteller)
|
||
local aussehen = nil
|
||
if charakter.appearance then
|
||
local erfolg, entschluesselt = pcall(json.decode, charakter.appearance)
|
||
if erfolg then aussehen = entschluesselt end
|
||
end
|
||
|
||
local spieler = setmetatable({}, SpielerMethoden)
|
||
|
||
spieler.quelle = quelle
|
||
spieler.lizenz = lizenz
|
||
spieler.charakterId = charakter.id
|
||
|
||
-- Identitaet
|
||
spieler.vorname = charakter.firstname
|
||
spieler.nachname = charakter.lastname
|
||
spieler.geburtstag = charakter.dob
|
||
spieler.geschlecht = charakter.gender
|
||
spieler.herkunft = charakter.nationality
|
||
spieler.aussehen = aussehen
|
||
|
||
-- Geld
|
||
spieler.bargeld = charakter.cash
|
||
spieler.konto = charakter.bank
|
||
spieler.schwarzgeld = charakter.dirty_money
|
||
spieler.iban = charakter.iban
|
||
|
||
-- Arbeit und Zugehoerigkeit
|
||
spieler.job = charakter.job
|
||
spieler.jobRang = charakter.job_grade
|
||
spieler.fraktion = charakter.faction
|
||
spieler.fraktionRang= charakter.faction_rank
|
||
|
||
-- Zustand
|
||
spieler.leben = charakter.health
|
||
spieler.hunger = charakter.hunger
|
||
spieler.durst = charakter.thirst
|
||
spieler.ausdauer = charakter.stamina
|
||
spieler.istTot = charakter.is_dead == 1
|
||
spieler.position = position
|
||
spieler.spielzeit = charakter.playtime or 0
|
||
|
||
Aether.Spieler[quelle] = spieler
|
||
return spieler
|
||
end
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Geld
|
||
-- ---------------------------------------------------------------------
|
||
|
||
--- Liefert den Kontostand einer Geldart
|
||
--- @param art string 'bargeld' | 'konto' | 'schwarzgeld'
|
||
--- @return number
|
||
function SpielerMethoden:HoleGeld(art)
|
||
return self[art] or 0
|
||
end
|
||
|
||
--- Fuegt Geld hinzu und meldet die Aenderung an den Client
|
||
--- @param art string 'bargeld' | 'konto' | 'schwarzgeld'
|
||
--- @param betrag number Positiver Betrag
|
||
--- @param grund string|nil Verwendungszweck fuer die Transaktionsliste
|
||
--- @return boolean Erfolg
|
||
function SpielerMethoden:FuegeGeldHinzu(art, betrag, grund)
|
||
if betrag <= 0 then return false end
|
||
self[art] = (self[art] or 0) + betrag
|
||
|
||
-- Kontobewegungen zusaetzlich protokollieren
|
||
if art == 'konto' then
|
||
Aether.DB.SchreibeTransaktion(self.charakterId, 'deposit', betrag, self.konto, grund)
|
||
end
|
||
|
||
self:SendeGeld()
|
||
return true
|
||
end
|
||
|
||
--- Zieht Geld ab, sofern gedeckt
|
||
--- @param art string 'bargeld' | 'konto' | 'schwarzgeld'
|
||
--- @param betrag number Positiver Betrag
|
||
--- @param grund string|nil Verwendungszweck
|
||
--- @return boolean Erfolg (false = nicht genug Geld)
|
||
function SpielerMethoden:EntferneGeld(art, betrag, grund)
|
||
if betrag <= 0 then return false end
|
||
if (self[art] or 0) < betrag then return false end -- Deckungspruefung
|
||
|
||
self[art] = self[art] - betrag
|
||
|
||
if art == 'konto' then
|
||
Aether.DB.SchreibeTransaktion(self.charakterId, 'withdraw', -betrag, self.konto, grund)
|
||
end
|
||
|
||
self:SendeGeld()
|
||
return true
|
||
end
|
||
|
||
--- Schickt die aktuellen Geldwerte an den Client (fuer HUD und Handy)
|
||
function SpielerMethoden:SendeGeld()
|
||
TriggerClientEvent('aether:client:geldAktualisiert', self.quelle, {
|
||
bargeld = self.bargeld,
|
||
konto = self.konto,
|
||
schwarzgeld = self.schwarzgeld,
|
||
})
|
||
end
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Zustand
|
||
-- ---------------------------------------------------------------------
|
||
|
||
--- Setzt einen Zustandswert (Hunger/Durst/Ausdauer) und begrenzt ihn auf 0–100
|
||
--- @param art string 'hunger' | 'durst' | 'ausdauer'
|
||
--- @param wert number Neuer Wert
|
||
function SpielerMethoden:SetzeZustand(art, wert)
|
||
self[art] = Aether.Begrenze(wert, 0, 100)
|
||
TriggerClientEvent('aether:client:zustandAktualisiert', self.quelle, {
|
||
hunger = self.hunger,
|
||
durst = self.durst,
|
||
ausdauer = self.ausdauer,
|
||
})
|
||
end
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Daten fuer den Client
|
||
-- ---------------------------------------------------------------------
|
||
|
||
--- Baut das Datenpaket, das der Client zum Spielen braucht
|
||
--- @return table
|
||
function SpielerMethoden:HoleDaten()
|
||
return {
|
||
charakterId = self.charakterId,
|
||
vorname = self.vorname,
|
||
nachname = self.nachname,
|
||
geburtstag = self.geburtstag,
|
||
geschlecht = self.geschlecht,
|
||
aussehen = self.aussehen,
|
||
bargeld = self.bargeld,
|
||
konto = self.konto,
|
||
schwarzgeld = self.schwarzgeld,
|
||
iban = self.iban,
|
||
job = self.job,
|
||
jobRang = self.jobRang,
|
||
fraktion = self.fraktion,
|
||
fraktionRang= self.fraktionRang,
|
||
leben = self.leben,
|
||
hunger = self.hunger,
|
||
durst = self.durst,
|
||
ausdauer = self.ausdauer,
|
||
istTot = self.istTot,
|
||
position = self.position,
|
||
}
|
||
end
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Speichern
|
||
-- ---------------------------------------------------------------------
|
||
|
||
--- Schreibt den aktuellen Stand des Spielers in die Datenbank
|
||
function SpielerMethoden:Speichere()
|
||
Aether.DB.SpeichereCharakter(self.charakterId, {
|
||
cash = self.bargeld,
|
||
bank = self.konto,
|
||
dirty_money = self.schwarzgeld,
|
||
job = self.job,
|
||
job_grade = self.jobRang,
|
||
faction = self.fraktion,
|
||
faction_rank = self.fraktionRang,
|
||
health = self.leben,
|
||
hunger = math.floor(self.hunger),
|
||
thirst = math.floor(self.durst),
|
||
stamina = math.floor(self.ausdauer),
|
||
position = self.position,
|
||
is_dead = self.istTot,
|
||
playtime = self.spielzeit,
|
||
})
|
||
end
|