Olá pessoal do TibiaKing, como na maioria das vezes estou tirando dúvidas e pedindo scripts, vi a oportunidade de fazer a postagem desse tutorial para inserir um Simple Crafting System dentro do seu otserver para TFS 1.2 / 1.3.
De quem são os créditos?
- Sistema retirado do otland
Modal Window por Non Sequitur
Simple Crafting System por strutZ
Porque não pegar direto lá?
- Porque a postagem no otland está picada em partes, eu mesmo demorei um bom tempo até juntar todas as partes, além dos items utilizados no script não estão 100% de acordo com alguns OTServers recentes, entao utilizei um padrão. Eu simplifiquei para o uso e escrevi o tutorial em português.
Onde foi testado?
- The Forgetten Base (TFS 1.3) fucionando 100%
Imagens do sistema
TUTORIAL
Será dividido em duas partes.
• Na primeira vamos instalar uma biblioteca de Janelas para seu otserver, pois não tem um padrão simples que possa ser utilizado.
• Na segunda parte instalar o sistema de crafting.
PARTE 1
Instalação da biblioteca de ModalWindow.
(1.1) Segundo Non Sequitur, existe uma grande dificuldade em ter que criar diversos códigos para fazer as Modais de Janelas (ModalWindow) funcionarem nas versões mais recentes do tibia, e isso não é bom, porém é uma solução sem erros Ok.
(1.2) Existem diversas funções para utilizar os Modais de Janela (ModalWindow) desde modalWindow:addButtons(...) ou modalWindow:addChoices(...), porém não vou me prolongar sobre o assunto da modal, pois o objetivo é que funcione o sistema de crafting.
(2) INSTALAÇÃO
(2.1) Na pasta data/lib/ abra o arquivo lib.lua
(2.2) Insira o seguinte código
-- Modal window
dofile('data/lib/modalwindow.lua')
(2.3) Na mesma pasta lib, crie um arquivo chamado modalwindow.lua e adicione o código abaixo no arquivo
if not modalWindows then
modalWindows = {
modalWindowConstructor = ModalWindow,
nextFreeId = 500,
windows = {}
}
end
local MT = {}
MT.__index = MT
function ModalWindow(...)
local args = {...}
if type(args[1]) == 'table' then
local self = setmetatable(args[1], MT)
local id = modalWindows.nextFreeId
self.id = id
self.buttons = {}
self.choices = {}
self.players = {}
self.created = false
modalWindows.nextFreeId = id + 1
table.insert(modalWindows.windows, self)
return self
end
return modalWindows.modalWindowConstructor(...)
end
function MT:setDefaultCallback(callback)
self.defaultCallback = callback
end
function MT:addButton(text, callback)
local button = {text = tostring(text), callback = callback}
table.insert(self.buttons, button)
return button
end
function MT:addButtons(...)
for _, text in ipairs({...}) do
table.insert(self.buttons, {text = tostring(text)})
end
end
function MT:addChoice(text)
local choice = {text = tostring(text)}
table.insert(self.choices, choice)
return choice
end
function MT:addChoices(...)
for _, text in ipairs({...}) do
table.insert(self.choices, {text = tostring(text)})
end
end
function MT:setDefaultEnterButton(text)
self.defaultEnterButton = text
end
function MT:setDefaultEscapeButton(text)
self.defaultEscapeButton = text
end
function MT:setTitle(title)
self.title = tostring(title)
end
function MT:setMessage(message)
self.message = tostring(message)
end
local buttonOrder = {
[4] = {3, 4, 2, 1},
[3] = {2, 3, 1},
[2] = {1, 2},
[1] = {1}
}
function MT:create()
local modalWindow = modalWindows.modalWindowConstructor(self.id, self.title, self.message)
local order = buttonOrder[math.min(#self.buttons, 4)]
if order then
for _, i in ipairs(order) do
local button = self.buttons[i]
modalWindow:addButton(i, button.text)
button.id = i
if button.text == self.defaultEnterButton then
modalWindow:setDefaultEnterButton(i)
elseif button.text == self.defaultEscapeButton then
modalWindow:setDefaultEscapeButton(i)
end
end
end
for _, choice in ipairs(self.choices) do
modalWindow:addChoice(_, choice.text)
choice.id = _
end
self.modalWindow = modalWindow
end
function MT:sendToPlayer(player)
if not self.modalWindow then
self:create()
end
player:registerEvent('ModalWindowHelper')
self.players[player:getId()] = true
return self.modalWindow:sendToPlayer(player)
end
(2.4) Agora em data/creaturescript/creaturescript.xml adicione
<event type="modalwindow" name="ModalWindowHelper" script="modalwindowhelper.lua" />
(2.5) Em data/creaturescript/scripts/ crie um arquivo lua chamado modalwindowhelper com o seguinte código
function onModalWindow(player, modalWindowId, buttonId, choiceId)
local modalWindow
for _, window in ipairs(modalWindows.windows) do
if window.id == modalWindowId then
modalWindow = window
break
end
end
if not modalWindow then
return true
end
local playerId = player:getId()
if not modalWindow.players[playerId] then
return true
end
modalWindow.players[playerId] = nil
local choice = modalWindow.choices[choiceId]
for _, button in ipairs(modalWindow.buttons) do
if button.id == buttonId then
local callback = button.callback or modalWindow.defaultCallback
if callback then
callback(button, choice)
break
end
end
end
return true
end
(2.6) PRONTO, AS MODAIS DE JANELA ESTÃO INSTALADAS E PRONTAS PARA USO! VAMOS A PARTE 2 COM A INSTALAÇÃO DO SIMPLE CRAFTING SYSTEM!
PARTE 2
Instalação do Simple Crafting System
(1.1) Existem alguns sistemas de crafting porém são mais complexos. Este sistema feito por strutZ é super simples e altamente configurável baseado nas suas necessidades! Apenas adicionando os scripts de actions e os de lib. Não há necessidade de registrar nada em login.lua ou em creaturescript.
(2) INFORMAÇÕES DO SISTEMA
Tem uma seção de configuração bem simples onde você pode colocar os items que deseja que sejam craftados e também os items necessários para craftar. Também a personalização dos textos que aparecem nas janelas modais que já foram instaladas na Parte 1.
• Abaixo alguma explicação sobre as configurações do código após ter instalado (para iniciar a instalação vamos ao (2.1)
local config = {
-- Configuracao da Janela Modal
mainTitleMsg = "Crafting System", -- TITUTLO DA JANELA QUE ABRE
mainMsg = "Welcome to the crafting system. Please choose a vocation to begin.", -- MENSAGEM DA JANELA
craftTitle = "Crafting System: ", -- Titulo da tela de craft apos o player selecionar a vocacao destinada dos items
craftMsg = "Here is a list of all items that can be crafted for the ", -- Mensagem na tela de craft após escolhas do jogador de vocação
-- Fim da config Janela Modal
-- Notificacoes ao player
needItems = "You do not have all the required items to make ", -- Mensagem que aparece quando o player tenta craftar e nao tem todos os items
-- Configuracoes de Crafting
system = {
[1] = {vocation = "Master Wizard", -- Isto e apenas uma categoria, nao influencia em nada
items = {
[1] = {item = "arcane staff", -- item name (O NOME TEM QUE SER EXATO, SENAO NAO FUNCIONA!)
itemID = 2453, -- ID do item que sera feito, tem que ser compativel com o nome acima
reqItems = { -- items e quantidade dos items necessarios para craftar
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
(2.1) Primeiramente vamos a pasta data/actions/actions.xml e adicionar a seguinte linha. O ITEMID é o ID do item que você deseja que seja utilizado para craftar. (no meu caso para teste das fotos usei o item id 8300, mas você pode colocar o que desejar)
<action itemid="ITEMID" script="crafting.lua"/>
(2.2) Ai em data/actions/scripts/ crie um arquivo lua chamado crafting com o seguinte código
local config = {
-- Configuracao da Janela Modal
mainTitleMsg = "Crafting System", -- TITUTLO DA JANELA QUE ABRE
mainMsg = "Welcome to the crafting system. Please choose a vocation to begin.", -- MENSAGEM DA JANELA
craftTitle = "Crafting System: ", -- Titulo da tela de craft apos o player selecionar a vocacao destinada dos items
craftMsg = "Here is a list of all items that can be crafted for the ", -- Mensagem na tela de craft após escolhas do jogador de vocação
-- Fim da config Janela Modal
-- Notificacoes ao player
needItems = "You do not have all the required items to make ", -- Mensagem que aparece quando o player tenta craftar e nao tem todos os items
-- Configuracoes de Crafting
system = {
[1] = {vocation = "Master Wizard", -- Isto e apenas uma categoria, nao influencia em nada
items = {
[1] = {item = "arcane staff", -- item name (O NOME TEM QUE SER EXATO, SENAO NAO FUNCIONA!)
itemID = 2453, -- ID do item que sera feito, tem que ser compativel com o nome acima
reqItems = { -- items e quantidade dos items necessarios para craftar
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
[2] = {item = "enchanted spear",
itemID = 7367,
reqItems = {
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
},
},
[2] = {vocation = "Elder Wizard",
items = {
[1] = {item = "arcane staff",
itemID = 2453,
reqItems = {
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
[2] = {item = "enchanted spear",
itemID = 7367,
reqItems = {
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
},
},
[3] = {vocation = "Swift Archer",
items = {
[1] = {item = "arcane staff",
itemID = 2453,
reqItems = {
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
[2] = {item = "enchanted spear",
itemID = 7367,
reqItems = {
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
},
},
[4] = {vocation = "Guardian Warrior",
items = {
[1] = {item = "arcane staff",
itemID = 2453,
reqItems = {
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
[2] = {item = "enchanted spear",
itemID = 7367,
reqItems = {
[1] = {item = 9969, count = 1}, -- Black Skull
[2] = {item = 5904, count = 30}, -- Magic Sulphur
},
},
},
},
},
}
local player = Player(cid)
function onUse(player, item, fromPosition, itemEx, toPosition, isHotkey)
player:sendMainCraftWindow(config)
return true
end
OBS: Coloquei os mesmos items e matéria prima para todas as categorias, para facilitar a edição e compreensão do código.
(2.3) Ai na pasta data/ abra o arquivo global.lua e adicionei no início o seguinte código, salve e mantenha o arquivo aberto.
dofile('data/lib/crafting.lua')
(2.3.1) No mesmo arquivo global.lua insira após o último end
function capAll(str)
local newStr = ""; wordSeparate = string.gmatch(str, "([^%s]+)")
for v in wordSeparate do
v = v:gsub("^%l", string.upper)
if newStr ~= "" then
newStr = newStr.." "..v
else
newStr = v
end
end
return newStr
end
(2.4) Em data/lib/ crie um arquivo lua chamado crafting e adicione o seguinte código dentro
-- Main Crafting Window -- This is the modal window that is displayed first
function Player:sendMainCraftWindow(config)
local function buttonCallback(button, choice)
-- Modal Window Functionallity
if button.text == "Select" then
self:sendVocCraftWindow(config, choice.id)
end
end
-- Modal window design
local window = ModalWindow {
title = config.mainTitleMsg, -- Title of the main craft modal window
message = config.mainMsg.."\n\n" -- Message of the main craft modal window
}
-- Add buttons to the window (Note: if you change the names of these you must change the functions in the modal window functionallity!)
window:addButton("Select", buttonCallback)
window:addButton("Exit", buttonCallback)
-- Add choices from the action script
for i = 1, #config.system do
window:addChoice(config.system[i].vocation)
end
-- Set what button is pressed when the player presses enter or escape.
window:setDefaultEnterButton("Select")
window:setDefaultEscapeButton("Exit")
-- Send the window to player
window:sendToPlayer(self)
end
-- End of the first modal window
-- This is the modal window that displays all avalible items for the chosen vocation.
function Player:sendVocCraftWindow(config, lastChoice)
local function buttonCallback(button, choice)
-- Modal Window Functionallity
-- If the user presses the back button they will be redirected to the main window.
if button.text == "Back" then
self:sendMainCraftWindow(config)
end
-- If the user presses the details button they will be redirected to a text window with information about the item they want to craft.
if button.text == "Details" then
local item = config.system[lastChoice].items[choice.id].item
local details = "In order to craft "..item.." you must collect the following items.\n\nRequired Items:"
for i = 1, #config.system[lastChoice].items[choice.id].reqItems do
local reqItems = config.system[lastChoice].items[choice.id].reqItems[i].item
local reqItemsCount = config.system[lastChoice].items[choice.id].reqItems[i].count
local reqItemsOnPlayer = self:getItemCount(config.system[lastChoice].items[choice.id].reqItems[i].item)
details = details.."\n- "..capAll(getItemName(reqItems).." ["..reqItemsOnPlayer.."/"..reqItemsCount.."]")
end
self:showTextDialog(item, details)
self:sendVocCraftWindow(config, lastChoice)
end
-- if the player presses the craft button then begin checks.
if button.text == "Craft" then
-- Check if player has required items to craft the item. If they dont send needItems message.
for i = 1, #config.system[lastChoice].items[choice.id].reqItems do
if self:getItemCount(config.system[lastChoice].items[choice.id].reqItems[i].item) < config.system[lastChoice].items[choice.id].reqItems[i].count then
self:say(config.needItems..config.system[lastChoice].items[choice.id].item, TALKTYPE_MONSTER_SAY)
return false
end
end
-- Remove the required items and there count from the player.
for i = 1, #config.system[lastChoice].items[choice.id].reqItems do
self:removeItem(config.system[lastChoice].items[choice.id].reqItems[i].item, config.system[lastChoice].items[choice.id].reqItems[i].count)
end
-- Send effect and give player item.
self:addItem(config.system[lastChoice].items[choice.id].itemID)
self:getPosition():sendMagicEffect(CONST_ME_FIREATTACK)
end
end
-- Modal window design
local window = ModalWindow {
title = config.craftTitle..config.system[lastChoice].vocation, -- The title of the vocation specific window
message = config.craftMsg..config.system[lastChoice].vocation..".\n\n", -- The message of the vocation specific window
}
-- Add buttons to the window (Note: if you change the names of these you must change the functions in the modal window functionallity!)
window:addButton("Back", buttonCallback)
window:addButton("Exit")
window:addButton("Details", buttonCallback)
window:addButton("Craft", buttonCallback)
-- Set what button is pressed when the player presses enter or escape
window:setDefaultEnterButton("Craft")
window:setDefaultEscapeButton("Exit")
-- Add choices from the action script
for i = 1, #config.system[lastChoice].items do
window:addChoice(config.system[lastChoice].items[i].item)
end
-- Send the window to player
window:sendToPlayer(self)
end
PRONTO! É apenas isso, seu sistema de crafting está 100% funcional se você fizer tudo corretamente. Ele está pronto para ser personalizado, onde você pode adicionar as categorias que quiser e os itens que quiser para craftar, tanto como as matérias primas.
Espero ter ajudado, nada aqui é de minha autoria, apenas minha edição para facilitar a compreensão e junção de todos os scripts em um post único para ter o sistema 100% funcional.
Fico a disposição e boa sorte!