[PAID] Drug Selling

Drug Empire – Elite Underground Commerce

Discreet Deals. Ruthless Negotiations. Dynamic Risk.
Step into the shadows of RedM with Drug Empire, a fully configurable, performance-friendly black market framework designed for immersive drug-selling experiences. From hunger-driven demand to unpredictable NPC robberies, every transaction is a roll of the dice—and every profit comes with a price.

:small_blue_diamond: Configurable Drug Zones
Define sale zones with unique hunger caps and sling radii across Valentine, Saint Denis, Rhodes, Armadillo, Blackwater, Strawberry, and Annesburg.
Config.DrugSellingLocations

:fire: Risk & Reward Tuning
Control the danger of every deal with:

  • Config.RobberyChance – NPCs might rob you.
  • Config.AlertChance – Risk triggering law enforcement.
  • Config.HaggleFlexPercent – How far an NPC will budge toward a player’s offer.
  • Config.HaggleCalculator– Max haggle ceiling (e.g., 3x asking price).

:handshake: Dynamic Haggling System

Negotiating prices isn’t just flavor — it’s built into the system. Players can counter-offer NPCs, but how much room they have to negotiate depends on configuration.


:game_die: Chance to Flex

Config.HaggleFlexPercent = 50

This defines the percentage chance an NPC is willing to negotiate.

Example: At 50, the NPC has a 50% chance to entertain a counter-offer and consider adjusting their price.


:chart_increasing: How Much They Flex

Config.FlexTowardPlayerPercent = 0.5

This controls how far the NPC will move toward the player’s offer if they agree to negotiate.

0.5 means the NPC meets the player halfway between their price and the player’s.
:warning: Do not set above 0.5 — otherwise, NPCs may consistently accept highball offers.


:money_bag: Haggle Limits

Config.HaggleCalculator = 3

This sets a ceiling for player offers based on the NPC’s maximum price.

Example: If an NPC’s price is $2 per unit and they want 2 units (total $4),
the player can counter-offer up to $4 * 3 = $12.

Setting this too high (e.g. above 10) can break balance and allow abuse.


Let me know if you’d like an illustrated step-by-step flow or example dialogue showcasing the haggle flow in-game!

:handshake: Immersive Interactions
Over 20 randomized greetings, job titles like Scam Artist or Gang Enforcer, and a mix of gritty Old West names.
Persistent names and jobs per ped—no more repeated introductions.

Config.RandomGreetingText, Config.RandomJobText, Config.RandomNames
:receipt: Haggle System
NPCs evaluate your offer and will meet halfway, refuse, or rob you based on the offer value and Config.HaggleFlexPercent.
Counter-offers are calculated dynamically from your price and the ped’s max payout.

:brain: Built-In Law Alert & Notification System

The included open-source file offers plug-and-play support for law enforcement alerts and player notifications:

function AlertLawDrugSelling(coords)
    local chance = math.random(1, 100)
    if chance > Config.AlertChance then return end

    local alertCoords = vector3(coords.x, coords.y, coords.z)

    if Config.AlertSystem == "outsider" then
        print(("[drugselling] Alerting law enforcement at %s"):format(tostring(alertCoords)))

        TriggerServerEvent("gs-drugempire:server:alertLawDrugSelling", 'outsider',alertCoords)

    elseif Config.AlertSystem == "bcc" then
        print(("[drugselling] Alerting BCC at %s"):format(tostring(alertCoords)))
        TriggerServerEvent("bcc:server:alertLawDrugSelling", 'bcc', alertCoords)

    else
        print(("[drugselling] Alert system not recognized: %s"):format(tostring(Config.AlertSystem)))
    end
end


function Notify(desc, ntype)
  lib.notify({
    title       = "Drug Selling",
    description = desc,
    type        = ntype  -- 'success', 'error', 'info'
  })
end

No external dependencies easily customize and integrate alerts, logging, and feedback into your law or player systems.

:luggage: Dynamic Inventory & Pricing
Add drugs in seconds via Config.DrugsPrice
Set custom:

  • Labels (e.g. “Devil’s Herb”)
  • Prop models and placement
  • Price ranges (min/max)
  • Supports visual attachment of drug packages using s_drugpackage_02x.

:oncoming_police_car: Law Enforcement Integration

Recognizes jobs like BWPolice, ValSheriff, etc.
Optional lawman pay reduction system:
If there aren’t enough lawmen around, players can still sell drugs — but their payout drops based on LawmenCheckPercent.

