From 5a59fc513f7e098097b6ec741301cd0ea429fdbb Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Tue, 11 Aug 2026 00:06:58 +0200 Subject: [PATCH] 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 --- PLAN.md | 14 ++++++++++- README.md | 21 ++++++++++++++-- client/main.lua | 49 +++++++++++++++++++++++++++++++++++++- config/client.lua | 14 +++++++++++ config/shared.lua | 16 +++++++++---- items_for_ox_inventory.lua | 13 +++++----- locales/de.json | 4 +++- locales/en.json | 4 +++- nui/index.html | 13 +++++++--- nui/script.js | 11 ++++++++- nui/style.css | 13 ++++++++-- server/main.lua | 3 ++- 12 files changed, 151 insertions(+), 24 deletions(-) diff --git a/PLAN.md b/PLAN.md index 13bfef8..e2ebb77 100644 --- a/PLAN.md +++ b/PLAN.md @@ -121,6 +121,15 @@ Tiefenanzeige und O2-Anzeige zusammen als ein NUI-Overlay — `qbx.drawText2d` - [x] Updates gedrosselt per `SendNUIMessage` (~4/s statt jeden Frame) - [x] Kein `backdrop-filter` (bekannter FiveM-Bug → schwarzer Hintergrund) - [x] `hudEnabled`, `hudUpdateIntervalMs` und `deepWarningDepth` in `config/client.lua` +- [x] **Nachgereicht:** Manometer in bar. `pressure * (Restluft / capacity)` — bei + konstantem Volumen ist der Druck proportional zur verbliebenen Gasmenge, die + lineare Abbildung ist also korrekt. Alle Flaschen starten bei 300 bar; dass + die große Flasche denselben Druck zeigt wie die kleine, ist gewollt (in echt + macht das Volumen die Laufzeit). Unter `reservePressure` wird rot eingefärbt +- [x] **Nachgereicht:** HUD ausblendbar für Unterwasser-Screenshots. Drei Wege, + weil `qbx_core` selbst keinen HUD-Toggle hat (es feuert nur `hud:client:*` + an eine separate HUD-Resource): `IsHudHidden()` folgen, eigene Taste, + und der Export `setHudVisible(bool)` - [ ] **Offen:** Position und Größe des HUD sind bisher nur in `nui/style.css` einstellbar, nicht in der Config @@ -234,7 +243,10 @@ Locale-JSONs und das HUD im Browser. tauschen oder das Vorzeichen drehen. 3. **Auto-Anziehen nach Relog** (siehe Phase 2) und **HUD-Position in der Config** (Phase 4) sind nicht umgesetzt. -4. **Balancing:** `maxDrainPerSecond = 5.0` muss über `maxDecayFactor = 4.0` liegen, +4. **`IsHudHidden()`** — ob die Native wirklich auf `DisplayHud(false)` anspringt, + ist ungetestet. Falls nicht, greifen weiterhin die eigene Taste und der Export; + `hudFollowGameHud` lässt sich dann abschalten. +5. **Balancing:** `maxDrainPerSecond = 5.0` muss über `maxDecayFactor = 4.0` liegen, sonst bekommen ehrliche Spieler in großer Tiefe vom Server Luft zurückgeschrieben. Wer den Tiefenfaktor hochdreht, muss beides nachziehen. diff --git a/README.md b/README.md index 8b61200..baea0ac 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ Animationen nicht verschwindet. Füllstand ist als Balken direkt im Inventar-Slot sichtbar. - **Gesyncte Tauchlampe** — an der Ausrüstung, An/Aus per Taste (Standard `L`). Andere Spieler sehen den Lichtkegel. -- **NUI-HUD** — Restluft als Ring mit `mm:ss` und Tauchtiefe in Metern, mit - Warnstufen. Nur sichtbar mit angelegter Ausrüstung unter Wasser. +- **NUI-HUD** — Restluft als Ring mit `mm:ss`, Tauchtiefe in Metern und + Manometer in bar, mit Warnstufen. Nur sichtbar mit angelegter Ausrüstung + unter Wasser, und ausblendbar für Unterwasser-Screenshots. - **Tiefenabhängiger Verbrauch** — tiefer tauchen kostet mehr Luft. Abschaltbar. - **Ausrüstung bleibt** — bei Emotes, Ragdoll, Kleiderwechsel und aufräumenden Fremd-Scripts. @@ -39,6 +40,22 @@ Animationen nicht verschwindet. | Flasche wählen | Menü beim Anlegen (entfällt bei nur einer Flasche) | | Flasche auffüllen | `diving_fill` benutzen, nicht unter Wasser | | Lampe an/aus | `L` (in den FiveM-Tastenbelegungen umlegbar) | +| HUD ausblenden | Eigene Taste, standardmäßig unbelegt — in den FiveM-Tastenbelegungen unter "FiveM" setzen | + +### HUD ausblenden für Screenshots + +Drei Wege, damit es unabhängig von der eingesetzten HUD-Resource funktioniert: + +1. **Automatisch** — das Tauch-HUD verschwindet mit dem Spiel-HUD. Greift bei + allem, was `DisplayHud(false)` setzt (Screenshot-Scripts, HUD-Toggles, + Cinematic-Modes). Abschaltbar über `hudFollowGameHud`. +2. **Eigene Taste** — `hudToggleKey` in der Config, oder vom Spieler selbst belegt. +3. **Export** — für andere Resources: + +```lua +exports.d4rk_divegear:setHudVisible(false) +local shown = exports.d4rk_divegear:isHudVisible() +``` ## Konfiguration diff --git a/client/main.lua b/client/main.lua index 7c8c5d3..0ab6b98 100644 --- a/client/main.lua +++ b/client/main.lua @@ -289,10 +289,20 @@ local function takeOffSuit() end end +--- Manometer-Anzeige. Bei konstantem Volumen ist der Druck proportional zur +--- verbliebenen Gasmenge, deshalb reicht der lineare Anteil an der Füllung. +--- @return number bar +local function getPressure() + if not activeTank or activeTank.capacity <= 0 then return 0 end + + return (activeTank.pressure or 0) * (activeTank.oxygen / activeTank.capacity) +end + --- @param visible boolean local function sendHudState(visible) local oxygen = activeTank and activeTank.oxygen or 0.0 local capacity = activeTank and activeTank.capacity or 1 + local pressure = getPressure() local level = 'ok' if oxygen <= dangerThreshold then @@ -307,6 +317,8 @@ local function sendHudState(visible) oxygen = oxygen, capacity = capacity, depth = currentDepth, + pressure = pressure, + reserve = pressure > 0 and pressure <= config.reservePressure, level = level, deep = currentDepth >= config.deepWarningDepth, hasLamp = true, @@ -314,6 +326,25 @@ local function sendHudState(visible) }) end +--- Manuell ausgeblendet (eigene Taste oder Export)? Unabhängig davon, ob das +--- Spiel-HUD gerade sichtbar ist. +local hudHidden = false + +--- Blendet das Tauch-HUD aus oder wieder ein, z.B. für Unterwasser-Screenshots. +--- Als Export nutzbar, damit HUD- und Screenshot-Resources es mitnehmen können: +--- exports.d4rk_divegear:setHudVisible(false) +--- @param visible boolean +local function setHudVisible(visible) + hudHidden = not visible + + if hudHidden then + SendNUIMessage({ action = 'state', visible = false }) + end +end + +exports('setHudVisible', setHudVisible) +exports('isHudVisible', function() return not hudHidden end) + --- Pflegt die geglättete Tiefe und schiebt den Zustand ins NUI. --- Läuft auch über Wasser weiter, weil getDecayFactor die Tiefe braucht - nur die --- Anzeige wird ausgeblendet. @@ -325,13 +356,20 @@ local function startHudThread() action = 'labels', air = locale('hud.air'), depth = locale('hud.depth'), + pressure = locale('hud.pressure'), lamp = locale('hud.lamp') }) while currentGear.enabled do currentDepth = currentDepth + (getDepth() - currentDepth) * 0.25 - local visible = config.hudEnabled and activeTank ~= nil and IsPedSwimmingUnderWater(cache.ped) + local visible = config.hudEnabled + and not hudHidden + and activeTank ~= nil + and IsPedSwimmingUnderWater(cache.ped) + -- Folgt dem Spiel-HUD: alles was DisplayHud(false) setzt, blendet + -- damit auch das Tauch-HUD aus. + and not (config.hudFollowGameHud and IsHudHidden()) if visible or wasVisible then sendHudState(visible) @@ -478,6 +516,15 @@ RegisterNetEvent('d4rk_divegear:client:useGear', function() end end) +lib.addKeybind({ + name = 'divegear_hud', + description = locale('keybind.toggle_hud'), + defaultKey = config.hudToggleKey, + onPressed = function() + setHudVisible(hudHidden) + end +}) + lib.addKeybind({ name = 'divegear_lamp', description = locale('keybind.toggle_lamp'), diff --git a/config/client.lua b/config/client.lua index 6b5c309..b456bae 100644 --- a/config/client.lua +++ b/config/client.lua @@ -36,9 +36,23 @@ return { --- ruhige Anzeige ohne unnötigen NUI-Traffic. hudUpdateIntervalMs = 250, + --- Tauch-HUD mit ausblenden, sobald das Spiel-HUD ausgeblendet ist. + --- Greift bei allem, was DisplayHud(false) setzt - Screenshot-Scripts, + --- HUD-Toggles, Cinematic-Modes -, ohne dass wir die Resource kennen müssen. + hudFollowGameHud = true, + + --- Eigene Taste zum Aus-/Einblenden des Tauch-HUD. Leer = keine Vorbelegung, + --- der Spieler kann sie in den FiveM-Tastenbelegungen unter "FiveM" setzen. + --- Zusätzlich gibt es den Export setHudVisible(bool) für andere Resources. + hudToggleKey = '', + --- Ab dieser Tiefe wird die Tiefenanzeige eingefärbt (Meter). deepWarningDepth = 25.0, + --- Reservedruck in bar. Darunter wird die Druckanzeige eingefärbt - in der + --- Tauchpraxis die Schwelle, ab der man den Aufstieg einleitet. + reservePressure = 50, + --------------------------------------------------------------------------- -- Taschenlampe --------------------------------------------------------------------------- diff --git a/config/shared.lua b/config/shared.lua index fee32fd..aa1f1b8 100644 --- a/config/shared.lua +++ b/config/shared.lua @@ -2,13 +2,19 @@ -- damit lib.require die Datei auf beiden Seiten laden kann. local config = { - --- Tauchflaschen. capacity = Laufzeit in Sekunden bei normalem Verbrauch. + --- Tauchflaschen. + --- capacity = Laufzeit in Sekunden bei normalem Verbrauch. + --- pressure = Fülldruck in bar. Reine Anzeige: der angezeigte Druck ist + --- pressure * (Restluft / capacity). Bei konstantem Volumen ist der + --- Druck proportional zur verbliebenen Gasmenge, das passt also. + --- Dass alle Flaschen denselben Fülldruck haben ist gewollt - in echt + --- macht das Volumen die Laufzeit, nicht der Druck. --- Reihenfolge = Reihenfolge im Auswahlmenü. tanks = { - { name = 'diving_tank_small', capacity = 300 }, -- 5 Minuten - { name = 'diving_tank_medium', capacity = 600 }, -- 10 Minuten - { name = 'diving_tank_large', capacity = 900 }, -- 15 Minuten - { name = 'diving_tank_xl', capacity = 1200 }, -- 20 Minuten + { name = 'diving_tank_small', capacity = 300, pressure = 300 }, -- 5 Minuten + { name = 'diving_tank_medium', capacity = 600, pressure = 300 }, -- 10 Minuten + { name = 'diving_tank_large', capacity = 900, pressure = 300 }, -- 15 Minuten + { name = 'diving_tank_xl', capacity = 1200, pressure = 300 }, -- 20 Minuten }, --- Wie oft der Client die Restluft an den Server meldet (Sekunden). diff --git a/items_for_ox_inventory.lua b/items_for_ox_inventory.lua index 199c7c4..f4121c2 100644 --- a/items_for_ox_inventory.lua +++ b/items_for_ox_inventory.lua @@ -5,8 +5,9 @@ -- (modules/items/server.lua) - die leere Flasche würde also verschwinden statt -- auffüllbar zu bleiben. -- --- Die Laufzeiten stehen in config/shared.lua (capacity in Sekunden). Wer die dort --- ändert, sollte die Labels hier mitziehen. +-- Laufzeit (capacity, Sekunden) und Fülldruck (pressure, bar) stehen in +-- config/shared.lua. Wer die dort ändert, sollte Labels und Beschreibungen hier +-- mitziehen. ['diving_gear'] = { label = 'Tauchausrüstung', @@ -34,7 +35,7 @@ stack = false, close = true, consume = 0, - description = 'Kleine Tauchflasche. Reicht für etwa 5 Minuten.', + description = 'Kleine Tauchflasche, 300 bar. Reicht für etwa 5 Minuten.', client = { image = 'diving_tank_small.png' } }, @@ -44,7 +45,7 @@ stack = false, close = true, consume = 0, - description = 'Mittlere Tauchflasche. Reicht für etwa 10 Minuten.', + description = 'Mittlere Tauchflasche, 300 bar. Reicht für etwa 10 Minuten.', client = { image = 'diving_tank_medium.png' } }, @@ -54,7 +55,7 @@ stack = false, close = true, consume = 0, - description = 'Große Tauchflasche. Reicht für etwa 15 Minuten.', + description = 'Große Tauchflasche, 300 bar. Reicht für etwa 15 Minuten.', client = { image = 'diving_tank_large.png' } }, @@ -64,6 +65,6 @@ stack = false, close = true, consume = 0, - description = 'Doppelflasche für lange Tauchgänge. Reicht für etwa 20 Minuten.', + description = 'Doppelflasche, 300 bar. Reicht für etwa 20 Minuten.', client = { image = 'diving_tank_xl.png' } }, diff --git a/locales/de.json b/locales/de.json index 134e009..f1b8fc2 100644 --- a/locales/de.json +++ b/locales/de.json @@ -25,9 +25,11 @@ "hud": { "air": "Luft", "depth": "Tiefe", + "pressure": "Druck", "lamp": "Lampe" }, "keybind": { - "toggle_lamp": "Tauchlampe an/aus" + "toggle_lamp": "Tauchlampe an/aus", + "toggle_hud": "Tauch-HUD ein-/ausblenden" } } diff --git a/locales/en.json b/locales/en.json index 6b48969..34d1c34 100644 --- a/locales/en.json +++ b/locales/en.json @@ -25,9 +25,11 @@ "hud": { "air": "Air", "depth": "Depth", + "pressure": "Pressure", "lamp": "Lamp" }, "keybind": { - "toggle_lamp": "Toggle dive lamp" + "toggle_lamp": "Toggle dive lamp", + "toggle_hud": "Toggle dive HUD" } } diff --git a/nui/index.html b/nui/index.html index cdb012f..116194d 100644 --- a/nui/index.html +++ b/nui/index.html @@ -18,9 +18,16 @@
-
- TIEFE -
0.0m
+
+
+ TIEFE +
0.0m
+
+ +
+ DRUCK +
0bar
+
diff --git a/nui/script.js b/nui/script.js index cb7ff52..fc7c86a 100644 --- a/nui/script.js +++ b/nui/script.js @@ -8,11 +8,14 @@ const hud = document.getElementById('hud'); const gaugeFill = document.getElementById('gauge-fill'); const airValue = document.getElementById('air-value'); const depthValue = document.getElementById('depth-value'); +const pressure = document.getElementById('pressure'); +const pressureValue = document.getElementById('pressure-value'); const lamp = document.getElementById('lamp'); const labels = { air: document.getElementById('air-label'), depth: document.getElementById('depth-label'), + pressure: document.getElementById('pressure-label'), lamp: document.getElementById('lamp-label') }; @@ -34,6 +37,8 @@ function render(state) { gaugeFill.style.strokeDashoffset = String(RING * (1 - ratio)); airValue.textContent = formatTime(state.oxygen); depthValue.textContent = state.depth.toFixed(1); + pressureValue.textContent = String(Math.round(state.pressure ?? 0)); + pressure.classList.toggle('reserve', !!state.reserve); // Warnstufen kommen als Restsekunden vom Client, damit sie zu // config.warnAtSeconds passen statt hier nochmal definiert zu werden. @@ -53,6 +58,7 @@ window.addEventListener('message', (event) => { if (data.action === 'labels') { labels.air.textContent = data.air; labels.depth.textContent = data.depth; + labels.pressure.textContent = data.pressure; labels.lamp.textContent = data.lamp; return; } @@ -62,5 +68,8 @@ window.addEventListener('message', (event) => { // Damit sich das HUD im Browser ohne FiveM anschauen laesst. if (typeof GetParentResourceName !== 'function') { - render({ visible: true, oxygen: 372, capacity: 600, depth: 12.4, level: 'ok', hasLamp: true, lamp: true }); + render({ + visible: true, oxygen: 372, capacity: 600, depth: 12.4, pressure: 186, + level: 'ok', hasLamp: true, lamp: true + }); } diff --git a/nui/style.css b/nui/style.css index 969e01f..0524a03 100644 --- a/nui/style.css +++ b/nui/style.css @@ -111,13 +111,18 @@ body { display: flex; flex-direction: column; gap: 10px; - min-width: 92px; +} + +.readout-row { + display: flex; + gap: 18px; } .readout { display: flex; flex-direction: column; gap: 3px; + min-width: 76px; } .value { @@ -140,6 +145,7 @@ body { .lamp { display: flex; align-items: center; + align-self: flex-start; /* sonst zieht der Flex-Container die Leiste auf volle Breite */ gap: 6px; padding: 5px 9px; border-radius: 8px; @@ -176,7 +182,10 @@ body { #hud.danger #air-value { color: var(--danger); } #hud.danger .gauge { animation: pulse 1s ease-in-out infinite; } -#hud.deep .value { color: var(--warn); } +#hud.deep #depth-value { color: var(--warn); } + +/* Reservedruck: in der Tauchpraxis die Schwelle zum Aufstieg. */ +#pressure.reserve .value { color: var(--danger); } @keyframes pulse { 0%, 100% { opacity: 1; } diff --git a/server/main.lua b/server/main.lua index ff63a24..381369a 100644 --- a/server/main.lua +++ b/server/main.lua @@ -67,7 +67,8 @@ local function getTankList(source) name = name, label = (exports.ox_inventory:Items(name) or {}).label or name, oxygen = math.max(0, math.min(oxygen, tankConfig.capacity)), - capacity = tankConfig.capacity + capacity = tankConfig.capacity, + pressure = tankConfig.pressure } end end