Installation

SCRIPT DOWNLOAD

🔐 Logging in to the CFX Portal & Downloading Assets

Before you begin, you must log in to the official CFX portal. This is required to download any assets you've been granted access to.

📥 Downloading Your Asset

Once logged in:

  1. Go to the "Granted Assets" section. This is where all your purchased or granted resources are listed.

  2. Locate for rd-interactions.

  3. Click the "Download" button next to the asset.

⚠️ You can download the asset as many times as you need — whether it's the first time or when updates are released in the future.

🛠️ Troubleshooting Issues

If you experience any issues while starting or using the asset, be sure to check the asset's official CFX page. Common issues and solutions are often documented there, which can help resolve your problem quickly.

DOWNLOADING DEPENDENCIES

The dependencies for this asset are mandatory, so please follow the dependency guide completely and use all required files.

When downloading a dependency, ensure the file is properly unzipped and does not include "-main" at the end of its name. If it does, please remove it.

UPDATE ARTIFACT AND GAMEBUILD

How to Properly Update Artifacts and Gamebuild for Your Server

Updating to the latest artifacts and gamebuild is essential for maintaining a stable and optimized server. This ensures that you're not only running the latest features but also avoiding common server issues caused by outdated versions. Follow the steps below to properly update your server.

Step 1: Update Artifacts

Artifacts are essential components that keep your server running smoothly. To update to the latest version of the artifacts:

  1. Completely Replace Your Current Artifacts It's important to fully replace your current artifacts folder with the latest version. This ensures you're not using outdated files that could cause compatibility issues.

  2. Download the Latest Artifacts for Your Operating System You need to download the appropriate artifacts based on your server’s operating system. Use the official links below:

    Make sure to download the correct version (32-bit or 64-bit) based on your system.

Step 2: Update Gamebuild

Using the latest gamebuild is crucial for taking advantage of new features, bug fixes, and ensuring compatibility with newer mods and scripts. Outdated gamebuilds can lead to errors and instability.

  1. Edit the server.cfg File To update the gamebuild, you need to specify the version in the server configuration. Open the server.cfg file located in your server directory and add the following line:

    sv_enforceGameBuild 3258
    • Note: The number 3258 is an example. You should always use the latest gamebuild number, which you can find from the official update logs or documentation.

  2. Save and Restart Your Server After updating the server.cfg file, save your changes and restart your server for the updates to take effect.

Complete Guide

For a more detailed step-by-step guide on how to update your server, including troubleshooting tips and further instructions.

RUN RD-INTERACTIONS

To ensure the script rd-interactions is properly loaded in the server.cfg, you need to add a line to the configuration file that starts the script when the server runs. This is typically done by adding the following line at the appropriate section in server.cfg:

ensure rd-interactions

INTEGRATION IN QBCORE, ESX AND OTHER FRAMEWORKS

QBCORE

📍 FOR QBCORE USERS

Setup Instructions 🔹 DrawText Setup

Go to:

qbcore/client/drawtext.lua

Paste the following code:
local function hideText()
    exports['rd-interactions']:hide()
end

local function drawText(text, _)
    exports['rd-interactions']:show(text, "left", "E", false)
end

local function keyPressed()
    CreateThread(function()
        hideText()
    end)
end

RegisterNetEvent('qb-core:client:DrawText', function(text, position)
    drawText(text, position)
end)

RegisterNetEvent('qb-core:client:HideText', function()
    hideText()
end)

exports('DrawText', drawText)
exports('HideText', hideText)
exports('KeyPressed', keyPressed)

🔹 Notify Setup

Go to:

qbcore/client/functions.lua

Find line 171 and replace the existing Notify function with the following:
function QBCore.Functions.Notify(text, texttype, length, icon)
    local message = {
        action = 'notify',
        type = texttype or 'primary',
        length = length or 5000,
    }

    if type(text) == 'table' then
        message.text = text.text or 'Placeholder'
        message.caption = text.caption or 'Placeholder'
    else
        message.text = text
    end

    if icon then
        message.icon = icon
    end

    SendNUIMessage(message)
