Files
d4rk_divegear/server/main.lua
T
D4rkst3randClaude Opus 5 5a59fc513f
Lint / Lint Resource (push) Has been cancelled
feat(hud): Manometer in bar und ausblendbares HUD
Druckanzeige:
- pressure * (Restluft / capacity). Bei konstantem Volumen ist der Druck
  proportional zur verbliebenen Gasmenge, die lineare Abbildung stimmt also
- Fuelldruck pro Flasche in config/shared.lua, alle bei 300 bar. Dass die grosse
  Flasche denselben Druck zeigt wie die kleine ist gewollt - in echt macht das
  Volumen die Laufzeit, nicht der Druck
- Unter config.reservePressure (50 bar) rot: in der Tauchpraxis die Schwelle,
  ab der man den Aufstieg einleitet

HUD ausblenden fuer Unterwasser-Screenshots. qbx_core hat selbst keinen
HUD-Toggle, es feuert nur hud:client:* an eine separate HUD-Resource - deshalb
drei voneinander unabhaengige Wege:
- IsHudHidden() folgen, greift bei allem was DisplayHud(false) setzt
- eigene Taste, standardmaessig unbelegt
- Export setHudVisible(bool) / isHudVisible() fuer andere Resources

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 00:06:58 +02:00

149 lines
4.8 KiB
Lua

local shared = require 'config.shared'
--- Zeitpunkt des letzten akzeptierten Syncs pro Spieler. Basis für die
--- Plausibilitätsprüfung: mehr als maxDrainPerSecond pro echter Sekunde kann
--- niemand verbrauchen.
local lastSync = {}
--- @param source number
--- @param slotId number
--- @return table? slot, table? tankConfig
local function getTankSlot(source, slotId)
if type(slotId) ~= 'number' then return end
local slot = exports.ox_inventory:GetSlot(source, slotId)
if not slot then return end
local tankConfig = shared.tankByName[slot.name]
if not tankConfig then return end
return slot, tankConfig
end
--- Schreibt die Restluft in die Metadata.
--- SetMetadata *ersetzt* die Metadata-Tabelle komplett (modules/inventory/server.lua),
--- deshalb wird der bestehende Inhalt kopiert statt nur oxygen gesetzt.
--- @return number oxygen akzeptierte Restsekunden
local function writeTank(source, slot, tankConfig, oxygen)
oxygen = math.floor(math.max(0, math.min(oxygen, tankConfig.capacity)))
local metadata = {}
for key, value in pairs(slot.metadata or {}) do
metadata[key] = value
end
metadata.oxygen = oxygen
-- durability zeigt den Füllstand direkt im Inventar-Slot an. Kein decay auf den
-- Items, sonst würde ox_inventory die leere Flasche löschen (items/server.lua).
metadata.durability = math.floor(oxygen / tankConfig.capacity * 100)
exports.ox_inventory:SetMetadata(source, slot.slot, metadata)
return oxygen
end
--- Alle Tauchflaschen im Inventar mit Füllstand.
--- @param source number
--- @return table[]
local function getTankList(source)
local found = exports.ox_inventory:Search(source, 'slots', shared.tankNames)
local tanks = {}
for i = 1, #shared.tankNames do
local name = shared.tankNames[i]
local slots = found and found[name]
local tankConfig = shared.tankByName[name]
if slots then
for j = 1, #slots do
local slot = slots[j]
local oxygen = tonumber(slot.metadata and slot.metadata.oxygen) or 0
tanks[#tanks + 1] = {
slot = slot.slot,
name = name,
label = (exports.ox_inventory:Items(name) or {}).label or name,
oxygen = math.max(0, math.min(oxygen, tankConfig.capacity)),
capacity = tankConfig.capacity,
pressure = tankConfig.pressure
}
end
end
end
return tanks
end
--- @return number? accepted
local function syncTank(source, slotId, oxygen)
oxygen = tonumber(oxygen)
if not oxygen then return end
local slot, tankConfig = getTankSlot(source, slotId)
if not slot then return end
local current = tonumber(slot.metadata and slot.metadata.oxygen) or tankConfig.capacity
local now = os.time()
local elapsed = lastSync[source] and (now - lastSync[source]) or shared.syncIntervalSeconds
lastSync[source] = now
-- Luft kann nur weniger werden, und nicht schneller als physikalisch möglich.
local floorValue = math.max(0, current - math.max(1, elapsed) * shared.maxDrainPerSecond)
local accepted = math.max(floorValue, math.min(oxygen, current))
return writeTank(source, slot, tankConfig, accepted)
end
lib.callback.register('d4rk_divegear:server:getTanks', getTankList)
lib.callback.register('d4rk_divegear:server:syncTank', function(source, slotId, oxygen)
return syncTank(source, slotId, oxygen)
end)
--- Fire-and-Forget-Variante für Tod und Resource-Stop, wo auf keine Antwort
--- mehr gewartet werden kann.
RegisterNetEvent('d4rk_divegear:server:syncTankNow', function(slotId, oxygen)
syncTank(source, slotId, oxygen)
end)
AddEventHandler('playerDropped', function()
lastSync[source] = nil
end)
exports.qbx_core:CreateUseableItem('diving_gear', function(source)
TriggerClientEvent('d4rk_divegear:client:useGear', source)
end)
exports.qbx_core:CreateUseableItem('diving_fill', function(source)
local tanks = getTankList(source)
if #tanks == 0 then
exports.qbx_core:Notify(source, locale('error.no_tank'), 'error')
return
end
local slotId = lib.callback.await('d4rk_divegear:client:fillTank', source, tanks)
if not slotId then return end
local slot, tankConfig = getTankSlot(source, slotId)
if not slot then return end
-- Nach dem Progressbar nochmal prüfen: das Item kann in der Zwischenzeit weg sein.
if not exports.ox_inventory:RemoveItem(source, 'diving_fill', 1) then return end
writeTank(source, slot, tankConfig, tankConfig.capacity)
lastSync[source] = os.time()
-- Erst melden wenn das Item tatsächlich weg und die Flasche voll ist.
exports.qbx_core:Notify(source, locale('success.tube_filled'), 'success')
TriggerClientEvent('d4rk_divegear:client:tankRefilled', source, slot.slot, tankConfig.capacity)
end)