From c6b35ebe289b23e17d9f568a75fdf05ca534bb99 Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Mon, 10 Aug 2026 23:12:59 +0200 Subject: [PATCH] chore: fork als d4rk_divegear aufsetzen - Umbauplan (PLAN.md) mit 5 Phasen: Gear-Persistenz, Sauerstoff-Logik, Flaschengroessen, NUI-HUD (Tiefe + O2), gesyncte Taschenlampe - fxmanifest auf D4rkst3r/Gitea umgestellt - Qbox-spezifische CI entfernt (Discord-Webhooks, GitHub-Releases, Issue-Projektzuordnung) - laeuft auf Gitea ins Leere bzw. gegen fremde Endpunkte. lint.yml bleibt. Co-Authored-By: Claude Opus 5 --- .github/actions/bump-manifest-version.js | 15 -- .github/workflows/discord-commit.yml | 9 - .github/workflows/discord-release.yml | 21 --- .github/workflows/issues-project.yml | 22 --- .github/workflows/release-action.yml | 58 ------ .github/workflows/release.yml | 42 ----- PLAN.md | 219 +++++++++++++++++++++++ fxmanifest.lua | 5 +- 8 files changed, 222 insertions(+), 169 deletions(-) delete mode 100644 .github/actions/bump-manifest-version.js delete mode 100644 .github/workflows/discord-commit.yml delete mode 100644 .github/workflows/discord-release.yml delete mode 100644 .github/workflows/issues-project.yml delete mode 100644 .github/workflows/release-action.yml delete mode 100644 .github/workflows/release.yml create mode 100644 PLAN.md diff --git a/.github/actions/bump-manifest-version.js b/.github/actions/bump-manifest-version.js deleted file mode 100644 index 2980cfc..0000000 --- a/.github/actions/bump-manifest-version.js +++ /dev/null @@ -1,15 +0,0 @@ -const fs = require('fs') - -const version = process.env.TGT_RELEASE_VERSION -const newVersion = version.replace('v', '') - -const manifestFile = fs.readFileSync('fxmanifest.lua', {encoding: 'utf8'}) - -const versionStr = `version '${newVersion}'` -let newFileContent = manifestFile.replace(/\bversion\s+(.*)$/gm, versionStr) - -if (!newFileContent.includes(versionStr)) { - newFileContent = manifestFile.replace(/\bgame\s+(.*)$/gm, `game 'gta5'\n${versionStr}`); -} - -fs.writeFileSync('fxmanifest.lua', newFileContent) \ No newline at end of file diff --git a/.github/workflows/discord-commit.yml b/.github/workflows/discord-commit.yml deleted file mode 100644 index 31f6a5f..0000000 --- a/.github/workflows/discord-commit.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: Discord Commit - -on: - push: - -jobs: - notify: - uses: Qbox-project/.github/.github/workflows/discord-commit.yml@main - secrets: inherit diff --git a/.github/workflows/discord-release.yml b/.github/workflows/discord-release.yml deleted file mode 100644 index a184717..0000000 --- a/.github/workflows/discord-release.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: "Discord Releases" - -on: - release: - types: [published] - -jobs: - github-releases-to-discord: - name: Discord Releases Changelog - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - name: Github Releases To Discord - uses: SethCohen/github-releases-to-discord@v1.16.2 - with: - webhook_url: ${{ secrets.WEBHOOK_URL }} - color: "15852866" - username: ${{ github.event.repository.name }} - avatar_url: "https://i.imgur.com/Eh1yiLI.png" - footer_timestamp: true diff --git a/.github/workflows/issues-project.yml b/.github/workflows/issues-project.yml deleted file mode 100644 index 4e5ed18..0000000 --- a/.github/workflows/issues-project.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Issues Project Management - -on: - issues: - types: - - opened - -jobs: - add-to-project: - name: Add issue to project - runs-on: ubuntu-latest - steps: - - name: Get App Token - uses: actions/create-github-app-token@v1 - id: generate_token - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.PRIVATE_KEY }} - - uses: actions/add-to-project@v0.5.0 - with: - project-url: https://github.com/orgs/Qbox-project/projects/4 - github-token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/release-action.yml b/.github/workflows/release-action.yml deleted file mode 100644 index 02df19e..0000000 --- a/.github/workflows/release-action.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: "release-action" - -on: - push: - tags: - - "v*" - -jobs: - release-action: - name: "Create Release" - runs-on: "ubuntu-latest" - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.repository.default_branch }} - - - name: Install ZIP - run: sudo apt install zip - - - name: Bundle files - run: | - rm -rf ./.github ./.vscode ./.git - shopt -s extglob - mkdir ./${{ github.event.repository.name }} - cp -r !(${{ github.event.repository.name }}) ${{ github.event.repository.name }} - zip -r ./${{ github.event.repository.name }}.zip ./${{ github.event.repository.name }} - - - name: Get App Token - uses: actions/create-github-app-token@v1 - id: generate_token - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.PRIVATE_KEY }} - - - name: Update CHANGELOG - id: changelog - uses: requarks/changelog-action@v1 - continue-on-error: true - with: - token: ${{ steps.generate_token.outputs.token }} - tag: ${{ github.ref_name }} - includeInvalidCommits: true - useGitmojis: false - writeToFile: false - - - name: Create Release - uses: ncipollo/release-action@v1.14.0 - with: - allowUpdates: true - draft: false - makeLatest: true - name: ${{ github.ref_name }} - tag: ${{ github.ref_name }} - body: ${{ steps.changelog.outputs.changes }} - artifacts: ${{ github.event.repository.name }}.zip - token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index f5cc276..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Create New Release - -on: - workflow_dispatch: - inputs: - version: - required: true - -jobs: - create-release: - name: Create New Release - runs-on: ubuntu-latest - steps: - - name: Get App Token - uses: actions/create-github-app-token@v1 - id: generate_token - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.PRIVATE_KEY }} - - - name: Checkout Repository - uses: actions/checkout@v4 - with: - token: ${{ steps.generate_token.outputs.token }} - - - name: Setup node - uses: actions/setup-node@v4 - with: - node-version: 20.x - - - name: Bump manifest version - run: node .github/actions/bump-manifest-version.js - env: - TGT_RELEASE_VERSION: ${{ inputs.version }} - - - name: Push manifest change - uses: EndBug/add-and-commit@v9 - with: - add: fxmanifest.lua - push: true - message: 'chore: bump manifest version to ${{ inputs.version }}' - tag: ${{ inputs.version }} diff --git a/PLAN.md b/PLAN.md new file mode 100644 index 0000000..4e9823a --- /dev/null +++ b/PLAN.md @@ -0,0 +1,219 @@ +# d4rk_divegear — Umbauplan + +Fork von [Qbox-project/qbx_divegear](https://github.com/Qbox-project/qbx_divegear) (v1.0.1). +Die Upstream-Historie ist vollständig erhalten und liegt auf dem Remote `upstream`, +damit spätere Upstream-Änderungen mergebar bleiben. + +**Ziel:** Gesyncte Taschenlampe, verschiedene Flaschengrößen mit echter Laufzeit, +Tiefenanzeige, und Ausrüstung die bei Animationen nicht mehr verschwindet. + +--- + +## Ausgangslage (Analyse des Originals) + +`client/main.lua` hat 173 Zeilen, `server/main.lua` 10. Drei strukturelle Probleme, +die die neuen Features blockieren: + +1. **Sauerstoff ist eine lokale Client-Variable** (`local oxygenLevel = 0`) — nicht + persistent, nicht am Item hängend, weg bei Relog/Restart. Blockiert Flaschengrößen. +2. **Laufzeit-Rechenfehler:** `startingOxygenLevel = 100` bei `decayRate = 1` pro + Sekunde ergibt **100 Sekunden**, nicht Minuten. Die Vergleiche + `oxygenLevel % 10 == 0` und `oxygenLevel == 0` (client/main.lua:121, :124) brechen, + sobald `decayRate` fraktional wird. +3. **Attachment ist soft-pinned** (client/main.lua:70, :75) — die Physik-Engine darf + die Props bei Ragdoll/Anims lösen. Kein Ped-Swap-Handling, kein Watchdog. + +--- + +## Phasen + +Reihenfolge ist bewusst gewählt: Phase 1 und 2 sind Fundament, Phase 3–5 bauen darauf auf. + +### Phase 1 — Gear-Persistenz (Bugfix, ~1 h) + +Zuerst, weil kleinster Aufwand, echter Bug, und die Taschenlampe dieselbe +Attach-Logik nutzt. + +- [ ] `attachGear()` auf harte Attachment umstellen: `useSoftPinning = false`, + `p9 = false`, `fixedRot = true` +- [ ] `isGearIntact()` Helper: `DoesEntityExist` + `GetEntityAttachedTo(...) == cache.ped` + für beide Props +- [ ] Watchdog-Thread (1000 ms) solange `currentGear.enabled`: bei Defekt + `deleteGear()` + `attachGear()`, und `enableScuba()` unbedingt neu setzen + (`SetEnableScuba`/`SetPedMaxTimeUnderwater` werden bei Respawn/Fahrzeugausstieg + vom Spiel zurückgesetzt) +- [ ] `lib.onCache('ped', ...)` → bei Model-Wechsel (Kleiderladen, Skin-Change) + Props neu anhängen, Scuba-State neu setzen +- [ ] Death-Handler: beim Tod Gear sofort ablegen (ohne Progressbar), Restluft + vorher sichern — Flasche bleibt im Inventar +- [ ] Inkonsistenz aufräumen: `takeOffSuit()` setzt `SetPedMaxTimeUnderwater(ped, 50.0)`, + `disableScuba()` setzt `1.0` — einen Wert festlegen +- [ ] `onResourceStop` Cleanup-Handler für Props + +### Phase 2 — Sauerstoff-Logik neu (~1 h) + +- [ ] Umstellung von "Punkte" auf **Restsekunden** (`oxygenSeconds`) +- [ ] Decrementer-Thread auf `GetGameTimer()`-Delta statt gezählter `Wait(1000)` + (driftfrei, überlebt Frame-Drops) +- [ ] Float-Vergleiche raus, Warnschwellen explizit als Config-Liste + (z.B. `warnAtSeconds = { 60, 30, 10 }`), jede Schwelle einmal auslösen +- [ ] Anzeige als `mm:ss` statt roher Zahl +- [ ] Persistenz: Restluft alle ~15 s und beim Ablegen per Server-Event in die + ox_inventory-Metadata des Tank-Items zurückschreiben + (`exports.ox_inventory:SetMetadata`) +- [ ] Beim Anlegen Metadata lesen → Restluft übernehmen +- [ ] Beim Spawn/Resource-Start: war die Gear angelegt, direkt wieder anziehen + +**Serverseitig validieren:** Der Client schickt den O2-Wert. Server prüft +Plausibilität (nur Abnahme, nicht mehr als seit letztem Update vergehen konnte), +sonst ist das ein trivialer Cheat-Vektor. + +### Phase 3 — Flaschengrößen (~1 h, nach Phase 2 trivial) + +Vier Items mit unterschiedlicher `capacity` in Sekunden: + +| Item | Laufzeit | capacity | +|---|---|---| +| `diving_tank_small` | 5 min | 300 | +| `diving_tank_medium` | 10 min | 600 | +| `diving_tank_large` | 15 min | 900 | +| `diving_tank_xl` | 20 min | 1200 | + +- [ ] Items in `config.lua` definieren (Label, capacity, Gewicht, Prop-Scale) +- [ ] **`items_for_ox_inventory.lua`** als Snippet-Datei zum manuellen Reinkopieren + in `ox_inventory/data/items.lua` — kein `RegisterItem`-Export +- [ ] `metadata.oxygen` (Restsekunden) + `metadata.durability` (Prozent, damit der + Füllstand direkt im Inventar-Slot sichtbar ist) +- [ ] `diving_fill` füllt bis `capacity` des jeweiligen Tanks statt auf Fixwert +- [ ] Verbrauch skaliert mit Tiefe: `decayRate * (1 + depth / 30)` — physikalisch + korrekt und macht große Flaschen erst sinnvoll (per Config abschaltbar) + +**Optik:** `p_s_scuba_tank_s` ist das einzige Basegame-Tank-Prop. Größenunterschied +über `SetEntityScale` ist bei attached Objects unzuverlässig — entweder akzeptieren, +dass alle vier gleich aussehen, oder Custom-Model. Für v1: gleich aussehen. + +### Phase 4 — NUI-HUD: Tiefe + Sauerstoff (~2,5 h) + +Tiefenanzeige und O2-Anzeige zusammen als ein NUI-Overlay — `qbx.drawText2d` +(aktuell `oxygenLevel..'⏱'` in der Bildschirmecke) fliegt raus. + +**Datenseite (~30 min):** +- [ ] Tiefe = Wasseroberfläche − Ped-Z, via `GetWaterHeight` (Fallback + `GetWaterHeightNoWaves`) +- [ ] Wert geglättet (Lerp) damit die Zahl nicht zappelt +- [ ] Liefert gleichzeitig den Faktor für den tiefenabhängigen Verbrauch aus Phase 3 + +**NUI (~2 h):** +- [ ] O2 als Balken/Ring + `mm:ss` Restzeit, Farbwechsel an den Warnschwellen aus + Phase 2, Pulsieren im kritischen Bereich +- [ ] Tiefe in Metern, Warnfarbe ab Config-Tiefe +- [ ] Slot für den Lampen-Status (wird in Phase 5 befüllt) +- [ ] Sichtbar nur wenn Gear angelegt **und** unter Wasser, mit Fade-In/Out +- [ ] Updates gedrosselt per `SendNUIMessage` (~4/s statt jeden Frame) +- [ ] Kein `backdrop-filter` (bekannter FiveM-Bug → schwarzer Hintergrund) +- [ ] Position/Scale in `config/client.lua` einstellbar + +### Phase 5 — Gesyncte Taschenlampe (~2–3 h) + +Licht ist in GTA nie synchronisiert — jeder Client zeichnet das Licht für jeden +Taucher in seiner Nähe selbst. + +- [ ] Torch-Prop mit derselben harten Attach-Methode aus Phase 1 anhängen + (Kopf-Bone oder Hand, Config) +- [ ] Toggle per `RegisterKeyMapping`/`lib.addKeybind` +- [ ] State replizieren: `LocalPlayer.state:set('divelight', true, true)` + — Statebag statt Event-Spam +- [ ] Draw-Thread: über `GetActivePlayers()`, `Player(serverId).state.divelight` + prüfen, `DrawSpotLightWithShadow` zeichnen +- [ ] **Richtung aus dem Prop, nicht aus dem Ped:** der Ped pitcht beim Tauchen, + `GetEntityForwardVector` geht schief. Position + Richtung über + `GetOffsetFromEntityInWorldCoords(torchProp, 0, 1, 0)` aus der Prop-Matrix +- [ ] Fremde Props: einmal pro Sekunde `GetGamePool('CObject')` nach Torch-Model + filtern, per `GetEntityAttachedTo` dem Ped zuordnen, Ergebnis cachen. + Fallback wenn nicht gefunden: Kopf-Bone + Ped-Matrix +- [ ] Performance: Distanzfilter ~40–50 m, max. 3–4 gleichzeitige Lichter + (`DrawSpotLightWithShadow` ist teuer) +- [ ] Lampen-Status (An/Aus) in das HUD aus Phase 4 einhängen — keine Batterie, + die Lampe läuft solange Sauerstoff da ist + +--- + +## Zielstruktur + +Die Config bleibt beim Upstream-Pattern (`config/client.lua` + `require`) statt beim +`shared_scripts`-Template, damit spätere Upstream-Änderungen mergebar bleiben. + +``` +d4rk_divegear/ +├── fxmanifest.lua +├── config/ +│ ├── client.lua # Timings, Tiefe, Licht, HUD +│ └── shared.lua # Tank-Definitionen (Client + Server brauchen capacity) +├── client/ +│ ├── main.lua # Gear an/aus, Watchdog +│ ├── oxygen.lua # Decrementer, Persistenz-Sync +│ ├── light.lua # Statebag + Draw-Thread +│ └── hud.lua # Tiefenberechnung + NUI-Bridge +├── nui/ +│ ├── index.html +│ ├── style.css +│ └── script.js +├── server/ +│ └── main.lua # Useable Items, Metadata schreiben + validieren +├── locales/ +│ ├── de.json +│ └── en.json +└── items_for_ox_inventory.lua # Snippet zum Reinkopieren +``` + +Locales: DE + EN werden gepflegt, die 12 übrigen Upstream-Sprachen bleiben liegen +(neue Keys fehlen dort → ox_lib fällt auf den Key-Namen zurück, kein Crash). + +--- + +## Aufwand + +| Phase | Aufwand | +|---|---| +| 1 — Gear-Persistenz | ~1 h | +| 2 — Sauerstoff-Logik | ~1 h | +| 3 — Flaschengrößen | ~1 h | +| 4 — NUI-HUD (Tiefe + O2) | ~2,5 h | +| 5 — Taschenlampe | 2–3 h | +| **Summe** | **~8 h** | + +--- + +## Entscheidungen + +Am 2026-08-10 festgelegt: + +1. **Tod:** Ausrüstung wird beim Respawn abgenommen, die Flasche bleibt mit ihrer + Restluft im Inventar. → Phase 1: Death-Handler ruft `takeOffSuit()` ohne + Progressbar, Phase 2 schreibt vorher die Restluft in die Metadata. +2. **Taschenlampe:** fest an der Ausrüstung, kein eigenes Item. An/Aus per Keybind. + → Phase 5: Torch-Prop wird zusammen mit Maske und Flasche angehängt. +3. **Batterie:** keine. Lampe läuft, solange Sauerstoff da ist. + → `metadata.battery` entfällt komplett, HUD zeigt nur An/Aus. +4. **Tiefenabhängiger Verbrauch:** an per Default, in der Config abschaltbar. + → Phase 3: `decayRate * (1 + depth / 30)`, braucht den Tiefenwert aus Phase 4. + Deshalb Phase 4 vor dem Feintuning der Verbrauchskurve fertigstellen. + +## Offene Fragen + +1. **Gitea:** Repo unter `git.d4rkst3r.de/D4rkst3r/d4rk_divegear` — soll ich das + Remote vorbereiten? (Push machst du selbst.) + +--- + +## Testplan + +- Anlegen/Ablegen an Land, im flachen Wasser, unter Wasser +- Emote starten (rpemotes o.ä.) → Props bleiben +- Ragdoll (aus Höhe fallen, angefahren werden) → Props bleiben +- Kleiderladen / Skin-Change → Props hängen am neuen Ped +- Relog mit halbvoller Flasche → Restluft stimmt +- Resource-Restart während des Tauchens → Gear wird wiederhergestellt +- Zwei Spieler, beide mit Lampe → jeder sieht das Licht des anderen +- Tank leer unter Wasser → Scuba aus, Ertrinken setzt ein +``` diff --git a/fxmanifest.lua b/fxmanifest.lua index b262777..1e6d561 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -1,8 +1,9 @@ fx_version 'cerulean' game 'gta5' -description 'items to help players breathe and move underwater' -repository 'https://github.com/Qbox-project/qbx_divegear' +author 'D4rkst3r' +description 'Tauchausrüstung mit Flaschengrößen, gesyncter Lampe und Tiefenanzeige' +repository 'https://git.d4rkst3r.de/D4rkst3r/d4rk_divegear' version '1.0.1' ox_lib 'locale'