end

New Function:

function QBCore.Functions.Notify(text, textype, length)
    if textype == 'primary' then textype = 'info' end 
    local ttype = textype ~= nil and textype or "info"
    local length = length ~= nil and length or 5000
    exports['rd-interactions']:SendNotify("RD NOTIFY", text, length, ttype)
end
ESX

✅ FOR ESX – Notification Function (Line 156)

function ESX.ShowNotification(message, type, length)
    if GetResourceState('rd-interactions') ~= 'missing' then
        local notifyType = 'info' 
        if type == 'error' then
            notifyType = 'error'
        elseif type == 'inform' then
            notifyType = 'info'
        elseif type == 'success' then
            notifyType = 'success'
        elseif type == 'warning' then
            notifyType = 'warning'
        end

        exports['rd-interactions']:SendNotify("RD NOTIFY", message, length or 5000, notifyType)
    else
        print('[^1ERROR^7] ^5rd-interactions^7 resource not found or not started!')
    end
end

✅ FOR ESX – TextUI Setup (Line 160)

function ESX.TextUI(message, type)
    if GetResourceState('rd-interactions') ~= 'missing' then
        exports['rd-interactions']:show(message, "left", "E", false)
    else
        print('[^1ERROR^7] ^5rd-interactions^7 is missing or not started!')
    end
end

function ESX.HideUI()
    if GetResourceState("rd-interactions") ~= "missing" then
        exports['rd-interactions']:hide()
    else 
        print('[^1ERROR^7] ^5rd-interactions^7 is missing or not started!')
    end
end
OTHERS

🔔 Notify UI

This shows a styled notification message on the screen.

Syntax:

exports['rd-interactions']:show("RD NOTIFY", text, length, ttype)

Parameters:

  • "RD NOTIFY": This stays as is – it defines the UI type.

  • text: The message you want to display.

  • length: Duration in milliseconds (e.g., 5000 for 5 seconds).

  • ttype: Type of notification style (examples: "info", "error", "success").

Example:

exports['rd-interactions']:show("RD NOTIFY", "You picked up a health pack!", 5000, "success")

📝 Text UI

This is used to show a text prompt, often at the bottom left (like "Press E to interact").

Show Text UI:

luaCopierModifierexports['rd-interactions']:show(text, "left", "E", false)

Parameters:

  • text: The message to display (e.g., "Open the door").

  • "left": Position on screen ("left" or "right").

  • "E": Key to show as hint (any string).

  • false: Usually set to false unless using some special condition.

Example:

luaCopierModifierexports['rd-interactions']:show(" enter the vault", "left", "E", false)

Hide Text UI:

luaCopierModifierexports['rd-interactions']:hide()

This will remove the TextUI from the screen.

DOORLOCK

QB-DOORLOCK

📄 File: qb-doorlock/client/main.lua
🔢 Line: 127

🔧 Replace this code:

local function hideNUI()
	SendNUIMessage({
		type = "setDoorText",
		enable = false
	})
	Wait(1)
end

✅ With this:

local function hideNUI()
    exports['rd-interactions']:interactOFF()
    Wait(0)
end

📄 File: qb-doorlock/client/main.lua 🔢 Line: 103

🔧 Replace this code:

local function displayNUIText(text)
    local color = Config.ChangeColor and (closestDoor.data.locked and Config.LockedColor or Config.UnlockedColor) or Config.DefaultColor
    SendNUIMessage({
        type = "setDoorText",
        enable = true,
        text = text,
        color = color
    })
    Wait(1)
end

✅ With this:

local function displayNUIText(displayText, coords)
    local interactionType

    if not closestDoor.data.locked then
        interactionType = "interact"
    else
        interactionType = "interact_off"
    end

    if authorized then
        interactionType = "interact_off"
    end

    exports['rd-interactions']:interactON(coords, interactionType)
    Wait(50)
end

📄 File: qb-doorlock/client/main.lua 🔢 Line: 823

🔧 Replace this code:

