Configuration

All settings live in hz_television/config.lua. This page covers every section — general, display, placement, DashScreen, billboards, channels, localization.

Framework / target / menu / notification / inventory detection is handled centrally by hz_lib — see hz_lib Configuration. HZ-Television's config.lua no longer contains a Bridge section.

General

Config.Locale = 'en'   -- 'en' | 'fr' | 'es' | 'de'
Config.Debug  = false  -- detailed console logs


Permissions

Config.Permissions = {
    ConfiguratorCommand = 'admin',    -- /tvcfg
    BillboardCommand    = 'admin',    -- /billboard
    UseTV               = 'everyone', -- interact with any TV
    PlaceTV             = 'everyone', -- place from inventory
}

FormatMeaning
'everyone'anyone can use
'admin'framework / ACE admins only
'job:police'single job gate
'job:ambulance,police'multi-job gate (comma-separated)

Display & Audio

Config.Display = {
    MaxSoundDistance          = 15.0,  -- meters; max range to hear TV
    MinSoundDistance          = 2.0,   -- meters; full volume range
    DefaultVolume             = 0.5,   -- 0.0 – 1.0
    RenderDistance            = 50.0,  -- meters; cut render past this
    DefaultRange              = 25.0,  -- detection range (v3.1+)
    InteractionMode           = 'raycast', -- 'raycast' (free cursor) | 'camera' (locked)
    RaycastMaxDistance        = 5.0,   -- auto-bumped to cam-distance × 2 on large props
    SpritePolyScaleMultiplier = 32.0,  -- internal render scale factor
    ClockMode                 = 'game',-- 'game' | 'real'
}

Volume decreases linearly between MinSoundDistance (max volume) and MaxSoundDistance (silent).


TV Placement & Abandon Protection

New in v3.4 — road guard + abandon-protection fields below.

Config.Placer = {
    Enabled                 = true,
    Persistence             = true,   -- requires oxmysql

    -- Abandon protection (v3.4)
    MaxPerPlayer            = 0,      -- 0 = unlimited; N = hard cap per identifier
    AutoCleanupDays         = 0,      -- 0 = disabled; N = auto-remove after N days inactive
    AutoCleanupInterval     = 6,      -- hours between cleanup ticks (0 = startup only)
    CleanupAdminPermission  = 'admin',-- ACE perm for /tvclean

    -- Road guard (v3.4)
    BlockOnRoad             = false,  -- true = red preview + block Enter when on road
                                      -- DashScreen in-vehicle placement is always exempt

    -- Legacy
    AllowPickup             = true,
    OnlyOwnerPickup         = true,   -- admin can always pick up

    -- Item → model mapping (pre-filled with the 10 shipped items)
    Items = {
        ['hz_tv_s']        = 'hz_tv_s',
        ['hz_tv_m']        = 'hz_tv_m',
        ['hz_tv_l']        = 'hz_tv_l',
        ['hz_tv_s_stand']  = 'hz_tv_s_stand',
        ['hz_tv_m_stand']  = 'hz_tv_m_stand',
        ['hz_tv_l_stand']  = 'hz_tv_l_stand',
        ['curved_screen_s'] = 'prop_curved_screen_s',
        ['curved_screen_m'] = 'prop_curved_screen_m',
        ['curved_screen_l'] = 'prop_curved_screen_l',
        ['hz_dashscreen']     = 'hz_dashscreen',   -- DashScreen — 11 more variants auto-registered
    },
}

Road Guard

Checks GetClosestVehicleNode around the preview coords. If the prop sits on a road:


  • Outline turns red (instead of the normal cyan)

  • Enter / Confirm is blocked — the player must move the prop off the road first

  • DashScreen in-vehicle placement is exempt (the whole point of DashScreen is to mount on a moving vehicle)


Abandon Protection

