Developer
Overview
Other resources work with crafting through server exports, a hook system any resource can attach to, and handler files in open/, which stay unencrypted.
Server Exports
Progression
getPlayerLevel(src): crafting level of the player, fromml_skillswhen external skills are ongetPlayerCategoryLevel(src, categoryId): level in one recipe category with the internal progression. Withml_skillsit returns the global leveladdXp(src, amount): gives crafting XP. Withml_skillsthe XP goes toConfig.ExternalSkills.Mapping.Category, so set it or the XP is skipped
Blueprints
getPlayerBlueprints(src): map of blueprint ID to{ type = 'permanent' }or{ type = 'item' }givePlayerBlueprint(src, blueprintId): unlocks a blueprint for good.unlockPermanentBlueprintdoes the sameremovePlayerBlueprint(src, blueprintId): removes a permanent unlock
Benches
setCoordAsBench(coord, benchTypeId, routingBucket?): creates a bench at a position, withcoord.was heading, saved in the database. Returns{ success, id, remove }, andremove()deletes it
To turn every prop of a model into a bench, use Config.ModelBenches instead.
Data and Queue
getCategories(): all recipe categoriesgetRecipe(recipeId): one recipe with ingredients, results and blueprintscreateRecipe(data): creates a recipe from codegetPlayerQueue(src): queued crafts of the playergetQueuedCrafts(benchLocationId): queue of one bench
Hooks
Hooks let another resource check, change or watch crafting from its own code:
1local hookId = exports.ml_crafting:registerHook('crafting:preCraft', {
2 priority = 10,
3 fn = function(ctx)
4 if ctx.quantity > 5 then
5 return { success = false, message = 'Max 5 at a time' }
6 end
7 return true
8 end
9})
10
11exports.ml_crafting:unregisterHook('crafting:preCraft', hookId)Hooks run from the lowest priority up. Return false, or { success = false, message = '...' }, to block. A table with no success field is merged into ctx. Hooks of a resource are removed when it stops. runHook, transformHook and emitHook run a hook point from your own code.
Hook Points
crafting:canInteract: a player opens a bench.ctx:src,benchType,benchLocationcrafting:enter: access was granted, before the menu data is built. Samectxcrafting:getCategories: filter or add categories.ctxaddscategoriescrafting:getRecipes: filter or add recipes of a category.ctxaddscategoryIdandrecipescrafting:getRecipe: change one recipe before it shows.ctxaddsrecipecrafting:preCraft: before the ingredients are taken.ctx:src,recipe,quantity,benchType,benchLocation. Can changequantityor blockcrafting:postCraft: after the results are given.ctxaddsresults,queueId,ownerIdentifierandrecipientIdentifier. Watch onlycrafting:exit: the player leaves the bench. Watch onlycrafting:projectContribution: a player donates to a community project.ctx:src,project,itemName,amount. Watch onlycrafting:projectCompleted: a project reaches 100%.ctx:project,contributors. Watch only
Server Handlers
Located in open/server.lua. Each handler is registered as a hook at priority 50.
1Handlers.CanPlayerUseBench = function(src, benchType, benchLocation)
2 return true
3end
4
5Handlers.OnBeforeCraft = function(src, recipe, quantity)
6 return true
7end
8
9Handlers.OnAfterCraft = function(src, recipe, quantity, results)
10end
11
12Handlers.OnBlueprintUnlocked = function(src, blueprintId)
13end
14
15Handlers.OnLevelUp = function(src, category, newLevel)
16end
17
18Handlers.ModifyCraftResults = function(src, recipe, results)
19 return nil
20end
21
22Handlers.VerifyWorldBench = function(src, model, coords, benchType)
23 return nil
24end
25
26Handlers.OnProjectContribution = function(src, project, itemName, amount)
27end
28
29Handlers.OnProjectCompleted = function(project, contributors)
30endCanPlayerUseBench: returnfalse, orfalseand a message, to refuse a benchOnBeforeCraft: returnfalseto block a craftOnAfterCraft: runs after the results are givenOnBlueprintUnlocked: a player unlocks a blueprintOnLevelUp: a global level up of the internal progression.categoryis'global'ModifyCraftResults: runs just before the items are given. Return a new results table to change themVerifyWorldBench: returnfalseto refuse a world prop as a model bench. WithConfig.ModelBenches.RequireVerification = true, returntrueto accept itOnProjectContribution,OnProjectCompleted: community project events
The same file holds the base check used by portable stations when Outpost runs. Leave it as it is unless you change the base rules.
Client Handlers
Located in open/client.lua.
1Handlers.CanInteractWithBench = function(benchType, entity)
2 return true
3end
4
5Handlers.OnCraftStarted = function(recipe)
6end
7
8Handlers.OnCraftCompleted = function(recipe, results)
9end
10
11Handlers.GetCustomAnimation = function(recipe)
12 return nil
13end
14
15Handlers.OnBenchEnter = function(benchType, entity)
16end
17
18Handlers.OnBenchExit = function(benchType, entity)
19endCanInteractWithBench: returnfalseto hide the bench optionOnCraftStarted,OnCraftCompleted: a craft starts or endsGetCustomAnimation: return{ dict = '...', clip = '...', flag = 49 }to replace the craft animationOnBenchEnter,OnBenchExit: the player starts or stops using a bench
The shipped file hides the HUD while the player uses a bench. Set HideHudAtBench = false at the top to keep it, or add your HUD to the HudResources list. OnCraftCompleted also holds a sample stress call, safe to remove.
Community Project Event
A server event fires when a project reaches 100%, for rewards of your own such as opening a door.
1AddEventHandler('ml_crafting:projectCompleted', function(data)
2 if data.id ~= 'the-project-id' then return end
3 for _, c in ipairs(data.contributors) do
4 print(c.name, c.score, c.share)
5 end
6end)data.id,data.title: the project. The editor shows this snippet with the real ID filled indata.contributors: list of{ playerId, name, score, share }, largest first.sharegoes from0to1, so0.42is 42% of the total
The event always carries the contributors, also for projects that hide them on the board.
Skill Bonuses
Set per recipe in the editor. The bonus applies while the player has the bound ml_skills skill. Values are percentages, and bonuses of the same type add up.
time_reduction: shorter craft time, up to 90%extra_xp: more XPextra_quantity: chance of extra outputextra_drop_chance: better odds on byproductsingredient_reduction: chance to use fewer ingredients
Examples
1local bench = exports.ml_crafting:setCoordAsBench(
2 vector4(-261.95, -956.42, 31.22, 90.0),
3 'weapons_bench'
4)
5
6if bench.success then
7 print('Bench created with id ' .. bench.id)
8end1Handlers.CanPlayerUseBench = function(src, benchType, benchLocation)
2 local job = Bridge.GetJob(src)
3 if benchType.id == 'medic_bench' and (not job or job.name ~= 'ambulance') then
4 return false, 'error_not_authorized'
5 end
6 return true
7end1exports.ml_crafting:registerHook('crafting:postCraft', {
2 priority = 10,
3 fn = function(ctx)
4 TriggerEvent('myeconomy:craftTax', ctx.src, ctx.recipe.id, ctx.quantity)
5 end
6})1Handlers.GetCustomAnimation = function(recipe)
2 if recipe.category_id == 'cooking' then
3 return { dict = 'amb@prop_human_bbq@male@base', clip = 'base', flag = 49 }
4 end
5 return nil
6end