OPX//77
opx_infinityhauling

hauling

Last updated on

A job with no start command — pick up a server-owned crate, load it into a vehicle trunk and sell it to the NPC at a drop-off.

A delivery job. Crates stand at surveyed points; every player in the site's routing bucket sees the same crates. A player picks one up with the target eye (ALT), carries it, loads it into a vehicle (it becomes a trunk item), drives to one of the site's drop-offs and sells it to the NPC there. No command, no start point: the eye is the whole entry. Sites are written in config/hauling.lua.

Sideboth
Requirescharacter (hard)
Optionaltarget, progress, inventory, animations
Contracthauling v1 — server
Configconfig/hauling.lua (shared)
Datanone of its own; loaded crates are hauling_crate items in vehicle trunks (inventory)
  • Without target there is no way into the job (the client says so once).
  • Without progress no bar is drawn, but the server still enforces the timing.
  • Without inventory nothing can be loaded or sold.
  • The manifest needs world.props (crates) and world.npcs (sellers).
  • Target rows: hauling.pickup (crates), hauling.load (vehicles), hauling.sell (NPCs).

How the job runs

  1. Pick up. ALT on a crate → Pick up the crate. The server checks distance (REACH), bucket and the site's job gate, claims the crate and runs a PICKUP_MS bar while the player plays the site's PICKUP_POSE. The crate is then attached (CARRY), the carry animation plays, weapons are holstered and blocked.
  2. Put down (optional). DROP_KEY (X by default, rebindable as Hauling: put the crate down). The crate lands DROP_DISTANCE m in front; anyone may pick it up. Untouched for DROP_RETURN_MS, it goes back to its point on the next refill pass.
  3. Load. ALT on a vehicle within VEHICLE_REACH → Load the crate, LOAD_MS bar. The prop is removed and one ITEM (hauling_crate, 10 kg) with metadata { site = <site key> } is added to that vehicle's trunk. A full, locked or foreign trunk refuses.
  4. Sell. ALT on the seller NPC at a drop-off → Sell the crates, DELIVER_MS bar. Every crate of that site in the player's bag and in the trunk of every vehicle parked within the drop-off's RADIUS that the player owns or holds a key to (vehiclekeys.Holds) is sold at once. Someone else's truck parked there is not counted. Selling from the bag is intended (a drop-off may sit where no vehicle can park). On a server with neither vehicles nor vehiclekeys, every parked trunk counts, and the log says so once. Pay per crate: the site's PAY, else the HAUL_PAY_PER_CRATE tunable (starts at PAY_PER_CRATE), paid in CURRENCY. If payment fails the crates are put back: first where each came from, then into the bag or another trunk of the sale. Crates that fit nowhere are lost and recorded for a refund (not_paid_lost, audit event hauling.crateLost). The seller's citizen id is pinned before the first yield: if the player left or switched character during the sale, that character is paid anyway (through AddMoneyOffline when offline), opx:on:hauling:sold names them, and no toast reaches whoever now holds the connection. Begin and finish run on their own thread.
  • Clocks. The server owns every clock: a completion sooner than the bar minus CLOCK_TOLERANCE_MS is refused. A claim not finished within its bar plus CLAIM_GRACE_MS is released by the refill pass.
  • Getting into a vehicle with a crate puts it down at the player's feet (You put the crate down to get in. Load it into the trunk to take it along.); a bar under way ends. It no longer goes back to its point.
  • Carry ends (crate back on its point) when the carrier disconnects, dies or changes bucket.
  • Refill. Every REFILL_MS (tunable HAUL_REFILL_MS, live), each usable site gets up to SPAWN_PER_PASS new crates on free points. A point stays empty RESPAWN_MS after its crate leaves. MAX_CRATES caps the total across all sites.
  • Disabled sites. At boot a site is disabled (with a log line) if any point or drop-off is the placeholder 0, 0, 0, two points are closer than MIN_POINT_GAP, a drop-off has no valid NPC, or the engine does not know its MODEL (tested by creating one crate).

Configuration

config/hauling.lua sets OPX.Config.MODULES.hauling. Shared script.

