feat(light): auf die spieleigene Scuba-Lampe umgestellt
Lint / Lint Resource (push) Has been cancelled
Lint / Lint Resource (push) Has been cancelled
GTA bringt seit v1493 eine eigene Lampe an der Tauchausruestung 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. Damit faellt der komplette
DrawSpotLight-Aufbau weg: Kegelwerte, Farbe, Schatten, Distanzfilter,
Lichter-Limit, die bone-relative Richtungsrechnung und das Sortieren der
Kandidaten pro Frame. client/light.lua schrumpft von ~110 auf ~55 Zeilen.
Gefunden ueber wobozkyng/esx_scuba.
- Der Flag ist ein lokaler Ped-Zustand, der Statebag bleibt also. Gesetzt wird
er per AddStateBagChangeHandler und zusaetzlich alle 500ms nachgezogen, weil
Peds beim Streaming, Respawn und Model-Wechsel neu erzeugt werden
- Die Lampe haengt an der Scuba-Kleidung (Component 8). Das Outfit wird bewusst
nicht angefasst; wer sie nicht traegt, bekommt beim Einschalten eine Meldung
statt stiller Wirkungslosigkeit
- /divelampcheck sagt, ob das aktuelle Outfit eine Lampe hat
- Prop ist jetzt optional und per Default aus
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+35
-86
@@ -1,111 +1,60 @@
|
||||
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.
|
||||
-- Tauchlampe über die spieleigene Scuba-Lampe.
|
||||
--
|
||||
-- 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.
|
||||
-- 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 eigenen Draw-Thread.
|
||||
--
|
||||
-- WICHTIG: Die Lampe hängt an der echten Scuba-Kleidung des Peds (Component 8).
|
||||
-- Trägt der Spieler die nicht, tut die Native nichts - dieses Script fasst das
|
||||
-- Outfit bewusst nicht an.
|
||||
--
|
||||
-- Der Flag ist ein lokaler Ped-Zustand und wird nicht von selbst repliziert.
|
||||
-- Deshalb wie vorher der Umweg über den Statebag: jeder Client setzt den Flag für
|
||||
-- jeden Taucher in seiner Nähe selbst.
|
||||
|
||||
local STATE_KEY = 'divelight'
|
||||
|
||||
--- Kandidaten des aktuellen Frames. Wird wiederverwendet statt jedes Frame neu
|
||||
--- angelegt, damit der GC bei Dauerbetrieb nichts zu tun bekommt.
|
||||
local candidates = {}
|
||||
--- @param ped number
|
||||
--- @param enabled boolean
|
||||
local function applyLight(ped, enabled)
|
||||
if ped == 0 or not DoesEntityExist(ped) then return end
|
||||
|
||||
--- 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
|
||||
-- Nur setzen wenn es sich tatsächlich ändert. Die Native ist billig, aber der
|
||||
-- Aufruf pro Ped und Durchlauf wäre trotzdem unnötig.
|
||||
if IsScubaGearLightEnabled(ped) == enabled then return end
|
||||
|
||||
local from = GetPedBoneCoords(ped, config.lampBone, offset.x, offset.y, offset.z)
|
||||
local ahead = GetPedBoneCoords(ped, config.lampBone,
|
||||
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
|
||||
SetEnableScubaGearLight(ped, enabled)
|
||||
end
|
||||
|
||||
local function drawLamp(ped)
|
||||
local from, direction = getLampVectors(ped)
|
||||
--- Reagiert sofort auf Änderungen, auch von entfernten Spielern.
|
||||
AddStateBagChangeHandler(STATE_KEY, nil, function(bagName, _, value)
|
||||
local player = GetPlayerFromStateBagName(bagName)
|
||||
|
||||
if not from then return end
|
||||
if player == 0 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
|
||||
applyLight(GetPlayerPed(player), value == true)
|
||||
end)
|
||||
|
||||
--- Der Statebag-Handler allein reicht nicht: Peds werden beim Streaming neu
|
||||
--- erzeugt, beim Respawn ausgetauscht und beim Model-Wechsel ersetzt - der Flag
|
||||
--- ist dann wieder weg, ohne dass sich der Statebag geändert hat. Deshalb
|
||||
--- regelmäßig nachziehen.
|
||||
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
|
||||
applyLight(GetPlayerPed(player), state and state[STATE_KEY] == true)
|
||||
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)
|
||||
Wait(config.lampReconcileIntervalMs)
|
||||
end
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user