Wagoncraft¶
hobbs_wagoncraft, current version 1.0.1
Overview¶
A wagon building job. Players with the workshop job order a wagon at the workshop NPC, pay the materials and a fee, and the bare wagon goes up on sawhorses at the building site. From there it is finished task by task: sawing and planing planks at the workbench, nailing the side panels, fitting every wheel, the shaft and the driver's seat, then sanding both sides. Every task is a small minigame played in an animation with the right tool in hand, and the lumber stack next to the bench shrinks as the work goes on. When the last task is done, the wagon is written into your stable script and waits there for whoever ordered it.
Thirty-eight wagons are included, from a light farm cart to the Concord stagecoach, the hearse and the heavy freight wagons. Each has its own price, materials and measured positions for the sawhorses and work stands. The workshop also has a shared storage where materials can be kept, and whatever is in there counts towards a build.
How it plays¶
- You walk up to the workshop NPC. If you have the workshop job, a prompt offers to open the workshop. Without the job nothing shows.
- The menu opens with a live preview: the wagon you pick stands next to the NPC and a camera looks at it. You drag to turn it, scroll to zoom and use Page Up and Page Down to tilt. The right side shows materials, price, number of tasks and storage slots. You can search, sort, and mark favorites.
- In the Storage tab you put materials from your bag into the workshop or take them back out. Everyone with the job shares that storage.
- You give the wagon a name (4 to 20 characters) and hold the build button. The server takes the fee from your cash and the materials, first from the workshop storage, then from your bag. If a building site is free, the wagon appears there on sawhorses.
- At the site you see a ground marker at every spot that can be worked on right now. You walk to one, press E, and your character is placed at the stand and starts the animation. After five seconds the minigame opens: saw, plane, nail, fix the wheel or sand. Pass it, work five more seconds, and the task is done.
- Steps come in order. Within a step several people can work at the same time, one per spot: for example one on the left side panel and one on the right. When a step is complete you get a short note ("The wheels are on.").
- The last task hands the wagon over to the stable. The person who finished and the person who ordered both get the message that the wagon is waiting at the stable.
- Whoever ordered the build can give it up by holding Del at the site. Materials and fee are gone. A build nobody has touched for 14 days can be cleared by anyone with the job.
Requirements¶
| Resource | Note |
|---|---|
vorp_core |
Characters, jobs, cash, notifications |
vorp_inventory |
Materials in the bag, item labels, workshop storage moves |
oxmysql |
Favorites, workshop storage, running builds, and the row in the stable table |
nss_libs |
Prompts and the optional map blip |
| A stable script | The finished wagon is inserted into its wagon table. Supported: VORP stables, sirevlc, syn and gum style tables (see Config.StableSystem) |
The shop is tied to one or more VORP job names. Create the job before you set up the shop, the default is carpenter.
Installation¶
- Unpack the resource into
resourcesand keep the folder namehobbs_wagoncraft. - Make sure these items exist in your
vorp_inventoryitems table, with images:carriage_parts,ironbar,copperbar,oilunrefined,nails,cotton,leather. The script reads their labels from the inventory database. A missing item is printed as a red line in the console on start, and the menu shows the raw item name instead of a label. You can rename the materials inConfig.Wagonsto items you already have. - Create the job
carpenterin VORP, or changejobinConfig.Shopsto a job you already use. - Open
config.luaand setConfig.StableSystemto the stable script you run. The default is"vorp". -
Add the resource to
server.cfgafter its dependencies and after your stable script: -
Start the server. No SQL file is needed. On first start the script creates three tables:
Table Holds hobbs_wagoncraft_buildsOne row per running build: site, owner, wagon, name and which tasks are done hobbs_workshop_storageMaterials stored in each workshop hobbs_wagoncraft_favoritesFavorite wagons per character -
Check the console for lines starting with
[hobbs_wagoncraft]. "Unknown StableSystem" means step 4 is wrong, "Item 'x' not in the database" means step 2 is missing something. WithConfig.Debug = trueyou also see how many sites are in use and how many items loaded. - If you want the Discord log, put your webhook URL in
server_config.lua.
Note
The included shop, its NPC, the preview spot and the building site are examples. Move them to where your workshop is. The debug command /wagoncraft_site prints the line for a new site, see Configuration below.
Configuration¶
Settings are split into three files. config.lua is the shop, the wagons and the prices, and every client downloads it. config_build.lua is the building process. server_config.lua never leaves the server and holds the webhook. Texts live in locales.lua.
Language and debug¶
Config.Debug turns on console output and the four setup commands. Keep it off on a live server, the commands are not protected by anything else.
Notifications¶
Config.Notify = {
dict = "generic_textures",
icon = "tick",
warnIcon = "cross", -- used for failures and aborts
duration = 5000,
}
All messages go through the VORP left notification. The title is notifyTitle in locales.lua.
Keys¶
The keys at the building site are in config_build.lua (see below).
Preview camera¶
Config.Camera = {
view = { distance = 8.5, height = 3.4, angle = 30, aim = 1.8, fov = 45 },
zoom = { min = 0.6, max = 1.8, step = 0.1 },
tilt = { min = -12, max = 88, step = 1.5 },
light = { enabled = true, color = { 255, 236, 205 }, range = 10.0, intensity = 20.0, toward = 0.4, height = 2.0 },
}
The camera stands next to the preview wagon on the NPC's side, turned angle degrees towards the front, and looks at a point aim meters above the ground. When a wagon is picked for the first time in a session the script probes a set of angles and distances around the wagon and keeps the one with the clearest view, so a wall behind the spawn point does not end up in the picture. The light is a warm lamp between camera and wagon so the preview reads well at night. Set light.enabled = false if you find it too bright in daylight.
Distances and names¶
Config.UI = {
promptDistance = 2.0, -- m, distance to the NPC for the prompt
shopReach = 10.0, -- m, max distance to the NPC for orders and storage (checked by the server)
nameMinLen = 4, -- wagon name length in characters
nameMaxLen = 20,
}
shopReach is checked on the server for every order and every storage move, so a client cannot do those from across the map. The name may not contain < or >.
Stable system¶
This decides which table the finished wagon is inserted into:
| Value | Table | What is written |
|---|---|---|
vorp |
stables |
type = 'wagon', status = 1, isDefault = 0, empty gear and inventory |
sirevlc |
sirevlc_wagons |
model hash, price = resalePrice, stashlimit and stashslots from the wagon entry, default equipment |
syn |
wagons |
selected = 0, empty items |
gum |
wagons |
like syn plus an empty weapons column |
Owner is the character who ordered the build. If the value is wrong the console says "Unknown StableSystem" on start and every order is refused with a note to tell the stable master.
Workshop storage¶
With storage on, the menu has a Storage tab. Materials in there count towards a build and are used before the bag. Storage is per shop and shared by everyone with the job. Turn it off and materials come from the bag only.
Shops¶
Config.Shops = {
[1] = {
name = "Wagon Workshop",
job = { "carpenter" }, -- jobs that may order and build here, "all" = everyone
npc = {
enabled = true,
model = "mp_u_m_m_fos_musician_01",
pos = {x = -371.22, y = 800.22, z = 115.13, heading = 313.87},
scenario = "WORLD_HUMAN_CLIPBOARD", -- "" = just stands there
},
wagonSpawn = {x = -395.18, y = 803.38, z = 115.95, heading = 269.83},
sites = {
{ x = -365.22, y = 805.17, z = 115.08, h = 269.16 },
},
blip = { enabled = false, sprite = -1989306548 },
},
}
| Key | Meaning |
|---|---|
name |
Shown in the menu header and on the blip |
job |
VORP job names allowed to order, build and use the storage. "all" opens it to everyone |
npc |
The clerk. pos is also where the prompt and the server distance check are |
wagonSpawn |
Where the preview wagon stands while the menu is open. Leave room around it, the camera needs it |
sites |
Building sites, one wagon per site. h points forward along the shaft. No free site means no new orders |
blip |
Map blip at the NPC, off by default |
Add more shops as [2], [3] and so on. A shop can have several sites, each one is a separate build. The build table stores the site as shopId:siteIndex, so do not reorder shops or sites while a build is running.
Fee¶
The fee is paid in cash on top of the materials when ordering. With the default 45 percent a cart with resalePrice = 120 costs 54 dollars plus materials. Set it to 0 if you want materials only.
Categories and wagons¶
Config.Categories = { "carts", "coaches", "travel", "work", "special" }
Config.Wagons = {
{ category = "carts", hash = "cart01", resalePrice = 120, stashLimit = 90, stashSlots = 90,
materials = { carriage_parts = 2, ironbar = 2, oilunrefined = 1, nails = 1 } },
{ category = "coaches", hash = "stagecoach001x", resalePrice = 450, stashLimit = 200, stashSlots = 200,
materials = { carriage_parts = 18, ironbar = 3, copperbar = 2, cotton = 2, leather = 2, oilunrefined = 2, nails = 2 } },
-- ...
}
| Key | Meaning |
|---|---|
category |
One of Config.Categories, menu group. Name is category_<id> in locales.lua |
hash |
Wagon model name. Display name is wagon_<hash> in locales.lua |
resalePrice |
Base for the fee, and the price written for sirevlc stables |
stashLimit, stashSlots |
Written to the sirevlc_wagons row, shown in the menu as storage slots |
materials |
Item name and amount, from your inventory |
The included list:
| Group | Hash | Name | Resale price |
|---|---|---|---|
| Carts | cart01 |
Light Farm Cart | 120 |
| Carts | cart02 |
Farm Cart with Side Boards | 120 |
| Carts | cart03 |
Small Market Cart | 250 |
| Carts | cart04 |
Small Yard Cart | 200 |
| Carts | cart05 |
Water Barrel Cart | 125 |
| Carts | cart07 |
Farmer's Cart | 200 |
| Carts | cart08 |
Country Cart | 200 |
| Carts | cart06 |
Large Freight Cart | 300 |
| Passenger Coaches | buggy02 |
Open Buggy | 250 |
| Passenger Coaches | buggy01 |
Buggy with Leather Top | 250 |
| Passenger Coaches | buggy03 |
Family Buggy | 250 |
| Passenger Coaches | coach5 |
Victoria | 300 |
| Passenger Coaches | coach4 |
Landau | 350 |
| Passenger Coaches | coach2 |
Closed Town Coach | 700 |
| Passenger Coaches | coach6 |
Wagonette | 300 |
| Passenger Coaches | stagecoach003x |
Town Stagecoach | 400 |
| Passenger Coaches | stagecoach002x |
Light Country Stagecoach | 400 |
| Passenger Coaches | stagecoach001x |
Concord Stagecoach | 450 |
| Passenger Coaches | stagecoach005x |
Overland Stagecoach | 400 |
| Passenger Coaches | stagecoach006x |
Horse Omnibus | 500 |
| Covered Wagons | wagon02x |
Camp Covered Wagon | 600 |
| Covered Wagons | wagon03x |
Reinforced Camp Covered Wagon | 500 |
| Covered Wagons | wagon06x |
Supply Covered Wagon | 300 |
| Work Wagons | utilliwag |
Flatbed Wagon | 300 |
| Work Wagons | wagon04x |
Light Farm Wagon | 700 |
| Work Wagons | wagon05x |
Open Freight Wagon | 900 |
| Work Wagons | chuckwagon000x |
Chuck Wagon | 700 |
| Work Wagons | chuckwagon002x |
Equipment Wagon | 600 |
| Work Wagons | supplywagon |
Large Supply Wagon | 500 |
| Work Wagons | gatchuck |
Heavy Freight Wagon | 800 |
| Work Wagons | logwagon |
Log Wagon | 700 |
| Work Wagons | coal_wagon |
Coal Wagon | 750 |
| Work Wagons | oilWagon01x |
Small Oil Wagon | 300 |
| Work Wagons | oilWagon02x |
Large Oil Wagon | 400 |
| Trade Wagons | wagondairy01x |
Dairy Wagon | 500 |
| Trade Wagons | wagonwork01x |
Delivery Wagon | 500 |
| Trade Wagons | wagontraveller01x |
Merchant Wagon | 750 |
| Trade Wagons | coach3 |
Hearse | 400 |
Remove entries you do not want. To add a wagon, add its entry here, a wagon_<hash> name in every language block of locales.lua, and ideally a measured entry in Config.Build.models (see below). Without a measured entry the wagon still works, it then gets one sawhorse per axle and calculated work stands.
Building site (config_build.lua)¶
Config.Build = {
siteRange = 80.0, -- m, the site is set up on the client within this range
siteReach = 15.0, -- m, max distance to the site for a task (checked by the server)
spotReach = 1.5, -- m, the prompt shows up within this range of a work spot
slack = 20, -- s, extra time a task may take (lag)
around = { before = 5, after = 5 }, -- s, work before and after a minigame
staleDays = 14, -- days without progress, then anyone with the job may clear the build
keys = {
work = 0xCEFD9220, -- E, start and stop working
abandon = 0x4AF4D473, -- DEL (hold), give up the build
},
marker = { type = 0x94FDAE17, size = 0.6, color = { 200, 160, 90, 120 } },
-- ...
}
siteRange is how close a player has to be before the wagon, sawhorses, workbench and lumber are spawned on their client. slack matters if your players have high ping: the server only accepts a task that took between the fastest possible run and the full minigame time plus slack. marker is the ground ring at every spot that is open right now, set it to false to turn it off.
Sawhorses, workbench and stock¶
sawhorse = { model = 'p_sawhorse03x', turn = 180.0 },
lift = 0.30,
standOff = { sides = 0.30, wheels = 0.45, front = 0.50, seat = 0.30 },
bench = {
model = 'p_group_vallogs01', x = -3.95, y = -1.70, z = 0.00, h = 90.0,
stands = {
saw = { forward = -0.623, right = 0.048, headingDelta = -10.17 },
plane = { forward = -0.623, right = 0.048, headingDelta = 79.83 },
},
},
props = {
{ model = 'p_bat_lumber05ax_stack_01c', x = -3.70, y = 4.55, z = 0.00, h = 272.5, till = 'saw' },
{ model = 'p_bat_lumber05ax_stack_01b', x = -3.15, y = 4.55, z = 0.00, h = 275.0, from = 'saw', till = 'plane' },
{ model = 'p_bat_lumber05ax_stack_01a', x = -2.55, y = 4.60, z = 0.00, h = 277.5, from = 'plane', till = 'body' },
},
All positions are relative to the site: x right, y forward, z up, h in degrees. The bench and the stock props are placed the same way at every site, so one measurement fits all your workshops as long as the ground around the site is flat enough. The three lumber stacks are the "shrinking" stock: a prop shows once the step in from is done and disappears once the step in till is done. standOff is how far in front of a part you stand for wagons without their own measured stands. Use /wagoncraft_setup to move all of this in game and print the result.
Steps and works¶
steps = {
{ key = 'saw', at = 'bench', count = 2, work = 'saw' },
{ key = 'plane', at = 'bench', count = 1, work = 'plane' },
{ key = 'body', at = 'sides', work = 'nail' },
{ key = 'wheels', at = 'wheels', work = 'wheel' },
{ key = 'front', at = 'front', work = 'stake' },
{ key = 'seat', at = 'seat', work = 'seat' },
{ key = 'sand', at = 'sides', work = 'sand' },
},
at |
Spots created |
|---|---|
bench |
count passes at the workbench |
sides |
One per side, left and right |
wheels |
One per wheel of that model, from data/wagon_wheels.lua |
front |
One at the shaft |
seat |
One at the driver's seat |
A two-wheel cart has 2 + 1 + 2 + 2 + 1 + 1 + 2 = 11 tasks, a four-wheel wagon 13. The menu shows that number. Prompt texts are step_<key> and the step-complete note is done_<key> in locales.lua. Remove a step to shorten the job, for example sand.
works = {
saw = { anim = { dict = 'amb_work@world_human_saw_wood@male_a@base', clip = 'base', flag = 1 }, tool = 'saw',
game = { name = 'Saegen', min = 6, time = 60, timeKey = 'saegTime',
settings = { saegStrokes = 16, saegSwing = 1.5, saegKnots = 2, saegLives = 1 } } },
nail = { anim = { dict = 'amb_work@world_human_hammer@wall@male_a@trans', clip = 'base_trans_base', flag = 1 }, tool = 'hammer',
game = { name = 'Nageln', min = 6, time = 60, timeKey = 'nagelTime',
settings = { nagelScene = 'wand', nagelSpare = 1, nagelSwing = 2.0, nagelWindow = 3 } } },
-- plane, wheel, stake, seat, sand ...
},
Each work is an animation, a tool in hand and a minigame. time is the time limit of the game in seconds, min the fastest run the server accepts. The settings are the difficulty: number of strokes, spare nails, how fast the gauge swings and so on. There are five games:
name |
Used for | How it plays |
|---|---|---|
Saegen |
Sawing planks | Hold to draw the saw back, release in the green band. Knots narrow the band |
Hobeln |
Planing | Hold to push the plane over the board, release in the green run-out |
Nageln |
Side panels, shaft, seat | Click when the swaying nail stands straight, then strike in rhythm. A bent nail is pulled with the claw |
Radreparatur |
Wheels | Wind up and release in the green band for each nail, then tighten the nut with the wrench |
Schleifen |
Sanding | Run the block over the wood paper by paper, pull splinters along the grain first |
Failing or quitting a game ends the task, the spot stays open and can be tried again. If you want a work without a game, replace game with seconds = 10 and the task is just the animation for that long.
Tools¶
tools = {
hammer = { model = 'p_hammer01x', bone = 'PH_R_Hand', pos = { 0.0, 0.0, 0.0 }, rot = { 0.0, 0.0, 0.0 } },
saw = { model = 'p_saw01', bone = 'PH_R_Hand', pos = { 0.0, 0.4, 0.0 }, rot = { 90.0, 0.0, 0.0 } },
plane = { model = 'p_woodplane01x', bone = 'PH_R_Hand', pos = { 0.0, 0.0, 0.0 }, rot = { 0.0, 0.0, 0.0 } },
},
The tool a worker holds is published through the player's state bag, so every player within 80 meters sees it in their hand. Sanding has no tool.
Measured models¶
models = {
["cart01"] = { lift = 0.00, pitch = -3.0, roll = 0.0,
horses = { { x = 0.00, y = -0.74, h = 180.0 }, { x = 0.00, y = 1.42, h = 180.0 } },
stands = { left = { x = -1.32, y = 0.66, h = 270.0 }, right = { x = 1.24, y = 0.56, h = 90.0 },
wheel_lf = { x = -1.44, y = -0.00, h = 270.0 }, wheel_rf = { x = 1.39, y = 0.04, h = 90.1 },
front = { x = -0.01, y = 2.24, h = 165.0 }, seat = { x = 0.02, y = -1.24, h = 3.2 } } },
-- ...
},
One entry per wagon model, lower case. lift, pitch and roll put the wagon on the sawhorses so the wheels do not sink into the ground, horses are the sawhorses relative to the wagon, stands are the work positions relative to the wagon. All 38 included wagons are measured. A wagon without an entry gets one sawhorse per axle and stands calculated from the model's bones and size, which is fine for a first test. /wagoncraft_measure <model> and /wagoncraft_stands <model> print a finished line for this table.
Discord webhook (server_config.lua)¶
One embed per event: build started (player, wagon, site, cost), wagon built (owner, wagon, value, stable id), build abandoned, delivery to the stable failed, and every storage deposit and withdrawal. Titles and bodies are the webhook* keys in locales.lua.
Languages¶
locales.lua ships English and German. Copy a block, translate it and set Config.Locale to its key. The block also holds the wagon names, the category names, the minigame hints and the webhook texts, so a new language needs all of them.
Items¶
| Item | Used for |
|---|---|
carriage_parts |
Every wagon, 2 to 35 pieces depending on size |
ironbar |
Every wagon |
nails |
Every wagon |
oilunrefined |
Every wagon |
copperbar |
The market cart, the large freight cart and many of the bigger wagons |
cotton |
Coaches, the hearse and the reinforced covered wagon |
leather |
Same wagons as cotton |
The script does not create or hand out any item, it only takes materials. Names are whatever you write in Config.Wagons, so you can swap them for your own crafting items. Labels in the menu come from your inventory database.
Commands and keys¶
Player¶
| Key | Where | Action |
|---|---|---|
| E | Within 2 m of the workshop NPC, with the job | Open the workshop |
| Mouse drag, mouse wheel, Page Up, Page Down | In the menu | Turn, zoom and tilt the preview camera |
| Hold the build button | In the menu | Order the selected wagon |
| E | At an open work spot | Start the task. Pressing E again during the task stops it |
| Hold Del | Near the wagon, as the owner (or after 14 days with the job) | Give up the build |
| Mouse button or Space | In a minigame | Play, as the hint at the top of each game says |
While a task runs, movement and weapons are blocked. Only looking around, E and the chat stay open. Walking away more than a meter, dying, or the build disappearing aborts the task.
There are no chat commands for players.
Setup (only with Config.Debug = true)¶
| Command | What it does |
|---|---|
/wagoncraft_site |
Prints your position as a sites line for config.lua, and relative to the nearest site if there is one within 30 m |
/wagoncraft_setup [model] |
Shows a site with wagon, workbench, stock props, a worker and rings at every stand. Walk through bench, stands, props and the standOff distances, move them, print the lines for config_build.lua. Default model is cart01 |
/wagoncraft_measure <model> |
Puts the wagon on sawhorses at the nearest site, or 6 m in front of you. Adjust lift, pitch, roll and each sawhorse, print the models line |
/wagoncraft_stands <model> |
Shows the worker at every stand of that model, lets you move each one and switch between the works done there, prints the stands line |
Keys in the setup tools: Enter prints the current values to F8, Del ends the tool, F picks the next part, the arrow keys move the part (for the wagon itself they pitch and roll it), Q and E turn it, and R switches the work shown at a stand. The prompts on screen name every key. The tools cannot be started while you are working on a build, and a build at that site is hidden while a tool is open.
Exports and events¶
The script offers no exports. Other resources do not need to call anything, the wagon arrives in the stable table directly.
FAQ¶
The prompt at the NPC does not show.
You need one of the jobs in job for that shop. The job is read when you pick your character and again whenever VORP changes it. The prompt also hides while the menu is open, on horseback or in a wagon.
"The building site is taken."
Every site holds one build. Either add another site to sites or wait until the running build is finished or given up. A build nobody has worked on for staleDays can be cleared by anyone with the job, hold Del at the wagon.
Where does the money go? The fee is taken from the player's cash when the wagon is ordered and is not paid to anyone. Materials come out of the workshop storage first, then out of the bag. Nothing is refunded when a build is given up. If the order itself fails, for example because the database does not answer, fee and materials go back, and if the bag is full by then the materials land in the workshop storage.
The wagon is finished but not in the stable.
The last task tried to insert the row and failed. The player gets "The wagon could not be taken to the stable", the last task is reopened, the console prints the reason in red and the webhook gets an error embed. Check Config.StableSystem and whether that table exists, then do the last task again.
"It doesn't go that fast, once more."
The server timed the task. With the defaults a task must take at least 5 + 6 + 5 - 1 = 15 seconds and at most 5 + 60 + 5 + 20 = 90 seconds. A player who got that message was faster than the minigame allows, usually a desync. Raise slack if players with bad connections see "That took too long" instead.
Can several players build the same wagon? Yes. Anyone with the shop job can take any open spot. Within a step every spot can be worked by a different player at the same time, the server marks a spot as busy while someone is on it. The next step opens once every spot of the current one is done.
What happens on a server restart?
Running builds are stored in hobbs_wagoncraft_builds and come back with the same progress. A task that was in progress during the restart is simply open again. If the database is down on start, the script retries every 30 seconds and refuses orders until it has loaded.
I removed a wagon from the config but a site is still blocked.
A build row for a wagon that is no longer in Config.Wagons cannot be loaded. The console names it on start. Either put the wagon back or delete that row from hobbs_wagoncraft_builds.
The preview wagon clips into a wall or the camera is inside something.
Move wagonSpawn to an open spot. The camera probes for a clear angle but it can only use what is free around the wagon.
The sawhorses or stands are off for a new wagon.
Turn on Config.Debug, run /wagoncraft_measure <model> and /wagoncraft_stands <model>, press Enter in each tool and paste the printed lines into Config.Build.models.
How heavy is it?
Nothing is spawned until a player is within siteRange (80 m) of a site with a build on it. Near a site the client checks every half second, otherwise every two seconds. Ground markers are only drawn while an open spot is near. The server validates every task but keeps no timers beyond the running task windows.
Changelog¶
1.0.1¶
First release.