docs: vollstaendige README mit HUD-Renderings, Luft-Ring mittig
Lint / Lint Resource (push) Has been cancelled

HUD-Layout symmetrisch: Tiefe links, Luft-Ring mittig, Druck rechts, Lampe
zentriert darunter. Umgestellt auf Grid, damit die drei Werte garantiert auf
einer Mittelachse sitzen und die Lampen-Leiste ohne Ausgleichs-Margins in einer
zweiten Zeile haengt.

README:
- Drei HUD-Renderings (normal, Warnung, kritisch) als SVG. Keine In-Game-Bilder -
  hier laeuft kein GTA -, sondern massstabsgetreu aus denselben Werten wie
  style.css erzeugt, per docs/gen-hud-svg.mjs reproduzierbar
- Vollstaendige Config-Referenz fuer client.lua und shared.lua
- Item-Tabelle mit Laufzeit, Fuelldruck, Gewicht, dazu die Metadata-Keys und
  warum kein decay gesetzt werden darf
- Installation, Bedienung, Exports, Balancing-Hinweis
- Bekannte Unsicherheiten offen benannt

Item-Snippet mit klarem Copy-Paste-Header. Bewusst keine fertige items.lua zum
Ersetzen: die wuerde die restlichen Items des Servers mitloeschen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 00:13:06 +02:00
co-authored by Claude Opus 5
parent 5a59fc513f
commit ce0f693266
8 changed files with 445 additions and 77 deletions
+180 -41
View File
@@ -1,36 +1,97 @@
# d4rk_divegear # d4rk_divegear
Tauchausrüstung für QBox. Fork von [qbx_divegear](https://github.com/Qbox-project/qbx_divegear) Tauchausrüstung für QBox. Fork von [qbx_divegear](https://github.com/Qbox-project/qbx_divegear)
mit Flaschengrößen, gesyncter Lampe, Tiefenanzeige und Ausrüstung, die bei mit Flaschengrößen, gesyncter Lampe, Tiefen- und Druckanzeige und Ausrüstung,
Animationen nicht verschwindet. die bei Animationen nicht mehr verschwindet.
## HUD
> Die Bilder sind maßstabsgetreue Renderings des Overlays aus denselben Werten wie
> `nui/style.css` — keine In-Game-Aufnahmen. Erzeugt mit `docs/gen-hud-svg.mjs`.
**Normal** — Luft, Tiefe, Fülldruck, Lampe an
![HUD, normaler Zustand](docs/hud-normal.svg)
**Warnung** — Restluft unter der ersten Schwelle, Tiefe über `deepWarningDepth`,
Druck unter `reservePressure`
![HUD, Warnstufe](docs/hud-warnung.svg)
**Kritisch** — letzte Sekunden, der Ring pulsiert
![HUD, kritischer Luftstand](docs/hud-kritisch.svg)
Das HUD erscheint nur mit angelegter Ausrüstung unter Wasser und blendet sich
weich ein und aus.
## Features ## Features
- **Vier Flaschengrößen** — 5, 10, 15 und 20 Minuten. Die Restluft liegt in der - **Vier Flaschengrößen** — 5, 10, 15 und 20 Minuten. Die Restluft liegt in der
Item-Metadata und überlebt Ablegen, Relog, Resource-Restart und Tod. Der Item-Metadata und überlebt Ablegen, Relog, Resource-Restart und Tod. Der
Füllstand ist als Balken direkt im Inventar-Slot sichtbar. Füllstand ist als Balken direkt im Inventar-Slot sichtbar.
- **Gesyncte Tauchlampe** — an der Ausrüstung, An/Aus per Taste (Standard `L`). - **Manometer in bar** — physikalisch korrekt aus dem Restanteil abgeleitet,
Andere Spieler sehen den Lichtkegel. Reservedruck wird eingefärbt.
- **NUI-HUD** — Restluft als Ring mit `mm:ss`, Tauchtiefe in Metern und - **Gesyncte Tauchlampe** — an der Ausrüstung, An/Aus per Taste. Andere Spieler
Manometer in bar, mit Warnstufen. Nur sichtbar mit angelegter Ausrüstung sehen den Lichtkegel, und zwar in die Richtung, in die tatsächlich geschaut wird.
unter Wasser, und ausblendbar für Unterwasser-Screenshots. - **Tiefenanzeige** und **tiefenabhängiger Verbrauch** — tiefer tauchen kostet
- **Tiefenabhängiger Verbrauch** — tiefer tauchen kostet mehr Luft. Abschaltbar. mehr Luft. Abschaltbar.
- **Ausrüstung bleibt** — bei Emotes, Ragdoll, Kleiderwechsel und aufräumenden - **Ausrüstung bleibt** — bei Emotes, Ragdoll, Kleiderwechsel und aufräumenden
Fremd-Scripts. Fremd-Scripts.
- **HUD ausblendbar** für Unterwasser-Screenshots.
- **Serverseitige Prüfung** — die Restluft kann nur sinken, und nicht schneller - **Serverseitige Prüfung** — die Restluft kann nur sinken, und nicht schneller
als physikalisch möglich. als physikalisch möglich.
## Abhängigkeiten ## Abhängigkeiten
`qbx_core` · `ox_lib` · `ox_inventory` [`qbx_core`](https://github.com/Qbox-project/qbx_core) ·
[`ox_lib`](https://github.com/overextended/ox_lib) ·
[`ox_inventory`](https://github.com/overextended/ox_inventory)
## Installation ## Installation
1. Resource nach `resources/` und in der `server.cfg` starten. 1. Ordner nach `resources/` und in der `server.cfg` starten**nach**
2. Die Einträge aus [`items_for_ox_inventory.lua`](items_for_ox_inventory.lua) in `ox_inventory` und `qbx_core`:
`ox_inventory/data/items.lua` kopieren.
3. Item-Icons nach `cdn.d4rkst3r.de/items/` legen (`diving_gear.png`, ```cfg
`diving_fill.png`, `diving_tank_small.png``diving_tank_xl.png`). ensure d4rk_divegear
```
2. [`items_for_ox_inventory.lua`](items_for_ox_inventory.lua) komplett markieren
und in `ox_inventory/data/items.lua` innerhalb der `return { … }`-Klammer
einfügen. Sechs Items, nichts weiter anzupassen.
3. Icons nach `cdn.d4rkst3r.de/items/` legen: `diving_gear.png`,
`diving_fill.png`, `diving_tank_small.png`, `diving_tank_medium.png`,
`diving_tank_large.png`, `diving_tank_xl.png`.
Es gibt bewusst **keine** fertige `items.lua` zum Ersetzen — die würde die
restlichen Items des Servers überschreiben.
## Items
| Item | Label | Laufzeit | Fülldruck | Gewicht |
|---|---|---|---|---|
| `diving_gear` | Tauchausrüstung | — | — | 5000 |
| `diving_fill` | Pressluft-Kartusche | — | — | 1000 |
| `diving_tank_small` | Tauchflasche (5 Min) | 300 s | 300 bar | 4000 |
| `diving_tank_medium` | Tauchflasche (10 Min) | 600 s | 300 bar | 7000 |
| `diving_tank_large` | Tauchflasche (15 Min) | 900 s | 300 bar | 10000 |
| `diving_tank_xl` | Tauchflasche (20 Min) | 1200 s | 300 bar | 13000 |
Dass alle Flaschen mit demselben Fülldruck starten, ist gewollt: in echt macht das
Volumen die Laufzeit, nicht der Druck. Die große Flasche hält 300 bar einfach länger.
### Metadata
| Key | Bedeutung |
|---|---|
| `oxygen` | Restluft in Sekunden — die eigentliche Wahrheit |
| `durability` | Füllstand in Prozent, nur für den Balken im Inventar-Slot |
**Kein `decay = true` auf den Flaschen.** ox_inventory löscht Items mit `decay`,
sobald `durability` 0 erreicht — die leere Flasche würde also verschwinden statt
auffüllbar zu bleiben.
## Bedienung ## Bedienung
@@ -38,46 +99,124 @@ Animationen nicht verschwindet.
|---|---| |---|---|
| Ausrüstung an-/ausziehen | `diving_gear` benutzen | | Ausrüstung an-/ausziehen | `diving_gear` benutzen |
| Flasche wählen | Menü beim Anlegen (entfällt bei nur einer Flasche) | | Flasche wählen | Menü beim Anlegen (entfällt bei nur einer Flasche) |
| Flasche auffüllen | `diving_fill` benutzen, nicht unter Wasser | | Flasche auffüllen | `diving_fill` benutzen nicht unter Wasser, nicht bei voller Flasche |
| Lampe an/aus | `L` (in den FiveM-Tastenbelegungen umlegbar) | | Lampe an/aus | `L` |
| HUD ausblenden | Eigene Taste, standardmäßig unbelegt — in den FiveM-Tastenbelegungen unter "FiveM" setzen | | HUD ein-/ausblenden | Eigene Taste, standardmäßig unbelegt |
### HUD ausblenden für Screenshots Beide Tasten sind vom Spieler in den FiveM-Einstellungen unter *Tastenbelegung →
FiveM* umlegbar.
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 ## Konfiguration
| Datei | Inhalt | ### `config/shared.lua` — Client und Server
|---|---|
| [`config/client.lua`](config/client.lua) | Timings, Verbrauch, Tiefe, HUD, Lampe |
| [`config/shared.lua`](config/shared.lua) | Flaschen und ihre Laufzeit, Sync-Intervall, Anti-Cheat-Grenze |
Laufzeiten ändert man über `capacity` (Sekunden) in `config/shared.lua`. Wer den | Option | Default | Bedeutung |
Tiefenfaktor (`maxDecayFactor`) hochdreht, muss `maxDrainPerSecond` mitziehen — |---|---|---|
sonst schreibt der Server ehrlichen Spielern in großer Tiefe Luft zurück. | `tanks` | 4 Flaschen | `name`, `capacity` (Sekunden), `pressure` (bar) |
| `syncIntervalSeconds` | `15` | Wie oft die Restluft an den Server gemeldet wird. Bei einem Crash gehen maximal so viele Sekunden verloren |
| `maxDrainPerSecond` | `5.0` | Obergrenze der serverseitigen Prüfung |
### `config/client.lua`
**Ablauf**
| Option | Default | Bedeutung |
|---|---|---|
| `putOnSuitTimeMs` / `takeOffSuitTimeMs` / `refillTankTimeMs` | `5000` | Dauer der Progressbars |
| `decayRate` | `1.0` | Sekunden Luft pro echter Sekunde, vor dem Tiefenfaktor |
| `warnAtSeconds` | `{60, 30, 10}` | Warnschwellen, jede einmal pro Tauchgang |
| `removeGearOnDeath` | `true` | Ausrüstung beim Tod abnehmen. Die Flasche bleibt mit ihrer Restluft im Inventar |
| `gearWatchdogIntervalMs` | `1000` | Prüfintervall, ob die Props noch am Ped hängen |
| `maxTimeUnderwater` | `50.0` | Sekunden unter Wasser ohne Ausrüstung (Vanilla-Verhalten) |
| `maxTimeUnderwaterOutOfAir` | `1.0` | Sekunden, sobald die Flasche leer ist |
**Tiefe**
| Option | Default | Bedeutung |
|---|---|---|
| `depthDecayEnabled` | `true` | Verbrauch steigt mit der Tiefe |
| `depthDecayReference` | `30.0` | Faktor `1 + tiefe / referenz` |
| `maxDecayFactor` | `4.0` | Deckel für den Faktor |
| `deepWarningDepth` | `25.0` | Ab hier wird die Tiefenanzeige eingefärbt |
**HUD**
| Option | Default | Bedeutung |
|---|---|---|
| `hudEnabled` | `true` | Ohne HUD bleibt die Ausrüstung voll nutzbar |
| `hudUpdateIntervalMs` | `250` | 4 Updates pro Sekunde |
| `hudFollowGameHud` | `true` | Mit dem Spiel-HUD ausblenden |
| `hudToggleKey` | `''` | Eigene Taste, leer = unbelegt |
| `reservePressure` | `50` | Ab hier wird die Druckanzeige rot |
**Lampe**
| Option | Default | Bedeutung |
|---|---|---|
| `lampKey` | `'l'` | Vorbelegung |
| `lampProp` | `'prop_cs_police_torch'` | Optik. Ungültiges Model → Warnung im Log, Licht funktioniert trotzdem |
| `lampOffset` | `vec3(0, 0.15, 0)` | Position relativ zum Kopf-Bone |
| `lampDirection` | `vec3(0, 1, 0)` | Richtung des Kegels, bone-relativ |
| `lampColour` | `{225, 245, 255}` | RGB |
| `lampDistance` / `lampBrightness` / `lampRoundness` / `lampRadius` / `lampFalloff` | `30/6/1/14/1.5` | Form des Lichtkegels |
| `lampShadows` | `true` | `false` nutzt `DrawSpotLight` statt der Schattenvariante — flacher, aber spürbar schneller |
| `lampDrawDistance` | `45.0` | Ab hier werden fremde Lampen nicht mehr gezeichnet |
| `lampMaxLights` | `4` | Gleichzeitige Lampen, die nächstgelegenen gewinnen |
> **Beim Balancing beachten:** `maxDrainPerSecond` (shared) muss über
> `maxDecayFactor` (client) liegen. Sonst hält der Server den ehrlichen Verbrauch
> in großer Tiefe für Cheat und schreibt Luft zurück.
## Exports
```lua
exports.d4rk_divegear:setHudVisible(false) -- HUD ausblenden
local shown = exports.d4rk_divegear:isHudVisible()
```
Gedacht für HUD- und Screenshot-Resources. Zusätzlich folgt das HUD automatisch
`IsHudHidden()`, greift also bei allem was `DisplayHud(false)` setzt, ohne dass
etwas angebunden werden muss.
## Wie die Lampe synchron wird
Licht ist in GTA nie synchronisiert — `DrawSpotLight` zeichnet nur lokal für den
aktuellen Frame. Der Lampenzustand wird deshalb als Statebag repliziert
(`LocalPlayer.state:set('divelight', …, true)`), und jeder Client zeichnet die
Lampen aller Taucher in seiner Nähe selbst.
Die Richtung kommt aus **zwei bone-relativen Punkten** am Kopf-Bone, nicht aus
`GetEntityForwardVector`: beim Tauchen liegt der Ped waagerecht im Wasser, der
Entity-Forward würde stur horizontal leuchten. Die Offsets von `GetPedBoneCoords`
sind laut Native-Doku *"relative to the bone's rotation"*, die Differenz zweier
solcher Punkte enthält also den Pitch.
## Entwicklung ## Entwicklung
```bash ```bash
node ../.tools/lint.mjs # Lua-Lint (CfxLua-tauglich) node ../.tools/lint.mjs # Lua-Lint, CfxLua-tauglich
node ../.tools/nui-preview.mjs nui # HUD im Browser anschauen node ../.tools/nui-preview.mjs nui # HUD im Browser anschauen
node docs/gen-hud-svg.mjs docs # Renderings für diese README neu bauen
``` ```
Der Umbau ist in [PLAN.md](PLAN.md) dokumentiert, inklusive der offenen Punkte. Der Umbau ist in [PLAN.md](PLAN.md) dokumentiert, inklusive der offenen Punkte.
## Bekannte Unsicherheiten
Bisher **nicht im Spiel getestet** — geprüft sind Lua-Syntax und Globals, die
Locale-JSONs und das HUD im Browser.
- **`lampProp`** — der Prop-Name ist ungeprüft. Falls falsch: Warnung im Log, das
Licht funktioniert weiter, nur ohne sichtbares Prop. Ersatz finden:
[forge.plebmasters.de/objects](https://forge.plebmasters.de/objects)
- **`lampDirection`** — welche lokale Achse des Kopf-Bones nach vorne zeigt, hängt
am Skelett. Leuchtet der Kegel falsch herum: Achse tauschen oder Vorzeichen drehen.
- **`IsHudHidden()`** — ob die Native auf `DisplayHud(false)` anspringt, ist
ungetestet. Falls nicht, greifen Taste und Export weiterhin.
- Nach einem Relog muss die Ausrüstung neu angelegt werden. Die Restluft geht
dabei nicht verloren, die liegt auf der Flasche.
## Lizenz ## Lizenz
GPL-3.0, wie das Original. Upstream-Historie ist erhalten und liegt auf dem GPL-3.0, wie das Original. Die Upstream-Historie ist erhalten und liegt auf dem
Remote `upstream`. Remote `upstream`.
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env node
// Erzeugt die HUD-Renderings für die README aus denselben Werten wie nui/style.css.
// Keine In-Game-Screenshots - eine massstabsgetreue Nachbildung des Overlays.
//
// Nutzung: node docs/gen-hud-svg.mjs [ziel-ordner] (default: docs)
import { writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
const OUT = process.argv[2] ?? 'docs';
mkdirSync(OUT, { recursive: true });
const C = {
accent: '#38bdf8',
warn: '#fbbf24',
danger: '#f87171',
text: '#e2e8f0',
muted: '#94a3b8',
panel: '#09121c',
border: '#94a3b8'
};
// Geometrie spiegelt das Grid aus style.css:
// padding 14/22, Spalten 84 | 92 | 84 mit column-gap 22, row-gap 8, Lampe 23 hoch.
const PAD_X = 22, PAD_Y = 14, COL = 84, GAUGE = 92, GAP = 22, ROW_GAP = 8, LAMP_H = 23;
const PW = PAD_X * 2 + COL * 2 + GAUGE + GAP * 2;
const PH = PAD_Y * 2 + GAUGE + ROW_GAP + LAMP_H;
const W = 520, H = PH + 70;
const PX = (W - PW) / 2, PY = (H - PH) / 2;
const GX = PX + PAD_X + COL + GAP;
const CX = GX + GAUGE / 2, CY = PY + PAD_Y + GAUGE / 2;
const R = 52 * (GAUGE / 120); // r=52 im viewBox 120, auf 92px skaliert
const SW = 8 * (GAUGE / 120);
const CIRC = 2 * Math.PI * R;
const DEPTH_X = PX + PAD_X + COL; // rechtsbündig, zeigt zum Ring
const PRESS_X = GX + GAUGE + GAP; // linksbündig, zeigt zum Ring
const CAP_Y = PY + PAD_Y + 35;
const VAL_Y = PY + PAD_Y + 61;
const FONT = "'Segoe UI',Roboto,system-ui,sans-serif";
const fmt = (s) => `${String(Math.floor(s / 60)).padStart(2, '0')}:${String(Math.floor(s % 60)).padStart(2, '0')}`;
function readout(x, anchor, label, value, unit, colour) {
const unitTspan = `<tspan font-size="13" font-weight="500" fill="${C.muted}" dx="3">${unit}</tspan>`;
return `<text x="${x}" y="${CAP_Y}" text-anchor="${anchor}" font-family="${FONT}" font-size="9" font-weight="600" letter-spacing="1.4" fill="${C.muted}">${label}</text>
<text x="${x}" y="${VAL_Y}" text-anchor="${anchor}" font-family="${FONT}" font-size="26" font-weight="600" fill="${colour}">${value}${unitTspan}</text>`;
}
function build({ file, title, oxygen, capacity, depth, pressure, level, deep, reserve, lamp }) {
const ratio = Math.max(0, Math.min(1, oxygen / capacity));
const ring = level === 'danger' ? C.danger : level === 'warn' ? C.warn : C.accent;
const lampColour = lamp ? C.accent : C.muted;
const lampX = CX - 43;
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}" role="img" aria-label="${title}">
<defs>
<linearGradient id="water" x1="0" y1="0" x2="0.6" y2="1">
<stop offset="0%" stop-color="#0d3550"/>
<stop offset="100%" stop-color="#071a26"/>
</linearGradient>
</defs>
<rect width="${W}" height="${H}" fill="url(#water)"/>
<rect x="${PX}" y="${PY}" width="${PW}" height="${PH}" rx="16"
fill="${C.panel}" fill-opacity="0.72" stroke="${C.border}" stroke-opacity="0.18"/>
<g transform="rotate(-90 ${CX} ${CY})">
<circle cx="${CX}" cy="${CY}" r="${R.toFixed(2)}" fill="none" stroke="${C.muted}" stroke-opacity="0.16" stroke-width="${SW.toFixed(2)}" stroke-linecap="round"/>
<circle cx="${CX}" cy="${CY}" r="${R.toFixed(2)}" fill="none" stroke="${ring}" stroke-width="${SW.toFixed(2)}" stroke-linecap="round"
stroke-dasharray="${CIRC.toFixed(2)}" stroke-dashoffset="${(CIRC * (1 - ratio)).toFixed(2)}"/>
</g>
<text x="${CX}" y="${CY - 1}" text-anchor="middle" font-family="${FONT}" font-size="21" font-weight="600" fill="${ring}">${fmt(oxygen)}</text>
<text x="${CX}" y="${CY + 15}" text-anchor="middle" font-family="${FONT}" font-size="9" font-weight="600" letter-spacing="1.4" fill="${C.muted}">LUFT</text>
${readout(DEPTH_X, 'end', 'TIEFE', depth.toFixed(1), 'm', deep ? C.warn : C.text)}
${readout(PRESS_X, 'start', 'DRUCK', String(Math.round(pressure)), 'bar', reserve ? C.danger : C.text)}
<g>
<rect x="${lampX}" y="${PY + PAD_Y + GAUGE + ROW_GAP}" width="86" height="${LAMP_H}" rx="8" fill="${lampColour}" fill-opacity="${lamp ? 0.15 : 0.1}"/>
<circle cx="${lampX + 14}" cy="${PY + PAD_Y + GAUGE + ROW_GAP + LAMP_H / 2}" r="4" fill="${lampColour}"/>
<text x="${lampX + 24}" y="${PY + PAD_Y + GAUGE + ROW_GAP + LAMP_H / 2 + 3.5}" font-family="${FONT}" font-size="10" font-weight="600" letter-spacing="1.2" fill="${lampColour}">LAMPE</text>
</g>
</svg>
`;
writeFileSync(join(OUT, file), svg);
console.log(file);
}
build({
file: 'hud-normal.svg', title: 'Tauch-HUD: normaler Zustand',
oxygen: 545, capacity: 1200, depth: 12.4, pressure: 136, level: 'ok', lamp: true
});
build({
file: 'hud-warnung.svg', title: 'Tauch-HUD: Warnstufe und Reservedruck',
oxygen: 92, capacity: 900, depth: 27.5, pressure: 31,
level: 'warn', deep: true, reserve: true, lamp: false
});
build({
file: 'hud-kritisch.svg', title: 'Tauch-HUD: kritischer Luftstand',
oxygen: 8, capacity: 600, depth: 31.7, pressure: 4,
level: 'danger', deep: true, reserve: true, lamp: true
});
+33
View File
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 221" width="520" height="221" role="img" aria-label="Tauch-HUD: kritischer Luftstand">
<defs>
<linearGradient id="water" x1="0" y1="0" x2="0.6" y2="1">
<stop offset="0%" stop-color="#0d3550"/>
<stop offset="100%" stop-color="#071a26"/>
</linearGradient>
</defs>
<rect width="520" height="221" fill="url(#water)"/>
<rect x="86" y="35" width="348" height="151" rx="16"
fill="#09121c" fill-opacity="0.72" stroke="#94a3b8" stroke-opacity="0.18"/>
<g transform="rotate(-90 260 95)">
<circle cx="260" cy="95" r="39.87" fill="none" stroke="#94a3b8" stroke-opacity="0.16" stroke-width="6.13" stroke-linecap="round"/>
<circle cx="260" cy="95" r="39.87" fill="none" stroke="#f87171" stroke-width="6.13" stroke-linecap="round"
stroke-dasharray="250.49" stroke-dashoffset="247.15"/>
</g>
<text x="260" y="94" text-anchor="middle" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="21" font-weight="600" fill="#f87171">00:08</text>
<text x="260" y="110" text-anchor="middle" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">LUFT</text>
<text x="192" y="84" text-anchor="end" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">TIEFE</text>
<text x="192" y="110" text-anchor="end" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="26" font-weight="600" fill="#fbbf24">31.7<tspan font-size="13" font-weight="500" fill="#94a3b8" dx="3">m</tspan></text>
<text x="328" y="84" text-anchor="start" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">DRUCK</text>
<text x="328" y="110" text-anchor="start" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="26" font-weight="600" fill="#f87171">4<tspan font-size="13" font-weight="500" fill="#94a3b8" dx="3">bar</tspan></text>
<g>
<rect x="217" y="149" width="86" height="23" rx="8" fill="#38bdf8" fill-opacity="0.15"/>
<circle cx="231" cy="160.5" r="4" fill="#38bdf8"/>
<text x="241" y="164" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="10" font-weight="600" letter-spacing="1.2" fill="#38bdf8">LAMPE</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+33
View File
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 221" width="520" height="221" role="img" aria-label="Tauch-HUD: normaler Zustand">
<defs>
<linearGradient id="water" x1="0" y1="0" x2="0.6" y2="1">
<stop offset="0%" stop-color="#0d3550"/>
<stop offset="100%" stop-color="#071a26"/>
</linearGradient>
</defs>
<rect width="520" height="221" fill="url(#water)"/>
<rect x="86" y="35" width="348" height="151" rx="16"
fill="#09121c" fill-opacity="0.72" stroke="#94a3b8" stroke-opacity="0.18"/>
<g transform="rotate(-90 260 95)">
<circle cx="260" cy="95" r="39.87" fill="none" stroke="#94a3b8" stroke-opacity="0.16" stroke-width="6.13" stroke-linecap="round"/>
<circle cx="260" cy="95" r="39.87" fill="none" stroke="#38bdf8" stroke-width="6.13" stroke-linecap="round"
stroke-dasharray="250.49" stroke-dashoffset="136.73"/>
</g>
<text x="260" y="94" text-anchor="middle" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="21" font-weight="600" fill="#38bdf8">09:05</text>
<text x="260" y="110" text-anchor="middle" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">LUFT</text>
<text x="192" y="84" text-anchor="end" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">TIEFE</text>
<text x="192" y="110" text-anchor="end" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="26" font-weight="600" fill="#e2e8f0">12.4<tspan font-size="13" font-weight="500" fill="#94a3b8" dx="3">m</tspan></text>
<text x="328" y="84" text-anchor="start" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">DRUCK</text>
<text x="328" y="110" text-anchor="start" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="26" font-weight="600" fill="#e2e8f0">136<tspan font-size="13" font-weight="500" fill="#94a3b8" dx="3">bar</tspan></text>
<g>
<rect x="217" y="149" width="86" height="23" rx="8" fill="#38bdf8" fill-opacity="0.15"/>
<circle cx="231" cy="160.5" r="4" fill="#38bdf8"/>
<text x="241" y="164" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="10" font-weight="600" letter-spacing="1.2" fill="#38bdf8">LAMPE</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+33
View File
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 221" width="520" height="221" role="img" aria-label="Tauch-HUD: Warnstufe und Reservedruck">
<defs>
<linearGradient id="water" x1="0" y1="0" x2="0.6" y2="1">
<stop offset="0%" stop-color="#0d3550"/>
<stop offset="100%" stop-color="#071a26"/>
</linearGradient>
</defs>
<rect width="520" height="221" fill="url(#water)"/>
<rect x="86" y="35" width="348" height="151" rx="16"
fill="#09121c" fill-opacity="0.72" stroke="#94a3b8" stroke-opacity="0.18"/>
<g transform="rotate(-90 260 95)">
<circle cx="260" cy="95" r="39.87" fill="none" stroke="#94a3b8" stroke-opacity="0.16" stroke-width="6.13" stroke-linecap="round"/>
<circle cx="260" cy="95" r="39.87" fill="none" stroke="#fbbf24" stroke-width="6.13" stroke-linecap="round"
stroke-dasharray="250.49" stroke-dashoffset="224.88"/>
</g>
<text x="260" y="94" text-anchor="middle" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="21" font-weight="600" fill="#fbbf24">01:32</text>
<text x="260" y="110" text-anchor="middle" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">LUFT</text>
<text x="192" y="84" text-anchor="end" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">TIEFE</text>
<text x="192" y="110" text-anchor="end" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="26" font-weight="600" fill="#fbbf24">27.5<tspan font-size="13" font-weight="500" fill="#94a3b8" dx="3">m</tspan></text>
<text x="328" y="84" text-anchor="start" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="9" font-weight="600" letter-spacing="1.4" fill="#94a3b8">DRUCK</text>
<text x="328" y="110" text-anchor="start" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="26" font-weight="600" fill="#f87171">31<tspan font-size="13" font-weight="500" fill="#94a3b8" dx="3">bar</tspan></text>
<g>
<rect x="217" y="149" width="86" height="23" rx="8" fill="#94a3b8" fill-opacity="0.1"/>
<circle cx="231" cy="160.5" r="4" fill="#94a3b8"/>
<text x="241" y="164" font-family="'Segoe UI',Roboto,system-ui,sans-serif" font-size="10" font-weight="600" letter-spacing="1.2" fill="#94a3b8">LAMPE</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+9 -1
View File
@@ -1,4 +1,12 @@
-- Zum Reinkopieren in ox_inventory/data/items.lua -- =============================================================================
-- COPY-PASTE NACH ox_inventory/data/items.lua
-- =============================================================================
--
-- Alles unten komplett markieren und innerhalb der `return {` ... `}`-Klammer
-- einfügen. Sechs Items, nichts weiter anzupassen.
--
-- Bewusst KEINE fertige items.lua zum Ersetzen: die würde die restlichen Items
-- des Servers mitlöschen.
-- --
-- WICHTIG: kein `decay = true` auf den Flaschen. Die durability trägt hier den -- WICHTIG: kein `decay = true` auf den Flaschen. Die durability trägt hier den
-- Füllstand, und ox_inventory löscht Items mit decay, sobald durability 0 erreicht -- Füllstand, und ox_inventory löscht Items mit decay, sobald durability 0 erreicht
+14 -18
View File
@@ -6,6 +6,11 @@
</head> </head>
<body> <body>
<div id="hud" class="hidden"> <div id="hud" class="hidden">
<div class="readout readout--right">
<span id="depth-label" class="caption">TIEFE</span>
<div class="value"><span id="depth-value">0.0</span><span class="unit">m</span></div>
</div>
<div class="gauge"> <div class="gauge">
<svg viewBox="0 0 120 120" aria-hidden="true"> <svg viewBox="0 0 120 120" aria-hidden="true">
<circle class="gauge-track" cx="60" cy="60" r="52"></circle> <circle class="gauge-track" cx="60" cy="60" r="52"></circle>
@@ -17,25 +22,16 @@
</div> </div>
</div> </div>
<div class="readouts"> <div class="readout" id="pressure">
<div class="readout-row"> <span id="pressure-label" class="caption">DRUCK</span>
<div class="readout"> <div class="value"><span id="pressure-value">0</span><span class="unit">bar</span></div>
<span id="depth-label" class="caption">TIEFE</span> </div>
<div class="value"><span id="depth-value">0.0</span><span class="unit">m</span></div>
</div>
<div class="readout" id="pressure"> <div id="lamp" class="lamp">
<span id="pressure-label" class="caption">DRUCK</span> <svg viewBox="0 0 24 24" aria-hidden="true">
<div class="value"><span id="pressure-value">0</span><span class="unit">bar</span></div> <path d="M9 21h6v-1H9v1zm3-19a7 7 0 0 0-4 12.7V17h8v-2.3A7 7 0 0 0 12 2z"></path>
</div> </svg>
</div> <span id="lamp-label">LAMPE</span>
<div id="lamp" class="lamp">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M9 21h6v-1H9v1zm3-19a7 7 0 0 0-4 12.7V17h8v-2.3A7 7 0 0 0 12 2z"></path>
</svg>
<span id="lamp-label">LAMPE</span>
</div>
</div> </div>
</div> </div>
+32 -17
View File
@@ -24,15 +24,22 @@ body {
user-select: none; user-select: none;
} }
/* Grid statt Flex: so sitzen Tiefe, Ring und Druck garantiert auf einer
gemeinsamen Mittelachse, und die Lampen-Leiste hängt in einer zweiten Zeile
mittig unter dem Ring - ohne Ausgleichs-Margins. */
#hud { #hud {
position: absolute; position: absolute;
left: 50%; left: 50%;
bottom: 7vh; bottom: 7vh;
transform: translateX(-50%) translateY(0); transform: translateX(-50%) translateY(0);
display: flex; display: grid;
grid-template-columns: auto auto auto;
grid-template-rows: auto auto;
align-items: center; align-items: center;
gap: 18px; justify-items: center;
padding: 14px 22px 14px 14px; column-gap: 22px;
row-gap: 8px;
padding: 14px 22px;
border-radius: 16px; border-radius: 16px;
background: var(--panel); background: var(--panel);
border: 1px solid rgba(148, 163, 184, 0.18); border: 1px solid rgba(148, 163, 184, 0.18);
@@ -53,7 +60,8 @@ body {
position: relative; position: relative;
width: 92px; width: 92px;
height: 92px; height: 92px;
flex-shrink: 0; grid-column: 2;
grid-row: 1;
} }
.gauge svg { .gauge svg {
@@ -107,22 +115,28 @@ body {
/* --- Tiefe + Lampe --- */ /* --- Tiefe + Lampe --- */
.readouts {
display: flex;
flex-direction: column;
gap: 10px;
}
.readout-row {
display: flex;
gap: 18px;
}
.readout { .readout {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 3px; gap: 3px;
min-width: 76px; min-width: 84px;
grid-row: 1;
}
/* Tiefe spiegelt den Druck: Werte zeigen jeweils zum Ring hin. */
.readout--right {
align-items: flex-end;
grid-column: 1;
justify-self: end;
}
.readout--right .value {
justify-content: flex-end;
}
#pressure {
grid-column: 3;
justify-self: start;
} }
.value { .value {
@@ -143,9 +157,10 @@ body {
} }
.lamp { .lamp {
grid-column: 2;
grid-row: 2;
display: flex; display: flex;
align-items: center; align-items: center;
align-self: flex-start; /* sonst zieht der Flex-Container die Leiste auf volle Breite */
gap: 6px; gap: 6px;
padding: 5px 9px; padding: 5px 9px;
border-radius: 8px; border-radius: 8px;