- 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>
165 lines
5.5 KiB
Lua
165 lines
5.5 KiB
Lua
-- =====================================================================
|
||
-- Aether Inventory — Sichtbare Boden-Drops
|
||
-- Fuer jedes fallengelassene Item steht ein Karton in der Welt.
|
||
-- Die Props werden nur lokal erzeugt und nach Entfernung ein-/ausgeblendet.
|
||
-- =====================================================================
|
||
|
||
Drops = {}
|
||
|
||
-- Bekannte Drops vom Server: [id] = { id, item, menge, x, y, z, prop }
|
||
local bekannteDrops = {}
|
||
-- Erzeugte Prop-Objekte: [id] = Objekt-Handle
|
||
local propObjekte = {}
|
||
|
||
-- Taste E zum Aufheben
|
||
local TASTE_AUFHEBEN = 38
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Prop erzeugen und entfernen
|
||
-- ---------------------------------------------------------------------
|
||
|
||
--- Erzeugt den Karton eines Drops in der Welt
|
||
--- @param drop table Datensatz vom Server
|
||
local function ErzeugeProp(drop)
|
||
if propObjekte[drop.id] then return end
|
||
|
||
local modell = GetHashKey(drop.prop or 'prop_cardbordbox_03a')
|
||
RequestModel(modell)
|
||
|
||
local versuche = 0
|
||
while not HasModelLoaded(modell) and versuche < 60 do
|
||
Wait(50)
|
||
versuche = versuche + 1
|
||
end
|
||
if not HasModelLoaded(modell) then return end
|
||
|
||
-- Nur lokal erzeugen: Jeder Client setzt seinen eigenen Karton
|
||
local objekt = CreateObject(modell, drop.x, drop.y, drop.z, false, false, false)
|
||
PlaceObjectOnGroundProperly(objekt)
|
||
FreezeEntityPosition(objekt, true)
|
||
SetEntityCollision(objekt, false, true) -- nicht wegschiebbar, aber sichtbar
|
||
SetModelAsNoLongerNeeded(modell)
|
||
|
||
propObjekte[drop.id] = objekt
|
||
end
|
||
|
||
--- Entfernt den Karton eines Drops wieder
|
||
--- @param id number Drop-ID
|
||
local function EntferneProp(id)
|
||
local objekt = propObjekte[id]
|
||
if objekt and DoesEntityExist(objekt) then
|
||
DeleteObject(objekt)
|
||
end
|
||
propObjekte[id] = nil
|
||
end
|
||
|
||
--- Entfernt alle erzeugten Kartons (beim Stoppen der Ressource)
|
||
function Drops.AlleEntfernen()
|
||
for id in pairs(propObjekte) do EntferneProp(id) end
|
||
end
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Ereignisse vom Server
|
||
-- ---------------------------------------------------------------------
|
||
|
||
--- Vollstaendige Liste beim Einsteigen ins Spiel
|
||
RegisterNetEvent('aether:inventory:dropListe', function(liste)
|
||
bekannteDrops = {}
|
||
for _, drop in pairs(liste or {}) do
|
||
bekannteDrops[drop.id] = drop
|
||
end
|
||
end)
|
||
|
||
--- Ein neues Item wurde fallengelassen
|
||
RegisterNetEvent('aether:inventory:dropHinzu', function(drop)
|
||
if not drop or not drop.id then return end
|
||
bekannteDrops[drop.id] = drop
|
||
end)
|
||
|
||
--- Ein Item wurde aufgehoben
|
||
RegisterNetEvent('aether:inventory:dropWeg', function(id)
|
||
bekannteDrops[id] = nil
|
||
EntferneProp(id)
|
||
end)
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Kartons je nach Entfernung ein- und ausblenden
|
||
-- ---------------------------------------------------------------------
|
||
CreateThread(function()
|
||
while true do
|
||
Wait(1000)
|
||
|
||
local position = GetEntityCoords(PlayerPedId())
|
||
|
||
for id, drop in pairs(bekannteDrops) do
|
||
local abstand = #(position - vector3(drop.x, drop.y, drop.z))
|
||
|
||
if abstand <= Inventar.DROP_SPAWN_WEITE then
|
||
ErzeugeProp(drop)
|
||
elseif propObjekte[id] then
|
||
EntferneProp(id)
|
||
end
|
||
end
|
||
|
||
-- Kartons entfernen, deren Drop es nicht mehr gibt
|
||
for id in pairs(propObjekte) do
|
||
if not bekannteDrops[id] then EntferneProp(id) end
|
||
end
|
||
end
|
||
end)
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Aufheben per Taste E am naechsten Karton
|
||
-- ---------------------------------------------------------------------
|
||
CreateThread(function()
|
||
while true do
|
||
local pause = 500
|
||
local position = GetEntityCoords(PlayerPedId())
|
||
|
||
-- Naechstgelegenen Drop in Reichweite suchen
|
||
local naechsteId, naechsterAbstand, naechsterDrop = nil, Inventar.DROP_HINWEIS, nil
|
||
for id, drop in pairs(bekannteDrops) do
|
||
local abstand = #(position - vector3(drop.x, drop.y, drop.z))
|
||
if abstand < naechsterAbstand then
|
||
naechsteId, naechsterAbstand, naechsterDrop = id, abstand, drop
|
||
end
|
||
end
|
||
|
||
if naechsteId then
|
||
pause = 0 -- fuer die Anzeige jeden Frame laufen
|
||
|
||
-- Hinweis ueber dem Karton einblenden
|
||
local def = Inventar.Items[naechsterDrop.item]
|
||
local beschriftung = def and def.name or naechsterDrop.item
|
||
if (naechsterDrop.menge or 1) > 1 then
|
||
beschriftung = beschriftung .. ' ×' .. naechsterDrop.menge
|
||
end
|
||
|
||
SetTextFont(4)
|
||
SetTextScale(0.32, 0.32)
|
||
SetTextColour(255, 255, 255, 215)
|
||
SetTextCentre(true)
|
||
SetTextOutline()
|
||
SetTextEntry('STRING')
|
||
AddTextComponentString('[E] ' .. beschriftung .. ' aufheben')
|
||
SetDrawOrigin(naechsterDrop.x, naechsterDrop.y, naechsterDrop.z + 0.6, 0)
|
||
DrawText(0.0, 0.0)
|
||
ClearDrawOrigin()
|
||
|
||
if IsControlJustReleased(0, TASTE_AUFHEBEN) then
|
||
TriggerServerEvent('aether:inventory:aufheben', naechsteId)
|
||
end
|
||
end
|
||
|
||
Wait(pause)
|
||
end
|
||
end)
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Aufraeumen
|
||
-- ---------------------------------------------------------------------
|
||
AddEventHandler('onResourceStop', function(ressource)
|
||
if ressource ~= GetCurrentResourceName() then return end
|
||
Drops.AlleEntfernen()
|
||
end)
|