RX Drug Sales Configuration

RX Drug Sales keeps its behaviour in config.lua. The file covers the framework bridge, how a sale is negotiated, how often the same customer can be approached, when law enforcement is alerted, and what can be sold where.

Restart ripex-selldrugs after changing the configuration so the server and every client use the same values.

Framework and language

SettingDefaultBehaviour
Framework'vorp'Framework bridge: 'qbr', 'vorp', or 'rsg'.
DefaultLanguage'en'Language used for resource text.
Config.Framework = 'vorp' -- 'qbr', 'vorp', 'rsg'
Config.DefaultLanguage = 'en'

Unlike some resources, this one does not auto-detect. Set Framework to match your server before the first start, otherwise item checks and payments will not work.

Selling interaction

These settings decide how close the player must be to a customer and how much the final price can move during bargaining.

SettingDefaultBehaviour
PromptKey0x760A9C6FRedM input hash used to start a sale.
SellDistance2.0Maximum distance to the NPC when selling.
PriceMultiplier.min0.8Lowest multiplier applied while bargaining.
PriceMultiplier.max1.2Highest multiplier applied while bargaining.
Config.PromptKey = 0x760A9C6F
Config.SellDistance = 2.0

Config.PriceMultiplier = {
    min = 0.8,
    max = 1.2
}

The multipliers bracket the base price of an item, so a 15 base price can settle anywhere between 12 and 18 before the location multiplier is applied. Widen the range for a more unpredictable economy, or narrow it if prices should stay close to the listed value.

Keep SellDistance short. The sale is meant to be a face-to-face exchange, not something a player can trigger from across the street.

NPC cooldown and repeat attempts

After buying, a customer goes on cooldown. Approaching them again during that window plays a suspicious-reaction dialogue instead of a normal greeting.

SettingDefaultBehaviour
enabledtrueEnables the per-NPC cooldown.
duration8000Cooldown length in milliseconds.
repeatChance80Percentage chance of showing the repeated-attempt dialogue.
Config.NPCCooldown = {
    enabled = true,
    duration = 8000,
    repeatChance = 80
}

Config.RepeatedAttempt = {
    successChance = 70
}

RepeatedAttempt.successChance is the chance that pushing a customer during their cooldown works anyway. At 70, persistence usually pays off, which makes the cooldown a soft deterrent rather than a hard block. Lower it if repeat selling should be genuinely risky.

The shipped duration of 8000 and repeatChance of 80 are testing values, marked as such in the config comments. Eight seconds is far too short for a live server, since it lets a player farm the same customer continuously. Raise the duration before going live.

Police dispatch

Dispatch decides when a sale draws law enforcement attention.

SettingDefaultBehaviour
enabledtrueMaster switch for the whole dispatch system.
useBuiltIntrueUses the built-in dispatch and ignores external triggers.
successChance20Percentage chance of an alert after a successful sale.
rejectChance70Percentage chance of an alert after a rejected sale.
Config.Dispatch = {
    enabled = true,
    useBuiltIn = true,
    successChance = 20,
    rejectChance = 70
}

A rejected sale is far riskier than a successful one, which is the point: an offended customer is the one who reports you. Keep rejectChance above successChance so approaching the wrong person stays the main danger.

Built-in dispatch

SettingDefaultBehaviour
policeJobspolice, sheriff, BWPoliceJobs that receive built-in dispatch alerts.
sendToAllfalseSends alerts to every player, ignoring jobs.
Config.Dispatch.policeJobs = { 'police', 'sheriff', 'BWPolice' }
Config.Dispatch.sendToAll = false

Job names must match the ones your framework uses. The defaults are examples, so replace them with your own law jobs.

sendToAll exists for debugging. Leave it false on a live server, or every player receives every drug alert.

Dispatch blip

SettingDefaultBehaviour
enabledtrueShows a blip when an alert fires.
duration12000How long the blip stays visible, in milliseconds.
spriteHash-1289383059Blip sprite used for the alert.
scale1.0Blip scale.
labelKey'sell.dispatch_blip_name'Locale key used for the blip title.
Config.Dispatch.blip = {
    enabled = true,
    duration = 12000,
    spriteHash = -1289383059,
    scale = 1.0,
    labelKey = 'sell.dispatch_blip_name'
}

