Skip to main content

Introduction

RSG Framework includes a built-in prompt system that creates native Red Dead Redemption 2 style interaction prompts. These prompts appear when players approach specific locations and can trigger events when the correct key is pressed. Location: resources/[framework]/rsg-core/client/prompts.lua
Prompts are the primary way players interact with the world in Red Dead Redemption 2. Using native prompts ensures a consistent and immersive experience.

Core Features

๐ŸŽฏ Native RDR2 Style

  • Authentic Look: Uses native RDR2 prompt system
  • Consistent UX: Same style as game prompts
  • Visual Feedback: Press-and-hold indicators

๐Ÿ“ Location-based

  • Distance Checking: Only shows when player is nearby
  • Coordinates: Precise 3D positioning
  • Automatic Hide: Disappears when player leaves area

โšก Event Integration

  • Client Events: Trigger client-side events
  • Server Events: Trigger server-side events
  • Arguments: Pass custom data to events

Creating Prompts

Basic Syntax

exports['rsg-core']:createPrompt(location, coords, key, label, options)

Parameters

ParameterTypeDescription
locationstringUnique identifier for this prompt
coordsvector3World coordinates for the prompt
keystringKey hash from RSGCore.Shared.Keybinds
labelstringText displayed to player
optionstableEvent configuration

Options Table

PropertyTypeDescription
typestring'client' or 'server'
eventstringEvent name to trigger
argstableArguments passed to event

Basic Examples

Example 1: Simple Client Prompt

-- CLIENT SIDE
local RSGCore = exports['rsg-core']:GetCoreObject()

-- Create a prompt at Valentine General Store
exports['rsg-core']:createPrompt(
    'valentine_general_store',                    -- Unique location ID
    vector3(-322.5, 773.2, 116.2),               -- Store coordinates
    RSGCore.Shared.Keybinds['E'],                -- Press E to interact
    'Open General Store',                         -- Display text
    {
        type = 'client',                          -- Trigger client event
        event = 'store:client:openMenu',          -- Event name
        args = { storeId = 'valentine_general' }  -- Event arguments
    }
)

Example 2: Server Event Prompt

-- CLIENT SIDE
exports['rsg-core']:createPrompt(
    'bank_valentine_teller',
    vector3(-312.8, 770.5, 117.1),
    RSGCore.Shared.Keybinds['E'],
    'Talk to Bank Teller',
    {
        type = 'server',                          -- Trigger server event
        event = 'bank:server:openAccount',
        args = { bankId = 'valentine' }
    }
)

Example 3: Multiple Prompts

-- CLIENT SIDE
local storeLocations = {
    {
        id = 'valentine_general',
        coords = vector3(-322.5, 773.2, 116.2),
        label = 'Valentine General Store'
    },
    {
        id = 'rhodes_general',
        coords = vector3(1295.3, -1293.7, 76.9),
        label = 'Rhodes General Store'
    },
    {
        id = 'strawberry_general',
        coords = vector3(-1776.5, -396.8, 159.7),
        label = 'Strawberry General Store'
    }
}

-- Create prompts for all stores
for _, store in ipairs(storeLocations) do
    exports['rsg-core']:createPrompt(
        store.id,
        store.coords,
        RSGCore.Shared.Keybinds['E'],
        'Open ' .. store.label,
        {
            type = 'client',
            event = 'store:client:openMenu',
            args = { storeId = store.id }
        }
    )
end

Deleting Prompts

Basic Syntax

exports['rsg-core']:deletePrompt(handle)

Example: Managing Prompt Lifecycle

-- CLIENT SIDE
local createdPrompts = {}

-- Create prompts and store handles
local function CreateJobPrompts()
    local prompt1 = exports['rsg-core']:createPrompt(
        'job_start_point',
        vector3(100.0, 200.0, 30.0),
        RSGCore.Shared.Keybinds['E'],
        'Start Job',
        {
            type = 'client',
            event = 'job:client:start',
            args = {}
        }
    )
    table.insert(createdPrompts, prompt1)
end

