rp_core (sessions, scrypt auth, self-written MySQL-wire driver rp_db), county-records NUI: residency-file auth, identity-record character intake, survey-grid spawn, procedural night-city loading screen. Secrets replaced with .example files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
189 lines
5.6 KiB
Lua
189 lines
5.6 KiB
Lua
-- ---------------------------------------------------------------------------
|
|
-- Scripted cameras for the pre-spawn screens.
|
|
--
|
|
-- One camera object is reused throughout. Everything is driven from a single
|
|
-- render thread that eases the live values towards target values, so a change
|
|
-- requested by the UI never snaps.
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
Cam = {}
|
|
|
|
local cam = nil
|
|
local mode = 'off' -- 'off' | 'shots' | 'orbit' | 'fly'
|
|
local renderThread = false
|
|
|
|
-- live and target orbit values
|
|
local orbit = {
|
|
angle = 180.0, targetAngle = 180.0,
|
|
radius = 1.6, targetRadius = 1.6,
|
|
height = 0.65, targetHeight = 0.65,
|
|
focus = nil, -- entity to orbit
|
|
pitch = 0.0, targetPitch = 0.0,
|
|
}
|
|
|
|
local shots = { list = {}, index = 0, startedAt = 0, duration = 22000 }
|
|
local fly = { from = nil, to = nil, lookFrom = nil, lookTo = nil, startedAt = 0, duration = 3000, done = nil }
|
|
|
|
local function ensureCam()
|
|
if cam and DoesCamExist(cam) then return cam end
|
|
cam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true)
|
|
SetCamActive(cam, true)
|
|
RenderScriptCams(true, false, 0, true, true)
|
|
return cam
|
|
end
|
|
|
|
local function lerp(a, b, t) return a + (b - a) * t end
|
|
|
|
local function vlerp(a, b, t)
|
|
return vector3(lerp(a.x, b.x, t), lerp(a.y, b.y, t), lerp(a.z, b.z, t))
|
|
end
|
|
|
|
-- smoothstep, so fly-throughs start and end at rest
|
|
local function ease(t)
|
|
t = math.max(0.0, math.min(1.0, t))
|
|
return t * t * (3.0 - 2.0 * t)
|
|
end
|
|
|
|
local function startRenderThread()
|
|
if renderThread then return end
|
|
renderThread = true
|
|
|
|
CreateThread(function()
|
|
while mode ~= 'off' do
|
|
local c = ensureCam()
|
|
|
|
if mode == 'shots' then
|
|
local shot = shots.list[shots.index]
|
|
if shot then
|
|
local t = (GetGameTimer() - shots.startedAt) / shots.duration
|
|
if t >= 1.0 then
|
|
shots.index = (shots.index % #shots.list) + 1
|
|
shots.startedAt = GetGameTimer()
|
|
shot = shots.list[shots.index]
|
|
t = 0.0
|
|
end
|
|
-- a slow linear dolly reads as a helicopter, not a spline
|
|
local pos = vlerp(shot.from, shot.to, t)
|
|
SetCamCoord(c, pos.x, pos.y, pos.z)
|
|
PointCamAtCoord(c, shot.look.x, shot.look.y, shot.look.z)
|
|
end
|
|
|
|
elseif mode == 'orbit' then
|
|
orbit.angle = lerp(orbit.angle, orbit.targetAngle, 0.14)
|
|
orbit.radius = lerp(orbit.radius, orbit.targetRadius, 0.10)
|
|
orbit.height = lerp(orbit.height, orbit.targetHeight, 0.10)
|
|
orbit.pitch = lerp(orbit.pitch, orbit.targetPitch, 0.10)
|
|
|
|
local ent = orbit.focus
|
|
if ent and DoesEntityExist(ent) then
|
|
local base = GetEntityCoords(ent)
|
|
local rad = math.rad(orbit.angle)
|
|
local px = base.x + math.sin(rad) * orbit.radius
|
|
local py = base.y + math.cos(rad) * orbit.radius
|
|
local pz = base.z + orbit.height
|
|
SetCamCoord(c, px, py, pz)
|
|
PointCamAtCoord(c, base.x, base.y, base.z + orbit.height - orbit.pitch)
|
|
end
|
|
|
|
elseif mode == 'fly' then
|
|
local t = ease((GetGameTimer() - fly.startedAt) / fly.duration)
|
|
local pos = vlerp(fly.from, fly.to, t)
|
|
local look = vlerp(fly.lookFrom, fly.lookTo, t)
|
|
SetCamCoord(c, pos.x, pos.y, pos.z)
|
|
PointCamAtCoord(c, look.x, look.y, look.z)
|
|
if t >= 1.0 then
|
|
mode = 'idle'
|
|
if fly.done then
|
|
local fn = fly.done
|
|
fly.done = nil
|
|
fn()
|
|
end
|
|
end
|
|
end
|
|
|
|
Wait(0)
|
|
end
|
|
renderThread = false
|
|
end)
|
|
end
|
|
|
|
--- Slow establishing shots over the city, cycled forever.
|
|
function Cam.Shots(list)
|
|
shots.list = list
|
|
shots.index = 1
|
|
shots.startedAt = GetGameTimer()
|
|
mode = 'shots'
|
|
ensureCam()
|
|
SetCamFov(cam, 42.0)
|
|
startRenderThread()
|
|
end
|
|
|
|
--- Orbit an entity. `snap` places the camera immediately instead of easing in.
|
|
function Cam.Orbit(entity, radius, height, angle, snap)
|
|
orbit.focus = entity
|
|
orbit.targetRadius = radius or orbit.targetRadius
|
|
orbit.targetHeight = height or orbit.targetHeight
|
|
if angle then orbit.targetAngle = angle end
|
|
if snap then
|
|
orbit.radius = orbit.targetRadius
|
|
orbit.height = orbit.targetHeight
|
|
orbit.angle = orbit.targetAngle
|
|
end
|
|
mode = 'orbit'
|
|
ensureCam()
|
|
SetCamFov(cam, 34.0)
|
|
startRenderThread()
|
|
end
|
|
|
|
--- Framing presets used by the creator's section tabs.
|
|
function Cam.OrbitFrame(radius, height, pitch)
|
|
orbit.targetRadius = radius
|
|
orbit.targetHeight = height
|
|
orbit.targetPitch = pitch or 0.0
|
|
end
|
|
|
|
function Cam.Nudge(deltaDegrees)
|
|
orbit.targetAngle = orbit.targetAngle + deltaDegrees
|
|
end
|
|
|
|
function Cam.SetAngle(deg)
|
|
orbit.targetAngle = deg
|
|
end
|
|
|
|
function Cam.Angle() return orbit.targetAngle end
|
|
|
|
--- Fly from wherever we are to a point looking at a target, then call `done`.
|
|
function Cam.FlyTo(fromPos, fromLook, toPos, toLook, duration, done)
|
|
fly.from = fromPos
|
|
fly.to = toPos
|
|
fly.lookFrom = fromLook
|
|
fly.lookTo = toLook
|
|
fly.duration = duration or 3000
|
|
fly.startedAt = GetGameTimer()
|
|
fly.done = done
|
|
mode = 'fly'
|
|
ensureCam()
|
|
startRenderThread()
|
|
end
|
|
|
|
function Cam.SetFov(fov)
|
|
if cam and DoesCamExist(cam) then SetCamFov(cam, fov + 0.0) end
|
|
end
|
|
|
|
function Cam.Position()
|
|
if cam and DoesCamExist(cam) then return GetCamCoord(cam) end
|
|
return GetEntityCoords(PlayerPedId())
|
|
end
|
|
|
|
--- Hand control back to the gameplay camera. With a duration the engine
|
|
--- interpolates between the two, which hides the cut entirely.
|
|
function Cam.Release(duration)
|
|
mode = 'off'
|
|
duration = duration or 0
|
|
RenderScriptCams(false, duration > 0, duration, true, true)
|
|
if cam and DoesCamExist(cam) then
|
|
DestroyCam(cam, true)
|
|
end
|
|
cam = nil
|
|
end
|