The Crafting Table is a mod that adds an item crafting interface to the game. It works as a visual guide, making it easier for players to craft various items in a simple and intuitive way.
💻Client Side
Create a folder named crafting_table inside Client\mods.
Inside it, create a folder named imgs, and inside imgs, create another folder called materiais.
Still inside the crafting_table folder, create the following files: configs.lua, tutorial.lua, tutorial.otmod, and tutorial.otui.
config.lua
tutorialsIndex = {"Construction", "Tools", "Box's", "Grounds", "Building Materials", "Traps"}
-- Cores das categorias (você pode editar aqui)
categoryColors = {
["Construction"] = "black",
["Tools"] = "black",
["Box's"] = "black",
["Grounds"] = "black",
["Building Materials"] = "black",
["Traps"] = "black"
}
-- Cor padrão para categorias não definidas
defaultCategoryColor = "#ffffff"
tutorialsInfo = {
-- Fances
{
{name = "Brown Fance", id = 10001, material = {'Wood'}, text = "[ENG]: A simple straight fence for marking areas. When used, it transforms into a different shape.\n[BRA]: Uma cerca reta simples para demarcar areas. Quando usada, ela se transforma em uma forma diferente."},
{name = "Horizontal Farm Door", id = 10002, material = {'8 Wood'}, text = "[ENG]: A horizontal door for your farm. When used, it opens and closes.\n[BRA]: Uma porta horizontal para sua fazenda. Quando usada, ela abre e fecha."},
{name = "Vertical Farm Door", id = 10003, material = {'8 Wood'}, text = "[ENG]: A vertical door for your farm. When used, it opens and closes.\n[BRA]: Uma porta vertical para sua fazenda. Quando usada, ela abre e fecha."},
},
-- Tools
{
{name = "Watering Can", id = 10004, material = {'8 Bucket'}, text = "[ENG]: A watering can with 90 charges.\n[BRA]: Um regador com 90 cargas."},
{name = "Wood Pick", id = 10005, material = {'2 Refined Wood', '3 Wood'}, text = "[ENG]: A watering can with 90 charges.\n[BRA]: Um regador com 90 cargas."},
{name = "Spray", id = 10010, material = {'1 Spray Empyt', '20 Nim'}, text = "[ENG]: Mix it all and you get the poison to kill the pests.\n[BRA]: Misturando tudo, voce consegue o veneno para matar as pestes."},
},
-- Box's
{
{name = "Cow Box", id = 10011, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with cow face design (20 slots).\n[BRA]: Uma caixa com aparencia de vaca (20 slots)."},
{name = "Chicken Box", id = 10012, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with chicken face design (20 slots).\n[BRA]: Uma caixa com aparencia de galinha (20 slots)."},
{name = "Fish Box", id = 10013, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with fish design (20 slots).\n[BRA]: Uma caixa com aparencia de peixe (20 slots)."},
{name = "Seeds Box", id = 10014, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with seed pattern (20 slots).\n[BRA]: Uma caixa com aparencia de sementes (20 slots)."},
{name = "Box", id = 10015, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A simple wooden box (20 slots).\n[BRA]: Uma caixa simples de madeira (20 slots)."},
{name = "Pumpkin Box", id = 10016, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with pumpkin design (20 slots).\n[BRA]: Uma caixa com aparencia de abobora (20 slots)."},
{name = "Tool Box", id = 10017, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with tool design (20 slots).\n[BRA]: Uma caixa com aparencia de ferramentas (20 slots)."},
{name = "Changin Box", id = 10018, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A customizable box (20 slots).\n[BRA]: Uma caixa personalizavel (20 slots)."}
},
-- Grounds
{
{name = "Stone Floor", id = 10008, material = {'10 Stone Fragment'}, text = "[ENG]: A beautiful Stone floor, perfect for elegant constructions.\n[BRA]: Um lindo piso de pedras, ideal para construcoes elegantes."},
},
-- Materiais
{
{name = "Refined Wood", id = 10006, material = {'2 Wood'}, text = "[ENG]: High-quality wood, perfect for crafting furniture and tools.\n[BRA]: Madeira de alta qualidade, perfeita para criar moveis e ferramentas."},
{name = "Board", id = 10007, material = {'4 Refined Wood'}, text = "[ENG]: A strong and versatile board, perfect for any kind of construction or crafting.\n[BRA]: Uma tabua resistente e versatil, perfeita para qualquer tipo de construcao ou criacao."},
{name = "Nail", id = 10019, material = {'1 Iron Fragment'}, text = "[ENG]: A simple nail, essential for woodworking and construction. (2 items)\n[BRA]: Um prego simples, essencial para marcenaria e construcao. (2 itens)"}
},
-- Traps
{
{name = "Chiken Trap", id = 10009, material = {'20 Iron Fragment', '10 Board'}, text = "[ENG]: A trap designed to catch chickens. Set it and wait for the cluck!\n[BRA]: Uma armadilha feita para capturar galinhas. Arme e espere o cocorico!"},
}
}
tutorial.lua
tutorialOpen = false
currentLabel = false
currentCategorie = false
maxCategories = 0
local form = {
assignWindow = nil,
oakImg = nil,
indexList = nil,
lines = {},
materialImages = {}, -- Array para armazenar as imagens dos materiais
}
local config = {
categoriesColor = "#afa9a0",
}
function setCategoriesColor()
for x = 1, #tutorialsIndex do
local categoryName = tutorialsIndex[x]
local color = categoryColors[categoryName] or defaultCategoryColor
form.indexList:getChildById("index" .. x):setColor(color)
end
end
-- Função para limpar as imagens dos materiais
function clearMaterialImages()
for i, img in ipairs(form.materialImages) do
if img then
img:destroy()
end
end
form.materialImages = {}
end
-- Função para criar as imagens dos materiais
function createMaterialImages(materials)
clearMaterialImages()
if not materials then return end
local imgSize = 32 -- Tamanho das imagens dos materiais
local verticalSpacing = 3 -- Espaçamento vertical entre materiais
local textMargin = 5 -- Espaçamento entre a imagem e o texto
local headerHeight = 20 -- Altura do cabeçalho
-- Pega o painel da imagem principal
local mainPanel = tutorialWindow:getChildById('tutorialImg')
-- Calcula a altura total necessária para todos os materiais (incluindo o cabeçalho)
local totalHeight = headerHeight + #materials * (imgSize + verticalSpacing) - verticalSpacing
-- Cria o cabeçalho "Materials Required"
local headerPanel = g_ui.createWidget('Panel', mainPanel)
headerPanel:setId("materialsHeader")
headerPanel:setSize({width = 200, height = headerHeight})
headerPanel:addAnchor(AnchorLeft, 'parent', AnchorLeft)
headerPanel:addAnchor(AnchorTop, 'parent', AnchorTop)
headerPanel:setMarginTop(10)
headerPanel:setMarginLeft(10)
local headerLabel = g_ui.createWidget('MaterialText', headerPanel)
headerLabel:setId("headerText")
headerLabel:setText("Materials Required")
headerLabel:setColor("#ffffff") -- Texto branco para destacar
headerLabel:addAnchor(AnchorLeft, 'parent', AnchorLeft)
headerLabel:addAnchor(AnchorVerticalCenter, 'parent', AnchorVerticalCenter)
table.insert(form.materialImages, headerPanel)
for i, material in ipairs(materials) do
-- Extrai o nome do material (remove a quantidade se existir)
local materialName = material:match("^%d+%s*(.+)") or material
local quantity = material:match("^(%d+)%s*") or "1"
-- Cria um painel container para cada material
local containerPanel = g_ui.createWidget('Panel', mainPanel)
containerPanel:setId("materialContainer" .. i)
containerPanel:setSize({width = 200, height = imgSize}) -- Largura maior para acomodar o texto
containerPanel:addAnchor(AnchorLeft, 'parent', AnchorLeft)
containerPanel:addAnchor(AnchorTop, 'parent', AnchorTop)
-- Calcula a posição de baixo para cima (agora considerando o cabeçalho)
containerPanel:setMarginTop(totalHeight - (i * (imgSize + verticalSpacing)) + 10)
containerPanel:setMarginLeft(10)
-- Cria a imagem do material
local materialImg = g_ui.createWidget('MaterialImage', containerPanel)
materialImg:setId("materialImg" .. i)
materialImg:setSize({width = imgSize, height = imgSize})
materialImg:addAnchor(AnchorLeft, 'parent', AnchorLeft)
materialImg:addAnchor(AnchorVerticalCenter, 'parent', AnchorVerticalCenter)
materialImg:setImageSource("imgs/materiais/" .. materialName)
-- Cria o label com a quantidade e nome
local textLabel = g_ui.createWidget('MaterialText', containerPanel)
textLabel:setId("materialText" .. i)
textLabel:setText(quantity .. "x " .. materialName)
textLabel:setMarginLeft(imgSize + textMargin) -- Posiciona o texto após a imagem
textLabel:addAnchor(AnchorLeft, 'parent', AnchorLeft)
textLabel:addAnchor(AnchorVerticalCenter, 'parent', AnchorVerticalCenter)
table.insert(form.materialImages, containerPanel)
end
end
tutorialsIndex = nil
tutorialsInfo = nil
dofile('configs.lua')
tutorialsIndex = tutorialsIndex
tutorialsInfo = tutorialsInfo
maxCategories = #tutorialsIndex
function init()
tutorialWindow = g_ui.loadUI('tutorial', rootWidget)
tutorialWindow:hide()
form.assignWindow = tutorialWindow:getChildById('tutorialImg')
form.oakImg = tutorialWindow:getChildById('oak')
form.indexList = tutorialWindow:getChildById('indexList')
-- Adiciona eventos
connect(g_game, {
onGameStart = onGameStart,
onGameEnd = hideAll,
onWalk = onWalk,
onAutoWalk = onAutoWalk
})
-- Adiciona handler para extended opcode
ProtocolGame.registerExtendedOpcode(50, function(protocol, opcode, buffer)
if buffer == "open_craft" then
if not tutorialOpen then
onOpenTutorial()
end
end
end)
-- Popular a lista de categorias e tutoriais
for index, widget in pairs(form.lines) do
widget:destroy()
end
form.lines = {}
for sectionIndex, section in pairs(tutorialsIndex) do
local label = g_ui.createWidget('TutorialLabel', form.indexList)
label:setId("index" .. sectionIndex)
label.index = sectionIndex
label:setText(section)
-- Usa a cor personalizada da categoria
local color = categoryColors[section] or defaultCategoryColor
label:setColor(color)
table.insert(form.lines, label)
for idx, currentTutorial in pairs(tutorialsInfo[sectionIndex]) do
local labelTree = g_ui.createWidget('TutorialLabel', form.indexList)
labelTree.img = currentTutorial.img
labelTree:setId("index" .. sectionIndex .. "labelTree" .. idx)
labelTree.description = currentTutorial.text
labelTree:setText(" " .. currentTutorial.name)
labelTree:setVisible(false)
labelTree.index = sectionIndex
labelTree.label = idx
labelTree.isTutorial = true
table.insert(form.lines, labelTree)
end
end
connect(form.indexList, {
onChildFocusChange = changeFocusIndexList
})
g_keyboard.bindKeyPress('Up', nextTutorial, tutorialWindow)
g_keyboard.bindKeyPress('Down', priorTutorial, tutorialWindow)
local quantityBar = tutorialWindow:getChildById('scrollablePainel'):getChildById('quantityPanel'):getChildById('quantityBar')
connect(quantityBar, { onValueChange = onQuantityBarValueChange })
end
function onGameStart()
print("[DEBUG] Game started")
end
function setToDefault(state)
if state then
tutorialWindow:getChildById('scrollablePainel'):setVisible(false)
tutorialWindow:getChildById('textScroll'):setVisible(false)
form.assignWindow:setHeight(375)
form.assignWindow:setImageSource("imgs/default")
form.oakImg:setVisible(true)
-- Limpa as imagens dos materiais
clearMaterialImages()
else
form.assignWindow:setHeight(206)
tutorialWindow:getChildById('scrollablePainel'):setVisible(true)
tutorialWindow:getChildById('textScroll'):setVisible(true)
form.oakImg:setVisible(false)
end
end
function changeFocusIndexList(self, focusedChild)
if focusedChild == nil then
tutorialWindow:getChildById('scrollablePainel'):getChildById('quantityPanel'):setVisible(false)
return
end
for x = 1, #tutorialsIndex do
if x == focusedChild.index then
for y = 1, #tutorialsInfo[x] do
if focusedChild.isOpen then
form.indexList:getChildById("index" .. x .. "labelTree" .. y):setVisible(false)
else
form.indexList:getChildById("index" .. x .. "labelTree" .. y):setVisible(true)
end
end
break
end
end
local quantityPanel = tutorialWindow:getChildById('scrollablePainel'):getChildById('quantityPanel')
if focusedChild.isTutorial then
quantityPanel:setVisible(true)
else
quantityPanel:setVisible(false)
end
if focusedChild.isTutorial then
setToDefault(false)
local tutorialText = tutorialWindow:getChildById('scrollablePainel'):getChildById('tutorialText')
tutorialText:setText(focusedChild.description)
-- Gera automaticamente o caminho da imagem a partir do nome
local imageName = focusedChild:getText():gsub("^%s+", ""):gsub("%s+$", "") -- Remove espaços no início e fim
local imagePath = "imgs/" .. imageName
form.assignWindow:setImageSource(imagePath)
-- Cria as imagens dos materiais
local currentTutorial = tutorialsInfo[focusedChild.index][focusedChild.label]
if currentTutorial and currentTutorial.material then
createMaterialImages(currentTutorial.material)
end
currentLabel = focusedChild.label
currentCategorie = focusedChild.index
else
if focusedChild.isOpen then focusedChild.isOpen = false else focusedChild.isOpen = true end
setToDefault(true)
currentCategorie = false
if focusedChild.isOpen then
form.indexList:focusChild(form.indexList:getChildById("index"..focusedChild.index.."labelTree1"))
else
form.indexList:focusChild(nil)
end
end
setCategoriesColor()
end
function nextTutorial()
if currentLabel and currentCategorie then
local child = form.indexList:getChildById("index"..currentCategorie.."labelTree"..currentLabel-1)
if child then
form.indexList:focusChild(child)
else
if currentCategorie-1 >= 1 then
form.indexList:getChildById("index"..currentCategorie-1).isOpen = true
form.indexList:focusChild(form.indexList:getChildById("index"..(currentCategorie-1).."labelTree"..(#tutorialsInfo[currentCategorie-1])))
end
end
end
end
function priorTutorial()
if currentLabel and currentCategorie then
local child = form.indexList:getChildById("index"..currentCategorie.."labelTree"..currentLabel+1)
if child then
form.indexList:focusChild(child)
else
if currentCategorie+1 <= maxCategories then
form.indexList:getChildById("index"..currentCategorie+1).isOpen = true
form.indexList:focusChild(form.indexList:getChildById("index"..(currentCategorie+1).."labelTree1"))
end
end
else
form.indexList:getChildById("index1").isOpen = true
form.indexList:focusChild(form.indexList:getChildById("index1labelTree1"))
end
end
function destroyAll()
tutorialWindow:destroy()
end
function hideAll()
tutorialWindow:hide()
end
function terminate()
disconnect(g_game, {
onGameEnd = hideAll,
onGameStart = onGameStart,
onWalk = onWalk,
onAutoWalk = onAutoWalk
})
clearMaterialImages()
destroyAll()
end
function onOpenTutorial()
form.indexList:focusChild(nil)
tutorialOpen = true
tutorialWindow:show()
tutorialWindow:raise()
tutorialWindow:focus()
setToDefault(true)
setCategoriesColor()
currentLabel = false
currentCategorie = false
end
function onClickTutorial()
if not tutorialOpen then
onOpenTutorial()
local node = g_settings.getNode('firsttimeseta') or {}
if node then
if not node['tutorialWindow'] then
node['tutorialWindow'] = true
g_settings.setNode('firsttimeseta', node)
end
end
else
tutorialOpen = false
tutorialWindow:hide()
end
end
function onQuantityBarValueChange()
local quantityBar = tutorialWindow:getChildById('scrollablePainel'):getChildById('quantityPanel'):getChildById('quantityBar')
local quantityValue = tutorialWindow:getChildById('scrollablePainel'):getChildById('quantityPanel'):getChildById('quantityValue')
quantityValue:setText(tostring(quantityBar:getValue()))
end
function onCreateClicked()
local painel = tutorialWindow:getChildById('scrollablePainel'):getChildById('quantityPanel')
local quantityBar = painel:getChildById('quantityBar')
local quantidade = quantityBar:getValue()
local itemId = 0
if currentLabel and currentCategorie then
itemId = tutorialsInfo[currentCategorie][currentLabel].id
end
if itemId > 0 then
local msg = "!craft " .. itemId .. " " .. tostring(quantidade)
print("[DEBUG CLIENTE] Enviando talkaction: " .. msg)
g_game.talk(msg)
end
end
function onWalk(direction)
if tutorialOpen then
tutorialOpen = false
tutorialWindow:hide()
return false
end
return true
end
function onAutoWalk(path)
if tutorialOpen then
tutorialOpen = false
tutorialWindow:hide()
return false
end
return true
end
Inside the imgsfolder, you will place, for example, the images that will be displayed on the right side, like this one.
Inside the imgs/materiais folder, you will place the icons needed, with a size of 32x32 pixels.
💻Server Side
Create a archive named workbench.lua (or any name you prefer) inside data/scripts.
workbench.lua
local workbenchItems = {
26453 -- IDs dos itens que abrem o craft
}
local workbenchAction = Action()
function workbenchAction.onUse(player, item, fromPosition, target, toPosition, isHotkey)
if table.contains(workbenchItems, item:getId()) then
player:sendExtendedOpcode(50, "open_craft")
return true
end
return false
end
-- Registra a ação para os itens
workbenchAction:id(26453)
workbenchAction:register()
I used item 26453 so that when I right-click, it opens the mod using OPCode 50.
player:sendExtendedOpcode(50, "open_craft")
In Server\data\scripts\talkactions\create crafting_table.lua
crafting_table.lua
local craftFence = TalkAction("!craft")
-- Tabela de materiais disponíveis
local materials = {
wood = {id = 5901, name = "Wood"},
bucket = {id = 26385, name = "Bucket"},
iron_fragment = {id = 26491, name = "Iron Fragment"},
gold = {id = 26401, name = "Gold"},
stone_fragments = {id = 26489, name = "Stone"},
leather = {id = 26403, name = "Leather"},
refined_wood = {id = 26498, name = "Refined Wood"},
board = {id = 26499, name = "Board"},
spray_empyt = {id = 26546, name = "Spray Empyt"},
nim = {id = 26544, name = "Nim"},
nail = {id = 8309, name = "Nail"},
}
-- Configuração de cargas para itens que precisam
local itemCharges = {
[26470] = 90, -- Regador Vazio
[26484] = 50, -- Wood Pick
[26539] = 100, -- spray
}
local recipes = {
-- Cercas e Portões
[10001] = {materials = {wood = 6}, itemid = 26454, name = "Cerca Marrom", level = 1, quant = 1},
[10002] = {materials = {wood = 8}, itemid = 26460, name = "Porteira Horizontal", level = 1, quant = 1},
[10003] = {materials = {wood = 8}, itemid = 26466, name = "Porteira Vertical", level = 1, quant = 1},
-- Ferramentas
[10004] = {materials = {bucket = 8}, itemid = 26470, name = "Regador Vazio", level = 1, quant = 1},
[10005] = {materials = {refined_wood = 2, wood = 3}, itemid = 26484, name = "Wood Pick", level = 1, quant = 1},
[10010] = {materials = {spray_empyt = 1, nim = 20}, itemid = 26539, name = "spray ant pest", level = 1, quant = 1},
-- Materiais
[10006] = {materials = {wood = 2}, itemid = 26498, name = "Madeira Refinada", level = 1, quant = 1},
[10007] = {materials = {refined_wood = 4}, itemid = 26499, name = "Board", level = 1, quant = 1},
[10019] = {materials = {iron_fragment = 1}, itemid = 8309, name = "Nail", level = 1, quant = 2},
-- Grounds
[10008] = {materials = {stone_fragments = 10}, itemid = 26500, name = "Stone Floor", level = 1, quant = 1},
-- Armadilhas
[10009] = {materials = {iron_fragment = 20, board = 10}, itemid = 26534, name = "Chicken Trap", level = 30, quant = 1},
-- Caixas
[10011] = {materials = {nail = 5, refined_wood = 4, board = 5}, itemid = 26550, name = "Cow Box", level = 1, quant = 1},
[10012] = {materials = {nail = 5, refined_wood = 4, board = 5}, itemid = 26551, name = "Chicken Box", level = 1, quant = 1},
[10013] = {materials = {nail = 5, refined_wood = 4, board = 5}, itemid = 26552, name = "Fish Box", level = 1, quant = 1},
[10014] = {materials = {nail = 5, refined_wood = 4, board = 5}, itemid = 26553, name = "Seeds Box", level = 1, quant = 1},
[10015] = {materials = {nail = 5, refined_wood = 4, board = 5}, itemid = 26554, name = "Box", level = 1, quant = 1},
[10016] = {materials = {nail = 5, refined_wood = 4, board = 5}, itemid = 26555, name = "Pumpkin Box", level = 1, quant = 1},
[10017] = {materials = {nail = 5, refined_wood = 4, board = 5}, itemid = 26556, name = "Tool Box", level = 1, quant = 1},
[10018] = {materials = {nail = 5, refined_wood = 4, board = 5}, itemid = 26557, name = "Changin Box", level = 1, quant = 1},
}
local function isNearWorkbench(player)
local pos = player:getPosition()
local workbenchIds = {26451, 26452}
-- Verifica em um raio de 1 SQM ao redor do jogador
for x = -1, 1 do
for y = -1, 1 do
local checkPos = Position(pos.x + x, pos.y + y, pos.z)
local tile = Tile(checkPos)
if tile then
for _, item in ipairs(tile:getItems()) do
if table.contains(workbenchIds, item:getId()) then
return true
end
end
end
end
end
return false
end
function craftFence.onSay(player, words, param)
if not isNearWorkbench(player) then
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You need to be near a workbench to craft items.")
return false
end
local args = param:split(" ")
local itemId = tonumber(args[1])
local amount = tonumber(args[2])
local recipe = recipes[itemId]
if not recipe then
player:showTextDialog(6578, "Invalid item ID.")
return false
end
-- Verifica o nível do jogador
if player:getLevel() < recipe.level then
player:showTextDialog(6578, "You need level " .. recipe.level .. " to craft this item.")
return false
end
-- Verifica se o jogador tem todos os materiais necessários
local missingMaterials = {}
for material, requiredAmount in pairs(recipe.materials) do
local totalRequired = requiredAmount * amount
local materialId = materials[material].id
if player:getItemCount(materialId) < totalRequired then
local missing = totalRequired - player:getItemCount(materialId)
table.insert(missingMaterials, {
name = materials[material].name,
amount = missing
})
end
end
if #missingMaterials > 0 then
local message = "You are missing the following materials:\n"
for _, material in ipairs(missingMaterials) do
message = message .. "- " .. material.amount .. "x " .. material.name .. "\n"
end
player:showTextDialog(recipe.itemid, message)
return false
end
-- Remove os materiais
for material, requiredAmount in pairs(recipe.materials) do
local totalRequired = requiredAmount * amount
local materialId = materials[material].id
player:removeItem(materialId, totalRequired)
end
-- Adiciona o item criado com as cargas corretas
for i = 1, amount do
local createdItem = player:addItem(recipe.itemid, recipe.quant or 1)
if createdItem and itemCharges[recipe.itemid] then
createdItem:setAttribute(ITEM_ATTRIBUTE_CHARGES, itemCharges[recipe.itemid])
end
end
local totalCreated = amount * (recipe.quant or 1)
local texto = "You crafted " .. totalCreated .. "x " .. recipe.name .. "!"
local itemId = recipe.itemid
player:showTextDialog(itemId, texto)
return false
end
craftFence:separator(" ")
craftFence:register()
👌 The system is now installed. Now I’m going to teach you how it works!
To add a new category, go to the config.lua file inside the crafting_table folder in the client.
Right below, in tutorialsInfo = {, you'll find several lines like this.
{name = "Brown Fance", id = 10001, material = {'Wood'}, text = "[ENG]: A simple straight fence for marking areas. When used, it transforms into a different shape."},
🧠 Meaning of Each Part:
name = "Brown Fence"
→ The name of the item. It will be displayed in the UI or tooltip.
id = 10001
→ A unique ID that identifies this item in the system.
material = {'Wood'}
→ The materials required to craft this item. In this case, only 'Wood' is needed.
text = "Text Here"
→ The item description in two languages:
The ID must always be unique because it is sent via talkaction to our script in order to create the item.
🚨Each key represents the order in which it appears in the categories. 'Construction' refers to the items that will appear inside it, except for the ones listed in the first key.
'Whenever you create a new item, the ID must be unique because we will use it in the talkaction.'
tutorialsIndex = {"Construction", "Tools", "Box's", "Grounds", "Building Materials", "Traps"}
-- Cores das categorias (você pode editar aqui)
categoryColors = {
["Construction"] = "black",
["Tools"] = "black",
["Box's"] = "black",
["Grounds"] = "black",
["Building Materials"] = "black",
["Traps"] = "black"
}
-- Cor padrão para categorias não definidas
defaultCategoryColor = "#ffffff"
tutorialsInfo = {
-- Construction
{
{name = "Brown Fance", id = 10001, material = {'Wood'}, text = "[ENG]: A simple straight fence for marking areas. When used, it transforms into a different shape.\n[BRA]: Uma cerca reta simples para demarcar areas. Quando usada, ela se transforma em uma forma diferente."},
{name = "Horizontal Farm Door", id = 10002, material = {'8 Wood'}, text = "[ENG]: A horizontal door for your farm. When used, it opens and closes.\n[BRA]: Uma porta horizontal para sua fazenda. Quando usada, ela abre e fecha."},
{name = "Vertical Farm Door", id = 10003, material = {'8 Wood'}, text = "[ENG]: A vertical door for your farm. When used, it opens and closes.\n[BRA]: Uma porta vertical para sua fazenda. Quando usada, ela abre e fecha."},
},
-- Tools
{
{name = "Watering Can", id = 10004, material = {'8 Bucket'}, text = "[ENG]: A watering can with 90 charges.\n[BRA]: Um regador com 90 cargas."},
{name = "Wood Pick", id = 10005, material = {'2 Refined Wood', '3 Wood'}, text = "[ENG]: A watering can with 90 charges.\n[BRA]: Um regador com 90 cargas."},
{name = "Spray", id = 10010, material = {'1 Spray Empyt', '20 Nim'}, text = "[ENG]: Mix it all and you get the poison to kill the pests.\n[BRA]: Misturando tudo, voce consegue o veneno para matar as pestes."},
},
-- Box's
{
{name = "Cow Box", id = 10011, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with cow face design (20 slots).\n[BRA]: Uma caixa com aparencia de vaca (20 slots)."},
{name = "Chicken Box", id = 10012, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with chicken face design (20 slots).\n[BRA]: Uma caixa com aparencia de galinha (20 slots)."},
{name = "Fish Box", id = 10013, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with fish design (20 slots).\n[BRA]: Uma caixa com aparencia de peixe (20 slots)."},
{name = "Seeds Box", id = 10014, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with seed pattern (20 slots).\n[BRA]: Uma caixa com aparencia de sementes (20 slots)."},
{name = "Box", id = 10015, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A simple wooden box (20 slots).\n[BRA]: Uma caixa simples de madeira (20 slots)."},
{name = "Pumpkin Box", id = 10016, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with pumpkin design (20 slots).\n[BRA]: Uma caixa com aparencia de abobora (20 slots)."},
{name = "Tool Box", id = 10017, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A box with tool design (20 slots).\n[BRA]: Uma caixa com aparencia de ferramentas (20 slots)."},
{name = "Changin Box", id = 10018, material = {'5 Nails', '4 Refined Wood', '5 board'}, text = "[ENG]: A customizable box (20 slots).\n[BRA]: Uma caixa personalizavel (20 slots)."}
},
-- Grounds
{
{name = "Stone Floor", id = 10008, material = {'10 Stone Fragment'}, text = "[ENG]: A beautiful Stone floor, perfect for elegant constructions.\n[BRA]: Um lindo piso de pedras, ideal para construcoes elegantes."},
},
-- Building Materials
{
{name = "Refined Wood", id = 10006, material = {'2 Wood'}, text = "[ENG]: High-quality wood, perfect for crafting furniture and tools.\n[BRA]: Madeira de alta qualidade, perfeita para criar moveis e ferramentas."},
{name = "Board", id = 10007, material = {'4 Refined Wood'}, text = "[ENG]: A strong and versatile board, perfect for any kind of construction or crafting.\n[BRA]: Uma tabua resistente e versatil, perfeita para qualquer tipo de construcao ou criacao."},
{name = "Nail", id = 10019, material = {'1 Iron Fragment'}, text = "[ENG]: A simple nail, essential for woodworking and construction. (2 items)\n[BRA]: Um prego simples, essencial para marcenaria e construcao. (2 itens)"}
},
-- Traps
{
{name = "Chiken Trap", id = 10009, material = {'20 Iron Fragment', '10 Board'}, text = "[ENG]: A trap designed to catch chickens. Set it and wait for the cluck!\n[BRA]: Uma armadilha feita para capturar galinhas. Arme e espere o cocorico!"},
}
}
When the client sends the talkaction to the server, it sends something like:!craft 10001, 2where 10001 is the unique ID and 2 is the quantity.
You might be wondering: if a player just pastes this command, will they be able to craft the item anywhere? I thought about that — the server performs a check first to verify if the player is next to the item that was right-clicked to activate the crafting system!
Now let's move on to the talkaction part on the server, in the file crafting_table.lua
Here, in the materials key, you can see the materials used for crafting — to be more specific, it’s composed of the item name and its ID.
local materials = {
wood = {id = 5901, name = "Wood"},
bucket = {id = 26385, name = "Bucket"},
iron_fragment = {id = 26491, name = "Iron Fragment"},
gold = {id = 26401, name = "Gold"},
stone_fragments = {id = 26489, name = "Stone"},
leather = {id = 26403, name = "Leather"},
refined_wood = {id = 26498, name = "Refined Wood"},
board = {id = 26499, name = "Board"},
spray_empyt = {id = 26546, name = "Spray Empyt"},
nim = {id = 26544, name = "Nim"},
nail = {id = 8309, name = "Nail"},
}
Because I used some tools that have a charge system, like the watering can and pickaxe, I added this section.
-- Configuração de cargas para itens que precisam
local itemCharges = {
[26470] = 90, -- Regador Vazio
[26484] = 50, -- Wood Pick
[26539] = 100, -- spray
}
Once the item is crafted, it receives a charge.
You can simply ignore this part or leave a comment if you prefer.
Here, in local recipes = {, you can see the recipe to craft an item.
The item ID given to the player if they have the required materials
26454
name
A name for you to identify what the item ID represents
"Brown Fence"
level
The player level required to craft the item
1
quant
The quantity of the item
(with the given itemid)
that will be given to the player
2
Here you define which item must be around the player so that the mod cannot be opened anywhere.
local function isNearWorkbench(player)
local pos = player:getPosition()
local workbenchIds = {26451, 26452} -- Item Here <--------
-- Verifica em um raio de 1 SQM ao redor do jogador
for x = -1, 1 do
for y = -1, 1 do
local checkPos = Position(pos.x + x, pos.y + y, pos.z)
local tile = Tile(checkPos)
if tile then
for _, item in ipairs(tile:getItems()) do
if table.contains(workbenchIds, item:getId()) then
return true
end
end
end
end
end
return false
end
I used an item that cannot be moved, fixed in the game.
I hope this helps anyone who needs it! 😄
If you'd like to support me, here are my details: