D4rkst3randClaude Opus 5 7c4004a378 docs: Testliste durch Issues ersetzt
TESTING.md war nicht filterbar, nicht zuweisbar und haette neben den Issues als
zweite Quelle gedriftet. Jetzt ein Issue pro Testbereich mit Labels (test,
prio:kritisch/mittel/niedrig, ungeprueft), plus bug und doku fuer spaeter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 09:20:27 +02:00
2026-08-11 09:11:12 +02:00
2026-08-11 09:20:27 +02:00
2026-08-11 09:20:27 +02:00

d4rk_divegear

Tauchausrüstung für QBox. Fork von 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

Warnung — Restluft unter der ersten Schwelle, Tiefe über deepWarningDepth, Druck unter reservePressure

HUD, Warnstufe

Kritisch — letzte Sekunden, der Ring pulsiert

HUD, kritischer Luftstand

Das HUD erscheint, sobald die Ausrüstung angelegt ist — also auch an Land, damit sich der Flaschendruck am Finimeter ablesen lässt, ohne das Inventar zu öffnen. Wer es nur unter Wasser will: hudOnlyUnderwater = true.

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 in Metern. Optional steigt der Luftverbrauch mit der Tiefe (depthDecayEnabled, per Default aus).
  • Tiefengrenze übers Atemgas — mit Pressluft ab 45 m Tiefenrausch, mit Trimix erst ab 100 m. Macht das Tiefenlimit mechanisch statt zur Absprache.
  • 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 · ox_lib · ox_inventory

Installation

  1. Ordner nach resources/ und in der server.cfg starten — nach ox_inventory und qbx_core:

    ensure d4rk_divegear
    
  2. items_for_ox_inventory.lua komplett markieren und in ox_inventory/data/items.lua innerhalb der return { … }-Klammer einfügen. Sieben Items, nichts weiter anzupassen.

  3. Die sieben PNGs aus 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 Gas max. Tiefe Gewicht
diving_gear Tauchausrüstung 5000
diving_fill Pressluft-Kartusche 1000
diving_tank_small Tauchflasche (5 Min) 300 s Pressluft 45 m 4000
diving_tank_medium Tauchflasche (10 Min) 600 s Pressluft 45 m 7000
diving_tank_large Tauchflasche (15 Min) 900 s Pressluft 45 m 10000
diving_tank_xl Tauchflasche (20 Min) 1200 s Pressluft 45 m 13000
diving_tank_trimix Trimix-Flasche (15 Min) 900 s Trimix 100 m 11000

Alle Flaschen starten bei 300 bar.

diving_gear und diving_fill stammen aus dem Original (dort hießen sie Diving Gear und Diving Tube). Neu sind nur die vier Flaschen: im Original war die Luft eine Client-Variable, die an keinem Item hing — eine Sauerstoffflasche als Item gab es dort gar nicht. Für unterschiedliche Laufzeiten und persistente Restluft braucht es aber ein Item, an dem beides hängen kann.

Ein Taucher trägt damit zwei Dinge: die Ausrüstung und eine Flasche. Die Kartusche nur, wer unterwegs nachfüllen will.

Die Icons liegen als 128×128 PNG mit transparentem Hintergrund in 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

Die Flasche wird nicht separat angezogen. Sie muss nur im Inventar liegen — beim Anlegen der Ausrüstung fragt das Script, welche angeschlossen werden soll.

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 5 Flaschen name, capacity (Sekunden), pressure (bar), maxDepth (Meter), gas (Label fürs Menü)
newTanksFull true Flaschen ohne Metadata (Shop, AddItem, Admin-Befehl) gelten als voll. Auf false muss jede neue Flasche erst mit einer Kartusche befüllt werden
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 false Verbrauch steigt mit der Tiefe. Aus, damit die 5/10/15/20 Minuten exakt stimmen
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

Tiefenrausch (narcosis)

Option Default Bedeutung
enabled true Komplett abschaltbar
warnBeforeMeters 5.0 Vorwarnung so viele Meter vor der Grenze
engineDepthLimit 130.0 Tiefe, ab der das Spiel selbst über den Wasserdruck tötet — dagegen hilft keine Flasche
engineWarnBeforeMeters 20.0 Vorwarnung davor
stages 3 Stufen Pro Stufe: over (Meter unter der Grenze), timecycle + strength, shake + shakeIntensity, drainFactor, damagePerTick

HUD

Option Default Bedeutung
hudEnabled true Ohne HUD bleibt die Ausrüstung voll nutzbar
hudUpdateIntervalMs 250 4 Updates pro Sekunde
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
reservePressure 50 Ab hier wird die Druckanzeige rot

Lampe

Option Default Bedeutung
lampKey 'l' Vorbelegung
lampReconcileIntervalMs 500 Wie oft der Lampen-Zustand auf die Peds in der Nähe nachgezogen wird
lampProp '' Optionales Prop am Kopf, rein optisch. Die Lampe leuchtet unabhängig davon
lampBone / lampPropOffset / lampPropRotation 12844 / vec3(0,0,0) Sitz des optionalen Props — mit /divelamp einstellen

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.

Debug-Befehle

Mit debug = true in config/client.lua:

/divelampcheck                        Hat das aktuelle Outfit eine Scuba-Lampe?
/divelamp <x> <y> <z> <rx> <ry> <rz>  Sitz des optionalen Lampen-Props