Config.RequiredLawmen = 1
Config.LawmenCheckPercent = 0.5

Config.LawmenJobs = {
  'BWPolice', 'RhoSheriff', 'SDPolice',
  'StrSheriff', 'ArmSheriff', 'ValSheriff'
}

Players receive full price when enough law enforcement is active.
If not, they earn reduced payouts (e.g., 50% if LawmenCheckPercent = 0.5), keeping the system balanced without blocking sales during low-pop hours.


:test_tube: New Features

Interaction Type Selector
Config.InteractType = "prompts" or "ox_target"
Use prompts or targeting for interactions.

Modular Alert System
Config.AlertSystem = "outsider" or "bcc"
Swap law alert methods by changing a single setting.

Optional System to Start Selling
Config.UsableItem = "itemmanhere" – Use this item to summon a helpful NPC or dog.

Prompt Keybinds (if using prompts)

Config.ControlKeys = {
    Search = { key = 0x760A9C6F, label = "Search for Stolen Drugs" }, -- G
    Leave = { key = 0x156F7119, label = "Leave" }, -- Backspace
    Haggle = { key = 0x760A9C6F, label = "Haggle Drugs" }, -- G
}

:chart_decreasing: Live Supply & Demand (Zone-Based)

Each town zone tracks drug demand using a 5-tier hunger system:

Starving → Hungry → Slightly Hungry → Normal → Full

This is defined in Config.HungerLevels.

:compass: Tracked by Zone, Not Individual Peds
Each selling area (Valentine, Saint Denis, etc.) has its own zoneName, maxHunger, and radius defined in Config.DrugSellingLocations.

:prohibited: Once a zone is Full, NPCs in that area will stop buying drugs.

:recycling_symbol: Hunger auto-decays every hour via:

Config.HungerDecayAmount      -- how much hunger drops per cycle  
Config.RefreshRateHunger      -- how often hunger updates (in ms)

:brain: Smarter Zone Logic

  • Config.ClearHungerOnServerStart toggle full resets.
  • Config.UseVisableRadius restrict sales within visible range.
  • Config.SellDistance max distance you can walk from a deal.
  • Config.CooldownTime time between sales.

:octagonal_sign: Blacklist & Restrictions

  • Block specific ped models from being dealers.
  • Prohibit drug sales from horses or wagons.
  • No sale zones supported.
    Config.BlacklistedPeds, Config.NoSellZone

:oncoming_police_car: Law Enforcement Support
Automatically detect lawmen jobs like:
BWPolice, SDPolice, ValSheriff, etc.
Trigger alerts when needed and prevent abuse with minimum police checks.

:brain: Clean Configuration
All settings are centralized in the Config table:
Config.Lan Config.Settings, Config.LawmenJobs, etc.
No need to touch core logic just adjust values.

:shield: Built for Performance
Clean threading and optimized logic ensure you can handle dozens of NPCs simultaneously without lag—perfect for populated RP servers.

:test_tube: Optional Start Without Commands
Want to auto-start the script? Just use this client-side event:
drugselling:client:opensourceStartScript

:tools: Currently Supported Frameworks

  • :white_check_mark: VORP Core
  • RSG (COMING SOON)

:compass: Coming Soon

  • Stash Houses: Hidden buildings for discreet high-volume deals.
  • Undercover Informants: Some peds might snitch—if you’re not careful.
  • Smuggle Missions
  • Dealer Shops

Buy Here: https://goldslush.tebex.io/package/6854025

:video_camera: Preview

Config = Config or {}

-- Selling ConfigDrug
Config.RobberyChance = 5 -- Percentage Chance of being robbed (max 100)
Config.AlertChance = 15 -- Percentage Chance of alerting police (max 100)
Config.HaggleFlexPercent = 50         -- % chance NPC is willing to flex
Config.FlexTowardPlayerPercent = 0.5  -- how far NPC flexes toward player's offer (0.5 = halfway) Dont go higher than 0.5
Config.HaggleCalculator = 3 -- 3x the asking price so if a ped wants to buy 2 bags for 2 dollars, the player can haggle up to 4 dollars (dont go higher then 10x the asking price)
Config.CooldownTime = 20 -- Cooldown time in seconds for selling drugs
Config.HungerDecayAmount = 100        -- How much hunger to remove every cycle
Config.RefreshRateHunger = 60 * 60 * 1000 -- Time between hunger reductions (1 hour)
Config.InteractType = "ox_target" -- Options: "prompts" or "ox_target" 
Config.AlertSystem = "outsider" -- Options: "outsider" or "bcc"(check opensource server and client to edit)
Config.UsableItem = "itemmanhere" -- Item used to summon the dog

