Skip to content

Broadcast Hijack

Broadcast Hijack is a FiveM Lua resource for hijacking a live Weazel-style news feed, letting one player run the broadcast while the rest of the server tunes in through a full-screen spectator overlay. It combines a physical camera-and-van setup flow, a live viewer overlay with chat, reactions, polls, and donations, and an optional post-broadcast revenue-ledger heist that branches into task-driven interiors and extraction minigames.

  • Hijack-Driven Broadcast Start: Retrieve a camera from an approved van, place the rig, attach the NHD, and take over the feed.
  • Viewer Tune-In Overlay: Players can tune in with commands or a hotkey and watch through the live spectator camera.
  • Live Audience Layer: Overlay supports chat, emotes, viewer count, polls, adverts, moderation, and SuperYap donations.
  • Static/Handheld Camera Modes: Broadcaster can switch between handheld live mode and tripod static shots during the session.
  • Revenue Ledger Heist: The broadcaster can convert the active session into a kf-tasks heist route with workstation tracing, rack hacking, extraction stalls, and follow-up cash pickup stages.
  • Framework/Inventory Adapters: Supports Ox, QBCore, QBox, and ESX framework modes, plus multiple inventory adapters.
  • News hijack scenes where a criminal crew takes over a live feed and the whole server watches it unfold.
  • Event servers that want a broadcast layer with audience interaction, donations, and on-screen polls.
  • Crime escalation loops where a successful broadcast can turn into a second-stage payout heist.

  1. Recommended stack:

    • ox_lib: must be version v3.30.0 or higher.
    • ox_target: the current target integration in this resource expects ox_target.
    • ox_inventory: recommended inventory provider for the setup below.
    • kf-tasks: required for the built-in revenue-ledger heist flow.

    Supported inventory adapters in kf-broadcast:

    • ox_inventory
    • codem-inventory
    • qs-inventory
    • qs-inventory-pro
    • origen_inventory
    • tgiann-inventory
  2. Move the folders kf-broadcast, kf-broadcast-assets, and the dependencies above into your server’s resources directory.

  3. server.cfg
    ensure ox_lib
    ensure ox_target
    ensure ox_inventory
    ensure kf-tasks
    ensure kf-broadcast-assets
    ensure kf-broadcast
  4. By default, Config.broadcast.startAce is false and Config.broadcast.allowedGroups is empty, so any player who passes the gameplay requirements can start a broadcast. If you want to lock it down, set an ACE permission and/or allowed framework groups in config.lua.

    Optional ACE example:

    server.cfg
    add_ace group.media kf.broadcast.start allow
  5. Add the broadcast items to your ox_inventory items config. Default location: resources/[ox]/ox_inventory/data/items.lua

    The example below is for ox_inventory. If you use another supported inventory, keep the same item names and adapt the item registration to that inventory’s format.

    items.lua
    ['kf_weazel_camera'] = {
    label = 'Weazel Camera',
    weight = 3000,
    stack = false,
    close = true,
    client = {
    export = 'kf-broadcast.deployCamera'
    }
    },
    ['kf_weazel_nhd'] = {
    label = 'Network Hijack Device',
    weight = 500,
    stack = false,
    close = true,
    },
    ['kf_weazel_revenue_ledger'] = {
    label = 'Copied Revenue Ledger',
    weight = 100,
    stack = false,
    close = true,
    },
  6. Add the images to the inventory images folder

    Section titled “Add the images to the inventory images folder”

    Add all the images files from kf-broadcast/inventory-images/ingame OR kf-broadcast/inventory-images/stylized into the ox_inventory images folder. Default location: resources/[ox]/ox_inventory/web/images

  7. Supply the broadcast hack device item through your own gameplay

    Section titled “Supply the broadcast hack device item through your own gameplay”

    Add the Network Hack Device (NHD) inventory item kf_weazel_nhd to your item dispenser method; you decide how players obtain the NHD in your server economy. Note: NHD can be disabled in the config Config.broadcast.hijack.requireNhd.


kf-broadcast-assets is required alongside kf-broadcast.

It does two jobs:

  • Streams the custom broadcast props used by the hijack flow: the camera, tripod, and NHD.
  • Streams a replacement YMAP that modifies the vehicle generator at the Weazel News HQ parking lot so a rumpo van can spawn there for the recommended setup.