/divelamp hängt das Prop sofort neu an und schreibt die fertige Config-Zeile ins Log — kein Resource-Restart pro Versuch. Alternativ mit einem Prop-Attach-Editor wie noted_propattacher ausmessen.

Ein Tauchlampen-Prop gibt es im Basegame nicht. Was es gibt, sind Varianten derselben Polizei-Taschenlampe: prop_cs_police_torch, prop_police_torch, prop_scn_police_torch. Für die Beleuchtung braucht es das Prop ohnehin nicht.

Tiefengrenze und Tiefenrausch

Warum 4045 m real das Limit sind, liegt nicht am Druck, sondern am Stickstoff in der Pressluft: tiefer wirkt er narkotisch, und ab etwa 56 m wird auch der Sauerstoff giftig. Wer tiefer will, atmet Trimix — dort ersetzt Helium einen Teil des Stickstoffs.

Genau so ist es hier umgesetzt: jede Flasche hat in config/shared.lua eine maxDepth. Darunter setzt Tiefenrausch ein, in drei Stufen, je tiefer desto heftiger — Bildschirmeffekt, Kamerawackeln, höherer Luftverbrauch, in der letzten Stufe Schaden. Fünf Meter vorher kommt eine Warnung, und die Tiefenanzeige im HUD wird rot und pulsiert, sobald die Grenze überschritten ist.

Damit ist ein 45-m-Limit keine Absprache mehr, sondern spürbar — und die tiefen Spots bleiben erreichbar, kosten aber die richtige Flasche.

Alles daran ist in config/client.lua unter narcosis einstellbar: Schwellen, Effekte, Stärke, Verbrauchsaufschlag, Schaden. enabled = false schaltet es ganz aus.

Die timecycle-Namen sind Spiel-Effekte, keine Natives. Existiert einer nicht, passiert einfach nichts — dann einen anderen eintragen.

Die Grenze der Engine

Unabhängig von allem hier tötet GTA selbst ab einer bestimmten Tiefe über den Wasserdruck — beim U-Boot liegt die Grenze bei rund 150 m mit Implosion ab ~168 m, zu Fuß verliert man ebenfalls Leben. Dagegen hilft kein Atemgas, das ist die Engine und nicht dieses Script.

Deshalb gibt es dafür nur eine Warnung (engineDepthLimit, konservativ auf 130 m), und Trimix hört mit 100 m bewusst darüber auf. Der Bereich zwischen den beiden ist die Zone, in der keine Ausrüstung mehr hilft.

Exports

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 funktioniert

Es wird kein Licht selbst gezeichnet. GTA bringt seit v1493 (Cayo Perico) eine eigene Lampe an der Tauchausrüstung mit:

SetEnableScubaGearLight(ped, toggle)  -- 0xEE2476B9EE4A094F
IsScubaGearLightEnabled(ped)          -- 0x88274C11CF0D866D

Die sitzt an der richtigen Stelle, leuchtet in Blickrichtung und braucht weder Prop noch Offsets noch einen Draw-Thread.

Voraussetzung: Die Lampe hängt an der echten Scuba-Kleidung des Peds (Component 8). Trägt der Spieler die nicht, tut die Native nichts. Diese Resource fasst das Outfit bewusst nicht an — das gehört ins Kleidungs-System. Beim Einschalten ohne passendes Outfit gibt es eine Meldung statt stiller Wirkungslosigkeit. Mit debug = true sagt /divelampcheck, ob das aktuelle Outfit eine Lampe hat.

Der Flag ist ein lokaler Ped-Zustand und wird nicht von selbst repliziert. Deshalb läuft der Zustand über einen Statebag (LocalPlayer.state:set('divelight', …, true)), und jeder Client setzt den Flag für die Taucher in seiner Nähe selbst — sofort per AddStateBagChangeHandler, plus ein Abgleich alle lampReconcileIntervalMs, weil Peds beim Streaming, Respawn und Model-Wechsel neu erzeugt werden und den Flag dabei verlieren.

Entwicklung

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 <text>-Element, auch im minimalsten Testfall — die Zahlen kommen korrekt raus.

Der Umbau ist in PLAN.md dokumentiert, inklusive der offenen Punkte. Die Abnahme läuft über die Issues — ein Issue pro Testbereich, mit prio:-Labels und ungeprueft für alles, was sich ohne laufendes Spiel nicht verifizieren ließ.

Offene Punkte

Ingame getestet sind Anlegen und Ablegen, Flaschenauswahl, Luftverbrauch, HUD mit Tiefe und Druck sowie die Lampe. Noch offen:

  • Scuba-Lampe ohne passendes Outfit — sie hängt an der Scuba-Kleidung (Component 8). Wer die nicht trägt, bekommt eine Meldung. Soll die Ausrüstung das Outfit selbst setzen, muss das noch gebaut werden (inklusive Sichern und Wiederherstellen der vorherigen Kleidung).
  • 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.
  • HUD-Position ist nur in nui/style.css einstellbar, nicht in der Config.
  • Balancing — die Laufzeiten sind gesetzt, aber nicht am Spielgefühl geprüft.

Lizenz

GPL-3.0, wie das Original. Die Upstream-Historie ist erhalten und liegt auf dem Remote upstream.

S
Description
Tauchausruestung mit Flaschengroessen, gesyncter Lampe und Tiefenanzeige (Fork von qbx_divegear)
Readme GPL-3.0
715 KiB
Languages
Lua 65%
JavaScript 21.7%
CSS 9.3%
HTML 4%