Config.GiveItemReward = true -- Set to true to give the player a reward item when selling drugs
Config.GiveItem = "water" -- Item to give when selling drugs (if GiveItemReward is true)

-- Lawmen Setttings
Config.RequiredLawmen = 1 -- Need to be a lawman to get full price for drugs, if not a lawman, they will get % of the LawmenCheckPercent below
Config.LawmenCheckPercent = 0.5 -- Percentage of the price they will get if not a lawman (0.5 = 50% of the price)
Config.LawmenJobs = { -- Jobs that can use the lawmen check
    'BWPolice',
    'RhoSheriff',
    'SDPolice',
    'StrSheriff',
    'ArmSheriff',
    'ValSheriff',
}

-- If InteractType = "prompts" 
Config.ControlKeys = {
    Search = { key = 0x760A9C6F, label = "Search for Stolen Drugs" }, -- G
    Leave = { key = 0x156F7119, label = "Leave" }, -- Backspace
    Haggle = { key = 0x760A9C6F, label = "Haggle Drugs" }, -- G
}

Config.MenuCam = false
Config.ClearHungerOnServerStart = false -- Set to true to clear all peds hunger on server start
Config.SellDistance = 20 -- Player can travel 20 meters from the selling location
Config.UseVisableRadius = true -- Use visible radius for selling drugs

-- Commands to start and stop selling
Config.Settings = {
    StartSelling = "sellstart",
    StopSelling = "sellstop",
}

-- Drug Config
Config.HaggleAmount = {
    Min = 1, -- Minimum amount of drugs to haggle
    Max = 5, -- Maximum amount of drugs to haggle
}

Config.DrugsPrice = {
    ["swampherbblueberry"] = {
        min = 2,
        max = 2,
        label = "blueberry herb",
        Model = `s_drugpackage_02x`,
        Pos = vector3(0.1, -0.05, -0.07),
        Rot = vector3(-85.0, 0.0, 0.0),
    },
    ["swampherbdevilscreek"] = {
        min = 2,
        max = 2,
        label = "devil's herb",
        Model = `s_drugpackage_02x`,
        Pos = vector3(0.1, -0.05, -0.07),
        Rot = vector3(-85.0, 0.0, 0.0),
    },
    ["swampherbstrawberry"] = {
        min = 2,
        max = 2,
        label = "strawberry herb",
        Model = `s_drugpackage_02x`,
        Pos = vector3(0.1, -0.05, -0.07),
        Rot = vector3(-85.0, 0.0, 0.0),
    },
}

-- Laungagues for Alerts/UI/Progressbars
Config.Lan = {
    HandingOffDrugs          = "Handing off drugs",
    Success                  = "You have successfully sold drugs!",
    Earned                   = "You earned $%s",
    Failed                   = "Failed",
    Robbed                   = "You were robbed of your drugs!",
    Returned                 = "You returned your stolen drugs!",
    NoDrugs                  = "You have no drugs to sell!",
    StopSelling              = "You stopped selling drugs.",
    CannotSearch             = "You cannot search this person they are still alive!",
    NothingToBuy             = "You have nothing to buy!",
    NoSellZone               = "You cannot sell drugs here!",
    CannotSellVehicle        = "You cannot sell drugs from a vehicle!",
    NotSelling               = "You are not selling drugs!",
    InvalidInput             = "Invalid input!",
    RegionFull               = "This region is already full. Try selling somewhere else.",
    LeftZone                 = "You left the selling zone.",
    OfferToHigh              = "Offer too high! The person will not buy at this price.",
    InviladData              = "Invalid data provided.",
    InvalidInputPosNumber    = "Invalid input. Please enter a valid number.",
    OfferAccepted            = "Alright, I'll pay $%d for %d units of %s. Deal?",
    CounterHalfway           = "Too high for me. I'll meet you halfway at $%d for %d %s.",
    CounterFinal             = "No. I'll only pay $%d for %d units of %s — final offer.",
    OfferTooHigh             = "Your price of $%d is too high. I’ll pay up to $%d for %d units of %s. Want to counteroffer?",
    AcceptLabel              = "Accept the Deal",
    CounterLabel             = "Counter Offer",
    LeaveLabel               = "Leave",
    SearchLabel              = "Search for Stolen Drugs",
    SearchIcon               = "fas fa-search",
    SearchProgress           = "Searching...",
    LeaveIcon                = "fas fa-sign-out-alt",
    Buyer                    = "A buyer is approaching you.",
    HaggleLabel              = "Haggle Drugs",
    HaggleIcon               = "fas fa-cannabis",
    InputConfirmButton       = "Confirm",
    InputPlaceholder         = "$0.00",
    InputHeader              = "Enter your price for %d units (Max: $%d)",
    InputTitle               = "Please enter only numbers",
    CounterInputButton       = "Confirm",
    CounterInputPlaceholder  = "1234",
    CounterInputHeader       = "Your counteroffer for %d units",
    CounterInputTitle        = "Please enter only numbers",
    Cooldown                 = "Wait %ds before selling again.",
    DialogHaggleLabel        = "Haggle Drugs",
    DialogLeaveLabel         = "Leave",
}

