Features

Sale flow, pricing behavior, risk mechanics, and lifecycle details for Oxide Drugselling.

Sale Flow

The selling interaction follows this sequence:

  1. oxide-drugselling registers a global ped interaction through olink.target.AddGlobalPed.
  2. The interaction only appears when all runtime checks pass:
    • selling is enabled
    • no sale is already active
    • the target is not a player ped
    • the target is not a mission entity
    • the target is not dead
    • neither the target nor the player is in a vehicle
    • the ped model is not blacklisted
    • the player is not on cooldown
    • the NPC location is not on cooldown
    • the player has at least one configured sellable item
  3. The NPC walks toward the player. If the ped cannot get within range, the interaction cancels.
  4. The resource freezes the NPC, turns both entities to face each other, creates a scripted camera, and opens the sell panel.
  5. The client requests sellable groups from the server, then resolves item image paths through o-link.inventory before sending data to the NUI.
  6. The player selects a grouped item stack, quantity, and asking price.
  7. The server validates the request, recalculates fair pricing from config, rolls risk outcomes, and returns the result.
  8. The client shows the result state, plays the relevant NPC animation when needed, then cleans up the camera and interaction state.

Sell Panel

The packaged UI is a Vue 3 NUI panel that shows:

  • grouped sellable inventory entries
  • item image paths from o-link.inventory.GetImagePath
  • quantity controls
  • direct price input and slider-driven pricing
  • fair-price feedback
  • bonus tags from pricing modifiers
  • result states for thinking, accept, counter, reject, and error

When the current NPC model is marked as a gang model, the panel also flags the deal as dangerous.

Pricing Pipeline

Fair price is calculated per unit with the internal pricing pipeline:

fairPrice = basePrice * product(all multiplier modifiers) * (1 + sum(all bonus modifiers))

The pipeline is driven entirely by Config.Drugs[drugKey].modifiers.

Supported modifier types in the shipped config:

  • score_multiplier
  • grade_multiplier
  • variant_multiplier
  • trait_bonus
  • metadata_bonus

Shipped Weed Pricing

Weed uses:

  • base prices per item size
  • a score_multiplier on quality_score
  • a fallback grade mapping on quality
  • a trait_bonus that calls oxide-weed:GetStrainData
  • a metadata_bonus on generation

Default weed base prices:

ItemBase Price
weed_1g25
weed_eighth75
weed_quarter140
weed_half260
weed_ounce480
weed_brick800

Shipped Meth Pricing

Meth uses:

  • a shared base price of 140
  • a grade_multiplier on purity
  • a variant_multiplier on variant

All shipped meth bag variants use the same base price and differentiate through modifier data.

Negotiation Rules

After the server calculates the fair total price for the selected quantity, it evaluates the sale in this order:

  1. police-dispatch roll
  2. flat rejection roll
  3. robbery roll for gang NPCs only
  4. price-ratio evaluation

Default pricing thresholds:

RatioOutcome
playerPrice / fairPrice <= 0.90auto-accept
<= 1.2550/50 accept or counter
> 1.25reject

Counter-offers use 95% of fair total price by default and expire after 60 seconds.

Risk Mechanics

Rejection

A flat 15% reject chance applies before price evaluation.

Robbery

Gang NPCs have a separate 15% robbery chance by default.

When robbery happens:

  • up to Config.Selling.risks.robberyItemLoss items are removed from the offered metadata group
  • Config.Selling.risks.robberyCashPercent of the player's current dirty-money balance is taken
  • the robber draws a pistol, aims at the player, then flees

Gang models are configured in shared/config/selling.lua.

Police Dispatch

Every interaction has a separate 15% dispatch chance by default.

When dispatch fires, the client sends an alert through o-link.dispatch with:

  • code 10-31
  • locale key sell_dispatch_message
  • player coordinates
  • configured blip data
  • configured police job recipients
  • configured display time

Dirty Money Handling

Dirty money is abstracted behind two modes:

  • account: uses o-link.money with Config.DirtyMoney.account
  • item: uses o-link.inventory with Config.DirtyMoney.item

The same mode is used for both sale payouts and robbery deductions.

Progression Integration

Each drug type can optionally define a progression block with:

  • source resource name
  • level export
  • XP export
  • sold-count export
  • XP per unit

Behavior is intentionally tolerant:

  • if no progression block exists, the drug sells normally
  • if the source resource is not started, level checks are skipped and tracking is skipped
  • if a configured export fails, the sale flow continues

This lets oxide-drugselling run standalone while still supporting tighter integration with production resources.

Cooldowns and Runtime Guards

Current runtime guards:

  • client-side sellable-item cache: 10 seconds
  • player interaction cooldown: 5000 ms
  • NPC location cooldown: 120000 ms
  • distance monitor closes the sale if the NPC disappears or the player moves more than 5.0 units away

Current server callback rate limits:

CallbackWindow
oxide-drugselling:server:getSellableItems3000 ms
oxide-drugselling:server:initSale5000 ms
oxide-drugselling:server:acceptCounter5000 ms

Lifecycle

The resource initializes from the current o-link lifecycle flow:

  • waits for olink:client:playerReady
  • cleans up on olink:client:playerUnload
  • also performs a delayed fallback check with olink.framework.GetIsPlayerLoaded()

This keeps the interaction registration tied to actual player readiness rather than resource start timing alone.

Next Steps