KeyDefaultEffect
enabledtruefalse switches the module off.
MODEL'crate.small'Prop alias a site uses unless it names its own. Must be a curated alias, never a .mesh path.
MAX_CRATES64Most crates standing in the world across all sites.
PICKUP_MS4000Pickup bar length.
LOAD_MS3000Load bar length.
DELIVER_MS5000Sell bar length.
CLOCK_TOLERANCE_MS500How early a completion may arrive and still count.
CLAIM_GRACE_MS20000Extra time past a bar before an unfinished claim is released.
REACH3.0Metres from a crate to act on it (3D, server-measured, no line of sight).
VEHICLE_REACH4.5Metres from a vehicle to load it, and from a drop-off to sell.
MIN_POINT_GAP2.0Minimum distance between two points of one site.
REFILL_MS60000Refill-and-reap interval. Seeds the HAUL_REFILL_MS tunable.
PAY_PER_CRATE150Pay per crate for a site without PAY. Seeds the HAUL_PAY_PER_CRATE tunable, which is what is paid.
CURRENCY'EDDIES'Money type paid.
TARGET_KIND'prop'prop: the eye lands on the crate. world: fallback that matches a ground hit within REACH of a crate.
CARRY{ BONE = 'Chest', OFFSET = { x = -0.135, y = -0.60, z = 0.008 }, ROTATION = { x = 0.0, y = 90.0, z = 0.0 } }How a carried crate sits on the body. BONE: a rig slot (RightHand, LeftHand, Chest, Head) or ''. A site may override.
PICKUP_POSE'scavenge'Animation during the pickup bar: scavenge, examine, repair, mechanic or '' for none. A site may override.
DROP_KEY'X'Default key to put a carried crate down.
DROP_DISTANCE0.7Metres in front of the player a dropped crate lands (0–2).
DROP_RETURN_MS300000How long a dropped crate may lie before it returns to its point.
ITEM'hauling_crate'Inventory item a loaded crate becomes. Declared in the inventory catalogue; its weight caps a trunk.
MEMBERSHIP'primary'How a site's JOBS gate reads jobs: primary (worked job) or any (every membership, grade only).
SITES12 sites (10 surveyed, docks and badlands placeholders)Every site. See below.
config/hauling.lua — one SITES entry
SITES = {
    pacifica_butcher = {                 -- the key: durable, used in logs and item metadata
        LABEL = 'Butcher shop and market',
        BUCKET = 0,
        MODEL = 'crate.delivery',        -- optional, else MODEL
        PICKUP_POSE = 'scavenge',        -- optional, else PICKUP_POSE
        -- CARRY = { ... },              -- optional, else CARRY
        BLIP = { X = -1819.71, Y = -1970.77, Z = 52.50 },  -- optional map pin (blips module)
        POINTS = {                       -- one crate per point
            { X = -1816.63, Y = -1977.91, Z = 52.50, YAW = 248.1 },
            { X = -1815.79, Y = -1975.94, Z = 52.50, YAW = 246.9 },
        },
        SPAWN_PER_PASS = 1,              -- required, whole number above 0
        RESPAWN_MS = 120000,             -- optional, default 0
        -- PAY = 220,                    -- optional, whole number >= 0
        -- JOBS = { nomad = 0 }, ON_DUTY = true,   -- optional job gate
        DROPOFFS = {
            sale = { LABEL = 'Sale', X = -1819.13, Y = -1964.37, Z = 51.50, RADIUS = 6.0,
                NPC = { RECORD = 'Character.VendorMale', YAW = 254.0 } },
        },
    },
}
  • X, Y, Z finite and within ±1,000,000 (the shared spot bound, see spot helpers).
  • Every drop-off needs an NPC with a RECORD of the form Character.*.
  • No JOBS = anyone may haul.
  • The shipped sites also carry a TARGET block (label, description, icon); the code does not read it — row texts come from the locale keys hauling.row.*.

Refusal codes

Sent as reason on opx:net:hauling:answer and shown as the toast hauling.refused.<reason> (fallback hauling.refused.generic).

CodeMeaning
no_such_crate, already_claimed, stale_revisionThe crate is gone or someone else got it.
already_carryingYou already hold a crate.
too_far, wrong_bucket, no_positionDistance, routing bucket or position read failed. wrong_bucket is checked on pick-up, load and sell: the player must be in the bucket of the crate, the vehicle or the seller.
job_required, grade_too_low, off_duty, job_stale, no_such_siteThe site's job gate refused.
claim_expiredYou took too long; the crate went back.
carry_droppedYou got into a vehicle while carrying: the crate is at your feet.
carry_endedThe platform ended the carry (death, bucket change, prop removed). Weapons are unblocked.
too_soonCompletion arrived before the server's clock allowed.
not_carrying, busy, nothing_runningNothing to load or drop, a bar is running, or the crate is still being written into the trunk.
no_such_vehicle, trunk_full, not_your_trunk, trunk_locked, no_trunk, trunk_refusedThe load into the trunk failed.
no_such_seller, no_cratesNo such seller, or no crates of this site in your bag or a parked vehicle.
not_paidThe payment failed; the crates were put back.
not_paid_lostThe payment failed and some crates could not be put back anywhere (bag or any trunk involved). The server logs [hauling] CRATES LOST: ... with the refund owed and writes the audit event hauling.crateLost. Toast hauling.refused.not_paid_lost.
attach_refused, no_carry_configThe crate could not be attached.
no_inventory, no_characterA needed module is missing.
rate_limited, invalid_subject, unknown_stepRequest limit (10 per 5 s) or a malformed request.
dropped, abortedSuccess answers for a drop and an abort.

On this page