-- UI Text Elements
Config.RandomJobText = {
    "Scam Artist",
    "Smuggler",
    "Snake Oil Salesman",
    "Stagecoach Guard",
    "Mail Carrier",
    "Cattle Trader",
    "Herbalist",
    "Gang Enforcer",
    "Scam Artist",
    "Information Broker",
    "Money Lender",
    "Courier",
    "Fence Runner",
    "Outlaw Tracker",
    "Stagecoach Engineer",
    "Town Marshall",
    "Fence Master"
}

Config.RandomNames = {
    Male = {
        "Tyler",
        "Jebediah", "Silas", "Wyatt", "Beau", "Clayton",
        "Moses", "Cyrus", "Rhett", "Eli", "Boone",
        "Colton", "Leander", "Harlan", "Jesse", "Zane",
        "Gideon", "Thaddeus", "Orson", "Rufus", "Felix" , "Mafia",
    },
    Female = {
        "Mabel", "Pearl", "Evangeline", "Rosalind", "Daphne",
        "Claudia", "Ada", "Flora", "Estelle", "Beatrice",
        "Matilda", "Cornelia", "Josephine", "Felicity", "Lavinia",
        "Myrtle", "Harriet", "Viola", "Prudence", "Minerva"
    }
}

Config.RandomGreetingText = {
    "Well, if it ain’t my favorite outlaw rolling into town!",
    "Evenin’, friend. You look like you’ve got a story to tell.",
    "Got a minute? I’ve got somethin’ special just for you.",
    "You’re lookin’ sharp today—business or pleasure?",
    "Name’s [Your Name]. What can I do for you?",
    "I’ve been expectin’ someone like you all day.",
    "Don’t wander off—you’ll miss the good stuff.",
    "They say you’re the best in these parts. Is that true?",
    "You got the look of someone who pays top dollar.",
    "I reckon you’ll want to hear my offer.",
    "Sun’s settin’ soon—best deals happen before dark.",
    "I heard you got coin to spend. Am I right?",
    "Step closer, stranger; I won’t bite… much.",
    "Looking for something discreet? You’ve come right.",
    "I hear you’ve been askin’ about rare goods.",
    "I’ve got the finest wares this side of the river.",
    "Ain’t often I see a face I recognize out here.",
    "Stick around—deals this good don’t last long.",
    "You’ve got the eyes of a dealmaker, I see.",
    "Let’s make this quick—business is good tonight."
}

-- Blacklisted Peds
Config.BlacklistedPeds = {
    `s_m_m_rhddealer_01`,
    `S_M_M_BLWDEALER_01`
}

-- Hunger Levels
Config.HungerLevels = {
    [1] = { -- 0-100
        min = 0,
        max = 100,
        label = "Starving",
        color = { r = 255, g = 0, b = 0 }
    },
    [2] = { -- 101-200
        min = 101,
        max = 200,
        label = "Hungry",
        color = { r = 255, g = 165, b = 0 }
    },
    [3] = { -- 201-300
        min = 201,
        max = 300,
        label = "Slightly Hungry",
        color = { r = 255, g = 255, b = 0 }
    },
    [4] = { -- 301-400
        min = 301,
        max = 400,
        label = "Normal",
        color = { r = 0, g = 255, b = 0 }
    },
    [5] = { -- 401-500
        min = 401,
        max = 500,
        label = "Full",
        color = { r = 0, g = 0, b = 255 }
    },
}