Leave labelKey alone. It points at a locale entry rather than holding the text itself, so changing it here removes the blip title instead of renaming it. Edit the locale file to change the wording.

scale may have limited effect in RedM; the value is accepted but the game does not always honour it.

Custom dispatch

Config.Dispatch.alert is a client-side function that runs when an alert fires. It ships empty, with commented examples for common setups:

Config.Dispatch.alert = function(coords, message)
    -- TriggerServerEvent('police:server:policeAlert', coords, message)
    -- TriggerServerEvent('vorp_police:addAlert', 'Drug Sale', coords, 0)
end

This function is only reached when useBuiltIn is false. While useBuiltIn stays true, the built-in dispatch handles alerts and anything written here is ignored, which is the usual reason a custom integration appears to do nothing.

Dialogue camera

Config.DialogCamera = {
    enabled = true,
    fov = 50.0,
    offsetX = 0.0,
    offsetY = 0.5,
    offsetZ = 0.05
}

The camera framing is calculated automatically. These values are exposed but are not intended to be tuned per server; leave them at their defaults unless you have a specific framing problem to solve.

Sellable items

Config.SellableItems defines what can be sold and how each item behaves in a negotiation. The key is the inventory item name.

FieldBehaviour
labelName shown to the player.
basePricePrice per item before multipliers.
bargainChancePercentage chance the NPC tries to bargain.
rejectChancePercentage chance the NPC refuses outright.
Config.SellableItems = {
    ['moonshine'] = {
        label = 'Moonshine',
        basePrice = 15,
        bargainChance = 70,
        rejectChance = 20
    },
    ['gold_nugget'] = {
        label = 'Gold Nugget',
        basePrice = 25,
        bargainChance = 80,
        rejectChance = 5
    }
}

The defaults ship with moonshine, tobacco, gold_nugget, medicine, and water. Each key must match an item that actually exists in your inventory, or the sale will never trigger for it.

Higher-value items carry higher bargainChance, which keeps expensive goods from being a guaranteed payout. water is included as a test entry with a basePrice of 1 and no bargaining or rejection; remove it once you have confirmed the flow works.

Sell locations

Config.SellLocations defines where selling is possible. Four locations ship by default: Saint Denis, Valentine, Rhodes, and Blackwater.

FieldBehaviour
nameLocation name used internally.
coordsCentre point of the sell area.
radiusSize of the area in which selling is possible.
itemsItem keys sellable here. An empty table allows every item.
priceMultiplierMultiplier applied to prices at this location.
dialoguesNPC lines used at this location.
Config.SellLocations = {
    {
        name = 'Saint Denis',
        coords = vector3(2632.32, -1225.63, 53.38),
        radius = 120.0,
        items = { 'moonshine', 'tobacco' },
        priceMultiplier = 1.2,
        dialogues = { ... }
    }
}

The default multipliers give each location its own character: Rhodes pays the most at 1.3 but only accepts gold_nugget and medicine, while Valentine pays the base rate at 1.0 and accepts everything. Use that pattern when adding your own, so travelling further is worth the risk.

An empty items table means every entry in Config.SellableItems is accepted. List item keys explicitly to give a location a specialty.

To add a location, copy an existing block and change its name, coordinates, radius, item list, and multiplier. Every location needs its own dialogues table.

Dialogues

Each location holds eight dialogue categories. One line is picked at random from the relevant list.

CategoryPlayed when
greetingThe player starts a sale.
bargainThe NPC counters with a lower price.
acceptThe sale is completed.
rejectThe NPC refuses the sale.
farewellThe interaction ends.
repeated_attemptThe player approaches an NPC still on cooldown.
repeated_successPushing a cooling-down NPC succeeds.
repeated_rejectPushing a cooling-down NPC fails.
dialogues = {
    bargain = {
        "That ain't worth that much, I'll give you %s.",
        'You are asking too much. How about %s?'
    }
}

Every bargain line must contain %s. That placeholder is replaced with the offered price, so a line without it shows no number at all.

The other categories take plain text. Add or remove lines freely; more lines simply means less repetition. Give each location its own voice, as the defaults do: Saint Denis is casual, Rhodes is nervous and secretive.

Debug mode

Config.Debug = false

Keep debug disabled in production. Enable it temporarily while diagnosing a framework mismatch, an item key that never appears, or a dispatch alert that does not fire, then turn it off again.