Bones Bar Service v3
Bartenders and liquor stores sell 15 custom Bones drinks and 5 kinds of glassware, each with its own 3D prop.
How drinking works: using a drink from the inventory puts it in your hand, and it stays there.
- Tap [E] to take a sip: the drink comes up to the mouth and back down, with no progress bar. [G] puts it away (using the item again also puts it away).
- Hold [E] with a bottle to chug: it stays at your lips and you take a gulp (one sip) every Config.Chug.gulpMs until you let go, it's empty, or you hit maxGulps.
- Hold [E] with a beer can to shotgun it:
1. The notepad pose brings both hands up in front. The can goes to the left hand and a pocket knife (bones_prop_pocketknife) comes out in the right. One stab (wind up, thrust with the arm following by IK, pull out) punches a hole near the bottom (bones_prop_beercan_holed). Then the knife is put away and the can goes back to the right hand.
2. The hole goes to your lips (placed between the lip bones), the tab cracks, and it drains for Config.Shotgun.ms with no stopping.
3. The can is crushed in your hand and dropped at your feet for dropSeconds.
Only drinks with shotgun = true can be shotgunned (the beer can). The seltzer is just sipped.
- Bottles come sealed. The cap, cork or wax comes off on the first sip or pour (the <prop>_open model).
- Each sip swaps the prop for the next fill-level model, so you see the liquid drop until the bottle or glass is empty. Ice settles to the bottom, and garnishes stay on.
- An empty bottle is held for a moment, then it's gone from your hand and your inventory. An empty cup stays, ready for a refill.
- A drink you put away keeps its remaining sips. The count shows in the item's description.
- The drink is put away automatically if you die, ragdoll, swim, climb, get cuffed, pull a weapon, or drop/give/stash the item.
- The server owns the sip count and timing: at most one sip per Config.SipDuration, checked against the actual inventory slot.
Ordering and stacking:
- Pick a drink or glass at the bar and type how many you want, up to Config.MaxOrder (20 by default).
- The server checks you're at the bar, that you can carry them all and that you have the money, then charges once for the lot.
- Full sealed bottles stack, and so do empty glasses.
- The one you open, pour from or fill is moved into its own inventory slot, so the rest of the stack stays full and a half-drunk bottle never merges back in. Opening one from a stack needs one free slot.
Pouring (ox_inventory): bottles hold real amounts. A 750 ml spirit is 25 swigs and pours 12 doubles, wine and champagne pour 5 glasses each, and a longneck fills one beer mug. - Pour into your own glass while holding a bottle, any of these ways: - Use (double-click) an empty glass in your inventory: it's filled instead of swapping hands. - Right-click the glass in the inventory and pick Pour held bottle in. - Press [H] (rebindable: Settings > Key Bindings > FiveM > "Bar: pour the held bottle into a glass"). It picks the empty glass that fits; with more than one kind, a menu asks which. - If a pour can't happen, the message says why (wrong glass for that drink, glass already full, no glass). - Look at a player holding an empty cup and use the target eye's Pour a drink to fill theirs (ox_target or qb-target). - A filled cup is drunk like any other drink. When it's empty, the cup stays in your hand for a refill, and with an iced drink the ice stays in it. - Alcohol per sip is scaled so a whole bottle hits the same whether you swig it or drink it by the glass.
While you hold a drink, a small card (bottom right, html/) shows its icon, name, a fill bar in the drink's colour, and the keys. Keys are in Config.HoldKeys, colours in Config.LiquidColors / Config.DrinkColors.
Drunk stages (Config.DrunkStages) build up with each sip and fade over time (Config.IntoxicationDecayPerMinute). The server keeps the level (server/drunk.lua): it can't be cheated from the client, it's saved per character (relogging doesn't sober you up; time away still counts), and dying resets it. A pill under the card shows your stage.
| Stage | From | BAC | What happens |
|---|---|---|---|
| Tipsy | 0.10 | 0.025 | light blur, slight camera sway |
| Buzzed | 0.22 | 0.055 | drunk walk; the car drifts now and then |
| Drunk | 0.38 | 0.095 | heavier walk, no sprinting or jumping, the odd stumble; swerves, late brakes |
| Wasted | 0.56 | 0.140 | staggering walk that drifts, frequent stumbles, might throw up; heavy swerving |
| Floored | 0.78 | 0.195 | your legs give out at random: you go down flat for a few seconds, then get back up on your own; throwing up; barely able to drive |
Drunk driving: stages with drive make the car pull left or right for a moment (the wheel recentres) and make your brakes react late. Only the driver, only above minSpeed.
Throwing up: stages with puke roll for it; the server checks Config.Puke.minLevel and a cooldown, takes a little alcohol off, and everyone nearby sees it.
Hangover: sobering up after a night that reached Wasted (Config.Hangover.fromLevel) gives 20 minutes of light blur, a throbbing camera and short sprints. Any drink cures it (hair of the dog).
Stress: every sip takes stress off (Qbox / qb-core stress metadata; the HUD updates). Config.Stress.perLevel, or plug in your own system with Config.Stress.custom.
Falling down (Floored): reaching the stage knocks you over once straight away. After that, every fall.every ms there's a fall.chance you go down (fall.movingChance while walking), and you stay down downMin..downMax ms before GTA's own get-up plays. You don't fall in a vehicle, in water, on a ladder, or in the middle of a sip or shotgun. The falls stop once you sober up below 0.78. The settings are on the Floored entry in Config.DrunkStages.
A whole 750 ml bottle is about 1.0, and a double from a cup about 0.08. A stumble or a fall doesn't make you drop your drink. Dying sobers you up.
Cheers: with a drink in hand, target someone else holding one and pick Cheers. When they pick Cheers on you (within 15 s) you both turn to each other, raise, clink and take a sip. Everyone sees the same thing.
Handing your drink over: target someone with empty hands and pick Offer your drink; they pick Take their drink on you within 20 s. The item moves inventories (sips left and all) and goes straight into their hand.
Empties: finishing a beer bottle or a can by drinking leaves a bar_empty_bottle / bar_empty_can in your pockets. Liquor and wine bottles don't (Config.Empties.bottleLiquids). A shotgunned can lands on the floor and anyone can Pick up the can. Return empties at any bartender or liquor store (Return empties in the menu) or a recycling point for cash (Config.Empties.pay).
Tabs: in the bar menu, tick Put it on my tab when you order (or pick it on a bartender's bill). Pay with Pay your tab in the bar menu or at a register. Walk 120 m away with an open tab and you get a warning; 250 m and it's marked as walked out: on-duty bartenders are told, it's logged, and you can't order anywhere until it's paid. Config.Tabs.onDash can send a police dispatch. Tab limit $500.
Breathalyzer (works with any PD script)
The server publishes everyone's BAC (level x 0.25, legal limit 0.08):
| Where | How |
|---|---|
| State bag | Player(id).state.bac / .alcohol (server), LocalPlayer.state.bac (their client). Keys in Config.Breathalyzer.stateKeys |
| Server exports | exports.bones_barservice:GetBAC(id), IsOverLimit(id), GetDrunkLevel(id), SetDrunkLevel(id, level), AddAlcohol(id, amount), IsHungover(id) |
| Client exports | exports.bones_barservice:GetBAC(), GetDrunkLevel(), IsHungover() |
| Server event | AddEventHandler('bones_barservice:bacChanged', function(source, bac, level) ... end) |
| Callback | lib.callback.await('bones_barservice:getBAC', false, targetId) (police only, someone within 5 m) |
So in your PD script's breathalyzer, wherever it gets its reading, use exports.bones_barservice:GetBAC(targetId) on the server (or Player(targetId).state.bac).
There's also a built-in one: the bones_breathalyzer item (police jobs / Qbox job type leo, on duty). Use it to test the closest player, or pick Breathalyzer test on someone with the target eye while you carry it. They get a Blow / Refuse prompt (refusals are logged); after 5 s the officer gets the reading and whether it's over the limit.
Bartender job
One shared job, bartender by default (Config.BarJob.name). Add it to qbx_core/shared/jobs.lua:
['bartender'] = {
label = 'Bartender',
defaultDuty = true,
offDutyPay = false,
grades = {
[0] = { name = 'Barback', payment = 50 },
[1] = { name = 'Bartender', payment = 75 },
[2] = { name = 'Head Bartender', payment = 100, isboss = true },
},
},
Place the bar's points in game with /barpoint (same permission as /placebartender): stand where it goes, pick the type. Remove the nearest point from the same menu.
| Point | Who | What |
|---|---|---|
| Register | on-duty bartenders; anyone to pay a tab | Ring up a customer: pick a nearby player, one of your sealed drinks, the amount and the price (blank = menu price). They get a bill: pay cash, pay by card, put it on their tab, or decline. The money goes to the bartender's bank. Tab money reaches them when the customer pays, even if they're offline then. |
| Stash | on-duty bartenders | shared bar storage (ox_inventory stash locked to the job) |
| Mixing station | on-duty bartenders | cocktails from bottle swigs (+ an empty glass) per Config.BarJob.recipes, e.g. an Old Fashioned = 2 swigs of bourbon + a rocks glass |
| Supplier (NPC) | on-duty bartenders | wholesale stock at 55% of the menu price, paid from their own cash or bank |
| Recycling point | anyone | return empties for cash |
No society accounts: sales pay the bartender who rang them up, and stock comes out of their own pocket.
Admin
/setdrunk <id> <0-1>and/sober [id]: console, oradd_ace group.admin bones_barservice.admin allow.- Bar log: purchases, sales, tabs, walk-outs, wholesale, mixing, empties, breath tests and admin actions go to the server console. For Discord, put the webhook in
server.cfg(not in the config, so it never ends up in a shared zip):set bones_barservice_webhook "https://discord.com/api/webhooks/...".
| Key | Item | Prop (base) | Hold | Sips / fill models | Pours into |
|---|---|---|---|---|---|
| bourbon | bar_bourbon | bones_prop_bourbon | bottle | 25 / 8 | skull cup, rocks |
| skullwhiskey | bar_skullwhiskey | bones_prop_skullwhiskey | bottle | 25 / 8 | skull cup, rocks |
| vodka | bar_vodka | bones_prop_vodka | bottle | 25 / 8 | skull cup, rocks |
| lager | bar_lager | bones_prop_lager | bottle | 8 / 6 | mug |
| rum | bar_rum | bones_prop_rum | bottle | 25 / 8 | skull cup, rocks |
| wine | bar_wine | bones_prop_wine | bottle | 15 / 7 | wine glass |
| tequila | bar_tequila | bones_prop_tequila | bottle | 25 / 8 | skull cup, rocks |
| gin | bar_gin | bones_prop_gin | bottle | 25 / 8 | skull cup, rocks |
| champagne | bar_champagne | bones_prop_champagne | bottle | 15 / 7 | flute |
| oldfashioned | bar_oldfashioned | bones_prop_oldfashioned | glass | 4 | |
| martini | bar_martini | bones_prop_martini | glass | 4 | |
| margarita | bar_margarita | bones_prop_margarita | glass | 4 | |
| purplereign | bar_purplereign | bones_prop_purplereign | glass | 5 | |
| tequilashot | bar_tequilashot | bones_prop_shot | shot | 1 | |
| seltzer | bar_seltzer | bones_prop_seltzer | can | 5 (one model; you can't see inside a can) | |
| beercan | bar_beercan | bones_prop_beercan | can (shotgun) | 6 (one model, plus _holed and _crushed) |
mug |
| Cup key | Item | Prop (base) | One pour | Liquids |
|---|---|---|---|---|
| skullcup | bar_cup_skull | bones_prop_cup_skull | 2 swigs → 3 sips, on ice | amber, clear |
| rocks | bar_cup_rocks | bones_prop_cup_rocks | 2 swigs → 3 sips, on ice | amber, clear |
| wineglass | bar_cup_wine | bones_prop_cup_wine | 3 swigs → 5 sips | red |
| flute | bar_cup_flute | bones_prop_cup_flute | 3 swigs → 4 sips | sparkling |
| mug | bar_cup_mug | bones_prop_cup_mug | 8 swigs → 8 sips, with a head | beer |
Cup models are <prop>_empty plus <prop>_<liquid>_<sips left>.
1. Build the props in Blender (one time)
The stream/ folder starts out empty. The Blender script fills it.
- Install Blender 4.2 or newer and the Sollumz add-on (https://docs.sollumz.org). In Sollumz preferences, install PyMateria if it asks. You need it to export native
.ydrfiles. - In Blender, open the Scripting tab, click Open, and pick
bones_barservice/blender/build_props.py. Then click Run Script. - The script builds every drink and cup at every fill level: 151 models in total. Each bottle model is named
bones_prop_<drink>_<level>, sobones_prop_bourbon_8is full and sealed,bones_prop_bourbon_openis full with the cap off, andbones_prop_bourbon_0is empty. It uses GTA shaders (specfor labels, caps and garnish;glass_spec/glassfor the glass, ice and clear drinks) and writes: -stream/bones_prop_*.ydr(151 files, geometry only) -stream/bones_prop_*_txd.ytd(one shared texture dictionary per prop, 20 files) -stream/bones_bar_props.ytyp-blender/bones_bar_props.blend(an editable scene: one row per drink, draining from full on the left to empty on the right)
If your Sollumz version has no texture dictionary support, the script embeds the textures in every .ydr instead. That still works, but the download is bigger.
Headless option: blender -b -P bones_barservice/blender/build_props.py
If PyMateria is missing, the script exports CodeWalker XML (.ydr.xml / .ytyp.xml) instead. Import those in CodeWalker and save the binaries into stream/.
To change a label or shape, edit blender/propgen/ (labels.py for label art, props.py for shapes). Run python blender/propgen/build.py (needs numpy and Pillow), then run the Blender script again.
build.py draws the label text with Linux fonts (TeX Gyre, Inter, Poppins, DejaVu, at /usr/share/...). On Windows, rebuild only the props you changed (python blender/propgen/build.py <out_dir> bones_prop_cup_mug ...). Then copy their glb/textures files and their manifest.json entries into source/, so the shipped label textures aren't redrawn with a fallback font. Geometry and UVs don't depend on the fonts.
2. Install on the server (Qbox + ox_inventory)
- Put the folder in your resources as
bones_barservice. - Open your active
ox_inventory/data/items.lua(every bar item is nowstack = true, so update the existing entries too): - Delete the oldbar_whiskeyandbar_beerentries. - Replace the oldbar_vodka,bar_rumandbar_wineentries, and add the rest, using the entries inox_inventory_items.lua. - Add the new v3 entries at the bottom of that file too:bar_empty_bottle,bar_empty_can,bones_breathalyzer. - Copy
icons/*.pngintoox_inventory/web/images/. The same icons also show in the bar menu. - Add the
bartenderjob toqbx_core/shared/jobs.lua(see "Bartender job" below), then place the bar points with/barpoint. - In
server.cfg, ensureox_lib,qbx_core,ox_inventoryandox_targetstart beforeensure bones_barservice. - Restart the full server. Changes to ox_inventory items only load on a full restart.
Run /barcheck in game. Every drink and cup should show props ok, use linked. It checks every fill-level model, including the opened bottles and every cup/liquid level.
3. Dial in hand positions: /bartune <drink or cup>
The attach offsets in config.lua are a first pass. Run /bartune bourbon (or any other drink or cup key, e.g. /bartune mug). Your character holds that prop, and F switches to the sip pose, which loops just the part of the clip a real sip uses.
- Arrow keys move X/Y. PgUp/PgDn move Z.
- Hold Shift to rotate instead. Hold Ctrl for fine steps.
- F switches between the holding pose and the sip animation. G freezes the current frame, so you can line up the bottle at the mouth.
- Enter saves. The
attach = {...}line is copied to your clipboard and also printed in F8. The new position applies for the rest of your session.
Paste the line into that drink's entry in config.lua. Tuning is open to everyone while Config.AllowAnyoneToPlace = true. Before launch, set that to false and add add_ace group.admin bones_barservice.tune allow (plus the existing bones_barservice.place ACE).
Shotgun pose: /bartune shotgun
Your character freezes at the drain moment: arm up, holed can on the lips, right hand on the can.
- Arrows move the lip point right/left and up/down. PgUp/PgDn move it forward/back.
- Shift+Up/Down changes the tilt. Shift+Left/Right moves the hand grip out/in. Shift+PgUp/PgDn moves the grip up/down the can.
- Ctrl gives fine steps. Enter copies tilt = ..., mouth = {...}, grip = {...} to your clipboard and keeps it active for the session.
- Paste those three values into Config.Shotgun.
Stab pose: /bartune shotgunpoke
This loops the stab pose with the can in your left palm and the knife in your right fist, the blade aimed at the hole.
- Arrows and PgUp/PgDn move the can in the palm (x/y/z).
- Shift with the same keys moves the knife grip instead. Ctrl gives fine steps.
- Enter copies leftPalm = {...}, knifeGrip = {...}. Paste it into Config.Shotgun.
During the real stab, both props follow the hands every frame. The right hand is pushed into the can by arm IK, and the knife goes with it.
Everyone sees the same thing
Your client sends what you hold, and what you're doing with it, to the server. The server checks it (only real bar models, sane offsets, rate-limited) and publishes it on your player's state bag (bonesHand). Your own hand is drawn from your local copy straight away.
- Every client, yours included, draws a local copy of the drink on your character's hand.
- Every client runs the same placement for the shotgun (can, knife, stab, can at the lips), pours and glass sips. That's timed from the network start time and your character's synced animation.
- Loose networked props, arm/head IK and paused animations don't sync in GTA, which is why other players used to see something different. None of that is relied on any more.
- The crushed can left on the floor is a single networked prop placed once, so it's in the same spot for everyone.
- Drunk falls are GTA ragdolls on your own character, which the game syncs, so you go down and get up in the same spot on everyone's screen.
- Config.MaskedGestures = false (default): one-arm "gesture" anims don't reliably sync, so holds and sips play as normal upper-body anims.
Other config
Config.Holdssets the hold loop, the sip animation, the hand bone, the default offset and the GTA fallback prop for each container type (bottle / can / glass / shot). Sip animations takefrom/to(seconds into the clip) so only the drink itself plays, plus an optionalspeed. The GTA idles are 10–13 s long with fidgeting around one drink. Bottles and glasses both sip with the beer swig, so only the right arm moves. The coffee idles fold the left arm across the chest, so glasses are held with the coffee pose masked to the right arm (mask, played as a GTA gesture layer), with the beer hold asfallbackif masked anims don't run. Straw drinks (Purple Reign) sip upright to the straw, on the right arm and head only. The swig leaves the hand ~15 cm from the lips (a bottle neck covers that), so during a glass sip the glass slides along its axis to the lips and back. The slide is the hold'sreachminus the model height and grip offset: ~9 cm for a rocks glass or shot, nothing for a flute.Config.Cupssets each cup's price, pour size, sips per pour and whichliquids it takes.Config.PourAnimis the pouring gesture: the arm reaches forward, and the bottle is tipped neck-forward and down bytiltdegrees as the hand getsreachmetres out in front.- If you change a drink's
sipswithout rebuilding the props,Config.ModelForscales the sip count to the nearest baked fill model, so nothing breaks. Rebuild with matchingLEVELSinblender/propgen/props.pyto get one step per sip again. If a custom prop isn't streamed, the drink uses the fallback prop instead of failing. Config.Categoriesgroups the bar menu into Bottles, Cocktails and Shots & Cans.- All brand names are original Bones/fictional brands.
Limits
- The Blender script was written against the current Sollumz source but couldn't be run during development. If a step fails, the Info log / System Console names the step and prop. The scene is still saved so you can finish that step by hand.
- The props have no collision. They're only ever attached to a hand.
- The attach offsets are untested in game. Use
/bartune. - On ESX (no item metadata), sips only persist while you hold the drink. Putting it away mid-drink resets it to full. Qbox/ox_inventory and QBCore keep the count.
- Glassware and pouring need ox_inventory (cups store their contents in item metadata). So do the v3 job, tab, hand-off, empties and breathalyzer features, and their target options need ox_target.
- Drunk levels, tabs, payouts and bar points are kept in the resource's KVP storage (no SQL needed).