-- Drug Selling Locations
Config.DrugSellingLocations = {
    {
        coords     = vector3(-304.82,  788.97, 117.80), -- Valentine
        zoneName   = "Valentine", -- zone name will appear in the database
        maxHunger  = 500, -- max hunger for the zone (once reached maxed peds wont buy drugs)
        radius     = 300.0, -- Sling drugs radius
    },
    {
        coords     = vector3(2602.16, -1263.50,  52.65),
        zoneName   = "Saint Denis",
        maxHunger  = 500,
        radius     = 300.0,
    },
    {
        coords     = vector3(1321.34, -1305.82,  76.37),
        zoneName   = "Rhodes",
        maxHunger  = 500,
        radius     = 300.0,
    },
    {
        coords     = vector3(-3683.77, -2608.82, -14.08),
        zoneName   = "Armadillo",
        maxHunger  = 500,
        radius     = 300.0,
    },
    {
        coords     = vector3(-826.42, -1306.93,  43.61),
        zoneName   = "Blackwater",
        maxHunger  = 500,
        radius     = 300.0,
    },
    {
        coords     = vector3(-1795.24,  -376.11, 159.80),
        zoneName   = "Strawberry",
        maxHunger  = 500,
        radius     = 300.0,
    },
    {
        coords     = vector3(2927.98,  1334.43,  44.08),
        zoneName   = "Annesburg",
        maxHunger  = 500,
        radius     = 300.0,
    },
}

Update

  • :white_check_mark: All UI elements have been moved to Config.Lan for easy customization, as requested.
  • :brain: A new mechanic has been added to the haggle system: Config.FlexTowardPlayerPercent = 0.5

This controls how far an NPC will flex toward the player’s offer when negotiating (e.g., 0.5 means halfway between their price and the player’s).
While you can increase it, we recommend keeping it at or below 0.5 to maintain balance. Example Player sets the finial counter offer to $30 the Ped will come up half there originally offer )

