# d4rk_divegear Tauchausrüstung für QBox. Fork von [qbx_divegear](https://github.com/Qbox-project/qbx_divegear) mit Flaschengrößen, gesyncter Lampe, Tiefen- und Druckanzeige — und Ausrüstung, 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 - **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 Füllstand ist als Balken direkt im Inventar-Slot sichtbar. - **Manometer in bar** — physikalisch korrekt aus dem Restanteil abgeleitet, Reservedruck wird eingefärbt. - **Gesyncte Tauchlampe** — an der Ausrüstung, An/Aus per Taste. Andere Spieler sehen den Lichtkegel, und zwar in die Richtung, in die tatsächlich geschaut wird. - **Tiefenanzeige** und **tiefenabhängiger Verbrauch** — tiefer tauchen kostet mehr Luft. Abschaltbar. - **Ausrüstung bleibt** — bei Emotes, Ragdoll, Kleiderwechsel und aufräumenden Fremd-Scripts. - **HUD ausblendbar** für Unterwasser-Screenshots. - **Serverseitige Prüfung** — die Restluft kann nur sinken, und nicht schneller als physikalisch möglich. ## Abhängigkeiten [`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 1. Ordner nach `resources/` und in der `server.cfg` starten — **nach** `ox_inventory` und `qbx_core`: ```cfg 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. Die sechs PNGs aus [`icons/`](icons) nach `cdn.d4rkst3r.de/items/` hochladen. Sie heißen genau wie die Items, deshalb braucht es kein `client.image`. Es gibt bewusst **keine** fertige `items.lua` zum Ersetzen — die würde die restlichen Items des Servers überschreiben. ## Items | Icon | 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 | Die Icons liegen als 128×128 PNG mit transparentem Hintergrund in [`icons/`](icons), die SVG-Quellen in `icons/src/`. Die Laufzeit steht als Zahl auf dem Farbband — ohne die sind die Einzelflaschen im Inventar nur noch an der Bandfarbe auseinanderzuhalten. Wer `capacity` ändert, sollte die Zahlen in `icons/gen-icons.mjs` mitziehen. 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 | Aktion | Wie | |---|---| | Ausrüstung an-/ausziehen | `diving_gear` benutzen | | Flasche wählen | Menü beim Anlegen (entfällt bei nur einer Flasche) | | Flasche auffüllen | `diving_fill` benutzen — nicht unter Wasser, nicht bei voller Flasche | | Lampe an/aus | `L` | | HUD ein-/ausblenden | Eigene Taste, standardmäßig unbelegt | Beide Tasten sind vom Spieler in den FiveM-Einstellungen unter *Tastenbelegung → FiveM* umlegbar. ## Konfiguration ### `config/shared.lua` — Client und Server | Option | Default | Bedeutung | |---|---|---| | `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 ```bash node ../.tools/lint.mjs # Lua-Lint, CfxLua-tauglich node ../.tools/nui-preview.mjs nui # HUD im Browser anschauen node docs/gen-hud-svg.mjs docs # Renderings für diese README neu bauen # Item-Icons neu bauen (SVG -> PNG, braucht ImageMagick mit librsvg) node icons/gen-icons.mjs for f in icons/src/*.svg; do magick -background none "$f" -resize 128x128 "icons/$(basename "$f" .svg).png" done ``` `gen-icons.mjs` schreibt außerdem `icons/_check.html` — alle Icons auf Schachbrett, in Slot-Größe und klein, zum Gegenschauen nach Änderungen. > librsvg 2.40 wirft beim Rendern der Zahlen ein > `Invalid UTF-8 string passed to pango_layout_set_text()`. Das ist harmlos und > passiert bei jedem ``-Element, auch im minimalsten Testfall — die Zahlen > kommen korrekt raus. 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 GPL-3.0, wie das Original. Die Upstream-Historie ist erhalten und liegt auf dem Remote `upstream`.