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.
| Side | both |
| Requires | character (hard) |
| Optional | target, progress, inventory, animations |
| Contract | hauling v1 — server |
| Config | config/hauling.lua (shared) |
| Data | none of its own; loaded crates are hauling_crate items in vehicle trunks (inventory) |
- Without
targetthere is no way into the job (the client says so once). - Without
progressno bar is drawn, but the server still enforces the timing. - Without
inventorynothing can be loaded or sold. - The manifest needs
world.props(crates) andworld.npcs(sellers). - Target rows:
hauling.pickup(crates),hauling.load(vehicles),hauling.sell(NPCs).
How the job runs
- 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 aPICKUP_MSbar while the player plays the site'sPICKUP_POSE. The crate is then attached (CARRY), thecarryanimation plays, weapons are holstered and blocked. - Put down (optional).
DROP_KEY(X by default, rebindable as Hauling: put the crate down). The crate landsDROP_DISTANCEm in front; anyone may pick it up. Untouched forDROP_RETURN_MS, it goes back to its point on the next refill pass. - Load. ALT on a vehicle within
VEHICLE_REACH→ Load the crate,LOAD_MSbar. The prop is removed and oneITEM(hauling_crate, 10 kg) with metadata{ site = <site key> }is added to that vehicle's trunk. A full, locked or foreign trunk refuses. - Sell. ALT on the seller NPC at a drop-off → Sell the crates,
DELIVER_MSbar. Every crate of that site in the player's bag and in the trunk of every vehicle parked within the drop-off'sRADIUSthat 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 neithervehiclesnorvehiclekeys, every parked trunk counts, and the log says so once. Pay per crate: the site'sPAY, else theHAUL_PAY_PER_CRATEtunable (starts atPAY_PER_CRATE), paid inCURRENCY. 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 eventhauling.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 (throughAddMoneyOfflinewhen offline),opx:on:hauling:soldnames 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_MSis refused. A claim not finished within its bar plusCLAIM_GRACE_MSis 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(tunableHAUL_REFILL_MS, live), each usable site gets up toSPAWN_PER_PASSnew crates on free points. A point stays emptyRESPAWN_MSafter its crate leaves.MAX_CRATEScaps 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 thanMIN_POINT_GAP, a drop-off has no validNPC, or the engine does not know itsMODEL(tested by creating one crate).
Configuration
config/hauling.lua sets OPX.Config.MODULES.hauling. Shared script.
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,Zfinite and within ±1,000,000 (the shared spot bound, see spot helpers).- Every drop-off needs an
NPCwith aRECORDof the formCharacter.*. - No
JOBS= anyone may haul. - The shipped sites also carry a
TARGETblock (label, description, icon); the code does not read it — row texts come from the locale keyshauling.row.*.
Refusal codes
Sent as reason on opx:net:hauling:answer and shown as the toast hauling.refused.<reason> (fallback hauling.refused.generic).
| Code | Meaning |
|---|---|
no_such_crate, already_claimed, stale_revision | The crate is gone or someone else got it. |
already_carrying | You already hold a crate. |
too_far, wrong_bucket, no_position | Distance, 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_site | The site's job gate refused. |
claim_expired | You took too long; the crate went back. |
carry_dropped | You got into a vehicle while carrying: the crate is at your feet. |
carry_ended | The platform ended the carry (death, bucket change, prop removed). Weapons are unblocked. |
too_soon | Completion arrived before the server's clock allowed. |
not_carrying, busy, nothing_running | Nothing 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_refused | The load into the trunk failed. |
no_such_seller, no_crates | No such seller, or no crates of this site in your bag or a parked vehicle. |
not_paid | The payment failed; the crates were put back. |
not_paid_lost | The 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_config | The crate could not be attached. |
no_inventory, no_character | A needed module is missing. |
rate_limited, invalid_subject, unknown_step | Request limit (10 per 5 s) or a malformed request. |
dropped, aborted | Success answers for a drop and an abort. |