Prevents long-running servers from filling up with TVs and DashScreens left behind by players who never come back.

  • MaxPerPlayer — hard cap per owner identifier. On DashScreen, upserts-on-plate count as replace (same car re-placement doesn't increment)
  • AutoCleanupDays — owners who haven't connected in N days get their placements removed. Recommended 30–60
  • AutoCleanupInterval — how often the server re-scans (in hours; 0 = startup only)
  • last_seen column on hz_television_placed and hz_dashscreen_placed — refreshed on playerJoining and every interaction. Additive migration runs at startup (ALTER TABLE ADD COLUMN IF NOT EXISTS)

/tvclean Admin Command

Sub-commandAction
/tvclean listTop 20 owners by inactivity — identifier, placement count, days inactive
/tvclean runTrigger auto-cleanup pass immediately (honors AutoCleanupDays)
/tvclean purge <identifier>Wipe ALL placements (TVs + DashScreens) for a specific identifier
Gated via Config.Placer.CleanupAdminPermission (ACE). Also exports["hz_television"]:RunHZTVCleanup().

TV Models

Each interactive screen is an entry in Config.Models keyed by model name:

Config.Models = {
    ['hz_tv_m'] = {
        Offset   = vector3(0.0, -0.02, 0.55),
        Scale    = { x = 0.42, y = 0.236 },       -- half-width / half-height
        Rotation = { x = 0.0, y = 0.0, z = 0.0 },
        RenderTexture = { dict = 'hz_tv_m', name = 'hz_tv_m_screen' },
    },

    -- Curved cinema screen (v3.3)
    ['prop_curved_screen_m'] = {
        Offset = vector3(0.0, -0.2, 2.7),
        Scale  = { x = 2.80, y = 1.575 },
        Rotation = { x = 0.0, y = 0.0, z = 0.0 },
        Curve  = {
            Radius = 23.00,   -- physical arc radius (meters)
            Arc    = 25.11,   -- arc sweep angle (degrees)
            Invert = false,   -- true = concave toward viewer
        },
        RenderTexture = { dict = 'prop_curved_screen_m', name = 'curved_screen_m' },
    },

    -- Vanilla GTA V huge billboard (render-target auto-detected)
    ['prop_huge_display_01'] = {
        Offset   = vector3(0.0, 0.15, 0.0),
        Scale    = { x = 8.0, y = 4.5 },
        Rotation = { x = 0.0, y = 0.0, z = 0.0 },
        RenderTexture = { dict = 'big_disp', name = 'big_disp' },  -- same dict/name = render target
    },
}

Adding a Custom Model

  1. Add an entry keyed by model name to Config.Models
  2. Launch in-game, get near the prop
  3. Run /tvcfg → select the model → the 3D gizmo lets you calibrate Scale / Offset / Rotation live
  4. For curved props, enable the cylindrical ray-cast: tick Curve, set Radius + Arc + (optionally) Invert. A live cyan arc overlay shows the exact hit-box
  5. Save writes the calibration to tv_config.json (persists across restarts)
The live preview overlay uses the same formula as RaycastTVCylinder — so "the cyan arc aligned with the prop surface" ↔ "clicking is accurate". If the overlay is off, so is the cursor.

SmartTV on Billboards & Cinema (v3.3)

Opt-in per-model: which billboard / cinema screens run the full Smart TV UI instead of the cyberpunk Ghost-Protocol billboard panel.

Config.BillboardUseSmartTV = {
    -- Curved cinema screens — always opt-in
    ['prop_curved_screen_s'] = true,
    ['prop_curved_screen_m'] = true,
    ['prop_curved_screen_l'] = true,

    -- Vanilla GTA V huge billboards — auto render-target detection in v3.3
    ['prop_huge_display_01'] = true,
    ['prop_huge_display_02'] = true,

    -- Add any other RenderTexture model here to enable Smart TV on it
}

Anything not in this list keeps the classic Ghost Protocol NUI panel (still fully functional for set-and-forget billboards).


DashScreen (v3.2 + v3.3 audio + v3.4 gizmo QoL)

DashScreen doesn't live in Config.Placer.Items alone — the dedicated config block tunes placement UX, audio occlusion, and the gizmo.

Enable / Disable DashScreen (v3.4.5)

Master switch — set Enabled = false to skip the entire DashScreen feature (item registration, target options, placer prompt, reconciliation tick). Wall / cinema / billboard TVs are unaffected.

Config.DashScreen = {
    Enabled = true,  -- v3.4.5+; set false to disable the in-vehicle DashScreen variant entirely
}

Cabin Audio Occlusion (v3.3)

The Web Audio pipeline inside the DashScreen DUI simulates RAGE's native vehicle sound filter (lowpass + bass shelf) because DUI audio bypasses the engine's own occlusion. No config usually needed — the defaults match RAGE's behavior — but the ranges can be tuned:

Config.DashScreen = {
    Enabled          = true,  -- v3.4.5+; master switch
    AudioCabinRange  = 5.0,   -- meters; beyond this, listeners outside the car hear silence
    SealedLowpass    = 600,   -- Hz cutoff when all doors closed
    OpenLowpass      = 2200,  -- Hz cutoff when all doors open
    SealedBassBoost  = 6,     -- dB peaking shelf at 80 Hz when sealed (decays as doors open)
    SealedGain       = 0.25,  -- master volume multiplier when sealed
    OpenGain         = 0.70,  -- master volume multiplier all doors open
    TransitionMs     = 80,    -- linearRampToValueAtTime smoothing for door-state changes
}

Placement UX (v3.4)

While the keyboard gizmo is open in a vehicle:

  • Vehicle frozen — FreezeEntityPosition(vehicle, true) + velocity zeroed, unfrozen on confirm / cancel. No drift
  • Vehicle controls disabled — throttle, brake, steering, handbrake cut (keepVehicleControls = false)
  • First-person cam reset — SetGameplayCamRelativeHeading(0.0) + SetGameplayCamRelativePitch(0.0, 1.0) so the cam faces forward regardless of where the player was aiming before

Pre-recorded Channels

Config.Channels = {
    {
        name     = 'Weazel News',
        url      = 'https://example.com/video.mp4',
        category = 'news',      -- optional grouping in the Smart TV UI
        thumbnail = 'https://...', -- optional card image
    },
    {
        name     = 'Los Santos Rock Radio',
        url      = 'https://example.com/stream.m3u8',
        category = 'music',
    },
}

Appears as a "Channels" section in the Smart TV home screen. Channels respect routing buckets and multi-player sync like any URL content.


Local Media

Host MP4 / MP3 files directly in hz_television/media/ and expose them as a "Local Media" section in the Smart TV and the LB Phone companion app:

Config.LocalMedia = {
    { title = "Server Intro",  file = "intro.mp4",  type = "video" },
    { title = "Ambient Radio", file = "radio.mp3",  type = "audio" },
    { title = "Event Clip",    file = "event.mp4",  type = "video" },
}

One tap plays the file on the current TV. Great for server intros, event clips, ambient radio loops, custom ads.


Silent Mode

One-tap mute for all nearby TVs — for content creators who need to cut game audio from their stream without touching other players' experience.

Config.Streamer = {
    Enabled            = true,
    DefaultMute        = false,
    ToggleCommand      = 'streamermode',  -- /streamermode
    AffectsNewScreens  = true,            -- auto-mute newly-detected screens (v3.3)
}

Controllable from other scripts via exports["hz_television"]:SetStreamerMode(true/false) — see Exports.


Commands

Config.Commands = {
    Configurator = 'tvcfg',        -- open live gizmo calibrator
    Billboard    = 'billboard',    -- edit nearby billboard content
    DashScreen      = 'dashscreen',      -- F5 keybind — interact with DashScreen in current vehicle
    DashScreenOn    = 'dashscreen_on',   -- power on + open SmartTV
    DashScreenRemove = 'dashscreenremove', -- remove DashScreen from current vehicle
    Cleanup      = 'tvclean',      -- admin cleanup CLI (v3.4)
    Streamer     = 'streamermode', -- toggle silent mode
}


Discord Webhooks (optional)

Config.Webhooks = {
    Enabled  = false,
    PlaceTV  = 'https://discord.com/api/webhooks/...',
    ConfigTV = 'https://discord.com/api/webhooks/...',
    Cleanup  = 'https://discord.com/api/webhooks/...',  -- v3.4 /tvclean audit log
}


Advanced Examples

Strict RP Server

Config.Permissions = {
    ConfiguratorCommand = 'admin',
    BillboardCommand    = 'job:police,admin',
    UseTV               = 'everyone',
    PlaceTV             = 'job:estate,admin',
}

Config.Placer = {
    Persistence          = true,
    MaxPerPlayer         = 3,        -- tight cap
    AutoCleanupDays      = 30,       -- sweep after 30 days inactive
    AutoCleanupInterval  = 6,
    BlockOnRoad          = true,     -- prevent road clutter
    OnlyOwnerPickup      = true,
}

Creative / Sandbox Server

Config.Permissions = {
    ConfiguratorCommand = 'everyone',
    BillboardCommand    = 'everyone',
    UseTV               = 'everyone',
    PlaceTV             = 'everyone',
}

Config.Placer = {
    Persistence          = true,
    MaxPerPlayer         = 0,         -- unlimited
    AutoCleanupDays      = 0,         -- disabled
    BlockOnRoad          = false,
    OnlyOwnerPickup      = false,     -- anyone can pick up
}


After editing config.lua, run restart hz_television from the server console — most settings take effect immediately without a full restart.