# Entscheidungen und Fallstricke Warum die Dinge so gebaut sind, wie sie gebaut sind. Wer hier etwas ändern will, sollte den jeweiligen Absatz vorher gelesen haben — das meiste davon hat genau einmal wehgetan. Fork von [qbx_divegear](https://github.com/Qbox-project/qbx_divegear) v1.0.1. Die Upstream-Historie liegt vollständig auf dem Remote `upstream`. --- ## Was am Original nicht funktionierte 1. **Der Sauerstoff war eine lokale Client-Variable**, die an keinem Item hing. Nicht persistent, nicht übertragbar, weg bei Relog. Damit war weder eine Flaschengröße noch eine ehrliche Restluft-Anzeige möglich. 2. **Die Laufzeit war verrechnet:** `startingOxygenLevel = 100` bei `decayRate = 1` pro Sekunde ergab 100 Sekunden, nicht Minuten. 3. **Die Props waren soft-pinned angehängt.** Mit `useSoftPinning = true` darf die Physik-Engine die Verbindung bei Ragdoll und harten Anim-Übergängen lösen — genau deshalb verschwand die Ausrüstung ständig. --- ## Ausrüstung **Attachment hart, nicht soft.** `useSoftPinning = false`; laut Native-Doku heißt das *"attached entity will not detach when fixed"*. Zusätzlich prüft ein Watchdog jede Sekunde, ob beide Props noch am **aktuellen** Ped hängen, und hängt sie sonst neu an. Er setzt auch `SetEnableScuba` und `SetPedMaxTimeUnderwater` neu, weil das Spiel beides bei Respawn und Fahrzeugausstieg zurücksetzt. **`lib.onCache('ped')` gibt den neuen Ped als Parameter — `cache.ped` nicht.** ox_lib ruft die Callbacks über `Citizen.CreateThreadNow` auf, *bevor* der neue Wert in den Cache geschrieben wird. Wer im Handler `cache.ped` benutzt, hängt die Props an den gerade verschwundenen Ped. Das ist der Kleiderladen-Fall, für den der Handler überhaupt existiert. **`SetPedMaxTimeUnderwater` hat zwei Werte, das ist Absicht.** `maxTimeUnderwater` (50 s) beim Ablegen = normal weiterschwimmen. `maxTimeUnderwaterOutOfAir` (1 s) bei leerer Flasche = absaufen. Sieht nach Inkonsistenz aus, ist keine. **Besitz wird laufend geprüft.** Ausrüstung und Flasche können das Inventar mitten im Tauchgang verlassen — weggegeben, abgelegt, vom Admin entfernt. Der Client hält nur eine Slot-Nummer; ohne Gegenprüfung taucht man unbegrenzt weiter. `ox_inventory:updateInventory` feuert bei jeder Änderung, dort wird beim Server nachgefragt. Wurde nur umsortiert, bindet der Client auf den neuen Slot um. ## Items **Kein `consume` bei Items mit `CreateUseableItem`.** ox_inventory verzweigt so: ```lua elseif consume then -- interner Pfad elseif not item.weapon and server.UseItem then -- hier erst CreateUseableItem ``` In Lua ist `0` wahr. `consume = 0` landet im ersten Zweig, der qbx-Callback feuert nie, und beim Benutzen passiert **gar nichts** — ohne Fehlermeldung. Steht so nicht in der ox-Doku, nur im Quellcode. **Kein `decay` auf den Flaschen.** `metadata.durability` trägt hier den Füllstand. ox_inventory löscht Items mit `decay`, sobald `durability` 0 erreicht — die leere Flasche würde also verschwinden statt auffüllbar zu bleiben. **`SetMetadata` ersetzt die Metadata-Tabelle komplett**, es merged nicht. Deshalb wird der bestehende Inhalt kopiert, bevor `oxygen` und `durability` gesetzt werden. **Flaschen ohne Metadata gelten als voll** (`newTanksFull`). Eine frisch gekaufte Flasche hat kein `oxygen` — ohne diese Regel wäre sie leer und man müsste erst eine Kartusche verbrauchen, um überhaupt tauchen zu können. ## Sauerstoff **Restsekunden statt Punkte**, und der Verbrauch läuft über `GetGameTimer()`-Deltas statt gezählter `Wait(1000)`. Ein Tick dauert unter Last länger als eine Sekunde; über einen 20-Minuten-Tauchgang summiert sich das sichtbar. **Der Server clampt.** Der Client meldet die Restluft, der Server akzeptiert nur Abnahme und höchstens `maxDrainPerSecond` pro echter Sekunde. Wer den Tiefenfaktor oder den Narkose-`drainFactor` hochdreht, muss diesen Wert mitziehen — sonst bekommen ehrliche Spieler in großer Tiefe Luft zurückgeschrieben. ## Tiefe **Die Grenze hängt am Atemgas, nicht an einer unsichtbaren Wand.** Real begrenzt nicht der Druck, sondern der Stickstoff in der Pressluft: tiefer wirkt er narkotisch, ab etwa 56 m wird auch der Sauerstoff giftig. Wer tiefer will, atmet Trimix. Genau so umgesetzt — `maxDepth` pro Flasche, darunter Tiefenrausch in Stufen. **Bei 160 m ist Schluss, das macht die Engine.** Dagegen hilft kein Atemgas, deshalb dort nur eine Warnung und kein eigener Effekt. Trimix hört mit 100 m bewusst darüber auf. ## Lampe **Die spieleigene Scuba-Lampe, kein Eigenbau.** GTA hat seit v1493 `SetEnableScubaGearLight` (`0xEE2476B9EE4A094F`). Die sitzt richtig, leuchtet in Blickrichtung und macht Prop, Offsets, Kegelwerte und einen Draw-Thread überflüssig. Gefunden über [esx_scuba](https://github.com/wobozkyng/esx_scuba). Der dortige Hinweis, das Licht hänge am Kleidungs-Component 8, **trifft nicht zu** — ingame geprüft, sie funktioniert auch ohne Scuba-Kleidung. Das Outfit wird nicht angefasst. **Der Flag ist lokal und repliziert sich nicht von selbst.** Deshalb der Umweg über einen Statebag: jeder Client setzt den Flag für die Taucher in seiner Nähe. Der Abgleich-Thread ist nötig, weil Peds beim Streaming, Respawn und Model-Wechsel neu erzeugt werden und den Flag dabei verlieren. ## HUD **Zeigerinstrumente ohne Skalenzahlen.** Bei 96 px kollidieren Zahlen mit Strichen, Zeiger und rotem Bereich, und gelesen werden sie ohnehin nicht. Der Zeiger zeigt die Tendenz, die Zahl darunter den Wert. **Kein `transform-origin` am Zeiger.** Das `rotate(winkel 50 50)` im transform-Attribut bringt den Drehpunkt schon mit; CSS rechnet eine zusätzliche Origin obendrauf und der Zeiger landet außerhalb der Uhr. **Das HUD skaliert mit der Bildschirmhöhe.** Entworfen auf 1080p; FiveM rendert das NUI in der echten Auflösung, ohne Nachskalieren wäre es auf 4K halb so groß. **Kein `backdrop-filter`** — bekannter FiveM-Bug, wird nach Updates zu einem schwarzen Kasten. --- ## Offen - Nach einem Relog muss die Ausrüstung neu angelegt werden. Die Restluft überlebt, die liegt auf der Flasche. - Die HUD-Position ist nur in `nui/style.css` einstellbar, nicht in der Config. - `IsHudHidden()` — ob die Native auf `DisplayHud(false)` anspringt, ist ungeprüft. Falls nicht, greifen Taste und Export weiterhin. - Die `timecycle`-Namen des Tiefenrauschs sind nicht verifiziert. Kommt kein Effekt, in `config/client.lua` unter `narcosis.stages` einen anderen eintragen. - Balancing: Laufzeiten und Narkose-Stufen sind gesetzt, aber nicht über längere Zeit am Spielgefühl geprüft.