CreateThread(function()
	if Config.PersistentDoorStates and isLoggedIn then Wait(1000) SetupDoors() end 

	updateDoors()
	HandleDoorDebug()
	while true do
		local sleep = 100
		if isLoggedIn and canContinue then
			playerPed = PlayerPedId()
			playerCoords = GetEntityCoords(playerPed)
			if not closestDoor.id then
				local distance = #(playerCoords - lastCoords)
				if distance > 15 then
					updateDoors()
					sleep = 1000
				else
					for k in pairs(nearbyDoors) do
						local door = Config.DoorList[k]
						if door.setText and door.textCoords then
							distance = #(playerCoords - door.textCoords)
							if distance < (closestDoor.distance or 15) then
								if distance < (door.distance or door.maxDistance) then
									closestDoor = {distance = distance, id = k, data = door}
									sleep = 0
								end
							end
						end
					end
				end
			end
			if closestDoor.id then
				while isLoggedIn do
					if not paused and IsPauseMenuActive() then
						hideNUI()
						paused = true
					elseif paused then
						if not IsPauseMenuActive() then paused = false end
					else
						playerCoords = GetEntityCoords(playerPed)
						closestDoor.distance = #(closestDoor.data.textCoords - playerCoords)
						if closestDoor.distance < (closestDoor.data.distance or closestDoor.data.maxDistance) then
							local authorized = isAuthorized(closestDoor.data)
							local displayText = ""

							if not closestDoor.data.hideLabel and Config.UseDoorLabelText and closestDoor.data.doorLabel then
								displayText = closestDoor.data.doorLabel
							else
								if not closestDoor.data.locked and not authorized then
									displayText = Lang:t("general.unlocked")
								elseif not closestDoor.data.locked and authorized then
									displayText = Lang:t("general.unlocked_button")
								elseif closestDoor.data.locked and not authorized then
									displayText = Lang:t("general.locked")
								elseif closestDoor.data.locked and authorized then
									displayText = Lang:t("general.locked_button")
								end
							end

							if displayText ~= "" and (closestDoor.data.hideLabel == nil or not closestDoor.data.hideLabel) then displayNUIText(displayText) end
						else
							hideNUI()
							break
						end
					end
					Wait(100)
				end
				closestDoor = {}
				sleep = 0
			end
		end
		Wait(sleep)
	end
end)

✅ With this:

CreateThread(function()
    if Config.PersistentDoorStates and isLoggedIn then Wait(1000) SetupDoors() end 

    updateDoors()
    HandleDoorDebug()
    
    local lastDisplayedText = ""

    while true do
        local sleep = 100

        if isLoggedIn and canContinue then
            playerPed = PlayerPedId()
            playerCoords = GetEntityCoords(playerPed)

            if not closestDoor.id then
                local distance = #(playerCoords - lastCoords)
                if distance > 15 then
                    updateDoors()
                    sleep = 1000
                else
                    for k in pairs(nearbyDoors) do
                        local door = Config.DoorList[k]
                        if door.setText and door.textCoords then
                            distance = #(playerCoords - door.textCoords)
                            if distance < (closestDoor.distance or 15) then
                                if distance < (door.distance or door.maxDistance) then
                                    closestDoor = {distance = distance, id = k, data = door}
                                    sleep = 0
                                end
                            end
                        end
                    end
                end
            end

            if closestDoor.id then
                while isLoggedIn do
                    if not paused and IsPauseMenuActive() then
                        hideNUI()
                        lastDisplayedText = ""  -- Reset lastDisplayedText when pausing
                        paused = true
                    elseif paused then
                        if not IsPauseMenuActive() then paused = false end
                    else
                        playerCoords = GetEntityCoords(playerPed)
                        closestDoor.distance = #(closestDoor.data.textCoords - playerCoords)
                        if closestDoor.distance < (closestDoor.data.distance or closestDoor.data.maxDistance) then
                            local authorized = isAuthorized(closestDoor.data)
                            local displayText = ""

                            if not closestDoor.data.hideLabel and Config.UseDoorLabelText and closestDoor.data.doorLabel then
                                displayText = closestDoor.data.doorLabel
                            else
                                if not closestDoor.data.locked and not authorized then
                                    displayText = Lang:t("general.unlocked")
                                elseif not closestDoor.data.locked and authorized then
                                    displayText = Lang:t("general.unlocked_button")
                                elseif closestDoor.data.locked and not authorized then
                                    displayText = Lang:t("general.locked")
                                elseif closestDoor.data.locked and authorized then
                                    displayText = Lang:t("general.locked_button")
                                end
                            end

                            local coords = closestDoor.data.textCoords

                            
                            if displayText ~= lastDisplayedText and displayText ~= "" and (closestDoor.data.hideLabel == nil or not closestDoor.data.hideLabel) then
                                
                                if lastDisplayedText ~= "" then
                                    hideNUI()  
                                end
                                displayNUIText(displayText, coords)
                                lastDisplayedText = displayText 
                            end
                        else
                            if lastDisplayedText ~= "" then
                                hideNUI()
                                lastDisplayedText = ""  -- Clear lastDisplayedText when hiding NUI
                            end
                            break
                        end
                    end
                    Wait(100)
                end
                closestDoor = {}
                sleep = 0
            end
        end
        Wait(sleep)
    end
end)
OX-DOORLOCK