This is separate from Config.broadcast.managedVans. The asset resource handles the streamed map/content side; the managed-van system is an optional server-side respawned van system in config.lua.


  • /tunein: viewer command to tune into the active broadcast.
  • /tuneout: viewer command to leave the active broadcast.
  • /broadcaststop: stops the active broadcast if the player has broadcast-start permission.
  • /broadcastpoll question | option 1 | option 2 | [option 3] | [option 4]: starts an audience poll during a live broadcast.
  • /broadcastpollstop: ends the active poll early.

By default, the viewer hotkey is also configured to J through Config.commands.tuneInKey.


The main configuration is located in config.lua.

config.lua
Config = {
debug = false,
notifications = true,
notify = 'ox_lib', -- 'ox_lib' | 'esx_notify' | 'qb-core' | 'qbx_core' | 'okokNotify' | 'wasabi_notify',
framework = 'auto', -- 'auto' | 'ox' | 'qbcore' | 'qbox' | 'esx'
target = 'ox_target',
inventory = 'auto', -- 'auto' | 'ox_inventory' | 'qs-inventory' | 'qs-inventory-pro' | 'codem-inventory' | 'origen_inventory' | 'tgiann-inventory'
commands = {
stop = 'broadcaststop',
tuneIn = 'tunein',
tuneOut = 'tuneout',
poll = 'broadcastpoll',
pollStop = 'broadcastpollstop',
tuneInKey = {
raw = 74, -- Virtual-key code, default J
label = 'J',
},
},
debugBlockTuneInOverlay = false,
tuneInPrepareTimeoutMs = 10000,
spectator = {
liveHandheldUseTeleportFollow = false, -- false = attach with teleport fallback, true = teleport only
},
headlineBannerEnabled = true,
headlineBannerDurationMs = 30000,
adverts = {
enabled = true,
movies = {
'WZL_Lifeinvader',
'WZL_Surano',
'WZL_Grand_Senora',
'WZL_BISON',
'WZL_EPSILON',
'WZL_Ammunation',
'WZL_Up_And_Atom',
},
tuneIn = {
enabled = true,
oncePerBroadcast = true,
displayDurationMs = 4000,
randomStartPercentMin = 0,
randomStartPercentMax = 80,
},
randomDuringBroadcast = {
enabled = false,
minDelaySec = 120,
maxDelaySec = 300,
requireViewer = true,
},
},
overlay = {
scanlineEnabled = true,
scanlineOpacity = 0.4, -- 0.0 to 1.0
tunePanelPosition = 'bottom-right', -- none | top-left | top-right | bottom-left | bottom-right
bootstrapChatMessageLimit = 10,
snapshotLatentBps = 50000,
},
hypeMeter = {
enabled = true,
triggerThreshold = 100,
maxHype = 150,
decayIntervalSec = 5,
decayAmount = 10,
triggerCooldownSec = 45,
viewerJoinPoints = 20,
chatMessagePoints = 10,
emotePoints = 4,
realSuperYapPoints = 45,
},
startAce = false, -- kf.broadcast.start | false
allowedGroups = {
--'admin',
},
headlineText = 'BREAKING NEWS: WEAZEL NEWS BROADCAST INTERCEPTED',
batteryDurationSec = 3600,
chatHistoryLimit = 50,
chatMaxLength = 120,
chatCooldownMs = 1500,
emoteCooldownMs = 250,
emoteBatch = {
enabled = true,
intervalMs = 100,
maxBatchSize = 32,
},
moderation = {
enabled = true,
ace = false, -- kf.broadcast.moderate | false
allowedGroups = {
'admin',
},
timeoutPresetsSec = {
60,
300,
900,
},
defaultTimeoutSec = 300,
},
hijack = {
debugDrawCaptureSphere = false,
interactionRadius = 2.5,
requireVanLink = true,
vanLinkRadius = 250.0,
vanLinkGraceSec = 60,
vanLinkWarningCooldownSec = 2,
tripodInteractRadius = 2.5,
tripodPlacementDistance = 10.0,
cameraItem = 'kf_weazel_camera',
nhdItem = 'kf_weazel_nhd',
showCameraRetrieveTarget = true,
grantNhdOnCameraRetrieve = false,
requireNhd = true,
allowedVans = {
{
model = 'rumpo',
liveries = {
0,
},
},
},
blip = {
enabled = true,
sprite = 184,
colour = 5,
scale = 0.85,
shortRange = true,
name = 'Broadcast Camera',
},
props = {
cameraModel = 'prop_kf_broadcast_cam',
nhdModel = 'prop_kf_broadcast_nhd',
tripodModel = 'prop_kf_broadcast_tri',
handBone = 28422,
handCameraOffset = vector3(0.0, 0.0, 0.0),
handCameraRotation = vector3(1.0, 0.0, 0.0),
cameraHold = {
dict = 'oddjobs@bailbond_mountain',
clip = 'Idle_Camman',
blendIn = 8.0,
blendOut = 8.0,
duration = -1,
playbackRate = 1.0,
lockX = false,
lockY = false,
lockZ = false,
reapplyIntervalMs = 500,
},
nhdOffset = vector3(-0.261731, 0.036752, 0.087659),
nhdRotation = vector3(180.0, 0.0, 0.0),
tripodCameraOffset = vector3(0.0, 0.0, 1.29),
tripodCameraRotation = vector3(0.0, 0.0, 0.0),
captureOffset = vector3(0.35, 0, 0.0),
captureFov = 50.0,
},
},
managedVans = {
enabled = false,
model = 'rumpo',
livery = 0,
respawnDelaySec = 60,
healthCheckIntervalMin = 30,
spawns = {
vector4(-543.7345, -888.2435, 25.1393, 187.3024),
vector4(-537.0546, -905.4167, 23.8630, 241.8772),
vector4(-539.3694, -909.0856, 23.8616, 238.7722),
vector4(-541.2062, -912.5158, 23.8616, 237.1201),
vector4(-556.7697, -937.6596, 23.8513, 93.5193),
vector4(-557.1396, -929.4688, 23.8594, 91.1856),
vector4(-556.9586, -924.8768, 23.8674, 93.2972),
vector4(-274.3217, -764.9600, 48.4239, 252.0183),
vector4(-302.4780, -742.4742, 43.6053, 339.3744),
vector4(938.6218, -48.4252, 78.7640, 237.6821),
vector4(737.0957, 1296.9409, 360.2958, 8.8485),
vector4(193.7172, 6381.9854, 31.3943, 121.4929),
},
dish = {
model = 'prop_satdish_s_02',
attachDistance = 100.0,
refreshIntervalMs = 1000,
forwardOffset = -1.2,
rightOffset = 0.0,
heightPadding = -0.1,
rotation = vector3(0.0, 0.0, 180.0),
},
},
heist = {
enabled = true,
deathHandling = 'interior_fail_task', -- 'none' | 'interior_fail_task' | 'any_fail_task'
computerBootDurationSec = 15,
rackHackCooldownSec = 10,
extractionPenaltySec = 30,
extractionChallengeIntervalMinSec = 35,
extractionChallengeIntervalMaxSec = 60,
extractionChallengeResponseWindowSec = 30,
extractionMinigames = {
pool = {
'timing_lock',
'command_chain',
'packet_match',
},
timingLock = {
prompt = 'STALL DETECTED. STABILIZE TRANSFER PULSE.',
zoneSizeMinPercent = 12,
zoneSizeMaxPercent = 18,
zoneStartMinPercent = 12,
zoneStartMaxPercent = 82,
cycleMsMin = 1400,
cycleMsMax = 1900,
roundsRequiredMin = 2,
roundsRequiredMax = 3,
maxStrikes = 2,
},
commandChain = {
prompt = 'STALL DETECTED. EXECUTE THE RECOVERY COMMAND CHAIN.',
inputPool = {
'W',
'A',
'S',
'D',
'Q',
'E',
},
sequenceLength = 5,
stepWindowMsMin = 2500,
stepWindowMsMax = 4500,
maxMistakes = 2,
},
packetMatch = {
prompt = 'CACHE DESYNC. REBUILD THE PACKET ORDER.',
valuePool = {
'A7',
'4C',
'D1',
'9F',
'EE',
'B2',
'6A',
'C8',
'31',
'F4',
'8D',
'72',
},
sequenceLength = 4,
optionCount = 8,
revealDurationMs = 5000,
},
},
serverRoom = {
hqEntryCoords = vector3(-536.10, -886.20, 25.60),
exteriorExitCoords = vector4(-537.1286, -886.4703, 25.2101, 180.0),
interiorEntrance = vector4(2154.8240, 2921.0779, -81.0755, 260.2079),
interiorExit = vector3(2156.40, 2922.18, -80.60),
rackRouteRevealDistance = 15.0,
revenueLedger = {
item = 'kf_weazel_revenue_ledger',
model = 'ch_prop_ch_usb_drive01x',
},
workstations = {
{
label = 'News Desk A',
coords = vector3(2166.60, 2925.50, -81.21),
targetSize = vector3(1.2, 0.9, 1.8),
},
{
label = 'News Desk B',
coords = vector3(2173.74, 2928.78, -81.22),
targetSize = vector3(1.2, 0.9, 1.8),
},
{
label = 'News Desk C',
coords = vector3(2180.17, 2924.32, -81.22),
targetSize = vector3(1.2, 0.9, 1.8),
},
{
label = 'News Desk D',
coords = vector3(2179.49, 2916.60, -81.22),
targetSize = vector3(1.2, 0.9, 1.8),
},
{
label = 'News Desk E',
coords = vector3(2172.36, 2913.29, -81.22),
targetSize = vector3(1.2, 0.9, 1.8),
},
},
racks = {
{
label = 'Rack A1',
coords = vector3(2170.64, 2934.42, -84.20),
},
{
label = 'Rack A3',
coords = vector3(2193.31, 2938.45, -84.19),
},
{
label = 'Rack A9',
coords = vector3(2222.08, 2942.52, -84.20),
},
{
label = 'Rack B2',
coords = vector3(2169.80, 2908.54, -84.20),
},
{
label = 'Rack B8',
coords = vector3(2206.57, 2900.97, -84.20),
},
{
label = 'Rack C1',
coords = vector3(2166.64, 2907.60, -84.20),
},
{
label = 'Rack C3',
coords = vector3(2154.34, 2906.52, -84.20),
},
{
label = 'Rack D2',
coords = vector3(2166.36, 2933.35, -84.19),
},
{
label = 'Rack D8',
coords = vector3(2130.74, 2940.81, -84.21),
},
{
label = 'Rack D15',
coords = vector3(2114.37, 2943.68, -84.21),
},
},
},
extractionDurationTiers = {
{
minAmount = 100000,
durationSec = 300,
},
{
minAmount = 50000,
durationSec = 240,
},
{
minAmount = 1,
durationSec = 180,
},
},
forgery = {
exchangeDurationMs = 3000,
interiorEntrance = vector4(1173.7865, -3196.5505, -39.0080, 89.5980),
laptopCoords = vector3(1159.46, -3192.41, -38.52),
npc = {
coords = vector4(1159.4460, -3192.3931, -39.0080, 284.1677),
models = {
'cs_carbuyer',
'g_m_m_chiboss_01',
'a_m_y_business_02',
'csb_money',
},
},
exteriorLocations = {
{ label = 'Forgery Site 1', coords = vector4(1240.1384, -3239.0088, 6.0288, 103.9778) },
{ label = 'Forgery Site 2', coords = vector4(646.6183, -3017.9878, 7.3362, 100.5362) },
{ label = 'Forgery Site 3', coords = vector4(-318.2638, -2718.1672, 7.5482, 46.3471) },
{ label = 'Forgery Site 4', coords = vector4(1125.2495, -1010.4940, 44.6702, 279.6118) },
{ label = 'Forgery Site 5', coords = vector4(916.4738, 3576.9683, 33.5560, 96.7104) },
{ label = 'Forgery Site 6', coords = vector4(1961.0105, 5184.9502, 47.9398, 94.8243) },
{ label = 'Forgery Site 7', coords = vector4(-58.1230, 6441.5439, 32.6857, 225.6540) },
{ label = 'Forgery Site 8', coords = vector4(-3179.1436, 1093.3934, 20.8407, 246.8654) },
{ label = 'Forgery Site 9', coords = vector4(-1100.3795, 2722.2498, 18.8004, 223.1808) },
{ label = 'Forgery Site 10', coords = vector4(172.3683, 2801.6531, 45.6606, 188.6549) },
},
},
money = {
pickupCoords = vector3(1123.3427, -3198.6250, -40.49136),
targetSize = vector3(0.45, 0.25, 0.3),
targetRotation = 0.0,
npc = {
coords = vector4(1121.8569, -3197.3987, -40.3949, 263.0054),
models = {
'cs_josh',
'g_m_y_korlieut_01',
},
},
stagedBag = {
model = 'prop_cs_heist_bag_02',
coords = vector4(1123.3427, -3198.6250, -40.49136, 357.0542),
},
interiorEntrances = {
entrance1 = vector4(1138.1208, -3199.1963, -39.6657, 13.0977),
entrance2 = vector4(1118.7831, -3193.3276, -40.3915, 186.0793),
},
exteriorLocations = {
{ label = 'Cash Site 1', coords = vector4(1239.3817, -3168.1074, 7.1049, 93.3469), entranceKey = 'entrance2' },
{ label = 'Cash Site 2', coords = vector4(448.6443, -2761.4460, 7.1009, 248.3218), entranceKey = 'entrance1' },
{ label = 'Cash Site 3', coords = vector4(257.4132, -3062.6667, 5.8630, 225.6768), entranceKey = 'entrance1' },
{ label = 'Cash Site 4', coords = vector4(-1150.4110, -1567.8495, 4.4282, 214.0638), entranceKey = 'entrance1' },
{ label = 'Cash Site 5', coords = vector4(323.0804, 364.0758, 105.3210, 176.7077), entranceKey = 'entrance2' },
{ label = 'Cash Site 6', coords = vector4(619.0322, 2784.5139, 43.4812, 185.1299), entranceKey = 'entrance2' },
{ label = 'Cash Site 7', coords = vector4(183.8829, 6373.5435, 32.3440, 36.9033), entranceKey = 'entrance2' },
{ label = 'Cash Site 8', coords = vector4(325.5690, -994.5185, 29.3124, 0.9140), entranceKey = 'entrance1' },
{ label = 'Cash Site 9', coords = vector4(-50.2415, 1911.1753, 195.7054, 278.1860), entranceKey = 'entrance1' },
{ label = 'Cash Site 10', coords = vector4(-512.0488, -1747.3262, 19.2469, 59.4107), entranceKey = 'entrance1' },
},
},
bag = {
model = 'prop_cs_heist_bag_02',
payoutItem = 'money',
countChunkAmount = 5000,
damagePenaltyAmount = 1000,
cashoutDurationMs = 2500,
},
dispatch = {
enabled = false,
provider = 'auto', -- auto | ps-dispatch | qb-dispatch | ls-dispatch | cd_dispatch
resources = {
psDispatch = 'ps-dispatch',
qbDispatch = 'qb-dispatch',
lsDispatch = 'ls-dispatch',
cdDispatch = 'cd_dispatch',
},
title = 'Server Farm Intrusion',
message = 'Unauthorized entry detected at the Weazel News server farm.',
code = '10-90',
codeName = 'suspiciousactivity',
priority = 2,
recipientJobs = {
'police',
},
psJobs = {
'leo',
},
blip = {
sprite = 161,
colour = 1,
scale = 1.2,
length = 3,
radius = 0,
flash = true,
sound = 1,
},
},
},
minigame = {
enabled = false,
provider = 'ox_lib',
},
superYap = {
enabled = true,
paymentSource = 'bank', -- bank | inventory
moneyItem = 'money',
permanentBadgeThreshold = 1000000,
presetAmounts = {
1000,
5000,
15000,
50000,
100000,
250000,
},
-- `max` is optional and auto-derived from the next tier's `min` when omitted.
tiers = {
[1] = {
min = 5000,
durationSec = 30,
maxChars = 60,
},
[2] = {
min = 15000,
durationSec = 60,
maxChars = 60,
},
[3] = {
min = 50000,
durationSec = 120,
maxChars = 80,
},
[4] = {
min = 100000,
durationSec = 300,
maxChars = 120,
},
[5] = {
min = 250000,
durationSec = 600,
maxChars = 120,
},
},
},
poll = {
enabled = true,
durationSec = 240,
resultDurationSec = 30,
questionMaxLength = 120,
optionMaxLength = 40,
minOptions = 2,
maxOptions = 4,
voteCooldownMs = 400,
},
}