-- Delete all created prompts
local function DeleteJobPrompts()
    for i, handle in ipairs(createdPrompts) do
        exports['rsg-core']:deletePrompt(handle)
    end
    createdPrompts = {}
end

-- Usage: Create prompts when player clocks in
RegisterNetEvent('job:client:clockIn', function()
    CreateJobPrompts()
end)

-- Delete prompts when player clocks out
RegisterNetEvent('job:client:clockOut', function()
    DeleteJobPrompts()
end)

Advanced Usage

Dynamic Prompts Based on Job

-- CLIENT SIDE
local RSGCore = exports['rsg-core']:GetCoreObject()
local jobPrompts = {}

-- Create prompts based on player's job
local function SetupJobPrompts()
    -- Clear existing prompts
    for _, handle in ipairs(jobPrompts) do
        exports['rsg-core']:deletePrompt(handle)
    end
    jobPrompts = {}

    local PlayerData = RSGCore.Functions.GetPlayerData()
    local jobName = PlayerData.job.name

    if jobName == 'vallaw' then
        -- Law enforcement prompts
        local handle = exports['rsg-core']:createPrompt(
            'sheriff_office_valentine',
            vector3(-275.5, 807.8, 118.6),
            RSGCore.Shared.Keybinds['E'],
            'Access Sheriff Office',
            {
                type = 'client',
                event = 'police:client:openOffice',
                args = { office = 'valentine' }
            }
        )
        table.insert(jobPrompts, handle)

    elseif jobName == 'valmedic' then
        -- Medic prompts
        local handle = exports['rsg-core']:createPrompt(
            'doctor_office_valentine',
            vector3(-290.3, 810.2, 118.3),
            RSGCore.Shared.Keybinds['E'],
            'Access Medical Supplies',
            {
                type = 'client',
                event = 'medic:client:openSupplies',
                args = { office = 'valentine' }
            }
        )
        table.insert(jobPrompts, handle)
    end
end

-- Update prompts when job changes
RegisterNetEvent('RSGCore:Client:OnJobUpdate', function(job)
    SetupJobPrompts()
end)

-- Setup on login
RegisterNetEvent('RSGCore:Client:OnPlayerLoaded', function()
    SetupJobPrompts()
end)

Conditional Prompts

-- CLIENT SIDE
-- Show prompt only if player has required item
local function CreateConditionalPrompt()
    -- First check if player has key
    RSGCore.Functions.TriggerCallback('housing:server:hasKey', function(hasKey)
        if hasKey then
            exports['rsg-core']:createPrompt(
                'house_door',
                vector3(-350.5, 800.2, 116.8),
                RSGCore.Shared.Keybinds['E'],
                'Enter House',
                {
                    type = 'client',
                    event = 'housing:client:enter',
                    args = { houseId = 1 }
                }
            )
        end
    end)
end

Prompt with Hold Duration

For prompts that require holding the key, you can handle this in your event:
-- The prompt system uses the native hold behavior
-- Configure hold time in your event handler

RegisterNetEvent('lockpick:client:start', function(args)
    -- Show progress bar or use native hold indication
    local success = exports['rsg-core']:Progressbar('lockpicking', 'Picking lock...', 5000, false, true, {
        disableMovement = true,
        disableMouse = false,
    }, {
        animDict = 'amb_work@world_human_pickaxe@base',
        anim = 'base',
        flags = 49,
    }, {}, {}, function()
        -- Success
        TriggerServerEvent('lockpick:server:success', args.doorId)
    end, function()
        -- Cancelled
        TriggerClientEvent('ox_lib:notify', source, {
            description = 'Lockpicking cancelled',
            type = 'error'
        })
    end)
end)

Keybinds Reference

RSG Framework includes predefined keybinds in RSGCore.Shared.Keybinds:
RSGCore.Shared.Keybinds = {
    ['E'] = 0x8CC9CD42,      -- E key
    ['G'] = 0x760A9C6F,      -- G key
    ['H'] = 0x24978A28,      -- H key
    ['F'] = 0xB2F377E8,      -- F key
    ['R'] = 0xE30CD707,      -- R key
    ['Q'] = 0xDE794E3E,      -- Q key
    ['TAB'] = 0xB238FE0B,    -- Tab key
    ['ENTER'] = 0xC7B5340A,  -- Enter key
    ['LSHIFT'] = 0x8FFC75D6, -- Left Shift
    ['LCTRL'] = 0xDB096B85,  -- Left Control
    -- ... more keybinds available
}

