Skip to content

ThrowPhone

The ThrowPhone & ThrowBall Negotiation Script introduces a realistic law-enforcement tool for hostage and crisis negotiations in FiveM. Inspired by real-world tactical equipment, it allows police negotiators to establish secure communication without physically exposing themselves too long or at all.

The ThrowPhone system allows law enforcement to deploy specialized communication devices into a building or area occupied by suspects. Criminals can retrieve the device and communicate directly with negotiators, while police maintain distance, control, and oversight.

Showcase: https://youtu.be/HgORbMqlxSs

  • Local-Object Optimization: Uses non-networked (local) objects for the phone while keeping state synced across clients, reducing network overhead.
  • Optimised: In game 0.00ms idle and 0.01-0.02ms client/server with deployed phones.
  • Control Box Management: Issue, manage, and listen to deployed phones from a dedicated control box.
  • Live Camera & GPS Tracking: View the throw phone camera and track the handset location when enabled.
  • Automatic Cleanup: Phones, prompts, and voice channels clear themselves when the scene wraps up, keeping your server tidy.
  • Permissions: Gate access by job/role (ESX, QBCore, QBox, Ox) or ACE permission.
  • Hostage negotiations where SWAT tosses in a phone and monitors from cover.
  • Undercover stings that need a disposable, hard-to-trace comms line.
  • Event control or heist scenarios that benefit from isolated, object-based voice channels.

  1. Move the dependencies and the folders kf-throw-phone & kf-throw-phone-assets into your server’s resources directory.

  2. server.cfg
    ensure ox_lib
    ensure pma-voice
    ensure ox_target
    ensure ox_inventory
    ensure kf-throw-phone
    ensure kf-throw-phone-assets

    Optional ACE gate for control-box access. This only applies when Config.controlBoxAcePermission is set to the same ACE string:

    server.cfg
    add_ace group.police kfthrowphone.use allow
    • Inventory mode: Use one of the supported inventory resources and follow the ox_inventory example below for item/weapon setup.
    • Standalone command mode: Set Config.inventory = 'standalone' and configure Config.controlBoxCommand to the client command you want players to use, for example throwphone. In this mode the control box is deployed from the command instead of an inventory item, and control-box retrieval uses the native weapon system for throw phones.
  3. The following item setup is for ox_inventory only. Please refer to the documentation for other supported inventory resources.

    Section titled “The following item setup is for ox_inventory only. Please refer to the documentation for other supported inventory resources.”
  4. Add the control box to the inventory item config file.

    Section titled “Add the control box to the inventory item config file.”

    Add to the ox_inventory items config file. Default location: resources/[ox]/ox_inventory/data/items.lua

    items.lua
    ['kf_throwphone_control'] = {
    label = 'Throw Phone Control',
    weight = 8000,
    stack = false,
    close = true,
    client = {
    export = 'kf-throw-phone.deployControl'
    }
    },
  5. Add the throwables to the inventory weapon config file.

    Section titled “Add the throwables to the inventory weapon config file.”

    Add to the ox_inventory weapons config file. Default location: resources/[ox]/ox_inventory/data/weapons.lua

    weapons.lua
    ['WEAPON_KFTHROWPHONE'] = {
    label = 'Throw Phone Case',
    weight = 1000,
    throwable = true,
    },
    ['WEAPON_KFTHROWPHONE_BALL'] = {
    label = 'Throw Phone Ball',
    weight = 300,
    throwable = true,
    },
    ['WEAPON_KFTHROWPHONE_HANDSET'] = {
    label = 'Throw Phone',
    weight = 190,
    throwable = 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-throw-phone/inventory-images/ingame OR kf-throw-phone/inventory-images/stylized into the ox_inventory images folder. Default location: resources/[ox]/ox_inventory/web/images

  7. Add the control box to your item dispesner method

    Section titled “Add the control box to your item dispesner method”

    Add only kf_throwphone_control to your item dispenser method (armoury, loot, etc). The other items are retrieved from the control box.


The main configuration is located in config.lua.

config.lua
Config = {
debug = false,
notifications = true,
textUiEnabled = true,
-- The notification resource to use, one of: 'ox_lib', 'esx_notify', 'qb-core', 'qbx_core', 'okokNotify', 'wasabi_notify',
notify = 'ox_lib',
-- The inventory resource to use
-- one of: 'ox_inventory', 'codem-inventory', 'qb-inventory', 'qs-inventory', 'qs-inventory-pro', 'origen_inventory', 'tgiann-inventory', 'standalone'
inventory = 'ox_inventory',
-- The target resource to use, one of: 'ox_target', 'sleepless_interact', or 'qb-target'
target = 'ox_target',
-- Control box
-- Allowed groups to use the control box - leave empty to allow all groups
-- List format (no grade requirement):
-- { 'police', 'sheriff', 'fbi' }
-- Grade format (minimum grade):
-- { police = 2, sheriff = 1 }
-- ESX also supports grade_name aliases:
-- { police = 'boss' }
controlBoxAllowedJobs = {
-- Example:
-- police = 2,
},
-- Optional ACE permission for control box access. Set to a string like 'kfthrowphone.use' to require it,
-- or leave false to disable ACE checks. When both ACE and jobs are configured, either permission path grants access.
controlBoxAcePermission = false,
controlBoxMaxDeployed = 1,
controlBoxMaxPhones = 5,
controlBoxAllowBallRetrieve = true,
-- Client command name used to start control box placement in standalone mode (eg: 'throwphone'). Set to false to disable.
controlBoxCommand = false,
-- Control box return mode:
-- 'inventory' = only return by removing a phone item from player inventory (default)
-- 'inventory_or_deployed' = if no inventory phone is found, return one deployed phone linked to this control box instead
controlBoxReturnMode = 'inventory',
-- Phone return mode:
-- 'equipped_only' = return-to-case requires actively holding phone weapon (default)
-- 'inventory_or_equipped' = return-to-case allows matching phone item in inventory even if not actively held
-- 'owner_fallback' = if matching phone item is missing, allow return by phone ownership state
phoneReturnMode = 'equipped_only',
-- Case call join behavior:
-- 'phone' = case phones join by retrieving a phone from the case
-- 'interact' = case phones join and leave through case/control box interactions instead of handset retrieval
caseJoinCall = {
mode = 'phone', -- 'phone' | 'interact'
interact = {
-- when enabled, players in an interact-mode case call automatically leave when they move away
autoLeave = {
enabled = true,
proximity = 6.0,
},
},
},
controlBoxJoinCall = {
mode = 'phone', -- 'phone' | 'interact'
interact = {
autoLeave = {
enabled = true,
proximity = 6.0,
},
},
},
-- When true, only the player who deployed the control box can interact with it.
controlBoxRequireOwnership = false,
ballJoinRadius = 3.0,
projectileVisibilityDistance = 150.0,
ringtone = {
-- When true, enables audio spatial ringtone support for deployed throw phone cases
enabled = false,
-- Relative path inside this resource for the ringtone asset, eg: 'web/build/ringtone.mp3'.
file = 'web/build/ringtone.mp3',
loop = true,
maxDurationMs = 30000,
updateIntervalMs = 200,
maxDistance = 15.0,
refDistance = 1.25,
rolloffFactor = 1.6,
volume = 0.6,
},
-- Enable so that players must hold attack for at least phoneThrowHoldDurationMs before release will throw the phone; quick taps show an info hint instead of throwing.
phoneThrowHoldEnabled = false,
phoneThrowHoldDurationMs = 450,
-- Throw phone camera
cameraEnabled = true,
cameraEnabledByType = {
case = false,
phone = true,
ball = true,
},
cameraNightVisionToggleEnabled = true,
cameraThermalVisionToggleEnabled = true,
cameraFOV = 100.0,
cameraRequireApproval = true, -- require nearby players to approve camera access
-- How nearby players approve camera requests: 'dialog' shows an approve/deny popup; 'target' prompts them to use the case/phone/ball target option; 'both' enables both methods.
cameraApprovalMethod = 'target', -- 'dialog', 'target', or 'both'
cameraApprovalTimeout = 300000, -- milliseconds camera approval timeout (0 to disable)
cameraViewTimeout = 300000, -- milliseconds active camera view timeout (0 to disable)
cameraApprovalRange = 20.0,
cameraMouseRotationEnabled = true,
cameraKeyboardPitchYawEnabled = true,
-- GPS tracker
gpsTrackerEnabled = true,
gpsTrackerUpdateInterval = 5000, -- milliseconds between GPS coordinate refreshes
gpsTrackerBlipSprite = 459, -- blip sprite
gpsTrackerBlipColour = 38, -- blip colour
gpsTrackerBlipScale = 0.8, -- blip scale
gpsTrackerBlipShortRange = true, -- blip short range on minimap
}