📄 File: ox_doorlock/client/main.lua
🔢 Line: 274

🔧 Replace this code:

```lua
CreateThread(function()
	local lockDoor = locale('lock_door')
	local unlockDoor = locale('unlock_door')
	local showUI
	local drawSprite = Config.DrawSprite

	if drawSprite then
		local sprite1 = drawSprite[0]?[1]
		local sprite2 = drawSprite[1]?[1]

		if sprite1 then
			RequestStreamedTextureDict(sprite1, true)
		end

		if sprite2 then
			RequestStreamedTextureDict(sprite2, true)
		end
	end

	local SetDrawOrigin = SetDrawOrigin
	local ClearDrawOrigin = ClearDrawOrigin
	local DrawSprite = drawSprite and DrawSprite

	while true do
		ClosestDoor = nearbyDoors[1]

		if nearbyDoorsCount > 0 then
			for i = 1, nearbyDoorsCount do
				local door = nearbyDoors[i]

				if door.distance < door.maxDistance then
					if door.distance < ClosestDoor.distance then
						ClosestDoor = door
					end

					if drawSprite and not door.hideUi then
						local sprite = drawSprite[door.state]

						if sprite then
							SetDrawOrigin(door.coords.x, door.coords.y, door.coords.z)
							DrawSprite(sprite[1], sprite[2], sprite[3], sprite[4], sprite[5], sprite[6] * ratio, sprite[7], sprite[8], sprite[9], sprite[10], sprite[11])
							ClearDrawOrigin()
						end
					end
				end
			end
		end

		if ClosestDoor and ClosestDoor.distance < ClosestDoor.maxDistance then
			if Config.DrawTextUI and not ClosestDoor.hideUi and ClosestDoor.state ~= showUI then
				lib.showTextUI(ClosestDoor.state == 0 and lockDoor or unlockDoor)
				showUI = ClosestDoor.state
			end

			if not PickingLock and IsDisabledControlJustReleased(0, 38) then
				useClosestDoor()
			end
		elseif showUI then
			lib.hideTextUI()
			showUI = nil
		end

		Wait(nearbyDoorsCount > 0 and 0 or 500)
	end
end)

✅ With this:


CreateThread(function()
	local lockDoor = locale('lock_door')
	local unlockDoor = locale('unlock_door')
	local showUI
	while true do
		local num = #nearbyDoors

		if num > 0 then
			local ratio = drawSprite and GetAspectRatio(true)
			for i = 1, num do
				local door = nearbyDoors[i]

				if door.distance < door.maxDistance then
					if door.distance < (ClosestDoor?.distance or 10) then
						ClosestDoor = door
					end
				end
			end
		else ClosestDoor = nil end

		if ClosestDoor and ClosestDoor.distance < ClosestDoor.maxDistance then
			if Config.DrawTextUI and not ClosestDoor.hideUi and ClosestDoor.state ~= showUI then
				lib.showTextUI(ClosestDoor.state == 0 and lockDoor or unlockDoor)
				showUI = ClosestDoor.state
				  local interactionType = ClosestDoor.state == 0 and "interact" or "interact_off"
				exports['rd-interactions']:interactON(ClosestDoor.coords, interactionType)
			end

			if not PickingLock and IsDisabledControlJustReleased(0, 38) then
				useClosestDoor()
			end
		elseif showUI then
			lib.hideTextUI()
			showUI = nil
			exports['rd-interactions']:interactOFF()
		end

		Wait(num > 0 and 0 or 500)
	end
end)
OTHERS

🔧 Using rd-interactions for Showing & Hiding Door Interactions

There are two interaction types available in the rd-interactions system:

  • "interact"Displays the interaction prompt (visible to the player).

  • "interact_off"Hides or removes the interaction prompt.


To SHOW the interaction

Use this to make an interaction prompt appear at a specific location:

exports['rd-interactions']:interactON(coords, "interact")
  • coords: The 3D vector location of the door or object.

  • "interact": The interaction type that shows the prompt.


🚫 To HIDE the interaction

Use this to turn off or remove any current interaction prompt:

exports['rd-interactions']:interactOFF()

Or you can also pass "interact_off" to remove a specific type if needed:

exports['rd-interactions']:interactON(coords, "interact_off")

3D TEXTUI

HOW TO USE

📌 rd-interactions Interaction System

This guide explains how to use the rd-interactions interaction exports to create world-based interactions in your resource.


✨ CreateInteraction

This export registers a world-based interaction at specific coordinates. When a player gets close, they’ll see options to interact with.

✅ Syntax

exports['rd-interactions']:CreateInteraction({
    coords = vec3(x, y, z),
    distance = number,         -- (Optional) Max render/display distance
    interactDst = number,      -- Distance within which interaction can be triggered
    name = 'unique_name',      -- Unique identifier for this interaction
    options = {                -- List of interaction options
        {
            label = 'Option Label',
            action = function(entity, coords, args) 
                -- client-side logic
            end,
            -- or use event/serverEvent with args
        }
    }
})


🧩 Example 1 – Basic Teleport Interaction

```lua
    exports['rd-interactions']:CreateInteraction({
        coords = vec3(-100.3031, -1144.3539, 25.8094),
        distance = 18.0,
        interactDst = 1.0,
        name = 'name',
        options = {
            {
                label = 'Hohaaa!',
                action = function(entity, coords, args)
                    print(entity, coords, json.encode(args))
                    local ped = PlayerPedId()
                    SetEntityCoords(ped, -100.3031, -1144.3539, 26.8094)
                end,
            },
        }
    })

