Bodybag Script

Bodybag Script

Do you want to bring more immersive roleplay to your server, even after the death of a player? This bodybag script might be the addition you are looking for.

:shopping_cart: Get it here: gamzkystore.com

:question: Support: Discord

:movie_camera: Detailed preview: Youtube

Features

  • Will work on any framework and only requires a target system to interact with dead players.
  • Optional job and/or item requirement before placing a bodybag.
  • Uses statebags for seamless synchronization between clients, the script can even be restarted when in use without causing problems.
  • Bodybags can be removed as well (this feature can also be disabled).
  • Exports and events are available to add or remove bodybags via other scripts, enabling easy integration into your server.
  • Highly configurable, bridge functions also enable you to edit the most important code.

Resmon: 0.00ms

The code snippets below are accessible in the encrypted version:

Config
Config = {}

-- The distance the player has to be to interact with the target
Config.InteractDistance = 2.0

-- A saveguard such that people cannot bodybag a target that is to far away due to desync
Config.MaximumBodybagDistance = 25.0

-- The prop used as a bodybag
Config.BodybagModel = 'xm_prop_body_bag'

-- If the bodybag should be frozen in place
Config.FreezeBodybag = true

-- If the players can also remove the bodybag
Config.CanRemoveBodybag = true

-- The job that is required to put the body in a bodybag, or set Enabled to false to not require a job
Config.RequiredJob = {
    Enabled = true,
    Name = 'police',
}

-- The item that is required to put the body in a bodybag, or set Enabled to false to not require an item
Config.RequiredItem = {
    Enabled = true,
    ItemName = 'bodybag',
    ItemAmount = 1,
}

-- The animations to play when putting a player in a bodybag or removing a bodybag
Config.Animations = {
    ['put_in_bodybag'] = {
        Dict = 'missexile3', -- Replace to '' to not play an animation
        Name = 'ex03_dingy_search_case_base_michael',
        Time = 2000,
        Blend = 8.0,
        Flag = 1,
    },
    ['remove_bodybag'] = {
        Dict = 'missexile3', -- Replace to '' to not play an animation
        Name = 'ex03_dingy_search_case_base_michael',
        Time = 2000,
        Blend = 8.0,
        Flag = 1,
    },
}

-- Language configuration
Config.Locales = {
    ['put_in_bodybag'] = 'Put in bodybag',
    ['remove_bodybag'] = 'Remove bodybag',
    ['missing_required_job'] = 'You do not have the required job',
    ['missing_required_item'] = 'You do not have the required item',
}

-- Adds some prints for debugging
Config.Debug = false
Accessible Client Bridge Functions
Functions = {}

-- A function that checks if the player can interact with the target
Functions.CanInteract = function()
    local ped = PlayerPedId()
    local isPedInVehicle = IsPedInAnyVehicle(ped, false)
    local isPedDead = IsPedDeadOrDying(ped, true)

    if (isPedInVehicle or isPedDead) then
        return false
    end

    return true
end

-- A function that checks if the target can be placed in a bodybag, by default returns only true if the target is dead
Functions.CanTargetBePlacedInBodybag = function(entity)
    local isPedDead = IsPedDeadOrDying(entity, true)
    return isPedDead
end

-- This function is triggered after the bodybag is created, you can execute any code you want here
Functions.OnBodybagCreated = function(bodybagEntity)
    local ped = PlayerPedId()
    SetEntityVisible(ped, false, 0)
    SetEntityCollision(ped, false, true)
    FreezeEntityPosition(ped, true)
end

-- This function is triggered after the bodybag is removed, you can execute any code you want here
Functions.OnBodybagRemoved = function()
    local ped = PlayerPedId()
    SetEntityCollision(ped, true, true)
    SetEntityVisible(ped, true, 0)
    FreezeEntityPosition(ped, false)
end

