OPX//77
opx_infinityfuel

fuel

Last updated on

A tank per vehicle that the server burns, pumps that fill it for money, and a jerry can — a port of ox_fuel onto Open77's fuel bag field.

A port of ox_fuel. Cyberpunk has no fuel model: REDengine 2.31 has no fuel level, consumption rate or petrol tank. The tank is a field of the vehicle's replicated state bag, fuel, in litres — the field and unit of the platform's sample open77_fuel, so a resource written for either reads the other's tank. fuelCapacity sits next to it so a gauge can draw a percentage. The server burns every tank, times every refuel and bills what its own clock poured.

Sideboth
Requiresnone
Optionalcharacter (paying), inventory (the can), progress (the bar; without it no refuel), target (eye rows), prompts (key row), menu (the pump menu), downed
Contractfuel v1 — server, client
ExportsFuel — GetFuel, GetFuelState, SetFuel
Configconfig/fuel.lua (shared)
Dataopx77_vehicles.fuel (litres, FLOAT NULL)
  • Without character nobody can pay and no pump sells. Without inventory there is no can. Without progress there is no refuel. The burn needs nothing but the host.

Driving

WhatHow
The gaugeThe vehicle dial shows FUEL and a thin bar under the speed, toned at the HUD's TONE_WARN (33) and TONE_BAD (15). A car whose tank was never written shows nothing. VEHICLE.FUEL = false leaves it off.
The burnEvery CONSUMPTION.TICK_MS the server burns every vehicle whose engine runs: the distance covered (litres per 100 km × MULTIPLIER × class usage × speed curve), or the idle rate when it stands. An engine that is off burns nothing.
What is billedThe larger of the reported speed × time and the distance the server saw the car move. A jump faster than MAX_SPEED_KPH is a teleport (garage recall, staff move) and is not billed.
EmptyAt 0 the server cuts the engine (Open77.vehicles.setEngine(id, false)) and cuts it again on every tick it is found running. Announced once on opx:on:fuel:changed with reason = 'empty'.
No tankA class with USAGE = 0 (AVs by default) is never burned, never cut and never filled.
A garageAn owned car keeps its litres when it is put away and comes out with them. A car never measured comes out at INITIAL_PERCENT.

At a pump

WhatHow
Open the pumpOn foot within USE_RADIUS of a pump, press E (KEY) — the strip reads Refuel (price/L) — or the eye row Use the pump. From a seat it reads Leave the vehicle to be able to start fueling. Away from a pump E does nothing. E is shared: the nearer spot wins (key contest).
The menuStart fueling, Buy a fuel can, Refill a fuel can (when one is in the bag), each with Pay cash (EDDIES) or Pay by bank (BANK) — PAYMENT.
RefuelThe nearest vehicle within VEHICLE_REACH of the player is filled at REFILL.LITRES_PER_SECOND behind a cancelable bar; the gauge rises while it runs. Done: Fueled to percent% - cost.
PricePer litre, rounded up: PRICE_PER_LITRE, or the station's own PRICE.

The refuel flow