-- Laungagues for Alerts/UI/Progressbars
Config.Lan = {
    HandingOffDrugs          = "Handing off drugs",
    Success                  = "You have successfully sold drugs!",
    Earned                   = "You earned $%s",
    Failed                   = "Failed",
    Robbed                   = "You were robbed of your drugs!",
    Returned                 = "You returned your stolen drugs!",
    NoDrugs                  = "You have no drugs to sell!",
    StopSelling              = "You stopped selling drugs.",
    CannotSearch             = "You cannot search this person they are still alive!",
    NothingToBuy             = "You have nothing to buy!",
    NoSellZone               = "You cannot sell drugs here!",
    CannotSellVehicle        = "You cannot sell drugs from a vehicle!",
    NotSelling               = "You are not selling drugs!",
    InvalidInput             = "Invalid input!",
    RegionFull               = "This region is already full. Try selling somewhere else.",
    LeftZone                 = "You left the selling zone.",
    OfferToHigh              = "Offer too high! The person will not buy at this price.",
    InviladData              = "Invalid data provided.",
    InvalidInputPosNumber    = "Invalid input. Please enter a valid number.",
    OfferAccepted            = "Alright, I'll pay $%d for %d units of %s. Deal?",
    CounterHalfway           = "Too high for me. I'll meet you halfway at $%d for %d %s.",
    CounterFinal             = "No. I'll only pay $%d for %d units of %s — final offer.",
    OfferTooHigh             = "Your price of $%d is too high. I’ll pay up to $%d for %d units of %s. Want to counteroffer?",
    AcceptLabel              = "Accept the Deal",
    CounterLabel             = "Counter Offer",
    LeaveLabel               = "Leave",
    SearchLabel              = "Search for Stolen Drugs",
    SearchIcon               = "fas fa-search",
    SearchProgress           = "Searching...",
    LeaveIcon                = "fas fa-sign-out-alt",
    Buyer                    = "A buyer is approaching you.",
    HaggleLabel              = "Haggle Drugs",
    HaggleIcon               = "fas fa-cannabis",
    InputConfirmButton       = "Confirm",
    InputPlaceholder         = "$0.00",
    InputHeader              = "Enter your price for %d units (Max: $%d)",
    InputTitle               = "Please enter only numbers",
    CounterInputButton       = "Confirm",
    CounterInputPlaceholder  = "1234",
    CounterInputHeader       = "Your counteroffer for %d units",
    CounterInputTitle        = "Please enter only numbers",
    Cooldown                 = "Wait %ds before selling again.",
    DialogHaggleLabel        = "Haggle Drugs",
    DialogLeaveLabel         = "Leave",
}```

Update

:oncoming_police_car: Law Enforcement Integration

Recognizes jobs like BWPolice, ValSheriff, etc.
Optional lawman pay reduction system:
If there aren’t enough lawmen around, players can still sell drugs — but their payout drops based on LawmenCheckPercent.

Config.RequiredLawmen = 1
Config.LawmenCheckPercent = 0.5

Config.LawmenJobs = {
  'BWPolice', 'RhoSheriff', 'SDPolice',
  'StrSheriff', 'ArmSheriff', 'ValSheriff'
}

Players receive full price when enough law enforcement is active.
If not, they earn reduced payouts (e.g., 50% if LawmenCheckPercent = 0.5), keeping the system balanced without blocking sales during low-pop hours.


:test_tube: New Features

Interaction Type Selector
Config.InteractType = "prompts" or "ox_target"
Use prompts or targeting for interactions.

Modular Alert System
Config.AlertSystem = "outsider" or "bcc"
Swap law alert methods by changing a single setting.

Optional System to Start Selling
Config.UsableItem = "itemmanhere" – Use this item to summon a helpful NPC or dog.

Prompt Keybinds (if using prompts)

Config.ControlKeys = {
    Search = { key = 0x760A9C6F, label = "Search for Stolen Drugs" }, -- G
    Leave = { key = 0x156F7119, label = "Leave" }, -- Backspace
    Haggle = { key = 0x760A9C6F, label = "Haggle Drugs" }, -- G
}

Small Update as requested

  • Config.GiveItemReward = true – Set to true to give the player a reward item when selling drugs,
  • Config.GiveItem = “water” – Item to give when selling drugs (if GiveItemReward is true),

Rather rewarding Cash you have the option for Items

:wrench: Small Update

  • :stopwatch: Improved Performance: Increased client-side wait times to reduce overall CPU load.
  • :tools: Robbery Fix: You can now only recover drugs from a ped after they are confirmed dead.
  • :handshake: Ped Behavior Fix: Fixed an issue where peds could become hostile during interactions they are now passive and won’t attack while haggling or waiting.
  • :moneybag: Configurable Buyer Demand: Added support for setting how much a ped wants to buy via config:
-- Drug Config
Config.HaggleAmount = {
    Min = 1, -- Minimum quantity a ped will offer to buy
    Max = 5, -- Maximum quantity a ped will offer to buy
}```

Patch Notes: Drug Empire Update

Version: v1.0.1
Date: June 6, 2025

:moneybag: Improved Haggling System

  • Dynamic Price Input: Players can now enter any number during drug sales — only upper limits are enforced. No more forced price ranges.
  • Smart Validation: If the player’s offer is above the allowed maximum, they are alerted and the input is reloaded for retry — no more dead ends or stuck input boxes.
  • Correct Input Looping: Fixed an issue where invalid entries (e.g. over max price) would show an error but not reopen the prompt. Input now reloads properly on all bad entries.

:receipt: Price Calculation Fixes

  • Max Price Logic Fixed: Previously, when selling low-quantity drugs (e.g. 1x item), the system incorrectly displayed max prices based on old or cached quantities. Now fixed to reflect true live values.
  • No Residual Price Carryover: Max price is recalculated every sale, ensuring values don’t carry from previous interactions.

:robot: Internal Optimization

  • Refactored haggleDrug to use centralized openHaggleInput() function for better maintainability.
  • Removed redundant logic and ensured clean reusability across haggleDrug, counterOffer, and processPlayerOffer.

:man_standing: User Experience & UI

  • Text prompts dynamically update to show unit count and true max price.
  • Inputs are no longer locked to the min value — players can offer any amount up to the max.
  • UI styling and error handling remain clean and immersive.

:bug: Bug Fixes

  • Fixed: Counter-offer prompt not reopening after invalid input.
  • Fixed: Drug amount mismatch between what player has vs what input prompt assumes.
  • Fixed: Some inputs showing stale max value after repeated haggles.

Only update to the Config OfferTooLow = "That offer is too low. Minimum is $%d.",