Functions.AddGlobalTargetOptions = function()
    -- For the people ussing ox-target
    if (GetResourceState('ox_target') == 'started') then
        exports.ox_target:addGlobalPlayer({
            {
                icon = 'fas fa-briefcase',
                label = Config.Locales['put_in_bodybag'],
                distance = Config.InteractDistance,
                groups = Config.RequiredJob.Enabled and Config.RequiredJob.Name or nil,
                items = Config.RequiredItem.Enabled and Config.RequiredItem.ItemName or nil,
                onSelect = function(data)
                    AttemptUseBodybag(data.entity)
                end,
                canInteract = function(entity)
                    return (Functions.CanInteract() and IsTargetValid(entity) and Functions.CanTargetBePlacedInBodybag(entity) and not IsTargetEntityInBodybag(entity))
                end
            },
        })
        return
    end

    -- For the people using qb-target
    if (GetResourceState('qb-target') == 'started') then
        exports['qb-target']:AddGlobalPlayer({
            options = {
                {
                    icon = 'fas fa-briefcase',
                    label = Config.Locales['put_in_bodybag'],
                    job = Config.RequiredJob.Enabled and Config.RequiredJob.Name or nil,
                    item = Config.RequiredItem.Enabled and Config.RequiredItem.ItemName or nil,
                    action = function(entity)
                        AttemptUseBodybag(entity)
                    end,
                    canInteract = function(entity)
                        return (Functions.CanInteract() and IsTargetValid(entity) and Functions.CanTargetBePlacedInBodybag(entity) and not IsTargetEntityInBodybag(entity))
                    end,
                },
            },
            distance = Config.InteractDistance,
        })
        return
    end

    print('gs_bodybag: [ERROR] No target interaction defined')
end

Functions.AddRemoveBodybagOption = function(bodybagEntityNetIds)
    -- For the people ussing ox-target
    if (GetResourceState('ox_target') == 'started') then
        exports.ox_target:addEntity(bodybagEntityNetIds, {
            {
                icon = 'fas fa-trash-can-arrow-up',
                label = Config.Locales['remove_bodybag'],
                distance = Config.InteractDistance,
                groups = Config.RequiredJob.Enabled and Config.RequiredJob.Name or nil,
                onSelect = function(data)
                    AttemptRemoveBodybag(data.entity)
                end,
                canInteract = function()
                    return (Config.CanRemoveBodybag and Functions.CanInteract())
                end
            },
        })
        return
    end

    -- For the people ussing qb-target
    if (GetResourceState('qb-target') == 'started') then
        exports['qb-target']:AddTargetEntity(bodybagEntityNetIds, {
            options = {
                {
                    icon = 'fas fa-trash-can-arrow-up',
                    label = Config.Locales['remove_bodybag'],
                    job = Config.RequiredJob.Enabled and Config.RequiredJob.Name or nil,
                    action = function(entity)
                        AttemptRemoveBodybag(entity)
                    end,
                    canInteract = function()
                        return (Config.CanRemoveBodybag and Functions.CanInteract())
                    end,
                },
            },
            distance = Config.InteractDistance,
        })
        return
    end

    print('gs_bodybag: [ERROR] No target interaction defined')
end

Accessible Server Bridge Functions
local ESX = nil
local QBCore = nil

-- Select the correct framework
CreateThread(function()
    if (GetResourceState('es_extended') == 'started') then
        ESX = exports['es_extended']:getSharedObject()
    elseif (GetResourceState('qb-core') == 'started') then
        QBCore = exports['qb-core']:GetCoreObject()
    end
end)

Functions = {}

Functions.CanSourcePlaceTargetInBodybag = function(source, target)
    -- You can add any additional checks here, to prevent abuse if you wish
    return true
end