The client sends only the payment method. Everything else is the server's:

  1. The request floor (REQUEST_MS); the player loaded, not down, on foot, no other session.
  2. The nearest surveyed pump to where the host says the player stands.
  3. The nearest vehicle within reach, a tank (usage > 0), at least one tick of room.
  4. What the account can pay for: the pour is capped at that many litres.
  5. A cancelable progress bar the server starts and times (capped at the progress module's MAX_MS, 60 s).
  6. Every REFILL.TICK_MS the gauge moves and the session is checked: the player at the pump, the car within MOVE_TOLERANCE and below STOP_SPEED, the money still there.
  7. However it ends, the bill is the litres the server's clock poured. A cancelled or stopped bar is a partial refuel. An account that ran short pours only what it can pay for. A disconnect pours and charges nothing.

Audit events fuel.refuel, fuel.can, fuel.buy, fuel.refill.

The jerry can

WhatHow
The itempetrolcan (CAN.ITEM). Not stackable; metadata.durability is the fill, 0..100, drawn as the wear bar.
BuyBuy a fuel can at a pump: a CAN.DURATION_MS bar, then CAN.PRICE is charged and a full can added. Refunded if the bag refuses it.
RefillRefill a fuel can: the emptiest can in the bag, for CAN.REFILL_PRICE.
PourAnywhere: use the can from the bag (nearest vehicle within CAN.REACH), or the eye row Refuel with the fuel can on a vehicle. CAN.LITRES_PER_SECOND behind a cancelable bar; the can in that slot is drained by what went in. A can moved out of its slot mid-pour pours nothing.

Stations and surveying

ox's stations are Los Santos. The five shipped Night City stations — badlands_sunset, badlands_rocky_ridge, santo_domingo_arroyo, watson_northside, heywood_wellsprings — are placeholders at 0, 0, 0 and disabled at boot: no blip, no eye row, no key. A station whose centre or any pump is at exactly 0, 0, 0 is disabled whole, and the boot log names it:

[fuel] 5 station(s) disabled until surveyed (/opx.fuel.capture): badlands_rocky_ridge, ...
[fuel] no station is open: every tank still burns, and only a can or staff fills one

To make one real:

  1. Stand on the forecourt (where the blip goes): /opx.fuel.capture <key> [label].
  2. Stand at each pump: /opx.fuel.capture <key> pump.
  3. Paste the printed lines into STATIONS in config/fuel.lua and restart. Nothing is stored in the database: the file is the city.
  4. /opx.fuel.stations lists open, unsurveyed and refused stations.
What capture prints
paste into STATIONS in config/fuel.lua, then restart:
  watson_northside = { LABEL = "Northside gas station", X = -1234.56, Y = 456.78, Z = 12.00, BUCKET = 0, PUMPS = { } },
paste into STATIONS in config/fuel.lua, then restart:
  watson_northside.PUMPS += { X = -1236.10, Y = 452.33, Z = 12.01 },

+= is a hint, not Lua: put each pump row inside the station's PUMPS.

config/fuel.lua — one surveyed station
STATIONS = {
  watson_northside = {
    LABEL = 'Northside gas station',
    X = -1234.56, Y = 456.78, Z = 12.00,   -- the centre: the blip
    BUCKET = 0,
    PRICE = 5,                             -- optional, else PRICE_PER_LITRE
    PUMPS = {
      { X = -1236.10, Y = 452.33, Z = 12.01 },
      { X = -1231.80, Y = 452.40, Z = 12.01 },
    },
  },
},
  • Key: 1 to 48 letters, digits, _ or -. At most MAX_PUMPS pumps.
  • A refused station (no pumps, too many, a non-finite coordinate, a bad BUCKET or LABEL) logs [fuel] config: STATIONS.<key> ....
  • The map pin is the blips fuel category: one pin per open station, sprite drop_point (2.31 has no fuel sprite).

open77_fuel

With STAND_DOWN on (the default) and open77_fuel running, this module stops burning and cutting engines (two burns would empty a tank twice as fast). Refuels, cans and staff writes go through open77_fuel's set export and the capacity is its capacity(). Stations, cans, persistence and the gauge keep working. Checked every 10 s, so either can start later. /opx.fuel.stations ends with burning: open77_fuel or burning: this module.

Commands

All restricted: the host checks command.<name>.

CommandAccessArgumentsEffect
/opx.fuel.setACL command.opx.fuel.set<vehicleId|near> <0-100>Sets a tank in percent. near: the seat, else the nearest vehicle within STAFF_REACH in your bucket. A typed id must be within STAFF_REACH unless you hold opx.admin.vehicle.anywhere.
/opx.fuel.captureACL command.opx.fuel.capture<station> [pump|label]Prints the config line for a station centre, or a pump row, where you stand. Not from the console.
/opx.fuel.stationsACL command.opx.fuel.stations—Open stations (position, bucket, pumps, price), unsurveyed and refused ones, and who burns.

Grant command.opx.fuel.* to an operator role. opx.admin.vehicle.anywhere is a raw right, not carried by command.*. Audit events: fuel.set, fuel.capture.

Configuration

config/fuel.lua sets OPX.Config.MODULES.fuel. Shared script. Every number is clamped; a bad one takes the default.

KeyDefaultEffect
enabledtruefalse switches the module off.
CAPACITY60.0Litres in a full tank, every vehicle.
INITIAL_PERCENT100How full a tank the server has never seen starts (staff spawn, dealer car, a car from before this module).
CONSUMPTIONsee belowThe burn.
STAND_DOWNtrueStop burning while open77_fuel runs. See open77_fuel.
PRICE_PER_LITRE4Eddies per litre, rounded up. A station's PRICE overrides it.
PAYMENT{ 'EDDIES', 'BANK' }Accounts the pump offers.
REFILL{ LITRES_PER_SECOND = 1.2, TICK_MS = 500 }Pump rate, and how often the session is checked and the gauge moved. 1.2 L/s fills 60 L in 50 s.
USE_RADIUS2.5Metres from a pump (plus the server's REACH_SLACK).
VEHICLE_REACH4.0Metres from the player to the vehicle a pump fills.
MOVE_TOLERANCE1.0Metres the car may move before a pour stops.
STOP_SPEED0.5m/s above which a pour stops.
ANIMATION''A progress animation name for the bar; '' plays none.
SCAN_MS500How often the client looks for the nearest pump (100..5000).
REQUEST_MS800Floor between two fuel requests from one player.
KEY{ ID = 'opx.fuel.use', NAME = 'fuel.key.use', DEFAULT = 'E' }The pump key, rebindable. DEFAULT = false declares none (the eye row stays).
CANsee belowThe jerry can.
STATIONSfive placeholdersSee Stations and surveying.
MAX_PUMPS16Most pumps per station (1..32).
STAFF_REACH100.0Metres for /opx.fuel.set (near, and a typed id).
config/fuel.lua — CONSUMPTION
CONSUMPTION = {
  TICK_MS = 1000,                 -- one burn pass over every vehicle
  LITRES_PER_100KM = 9.0,         -- open77_fuel's numbers
  IDLE_LITRES_PER_MINUTE = 0.05,
  MULTIPLIER = 12.0,              -- ox's globalFuelConsumptionRate: ~55 km on a full 60 L tank
  MAX_SPEED_KPH = 350,            -- faster is clamped; a longer jump is a teleport
  SPEED_CURVE = { { 0, 1.3 }, { 50, 1.0 }, { 90, 0.9 }, { 130, 1.15 }, { 180, 1.5 }, { 250, 2.0 } },
  CLASSES = {                     -- substring of the TweakDB record, first match wins
    { MATCH = 'vehicle.av_', USAGE = 0 },   -- 0: no tank at all
    { MATCH = '_sportbike', USAGE = 0.55 },
    { MATCH = 'v_sport2_', USAGE = 1.5 },
    -- ...
  },
  DEFAULT_USAGE = 1.0,
  LEAK = { BELOW_HEALTH = 0.25, LITRES_PER_MINUTE = 0.5 },  -- 0 turns it off
},
  • SPEED_CURVE: { km/h, factor } pairs, straight lines between them, flat past the ends. Road speed, not RPM: the server is not sent an RPM.
  • LEAK: below that share of vehicle health (0..1) a running engine loses that much more. Cyberpunk has one health pool.
  • A tick that comes late is billed as five ticks at most.
config/fuel.lua — CAN
CAN = {
  ENABLED = true,
  ITEM = 'petrolcan',
  CAPACITY = 10.0,            -- litres in a full can
  DURATION_MS = 5000,         -- the buy / refill bar
  PRICE = 100, REFILL_PRICE = 60,
  LITRES_PER_SECOND = 0.8,
  REACH = 3.0,                -- metres to the vehicle poured into
},

Refusal codes

Sent on opx:net:fuel:answer and shown as toasts, locale key fuel.error.<code>.

CodeMeaning
too_fastUnder REQUEST_MS since the last request.
bad_methodNot one of PAYMENT.
player_down, no_character, no_positionYou are down, not loaded, or the server could not read you.
busyYou or that vehicle are already in a session, or the bar is taken.
no_pumpNot within USE_RADIUS of an open station's pump.
leave_vehicleSeated.
vehicle_farNo vehicle within reach.
no_tankThe vehicle's class has USAGE = 0.
tank_fullLess than one tick of room.
not_enough_moneyNot even one tick, or the can's price (price in the answer). Mid-pour: the money ran out.
no_barNo progress module.
no_menuNo menu module (client toast).
no_can_sold, no_inventory, no_can, can_full, cannot_carryBuying or refilling a can.
petrolcan_not_equipped, petrolcan_not_enough_fuelPouring: no can (in that slot), or less than one tick in it.
cancelledThe bar ended and nothing went in.
moved_away, vehicle_moved, vehicle_goneThe pour stopped.
bad_amount, no_vehicle, out_of_reach, refused/opx.fuel.set.

Successes (fuel.answer.<code>): refuelled, poured, can_bought, can_refilled. refuelling and pouring start a bar and show nothing.

Contract and export errors: fuel.noVehicle, fuel.badAmount, fuel.busy (a pour holds the tank), fuel.refused.

Platform limits

  • No fuel in the engine. The bag field is the whole tank; an engine cut is the only "won't start". A driver restarting gets cut again within a tick.
  • No pump entities. A Night City pump is part of the map: a pump is a surveyed position and the eye row a sphere round it (four spheres per row).
  • No RPM on the server. Consumption follows road speed through SPEED_CURVE.
  • One health pool. The leak reads vehicle health, not a tank's.
  • No fuel blip sprite in 2.31: the pin is drop_point.
  • Not ported: setPaymentMethod / setMoneyCheck (a function cannot cross a resource; the pump offers both accounts), the nozzle prop and the gardener animation (no assets), RPM-based consumption.
  • Not yet verified on a live client: the engine cut against a restarting driver, the eye spheres on real pumps, the drop_point pin on the big map (docs/IN_GAME_CHECKLIST.md §7).

On this page