From d9643620ffca005a70413fa42164e636c5d08a00 Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Mon, 10 Aug 2026 23:56:40 +0200 Subject: [PATCH] feat(light): gesyncte Tauchlampe Licht ist in GTA nie synchronisiert - DrawSpotLight zeichnet nur lokal fuer den aktuellen Frame. "Gesynct" heisst deshalb: der Zustand wird als Statebag repliziert und jeder Client zeichnet die Lampen aller Taucher in seiner Naehe selbst. Muster uebernommen aus d4rk_phone (Statebag, adaptives Polling, Distanzfilter). client/light.lua ist ein reiner Renderer ohne gemeinsamen Zustand mit main.lua - er kommt mit dem Statebag allein aus und kennt weder Ausruestung noch Flasche. - Richtung ueber zwei bone-relative Punkte am Kopf-Bone statt ueber GetEntityForwardVector: beim Tauchen liegt der Ped waagerecht im Wasser, der Entity-Forward wuerde stur horizontal leuchten. Die Offsets von GetPedBoneCoords sind laut Native-Doku "relative to the bone's rotation", die Differenz zweier solcher Punkte enthaelt also den Pitch - Toggle per lib.addKeybind, Standard L, vom Spieler umlegbar - Distanzfilter und Limit auf die naechsten N Lampen, Schatten abschaltbar - Prop-Model wird vorher mit IsModelValid geprueft: lib.requestModel wirft bei ungueltigen Models einen Error, der sonst die ganze Ausruestung lahmlegt - Lampen-Status im HUD Co-Authored-By: Claude Opus 5 --- PLAN.md | 115 +++++++++++++++++++++++++++++----------------- client/light.lua | 112 ++++++++++++++++++++++++++++++++++++++++++++ client/main.lua | 53 ++++++++++++++++++++- config/client.lua | 41 +++++++++++++++++ fxmanifest.lua | 5 +- locales/de.json | 3 ++ locales/en.json | 3 ++ 7 files changed, 288 insertions(+), 44 deletions(-) create mode 100644 client/light.lua diff --git a/PLAN.md b/PLAN.md index d8538da..13bfef8 100644 --- a/PLAN.md +++ b/PLAN.md @@ -58,17 +58,19 @@ ohne Luft absaufen). Beide Werte sind jetzt als `maxTimeUnderwater` / ### Phase 2 — Sauerstoff-Logik neu (~1 h) -- [ ] Umstellung von "Punkte" auf **Restsekunden** (`oxygenSeconds`) -- [ ] Decrementer-Thread auf `GetGameTimer()`-Delta statt gezählter `Wait(1000)` +- [x] Umstellung von "Punkte" auf **Restsekunden** (`oxygenSeconds`) +- [x] Decrementer-Thread auf `GetGameTimer()`-Delta statt gezählter `Wait(1000)` (driftfrei, überlebt Frame-Drops) -- [ ] Float-Vergleiche raus, Warnschwellen explizit als Config-Liste +- [x] 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 +- [x] Anzeige als `mm:ss` statt roher Zahl +- [x] 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 +- [x] Beim Anlegen Metadata lesen → Restluft übernehmen +- [ ] **Offen:** Beim Spawn/Resource-Start die angelegte Ausrüstung automatisch + wieder anziehen. Die Restluft überlebt bereits (liegt auf der Flasche), aber + nach einem Relog steht man ohne Maske da und muss sie neu anlegen **Serverseitig validieren:** Der Client schickt den O2-Wert. Server prüft Plausibilität (nur Abnahme, nicht mehr als seit letztem Update vergehen konnte), @@ -85,13 +87,14 @@ Vier Items mit unterschiedlicher `capacity` in Sekunden: | `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 +- [x] `capacity` in `config/shared.lua` (Client *und* Server brauchen sie), Label + und Gewicht im Item-Snippet. Kein Prop-Scale — siehe Optik-Hinweis unten +- [x] **`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 +- [x] `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 +- [x] `diving_fill` füllt bis `capacity` des jeweiligen Tanks statt auf Fixwert +- [x] 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 @@ -104,42 +107,49 @@ 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 +- [x] 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 +- [x] Wert geglättet (Lerp) damit die Zahl nicht zappelt +- [x] 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 +- [x] 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 +- [x] Tiefe in Metern, Warnfarbe ab Config-Tiefe +- [x] Slot für den Lampen-Status (wird in Phase 5 befüllt) +- [x] Sichtbar nur wenn Gear angelegt **und** unter Wasser, mit Fade-In/Out +- [x] Updates gedrosselt per `SendNUIMessage` (~4/s statt jeden Frame) +- [x] Kein `backdrop-filter` (bekannter FiveM-Bug → schwarzer Hintergrund) +- [x] `hudEnabled`, `hudUpdateIntervalMs` und `deepWarningDepth` in `config/client.lua` +- [ ] **Offen:** Position und Größe des HUD sind bisher nur in `nui/style.css` + einstellbar, nicht in der Config ### 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 +- [x] 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)` +- [x] Toggle per `RegisterKeyMapping`/`lib.addKeybind` +- [x] State replizieren: `LocalPlayer.state:set('divelight', true, true)` — Statebag statt Event-Spam -- [ ] Draw-Thread: über `GetActivePlayers()`, `Player(serverId).state.divelight` +- [x] 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 +- [x] **Richtung anders gelöst als geplant:** statt Prop-Matrix und Objekt-Pool + werden zwei *bone-relative* Punkte am Kopf-Bone benutzt. Die Offsets von + `GetPedBoneCoords` sind laut Native-Doku "relative to the bone's rotation", + die Differenz zweier solcher Punkte ergibt also die Blickrichtung inklusive + Pitch. Das löst das Problem (`GetEntityForwardVector` würde beim waagerecht + im Wasser liegenden Ped stur horizontal leuchten), ohne fremde Props über + `GetGamePool('CObject')` suchen und cachen zu müssen — der Renderer braucht + dadurch überhaupt keinen Prop-Zugriff und kommt mit dem Statebag allein aus +- [x] Prop-Model wird über `IsModelValid`/`IsModelInCdimage` geprüft, weil + `lib.requestModel` bei ungültigen Models einen Error wirft. Ein falscher + Prop-Name kostet so nur die Optik, nicht die ganze Ausrüstung +- [x] 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, +- [x] Lampen-Status (An/Aus) in das HUD aus Phase 4 einhängen — keine Batterie, die Lampe läuft solange Sauerstoff da ist --- @@ -149,6 +159,11 @@ Taucher in seiner Nähe selbst. Die Config bleibt beim Upstream-Pattern (`config/client.lua` + `require`) statt beim `shared_scripts`-Template, damit spätere Upstream-Änderungen mergebar bleiben. +Tatsächlich gebaut (die geplante Aufteilung in `oxygen.lua`/`hud.lua` ist entfallen — +die teilen sich alle denselben Zustand mit `main.lua`, eine Trennung hätte nur +Plumbing erzeugt. `light.lua` hat dagegen eine echte Naht: es kommt mit dem +Statebag allein aus und kennt weder Ausrüstung noch Flasche): + ``` d4rk_divegear/ ├── fxmanifest.lua @@ -156,10 +171,8 @@ d4rk_divegear/ │ ├── 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 +│ ├── main.lua # Gear an/aus, Watchdog, Sauerstoff, HUD-Bridge +│ └── light.lua # Statebag + Draw-Thread, ohne gemeinsamen Zustand ├── nui/ │ ├── index.html │ ├── style.css @@ -205,10 +218,25 @@ Am 2026-08-10 festgelegt: → 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.) +## Offen + +Alles unten ist **ungetestet im Spiel** — es gibt hier keinen FiveM-Server. Geprüft +sind nur Lua-Syntax und Globals (`node ../.tools/lint.mjs`, 0 Warnungen), die +Locale-JSONs und das HUD im Browser. + +1. **Lampen-Prop `prop_cs_police_torch`** — Name ungeprüft. Ist er falsch, meldet + das Script eine Warnung und leuchtet ohne sichtbares Prop weiter. + Ersatz suchen: [forge.plebmasters.de/objects](https://forge.plebmasters.de/objects) +2. **`lampDirection`** — welche lokale Achse des Kopf-Bones nach vorne zeigt, hängt + am Skelett. Leuchtet der Kegel falsch herum, in `config/client.lua` die Achse + tauschen oder das Vorzeichen drehen. +3. **Auto-Anziehen nach Relog** (siehe Phase 2) und **HUD-Position in der Config** + (Phase 4) sind nicht umgesetzt. +4. **Balancing:** `maxDrainPerSecond = 5.0` muss über `maxDecayFactor = 4.0` liegen, + sonst bekommen ehrliche Spieler in großer Tiefe vom Server Luft zurückgeschrieben. + Wer den Tiefenfaktor hochdreht, muss beides nachziehen. --- @@ -221,5 +249,10 @@ Am 2026-08-10 festgelegt: - 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 +- Lampe an, senkrecht nach unten tauchen → Kegel folgt der Blickrichtung nach unten + (das ist der Fall, an dem `GetEntityForwardVector` gescheitert wäre) - Tank leer unter Wasser → Scuba aus, Ertrinken setzt ein +- Mehrere Flaschen im Inventar → Auswahlmenü mit Füllstand pro Flasche +- Volle Flasche auffüllen → Meldung, Kartusche wird **nicht** verbraucht +- Flasche im Inventar ansehen → durability-Balken zeigt den Füllstand ``` diff --git a/client/light.lua b/client/light.lua new file mode 100644 index 0000000..ea98fc2 --- /dev/null +++ b/client/light.lua @@ -0,0 +1,112 @@ +local config = require 'config.client' + +-- Reiner Renderer: zeichnet die Lampen aller Taucher in der Nähe, gesteuert +-- ausschließlich über den replizierten Statebag. Kein gemeinsamer Zustand mit +-- client/main.lua - das hier läuft auch für Spieler, deren Ausrüstung wir gar +-- nicht kennen. +-- +-- Licht ist in GTA nie synchronisiert: DrawSpotLight zeichnet nur lokal, für den +-- aktuellen Frame. "Gesynct" heißt deshalb, dass jeder Client das Licht jedes +-- anderen Tauchers selbst zeichnet. + +local STATE_KEY = 'divelight' +local HEAD_BONE = 31086 -- SKEL_Head + +--- Kandidaten des aktuellen Frames. Wird wiederverwendet statt jedes Frame neu +--- angelegt, damit der GC bei Dauerbetrieb nichts zu tun bekommt. +local candidates = {} + +--- Position und Richtung der Lampe eines Peds. +--- +--- Die Offsets von GetPedBoneCoords sind laut Native-Doku "relative to the bone's +--- rotation" - zwei bone-relative Punkte ergeben also die echte Blickrichtung +--- inklusive Pitch. Genau das wird beim Tauchen gebraucht: der Ped liegt waagerecht +--- im Wasser, GetEntityForwardVector würde die Lampe stur horizontal leuchten lassen. +local function getLampVectors(ped) + local offset = config.lampOffset + local direction = config.lampDirection + + local from = GetPedBoneCoords(ped, HEAD_BONE, offset.x, offset.y, offset.z) + local ahead = GetPedBoneCoords(ped, HEAD_BONE, + offset.x + direction.x, + offset.y + direction.y, + offset.z + direction.z) + + local delta = ahead - from + local length = #delta + + if length <= 0 then return end + + return from, delta / length +end + +local function drawLamp(ped) + local from, direction = getLampVectors(ped) + + if not from then return end + + local colour = config.lampColour + + if config.lampShadows then + DrawSpotLightWithShadow(from.x, from.y, from.z, direction.x, direction.y, direction.z, + colour[1], colour[2], colour[3], + config.lampDistance, config.lampBrightness, config.lampRoundness, + config.lampRadius, config.lampFalloff, 0) + else + DrawSpotLight(from.x, from.y, from.z, direction.x, direction.y, direction.z, + colour[1], colour[2], colour[3], + config.lampDistance, config.lampBrightness, config.lampRoundness, + config.lampRadius, config.lampFalloff) + end +end + +CreateThread(function() + while true do + local players = GetActivePlayers() + local myCoords = GetEntityCoords(cache.ped) + local count = 0 + + for i = 1, #players do + local player = players[i] + local state = Player(GetPlayerServerId(player)).state + + if state and state[STATE_KEY] then + local ped = GetPlayerPed(player) + + if ped ~= 0 and DoesEntityExist(ped) then + local distance = #(myCoords - GetEntityCoords(ped)) + + if distance < config.lampDrawDistance then + count += 1 + local entry = candidates[count] + + if entry then + entry.ped, entry.distance = ped, distance + else + candidates[count] = { ped = ped, distance = distance } + end + end + end + end + end + + -- Reste des letzten Frames abschneiden, sonst sortiert table.sort veraltete + -- Einträge mit und ein alter, näherer Eintrag verdrängt einen echten. + for i = #candidates, count + 1, -1 do + candidates[i] = nil + end + + -- Nur sortieren wenn tatsächlich mehr Lampen da sind als gezeichnet werden + -- dürfen. DrawSpotLightWithShadow ist teuer, in einer vollen Höhle sonst + -- der erstbeste Frame-Killer. + if count > config.lampMaxLights then + table.sort(candidates, function(a, b) return a.distance < b.distance end) + end + + for i = 1, math.min(count, config.lampMaxLights) do + drawLamp(candidates[i].ped) + end + + Wait(count > 0 and 0 or 300) + end +end) diff --git a/client/main.lua b/client/main.lua index 1e67487..7c8c5d3 100644 --- a/client/main.lua +++ b/client/main.lua @@ -4,9 +4,14 @@ local shared = require 'config.shared' local currentGear = { mask = 0, tank = 0, + lamp = 0, enabled = false } +--- Lampe an? Der eigentliche Lichtkegel wird in client/light.lua gezeichnet, +--- gesteuert über den replizierten Statebag. +local lampOn = false + --- Aktuell getragene Flasche. { slot, name, label, oxygen, capacity } --- oxygen sind Restsekunden, kein Prozentwert. local activeTank = nil @@ -86,6 +91,12 @@ local function deleteGear() DeleteEntity(currentGear.tank) currentGear.tank = 0 end + + if currentGear.lamp ~= 0 then + DetachEntity(currentGear.lamp, false, true) + DeleteEntity(currentGear.lamp) + currentGear.lamp = 0 + end end --- Hängt ein Prop hart an einen Ped-Bone. @@ -111,11 +122,34 @@ local function attachProp(ped, model, bone, offset, rotation) return prop end +--- Lampen-Model einmalig prüfen. lib.requestModel wirft bei ungültigen Models einen +--- Error - ohne diese Prüfung würde ein falsch geschriebener Prop-Name die komplette +--- Ausrüstung unbenutzbar machen, statt nur die Optik der Lampe zu kosten. +local lampModel = joaat(config.lampProp) +local hasLampProp = IsModelValid(lampModel) or IsModelInCdimage(lampModel) + +if not hasLampProp then + lib.print.warn(('lampProp "%s" ist kein gültiges Model - die Lampe leuchtet, das Prop fehlt.'):format(config.lampProp)) +end + --- @param ped number? default cache.ped local function attachGear(ped) ped = ped or cache.ped currentGear.tank = attachProp(ped, TANK_MODEL, TANK_BONE, vec3(-0.25, -0.25, 0.0), vec3(180.0, 90.0, 0.0)) currentGear.mask = attachProp(ped, MASK_MODEL, MASK_BONE, vec3(0.0, 0.0, 0.0), vec3(180.0, 90.0, 0.0)) + + if hasLampProp then + currentGear.lamp = attachProp(ped, lampModel, MASK_BONE, vec3(0.1, 0.05, 0.0), vec3(180.0, 90.0, 0.0)) + end +end + +--- @param state boolean +local function setLamp(state) + if not currentGear.enabled then state = false end + + lampOn = state + -- Repliziert: client/light.lua zeichnet daraus die Lampen aller Taucher. + LocalPlayer.state:set('divelight', state, true) end local function isPropIntact(prop) @@ -123,6 +157,8 @@ local function isPropIntact(prop) end local function isGearIntact() + if hasLampProp and not isPropIntact(currentGear.lamp) then return false end + return isPropIntact(currentGear.mask) and isPropIntact(currentGear.tank) end @@ -151,6 +187,7 @@ local function removeGear(outOfAir, immediateSync) syncTank(immediateSync) currentGear.enabled = false + setLamp(false) activeTank = nil warned = {} @@ -272,8 +309,8 @@ local function sendHudState(visible) depth = currentDepth, level = level, deep = currentDepth >= config.deepWarningDepth, - hasLamp = false, - lamp = false + hasLamp = true, + lamp = lampOn }) end @@ -441,6 +478,17 @@ RegisterNetEvent('d4rk_divegear:client:useGear', function() end end) +lib.addKeybind({ + name = 'divegear_lamp', + description = locale('keybind.toggle_lamp'), + defaultKey = config.lampKey, + onPressed = function() + if not currentGear.enabled then return end + + setLamp(not lampOn) + end +}) + --- Beim Model-Wechsel (Kleiderladen, Skin-Change) ist der Ped ein anderer - --- alle attached Props sind weg und die Scuba-Flags gelten für den alten Ped. --- @@ -463,6 +511,7 @@ AddEventHandler('onResourceStop', function(resource) if resource ~= GetCurrentResourceName() then return end syncTank(true) + setLamp(false) deleteGear() SendNUIMessage({ action = 'state', visible = false }) SetEnableScuba(cache.ped, false) diff --git a/config/client.lua b/config/client.lua index 40c414b..6b5c309 100644 --- a/config/client.lua +++ b/config/client.lua @@ -38,4 +38,45 @@ return { --- Ab dieser Tiefe wird die Tiefenanzeige eingefärbt (Meter). deepWarningDepth = 25.0, + + --------------------------------------------------------------------------- + -- Taschenlampe + --------------------------------------------------------------------------- + + --- Taste zum An- und Ausschalten. Der Spieler kann sie in den FiveM- + --- Tastenbelegungen unter "FiveM" umlegen. + lampKey = 'l', + + --- Prop am Kopf. Rein optisch - das Licht selbst hängt nicht daran. + --- Ist das Model ungültig, wird nur das Licht gezeichnet und eine Warnung + --- ins Log geschrieben. Alternativen: siehe forge.plebmasters.de/objects + lampProp = 'prop_cs_police_torch', + + --- Position des Lichts relativ zum Kopf-Bone (bone-relativ, nicht Weltkoordinaten). + lampOffset = vec3(0.0, 0.15, 0.0), + + --- Richtung des Lichtkegels, ebenfalls bone-relativ. Leuchtet der Kegel in die + --- falsche Richtung, hier die Achse tauschen oder das Vorzeichen drehen - + --- welche lokale Achse des Kopf-Bones nach vorne zeigt, hängt am Skelett. + lampDirection = vec3(0.0, 1.0, 0.0), + + --- Lichtfarbe als { r, g, b }, 0-255. Leicht kühl für Unterwasser. + lampColour = { 225, 245, 255 }, + + lampDistance = 30.0, -- Reichweite in Metern + lampBrightness = 6.0, -- Helligkeit + lampRoundness = 1.0, -- Weichzeichnung des Kegelrands + lampRadius = 14.0, -- Öffnungswinkel + lampFalloff = 1.5, -- Abfall zum Rand hin + + --- Schatten kosten deutlich Performance. Auf false wird DrawSpotLight statt + --- DrawSpotLightWithShadow benutzt - sieht flacher aus, läuft aber spürbar besser. + lampShadows = true, + + --- Ab dieser Entfernung werden fremde Lampen nicht mehr gezeichnet (Meter). + lampDrawDistance = 45.0, + + --- Wie viele Lampen gleichzeitig gezeichnet werden. Die nächstgelegenen + --- gewinnen. Schützt vor Frame-Einbrüchen bei Gruppentauchgängen. + lampMaxLights = 4, } diff --git a/fxmanifest.lua b/fxmanifest.lua index 5737f94..58798d0 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -15,7 +15,10 @@ shared_scripts { server_script 'server/main.lua' -client_script 'client/main.lua' +client_scripts { + 'client/main.lua', + 'client/light.lua', +} ui_page 'nui/index.html' diff --git a/locales/de.json b/locales/de.json index 2b284d9..134e009 100644 --- a/locales/de.json +++ b/locales/de.json @@ -26,5 +26,8 @@ "air": "Luft", "depth": "Tiefe", "lamp": "Lampe" + }, + "keybind": { + "toggle_lamp": "Tauchlampe an/aus" } } diff --git a/locales/en.json b/locales/en.json index c8d5ce8..6b48969 100644 --- a/locales/en.json +++ b/locales/en.json @@ -26,5 +26,8 @@ "air": "Air", "depth": "Depth", "lamp": "Lamp" + }, + "keybind": { + "toggle_lamp": "Toggle dive lamp" } }