-- Check if the player has the required job
Functions.DoesPlayerHaveRequiredJob = function(src)
    if (not Config.RequiredJob.Enabled) then
        return true
    end

    -- Case for ESX
    if (ESX ~= nil) then
        local xPlayer = ESX.GetPlayerFromId(src)
        if xPlayer.job.name == Config.RequiredJob.Name then
            return true
        end
        TriggerClientEvent('esx:showNotification', src, Config.Locales['missing_required_job'], 'error')

        -- Case for QBCore
    elseif (QBCore ~= nil) then
        local Player = QBCore.Functions.GetPlayer(src)
        if Player.PlayerData.job.name == Config.RequiredJob.Name then
            return true
        end
        TriggerClientEvent('QBCore:Notify', src, Config.Locales['missing_required_job'], 'error')
    end
    return false
end

-- Check if the player has the required item and remove it
Functions.UseBodybagItem = function(src)
    if (not Config.RequiredItem.Enabled) then
        return true
    end

    -- Case for ESX
    if (ESX ~= nil) then
        local xPlayer = ESX.GetPlayerFromId(src)
        if xPlayer.getInventoryItem(Config.RequiredItem.ItemName).count >= Config.RequiredItem.ItemAmount then
            xPlayer.removeInventoryItem(Config.RequiredItem.ItemName, Config.RequiredItem.ItemAmount)
            return true
        end
        TriggerClientEvent('esx:showNotification', src, Config.Locales['missing_required_item'], 'error')

        -- Case for QBCore
    elseif (QBCore ~= nil) then
        local Player = QBCore.Functions.GetPlayer(src)
        if Player.Functions.GetItemByName(Config.RequiredItem.ItemName).amount >= Config.RequiredItem.ItemAmount then
            Player.Functions.RemoveItem(Config.RequiredItem.ItemName, Config.RequiredItem.ItemAmount)
            return true
        end
        TriggerClientEvent('QBCore:Notify', src, Config.Locales['missing_required_item'], 'error')
    end
    return false
end

Functions.ReturnBodybagItem = function(src)
    if (not Config.RequiredItem.Enabled) then
        return
    end

    -- Case for ESX
    if (ESX ~= nil) then
        local xPlayer = ESX.GetPlayerFromId(src)
        xPlayer.addInventoryItem(Config.RequiredItem.ItemName, Config.RequiredItem.ItemAmount)

        -- Case for QBCore
    elseif (QBCore ~= nil) then
        local Player = QBCore.Functions.GetPlayer(src)
        Player.Functions.AddItem(Config.RequiredItem.ItemName, Config.RequiredItem.ItemAmount)
    end
end

-- The exports below can be triggered from the SERVER which will add or remove a bodybag to/from a specific player (this skips the check for the correct job and/or item)
exports('AddBodybag', AddBodybag)       -- Call through exports.gs_bodybag:AddBodybag(playerServerId)
exports('RemoveBodybag', RemoveBodybag) -- Call through exports.gs_bodybag:RemoveBodybag(playerServerId)

-- The events below can be triggered from the CLIENT which will add or remove a bodybag to/from to the source of the event (this skips the check for the correct job and/or item)
RegisterNetEvent('gs_bodybag:AddBodybagForPlayer', function()
    local src = source
    AddBodybag(src)
end)

RegisterNetEvent('gs_bodybag:RemoveBodybagForPlayer', function()
    local src = source
    RemoveBodybag(src)
end)

Code is accessible No, but there is an unencrypted version
Subscription-based No
Lines (approximately) ~600
Requirements Only a target script (i.e. ox-target or qb-target)
Support Yes
2 Likes

possibly also possible for npc’ ? so you could also take npcs away with you

As of now it is only possible to bodybag real players.

and the player stays inside the bag? what is his perspective? the placed bag cannot be moved? if it cannot be moved, what is the use of this script?

Hi!

What happens to the player can be edited in accessible bridge functions, by default the player in the bodybag is hidden. There is no feature to move the bodybag as of now.

Most servers have their own death system which handles the player death screen so the bodybag script does not change the perspective of the bodybagged players.