From fcca73bc7a863a9d35d78ec0933b7c506071dc82 Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Tue, 11 Aug 2026 10:04:08 +0200 Subject: [PATCH] fix: weggegebene Flasche beendet den Tauchgang + HUD-Feinschliff Bug: wer die getragene Flasche im Tauchgang weggab, konnte unbegrenzt weiteratmen. Der Client hielt nur eine Slot-Nummer, die niemand mehr gegenpruefte, und der Sync lief ins Leere. ox_inventory feuert clientseitig bei jeder Inventar-Aenderung - dort wird jetzt beim Server nachgefragt. Ist die Flasche weg, kommt die Ausruestung ab; wurde sie nur umsortiert, bindet der Client auf den neuen Slot um, damit Aufraeumen im Inventar nichts kostet. HUD: - Werte aus den Uhren heraus in eine zweite Zeile: Druck, Lampe und Tiefe liegen jetzt auf einer Linie. In der Skala kollidierte die Zahl mit Strichen, Zeiger und rotem Bereich - Skaliert mit der Bildschirmhoehe (entworfen auf 1080p), sonst waere das in festen Pixeln gebaute HUD auf 1440p und 4K deutlich zu klein. config.hudScale kommt als Geschmacksregler obendrauf - Zustandsklassen sitzen jetzt am HUD statt an den Uhren, weil Instrument und Wert in getrennten Grid-Zellen liegen README-Renderings nachgezogen. Co-Authored-By: Claude Opus 5 --- README.md | 5 ++- client/main.lua | 25 ++++++++++++ config/client.lua | 5 +++ docs/gen-hud-svg.mjs | 38 +++++++++--------- docs/hud-kritisch.svg | 30 ++++++++------ docs/hud-normal.svg | 30 ++++++++------ docs/hud-warnung.svg | 30 ++++++++------ locales/de.json | 3 +- locales/en.json | 3 +- nui/index.html | 60 +++++++++++++++------------- nui/script.js | 27 ++++++++++--- nui/style.css | 93 +++++++++++++++++++++---------------------- server/main.lua | 16 ++++++++ 13 files changed, 223 insertions(+), 142 deletions(-) diff --git a/README.md b/README.md index 0a64441..a553fd8 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ die bei Animationen nicht mehr verschwindet. > `nui/style.css` — keine In-Game-Aufnahmen. Erzeugt mit `docs/gen-hud-svg.mjs`. Eine Dreier-Konsole wie am echten Tauchgerät: **Finimeter** links, Restluft als -Ring in der Mitte, **Tiefenmesser** rechts. +Ring in der Mitte, **Tiefenmesser** rechts; die Werte darunter auf einer Linie. +Das HUD ist auf 1080p entworfen und skaliert mit der Bildschirmhöhe mit, damit es +auf 1440p und 4K nicht schrumpft. **Normal** — Druck im grünen Bereich, flach getaucht, Lampe an @@ -187,6 +189,7 @@ auf z.B. `2`. |---|---|---| | `hudEnabled` | `true` | Ohne HUD bleibt die Ausrüstung voll nutzbar | | `hudUpdateIntervalMs` | `250` | 4 Updates pro Sekunde | +| `hudScale` | `1.0` | Größenfaktor obendrauf. Das HUD skaliert schon selbst mit der Bildschirmhöhe | | `hudOnlyUnderwater` | `false` | Auf `false` schon sichtbar, sobald die Ausrüstung an ist — Finimeter ablesen ohne Inventar | | `hudFollowGameHud` | `true` | Mit dem Spiel-HUD ausblenden | | `hudToggleKey` | `''` | Eigene Taste, leer = unbelegt | diff --git a/client/main.lua b/client/main.lua index 8491568..df3d29c 100644 --- a/client/main.lua +++ b/client/main.lua @@ -427,6 +427,7 @@ local function startHudThread() SendNUIMessage({ action = 'labels', + scale = config.hudScale, air = locale('hud.air'), depth = locale('hud.depth'), pressure = locale('hud.pressure'), @@ -740,6 +741,30 @@ lib.onCache('ped', function(ped) end end) +--- Die Flasche kann während des Tauchgangs das Inventar verlassen - weggegeben, +--- abgelegt, vom Admin entfernt. Ohne diese Prüfung hielt der Client nur eine +--- Slot-Nummer, die niemand mehr gegenprüfte, und man konnte unbegrenzt +--- weitertauchen. ox_inventory feuert bei jeder Änderung, also einmal nachfragen. +AddEventHandler('ox_inventory:updateInventory', function() + if not currentGear.enabled or not activeTank then return end + + CreateThread(function() + local expected = activeTank + local slot = lib.callback.await('d4rk_divegear:server:verifyTank', false, expected.slot, expected.name) + + -- Während des Callbacks kann die Ausrüstung schon abgelegt worden sein. + if not currentGear.enabled or activeTank ~= expected then return end + + if not slot then + exports.qbx_core:Notify(locale('error.tank_gone'), 'error') + removeGear(true) + return + end + + activeTank.slot = slot + end) +end) + AddEventHandler('onResourceStop', function(resource) if resource ~= GetCurrentResourceName() then return end diff --git a/config/client.lua b/config/client.lua index feed1f5..3e7a0b6 100644 --- a/config/client.lua +++ b/config/client.lua @@ -41,6 +41,11 @@ return { --- ruhige Anzeige ohne unnötigen NUI-Traffic. hudUpdateIntervalMs = 250, + --- Zusätzlicher Größenfaktor für das HUD. Es skaliert schon von selbst mit + --- der Bildschirmhöhe (entworfen auf 1080p), das hier ist der Geschmacks- + --- regler obendrauf. 1.2 = 20 % größer. + hudScale = 1.0, + --- HUD nur unter Wasser zeigen. Auf false ist es sichtbar, sobald die --- Ausrüstung angelegt ist - dann lässt sich der Flaschendruck am Finimeter --- ablesen, ohne extra das Inventar zu öffnen. diff --git a/docs/gen-hud-svg.mjs b/docs/gen-hud-svg.mjs index 7ceb528..90a619d 100644 --- a/docs/gen-hud-svg.mjs +++ b/docs/gen-hud-svg.mjs @@ -25,9 +25,9 @@ const F = "'Segoe UI',Roboto,system-ui,sans-serif"; // Masse spiegeln das Grid aus style.css: padding 14/20, column-gap 20, // Uhr 96, Ring 92, Beschriftung darunter, Lampe in Zeile 2. const PAD_X = 20, PAD_Y = 14, DIAL = 96, GAUGE = 92, GAP = 20; -const ROW1 = 112, ROW_GAP = 6, LAMP_H = 23; +const ROW1 = 96, ROW_GAP = 6, ROW2 = 32, LAMP_H = 23; const PW = PAD_X * 2 + DIAL * 2 + GAUGE + GAP * 2; -const PH = PAD_Y * 2 + ROW1 + ROW_GAP + LAMP_H; +const PH = PAD_Y * 2 + ROW1 + ROW_GAP + ROW2; const W = 560, H = PH + 70; const PX = (W - PW) / 2, PY = (H - PH) / 2; @@ -36,7 +36,8 @@ const LEFT_CX = PX + PAD_X + DIAL / 2; const RIGHT_CX = PX + PW - PAD_X - DIAL / 2; const MID_CX = PX + PW / 2; const DIAL_CY = PY + PAD_Y + DIAL / 2; -const GAUGE_CY = PY + PAD_Y + ROW1 / 2; +const GAUGE_CY = PY + PAD_Y + 2 + GAUGE / 2; +const ROW2_Y = PY + PAD_Y + ROW1 + ROW_GAP; // Werte und Lampe auf einer Linie const n = (v) => Number(v.toFixed(2)); const rad = (d) => ((d - 90) * Math.PI) / 180; @@ -45,7 +46,7 @@ const pt = (cx, cy, r, d) => [cx + r * Math.cos(rad(d)), cy + r * Math.sin(rad(d const fmt = (s) => `${String(Math.floor(s / 60)).padStart(2, '0')}:${String(Math.floor(s % 60)).padStart(2, '0')}`; /** Zeigerinstrument. 270-Grad-Skala, unten offen - wie ein echtes Finimeter. */ -function dial(cx, cy, { value, max, unit, label, zoneFrom, zoneTo, needleColour, valueColour, decimals = 0 }) { +function dial(cx, cy, { value, max, zoneFrom, zoneTo, needleColour }) { // 96px Uhr, Geometrie aus dem viewBox 0 0 100 100 skaliert const k = DIAL / 100; const R = 42 * k, A0 = -135, SWEEP = 270; @@ -72,12 +73,15 @@ function dial(cx, cy, { value, max, unit, label, zoneFrom, zoneTo, needleColour, s += ``; s += ``; - s += `${value.toFixed(decimals)}${unit}`; - s += `${label}`; - return s; } +/** Beschriftung und Wert unter der Uhr - eine Zeile mit der Lampe. */ +function readout(cx, label, value, unit, colour) { + return `${label} + ${value}${unit}`; +} + function build({ file, title, oxygen, capacity, level, depth, depthLimit, pressure, reservePressure, lamp, deep }) { const ratio = Math.max(0, Math.min(1, oxygen / capacity)); const ringColour = level === 'danger' ? C.danger : level === 'warn' ? C.warn : C.accent; @@ -101,11 +105,8 @@ function build({ file, title, oxygen, capacity, level, depth, depthLimit, pressu - ${dial(LEFT_CX, DIAL_CY, { - value: pressure, max: 300, unit: 'bar', label: 'DRUCK', - zoneFrom: 0, zoneTo: reservePressure, - needleColour: pressureColour, valueColour: pressureColour - })} + ${dial(LEFT_CX, DIAL_CY, { value: pressure, max: 300, zoneFrom: 0, zoneTo: reservePressure, needleColour: pressureColour })} + ${readout(LEFT_CX, 'DRUCK', Math.round(pressure), 'bar', pressureColour)} @@ -114,15 +115,12 @@ function build({ file, title, oxygen, capacity, level, depth, depthLimit, pressu ${fmt(oxygen)} LUFT - ${dial(RIGHT_CX, DIAL_CY, { - value: depth, max: 100, unit: 'm', label: 'TIEFE', decimals: 1, - zoneFrom: depthLimit, zoneTo: 100, - needleColour: depthColour, valueColour: depthColour - })} + ${dial(RIGHT_CX, DIAL_CY, { value: depth, max: 100, zoneFrom: depthLimit, zoneTo: 100, needleColour: depthColour })} + ${readout(RIGHT_CX, 'TIEFE', depth.toFixed(1), 'm', depthColour)} - - - LAMPE + + + LAMPE `; diff --git a/docs/hud-kritisch.svg b/docs/hud-kritisch.svg index 7808dcb..91fb227 100644 --- a/docs/hud-kritisch.svg +++ b/docs/hud-kritisch.svg @@ -1,4 +1,4 @@ - + @@ -6,21 +6,25 @@ - - + + - 13barDRUCK + + DRUCK + 13bar - - - + + + - 00:38 - LUFT + 00:38 + LUFT - 52.4mTIEFE + + TIEFE + 52.4m - - - LAMPE + + + LAMPE diff --git a/docs/hud-normal.svg b/docs/hud-normal.svg index 5273f6f..244c596 100644 --- a/docs/hud-normal.svg +++ b/docs/hud-normal.svg @@ -1,4 +1,4 @@ - + @@ -6,21 +6,25 @@ - - + + - 136barDRUCK + + DRUCK + 136bar - - - + + + - 09:05 - LUFT + 09:05 + LUFT - 12.4mTIEFE + + TIEFE + 12.4m - - - LAMPE + + + LAMPE diff --git a/docs/hud-warnung.svg b/docs/hud-warnung.svg index ca5c262..5fd49ab 100644 --- a/docs/hud-warnung.svg +++ b/docs/hud-warnung.svg @@ -1,4 +1,4 @@ - + @@ -6,21 +6,25 @@ - - + + - 31barDRUCK + + DRUCK + 31bar - - - + + + - 01:32 - LUFT + 01:32 + LUFT - 27.5mTIEFE + + TIEFE + 27.5m - - - LAMPE + + + LAMPE diff --git a/locales/de.json b/locales/de.json index 9c9dd67..f90bd9a 100644 --- a/locales/de.json +++ b/locales/de.json @@ -8,7 +8,8 @@ "tank_already_full": "Diese Tauchflasche ist bereits voll!", "out_of_air": "Deine Luft ist alle! Sofort auftauchen!", "no_lamp_on_outfit": "Deine Tauchausrüstung hat keine Lampe.", - "crush_depth": "Lebensgefahr! Ab %d m zerdrückt dich der Wasserdruck." + "crush_depth": "Lebensgefahr! Ab %d m zerdrückt dich der Wasserdruck.", + "tank_gone": "Deine Tauchflasche ist weg!" }, "success": { "took_out": "Du hast deine Ausrüstung abgelegt!", diff --git a/locales/en.json b/locales/en.json index fc88375..24f385f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -8,7 +8,8 @@ "tank_already_full": "This air tank is already full!", "out_of_air": "You're out of air! Surface now!", "no_lamp_on_outfit": "Your diving suit doesn't have a lamp.", - "crush_depth": "Danger! Below %d m the water pressure will crush you." + "crush_depth": "Danger! Below %d m the water pressure will crush you.", + "tank_gone": "Your air tank is gone!" }, "success": { "took_out": "You've taken your gear off!", diff --git a/nui/index.html b/nui/index.html index be92647..1870982 100644 --- a/nui/index.html +++ b/nui/index.html @@ -5,22 +5,20 @@ +