🧩 Example 2 – Multi-Option Interaction

exports['rd-interactions']:CreateInteraction({
    coords = vec3(-95.0375, -1135.0963, 25.8317),
    distance = 18.0,
    interactDst = 1.0,
    name = 'name2',
    options = {
        {
            label = 'Give Weapon to Player',
            action = function(coords, args)
                local ped = PlayerPedId()
                GiveWeaponToPed(ped, GetHashKey("WEAPON_PISTOL"), 250, false, true)
                print("Weapon given to player!")
            end,
        },
        {
            label = 'Send Client Notification',
            event = 'test:clientNotify',
            args = {"Hello, this is a test notification from the server!"},
        },
        {
            label = 'Broadcast Message to All Players',
            serverEvent = 'test:serverBroadcast',
            args = {"This is a server-wide test message!"},
        },
    }
})

This interaction point gives players three options:

  • Equip a pistol

  • Trigger a client notification

  • Trigger a server-wide broadcast message

❌ RemoveInteraction 💬 Command Example

RegisterCommand('testremove', function(source, args)
    exports['rd-interactions']:RemoveInteraction(args[1])
end, false)

/testremove name2

📝 Notes

coords should be a vec3.

interactDst defines the trigger radius, while distance defines the render radius.

Use action, event, or serverEvent to define behavior.

The name field must be unique per interaction.

Last updated