aether-framework/resources/[aether]/aether-character/client/vorschau.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

190 lines
6.6 KiB
Lua

-- =====================================================================
-- Aether Character — 3D-Vorschau
-- Spawnt einen lokalen Ped als lebende Vorschau und richtet eine Kamera
-- darauf. Die NUI liegt mit einem transparenten Bereich darueber.
-- =====================================================================
Vorschau = {}
-- Ort, an dem der Vorschau-Ped steht (ruhige Ecke am Spawnbereich)
local VORSCHAU_POS = { x = -1040.0, y = -2748.0, z = 21.36 }
-- Kameraeinstellungen je Ansicht
-- abstand = Entfernung vor dem Ped, hoehe = Kamerahoehe, ziel = Blickpunkt am Ped
local MODI = {
gesicht = { abstand = 1.05, hoehe = 0.68, ziel = 0.68, sicht = 28.0 },
koerper = { abstand = 2.90, hoehe = 0.25, ziel = 0.20, sicht = 40.0 },
}
-- Laufender Zustand der Vorschau
local vorschauPed = nil -- Handle des Vorschau-Peds
local vorschauKamera= nil -- Handle der Kamera
local aktuellesModell = nil -- Aktuell geladenes Ped-Modell (fuer Geschlechtswechsel)
local aktuelleDrehung = 0.0 -- Zusaetzliche Drehung durch den Spieler
local aktuellerModus = 'koerper'
-- ---------------------------------------------------------------------
-- Interne Helfer
-- ---------------------------------------------------------------------
--- Laedt ein Ped-Modell und wartet, bis es bereit ist
--- @param modellName string Name des Modells
--- @return number|nil Modell-Hash oder nil bei Fehlschlag
local function LadeModell(modellName)
local modell = GetHashKey(modellName)
RequestModel(modell)
local versuche = 0
while not HasModelLoaded(modell) and versuche < 100 do
Wait(50)
versuche = versuche + 1
end
if not HasModelLoaded(modell) then return nil end
return modell
end
--- Setzt die Kamera auf die gewuenschte Ansicht
--- @param modus string 'gesicht' | 'koerper'
--- @param weich boolean|nil Sanft ueberblenden statt springen
local function SetzeKameraAuf(modus, weich)
if not vorschauKamera then return end
local einstellung = MODI[modus] or MODI.koerper
aktuellerModus = modus
-- Kamera steht noerdlich vor dem Ped; der Ped schaut sie an.
-- Fest im Raum verankert, damit das Drehen des Peds die Kamera nicht mitbewegt.
SetCamCoord(vorschauKamera,
VORSCHAU_POS.x,
VORSCHAU_POS.y + einstellung.abstand,
VORSCHAU_POS.z + einstellung.hoehe)
PointCamAtCoord(vorschauKamera,
VORSCHAU_POS.x, VORSCHAU_POS.y, VORSCHAU_POS.z + einstellung.ziel)
SetCamFov(vorschauKamera, einstellung.sicht)
if weich then
-- Kurze Ueberblendung fuer einen weichen Wechsel
SetCamActiveWithInterp(vorschauKamera, vorschauKamera, 400, 1, 1)
end
end
-- ---------------------------------------------------------------------
-- Oeffentliche Schnittstelle
-- ---------------------------------------------------------------------
--- Startet die Vorschau: erzeugt Ped und Kamera
--- @param aussehen table|nil Startaussehen (bestimmt auch das Geschlecht)
--- @param modus string|nil Startansicht ('gesicht' | 'koerper')
function Vorschau.Starten(aussehen, modus)
-- Umgebung laden, damit der Ped nicht durch den Boden faellt
RequestCollisionAtCoord(VORSCHAU_POS.x, VORSCHAU_POS.y, VORSCHAU_POS.z)
local modellName = AetherAussehen.HoleModell(aussehen and aussehen.geschlecht)
local modell = LadeModell(modellName)
if not modell then
print('^1[Aether]^7 Vorschau-Modell konnte nicht geladen werden: ' .. modellName)
return
end
-- Ped nur lokal erzeugen (nicht im Netzwerk sichtbar)
vorschauPed = CreatePed(4, modell, VORSCHAU_POS.x, VORSCHAU_POS.y, VORSCHAU_POS.z,
0.0, false, false)
aktuellesModell = modellName
aktuelleDrehung = 0.0
SetEntityHeading(vorschauPed, 0.0) -- schaut Richtung Kamera
FreezeEntityPosition(vorschauPed, true)
SetEntityInvincible(vorschauPed, true)
SetBlockingOfNonTemporaryEvents(vorschauPed, true)
SetModelAsNoLongerNeeded(modell)
if aussehen then
AetherAussehen.Anwenden(vorschauPed, aussehen)
end
-- Kamera erzeugen und aktivieren
vorschauKamera = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
SetzeKameraAuf(modus or 'koerper', false)
SetCamActive(vorschauKamera, true)
RenderScriptCams(true, false, 0, true, true)
end
--- Uebertraegt ein Aussehen auf den Vorschau-Ped.
--- Wechselt das Geschlecht, wird der Ped neu erzeugt.
--- @param aussehen table Aussehen aus dem Ersteller
function Vorschau.Anwenden(aussehen)
if not aussehen then return end
-- Geschlechtswechsel erfordert ein anderes Modell
local gewuenschtesModell = AetherAussehen.HoleModell(aussehen.geschlecht)
if gewuenschtesModell ~= aktuellesModell then
local drehung = aktuelleDrehung
local modus = aktuellerModus
if vorschauPed and DoesEntityExist(vorschauPed) then
DeletePed(vorschauPed)
end
local modell = LadeModell(gewuenschtesModell)
if not modell then return end
vorschauPed = CreatePed(4, modell, VORSCHAU_POS.x, VORSCHAU_POS.y, VORSCHAU_POS.z,
0.0, false, false)
aktuellesModell = gewuenschtesModell
FreezeEntityPosition(vorschauPed, true)
SetEntityInvincible(vorschauPed, true)
SetBlockingOfNonTemporaryEvents(vorschauPed, true)
SetModelAsNoLongerNeeded(modell)
-- Vorherige Drehung und Ansicht wiederherstellen
aktuelleDrehung = drehung
SetEntityHeading(vorschauPed, aktuelleDrehung)
SetzeKameraAuf(modus, false)
end
if vorschauPed and DoesEntityExist(vorschauPed) then
AetherAussehen.Anwenden(vorschauPed, aussehen)
end
end
--- Wechselt zwischen Gesichts- und Koerperansicht
--- @param modus string 'gesicht' | 'koerper'
function Vorschau.SetzeModus(modus)
SetzeKameraAuf(modus, true)
end
--- Dreht den Vorschau-Ped um einen Winkel
--- @param grad number Drehung in Grad (positiv = nach rechts)
function Vorschau.Drehen(grad)
if not vorschauPed or not DoesEntityExist(vorschauPed) then return end
aktuelleDrehung = (aktuelleDrehung + (grad or 0.0)) % 360.0
SetEntityHeading(vorschauPed, aktuelleDrehung)
end
--- Beendet die Vorschau und raeumt Ped und Kamera auf
function Vorschau.Beenden()
if vorschauKamera then
RenderScriptCams(false, false, 0, true, true)
DestroyCam(vorschauKamera, false)
vorschauKamera = nil
end
if vorschauPed and DoesEntityExist(vorschauPed) then
DeletePed(vorschauPed)
end
vorschauPed = nil
aktuellesModell = nil
aktuelleDrehung = 0.0
end
--- Ist gerade eine Vorschau aktiv?
--- @return boolean
function Vorschau.IstAktiv()
return vorschauPed ~= nil
end