Usage

-- Use keybind in prompt
RSGCore.Shared.Keybinds['E']     -- E key
RSGCore.Shared.Keybinds['G']     -- G key
RSGCore.Shared.Keybinds['F']     -- F key

Best Practices

Use Unique Location IDs: Each prompt needs a unique location identifier to prevent conflicts
-- Good: Descriptive and unique
'valentine_bank_teller_1'
'rhodes_general_store_entrance'

-- Bad: Generic or duplicate
'bank'
'store'
Always Clean Up Prompts: Delete prompts when theyโ€™re no longer needed to prevent memory leaks and ghost prompts
-- Store handles when creating
local handle = exports['rsg-core']:createPrompt(...)
table.insert(myPrompts, handle)

-- Delete when done
for _, h in ipairs(myPrompts) do
    exports['rsg-core']:deletePrompt(h)
end
Use Config for Coordinates: Store prompt locations in config files for easy adjustment
Config.Prompts = {
    {
        id = 'valentine_store',
        coords = vector3(-322.5, 773.2, 116.2),
        label = 'General Store'
    }
}

Performance Tips

  1. Batch Creation: Create multiple prompts in a single loop, not in separate threads
  2. Clean on Unload: Delete all prompts in onResourceStop
  3. Minimal Distance: Use appropriate interaction distances
  4. Avoid Duplicates: Check if prompt exists before creating
-- Clean up on resource stop
AddEventHandler('onResourceStop', function(resource)
    if resource == GetCurrentResourceName() then
        for _, handle in ipairs(createdPrompts) do
            exports['rsg-core']:deletePrompt(handle)
        end
    end
end)

Common Patterns

Pattern 1: Shop System

-- config.lua
Config.Shops = {
    {
        id = 'valentine_general',
        name = 'Valentine General Store',
        coords = vector3(-322.5, 773.2, 116.2),
        items = { 'bread', 'water', 'bandage' }
    }
}

-- client/main.lua
CreateThread(function()
    for _, shop in ipairs(Config.Shops) do
        exports['rsg-core']:createPrompt(
            shop.id,
            shop.coords,
            RSGCore.Shared.Keybinds['E'],
            'Browse ' .. shop.name,
            {
                type = 'client',
                event = 'shop:client:open',
                args = { shopId = shop.id, items = shop.items }
            }
        )
    end
end)

Pattern 2: Door System

-- Create door prompts that change based on lock state
local function UpdateDoorPrompt(doorId, isLocked)
    -- Delete existing prompt
    if doorPrompts[doorId] then
        exports['rsg-core']:deletePrompt(doorPrompts[doorId])
    end

    local doorConfig = Config.Doors[doorId]
    local label = isLocked and 'Unlock Door' or 'Lock Door'

    doorPrompts[doorId] = exports['rsg-core']:createPrompt(
        'door_' .. doorId,
        doorConfig.coords,
        RSGCore.Shared.Keybinds['E'],
        label,
        {
            type = 'server',
            event = 'doors:server:toggle',
            args = { doorId = doorId }
        }
    )
end

Troubleshooting

Prompt Not Appearing

Make sure the coordinates are correct and the player can actually reach that location:
-- Debug: Print player coords
local coords = GetEntityCoords(PlayerPedId())
print(coords)
Each prompt must have a unique location ID. If you use the same ID twice, the second one wonโ€™t create.
Make sure youโ€™re using a valid keybind from RSGCore.Shared.Keybinds:
print(RSGCore.Shared.Keybinds['E'])  -- Should print a hash number

Event Not Triggering

  • Verify the event is properly registered
  • Check if itโ€™s โ€˜clientโ€™ or โ€˜serverโ€™ type
  • Ensure event name matches exactly (case-sensitive)

Next Steps


Need help? Join the RSG Framework Discord!