1. Introdução
Lua é uma linguagem de programação poderosa e flexível que pode ser usada em uma ampla variedade de aplicações, incluindo jogos online. É uma das linguagens mais utilizadas em servidores de jogos online, incluindo OT (Open Tibia) Servers.
Este guia tem como objetivo fornecer uma introdução ao básico de programação em Lua, focando na programação em OT Servers. Vamos cobrir desde os conceitos básicos da linguagem até a implementação de eventos do servidor.
Se você é um iniciante em programação ou apenas deseja expandir seus conhecimentos sobre Lua, este guia é para você. Vamos começar pelos conceitos básicos da programação em Lua.
2. Conceitos básicos de programação em Lua
Lua é uma linguagem de programação de alto nível que é simples e fácil de aprender. Ela foi projetada para ser uma linguagem extensível e é frequentemente usada em jogos e outros tipos de aplicativos que requerem uma alta velocidade de processamento e recursos de memória limitados.
Aqui estão alguns dos conceitos básicos que você precisa saber para começar a programar em Lua:
- Comentários: Você pode inserir comentários em seu código para explicar o que está acontecendo em seu programa. Comentários começam com “–” e continuam até o final da linha.
- Variáveis: As variáveis são usadas para armazenar dados em seu programa. Em Lua, as variáveis não precisam ser declaradas com um tipo específico e podem ser alteradas a qualquer momento.
- Operadores: Os operadores são usados para executar cálculos em seus dados. Existem operadores aritméticos (+, -, *, /) e operadores de comparação (==, ~=, <, >, <=, >=).
- Estruturas de controle de fluxo: As estruturas de controle de fluxo são usadas para controlar o fluxo de execução em seu programa. Lua suporta estruturas de controle de fluxo como if, else, elseif, for, while e repeat-until.
- Funções: As funções são usadas para executar tarefas específicas em seu programa. As funções em Lua são definidas usando a palavra-chave “function”.
- Tabelas: As tabelas são usadas para armazenar dados em uma estrutura de dados que pode ser acessada por um índice ou chave. Em Lua, as tabelas podem armazenar outros tipos de dados, como funções e outras tabelas.
Entender esses conceitos básicos é fundamental para começar a programar em Lua. Com essas ferramentas em mãos, você pode começar a criar scripts para o seu servidor OT.
2.1. Variáveis
Em Lua, as variáveis são declaradas usando a palavra-chave local
ou global
. A diferença entre elas é que as variáveis locais são acessíveis somente dentro do escopo onde foram definidas, enquanto as globais podem ser acessadas de qualquer lugar do código.
Vamos ver um exemplo de declaração de variáveis:
-- variável local
local meuNumero = 42
-- variável global
meuTexto = "Olá, mundo!"
Neste exemplo, a variável meuNumero
é local e só pode ser acessada dentro do escopo onde foi definida. Já a variável meuTexto
é global e pode ser acessada de qualquer lugar no código.
É importante tomar cuidado com o uso de variáveis globais, pois elas podem causar efeitos colaterais inesperados no código e dificultar a manutenção do sistema.
Além disso, em Lua, as variáveis não possuem um tipo definido. O tipo de uma variável é determinado automaticamente de acordo com o valor atribuído a ela. Por exemplo:
local meuNumero = 42 -- tipo number
local meuTexto = "Olá, mundo!" -- tipo string
local minhaTabela = {} -- tipo table
Na próxima seção, veremos os operadores em Lua.
2.2. Operadores
Na seção anterior, discutimos sobre variáveis em Lua e como elas podem ser usadas em seus programas. Agora, vamos dar uma olhada nos operadores disponíveis em Lua.
Em programação, operadores são símbolos que realizam uma ação em cima de um ou mais valores, produzindo um novo valor. Lua possui diversos operadores aritméticos, de comparação, lógicos e de concatenação de strings.
Aritméticos:
+
adição-
subtração*
multiplicação/
divisão%
resto da divisão^
potenciação
Comparação:
==
igualdade~=
diferença<
menor que>
maior que<=
menor ou igual que>=
maior ou igual que
Lógicos:
and
e lógicoor
ou lógiconot
negação lógica
Concatenação de strings:
..
concatenação de duas strings
É importante lembrar que, assim como em matemática, a ordem de precedência dos operadores aritméticos é respeitada em Lua. Por exemplo, em uma expressão com as operações *
e +
, o *
será realizado antes do +
. Caso você precise forçar a ordem de execução, você pode utilizar parênteses.
Além disso, os operadores de comparação podem ser usados em expressões lógicas, como em estruturas de controle de fluxo (que discutiremos mais adiante).
Vamos ver alguns exemplos de uso de operadores em Lua:
-- Operadores aritméticos
a = 10
b = 5
c = a + b -- c = 15
d = a * b -- d = 50
e = a / b -- e = 2
f = a % b -- f = 0
g = a ^ b -- g = 100000
-- Operadores de comparação
x = 10
y = 20
z = x < y -- z = true
w = x >= y -- w = false
-- Operadores lógicos
p = true
q = false
r = not p -- r = false
s = p and q -- s = false
t = p or q -- t = true
-- Concatenação de strings
nome = "John"
sobrenome = "Doe"
nome_completo = nome .. " " .. sobrenome -- nome_completo = "John Doe"
Pratique utilizando os operadores em seus programas para melhor fixação do conteúdo!
2.3. Estruturas de controle de fluxo
As estruturas de controle de fluxo são ferramentas importantes para controlar o fluxo do seu programa, permitindo que você tome decisões e execute código com base em condições específicas. Em Lua, temos três tipos principais de estruturas de controle de fluxo: if-then-else
, while
e for
.
if-then-else
A estrutura if-then-else
permite que você execute uma ação se uma determinada condição for atendida, e outra ação se não for. Aqui está um exemplo simples:
local a = 10
if a > 5 then
print("a é maior que 5")
else
print("a é menor ou igual a 5")
end
Neste exemplo, a variável a
é definida como 10. Em seguida, o programa verifica se a
é maior que 5. Se sim, ele imprime “a é maior que 5”. Se não, ele imprime “a é menor ou igual a 5”. A palavra-chave else
é opcional e só é executada se a condição do if
não for atendida.
while
A estrutura while
permite que você execute um bloco de código enquanto uma determinada condição for verdadeira. Aqui está um exemplo:
local i = 1
while i <= 10 do
print(i)
i = i + 1
end
Neste exemplo, a variável i
é definida como 1. Em seguida, o programa entra em um loop while
que é executado enquanto i
for menor ou igual a 10. Dentro do loop, o programa imprime o valor atual de i
e incrementa i
em 1. O loop continua a ser executado até que i
seja maior que 10.
for
A estrutura for
é semelhante à estrutura while
, mas é mais adequada para loops que iteram em uma sequência. Aqui está um exemplo:
for i = 1, 10 do
print(i)
end
Neste exemplo, o loop for
começa com i
definido como 1 e continua até que i
seja igual a 10. A cada iteração do loop, o programa imprime o valor atual de i
. Note que a palavra-chave do
é usada para indicar o início do bloco de código a ser executado em cada iteração.
Com essas estruturas de controle de fluxo em Lua, você pode tomar decisões com base em condições específicas e criar loops que permitem que seu programa repita o código enquanto as condições são atendidas. Na próxima seção, falaremos sobre funções em Lua.
2.4. Funções
Funções são blocos de código que executam uma tarefa específica. Elas são muito úteis para evitar repetições de código e tornar o código mais organizado e legível.
Em Lua, as funções podem receber argumentos e retornar valores. Veja um exemplo de uma função simples:
function soma(a, b)
return a + b
end
Essa função recebe dois argumentos, a
e b
, e retorna a soma deles. Para chamar essa função e imprimir o resultado, basta fazer o seguinte:
print(soma(3, 5)) -- imprime 8
Também é possível criar funções anônimas, que não possuem nome e são utilizadas apenas em determinado trecho do código. Essas funções podem ser atribuídas a variáveis e passadas como argumentos para outras funções. Veja um exemplo:
-- atribui uma função anônima à variável "dobro"
dobro = function(x)
return x * 2
end
-- chama a função "dobro" e imprime o resultado
print(dobro(5)) -- imprime 10
-- define uma função que recebe uma função como argumento e a chama
function executa_funcao(funcao, argumento)
return funcao(argumento)
end
-- chama a função "executa_funcao" com a função "dobro" como argumento
print(executa_funcao(dobro, 4)) -- imprime 8
Além disso, em Lua é possível ter múltiplos valores de retorno em uma função. Para isso, basta separar os valores com vírgulas na hora de fazer o retorno. Veja um exemplo:
-- define uma função que retorna o quadrado e o cubo de um número
function quadrado_e_cubo(x)
return x^2, x^3
end
-- chama a função e imprime os resultados
a, b = quadrado_e_cubo(2)
print(a) -- imprime 4
print(b) -- imprime 8
Essa função retorna dois valores: o quadrado e o cubo do número passado como argumento. Quando a função é chamada e seus valores de retorno são atribuídos às variáveis a
e b
, cada valor é armazenado em uma variável separada.
2.5. Tabelas
Tabelas são uma estrutura de dados poderosa em Lua, permitindo que você crie arrays, dicionários e outras estruturas de dados complexas. Em Lua, tabelas são usadas para representar quase tudo – desde matrizes simples até objetos complexos.
A sintaxe para criar uma tabela é simples:
tabela = {}
Você pode inicializar a tabela com valores:
tabela = {1, 2, 3}
Tabelas em Lua não têm tamanho fixo e podem ser modificadas a qualquer momento. Você pode adicionar elementos a uma tabela usando o operador de concatenação ..
ou o operador table.insert
. Você pode remover elementos de uma tabela usando o operador table.remove
.
-- Adicionando elementos a uma tabela
tabela = {1, 2, 3}
tabela[4] = 4
tabela[5] = 5
tabela[#tabela + 1] = 6 -- usando o operador de concatenação para adicionar 6
-- Removendo elementos de uma tabela
table.remove(tabela, 5) -- remove o quinto elemento (6)
Você pode acessar os elementos de uma tabela usando colchetes []
. Se você quiser acessar um valor dentro da tabela que está dentro de outra tabela, basta usar colchetes adicionais.
tabela = {
jogador1 = {
nome = "João",
nivel = 10
},
jogador2 = {
nome = "Maria",
nivel = 12
}
}
print(tabela.jogador1.nome) -- imprime "João"
print(tabela.jogador2.nivel) -- imprime 12
As tabelas também podem ser usadas como estruturas de dados para armazenar informações complexas. Por exemplo, você pode usar uma tabela para armazenar informações sobre um item do jogo.
item = {
nome = "Espada longa",
tipo = "arma",
atributos = {
ataque = 10,
defesa = 5
}
}
print(item.nome) -- imprime "Espada longa"
print(item.atributos.ataque) -- imprime 10
Em Lua, as tabelas são passadas por referência, o que significa que, se você passar uma tabela para uma função, qualquer alteração feita na tabela dentro da função afetará a tabela original.
function aumentarAtaque(item, valor)
item.atributos.ataque = item.atributos.ataque + valor
end
item = {
nome = "Espada longa",
tipo = "arma",
atributos = {
ataque = 10,
defesa = 5
}
}
print(item.atributos.ataque) -- imprime 10
aumentarAtaque(item, 5)
print(item.atributos.ataque) -- imprime 15
Tabelas em Lua podem ser poderosas e versáteis. Quando combinadas com outras funcionalidades da linguagem, como funções, elas podem ser usadas para criar estruturas de dados complexas e sistemas de jogos eficientes.
3. Programação em OT Servers com Lua
Lua é uma das linguagens de script mais utilizadas em OT Servers para a criação de NPCs, manipulação de itens, implementação de quests, entre outras funcionalidades. A seguir, vamos ver alguns exemplos de como Lua é utilizada em OT Server.
3.1. Scripting de NPC
Os NPC’s são elementos fundamentais em qualquer OT Server e, portanto, é crucial saber como manipulá-los. A programação de NPC’s em Lua envolve a criação de scripts para controlar o comportamento do NPC. Vamos ver alguns exemplos de como isso pode ser feito.
onCreatureAppear
A função onCreatureAppear
é acionada sempre que um personagem aparece na frente do NPC. É comumente usado para exibir uma mensagem de boas-vindas ou iniciar uma conversa com o personagem. O exemplo a seguir mostra como exibir uma mensagem de boas-vindas para o personagem:
function onCreatureAppear(cid)
selfSay("Bem-vindo ao meu estabelecimento, " .. getPlayerName(cid) .. "!", cid)
end
onCreatureDisappear
A função onCreatureDisappear
é acionada sempre que um personagem desaparece da frente do NPC. É comumente usado para interromper qualquer conversa em andamento ou fazer com que o NPC volte a seu comportamento normal. O exemplo a seguir mostra como interromper uma conversa em andamento:
function onCreatureDisappear(cid)
selfSay("Volte logo, " .. getPlayerName(cid) .. "!", cid)
end
onPlayerCloseChannel
A função onPlayerCloseChannel
é acionada sempre que um jogador fecha uma conversa com o NPC. É comumente usado para interromper qualquer conversa em andamento ou fazer com que o NPC volte a seu comportamento normal. O exemplo a seguir mostra como interromper uma conversa em andamento:
function onPlayerCloseChannel(cid)
selfSay("Até a próxima, " .. getPlayerName(cid) .. "!", cid)
end
onPlayerBuyItem
A função onPlayerBuyItem
é acionada sempre que um jogador compra um item do NPC. É comumente usado para executar a lógica do negócio de compra, como verificar o preço do item e verificar se o jogador tem dinheiro suficiente. O exemplo a seguir mostra como executar a lógica de compra de um item:
function onPlayerBuyItem(cid, item, subType, amount, ignoreCap, inBackpacks)
local price = getItemPrice(item, subType)
if getPlayerMoney(cid) < price * amount then
selfSay("Você não tem dinheiro suficiente para comprar este item!", cid)
else
doPlayerRemoveMoney(cid, price * amount)
doPlayerAddItem(cid, item, amount)
selfSay("Obrigado por sua compra!", cid)
end
end
Certo, vamos continuar com alguns exemplos de como usar as funções do TFS para criar scripts de NPC:
-- Script básico de saudação de um NPC
function onCreatureAppear(cid)
selfSay("Olá, seja bem-vindo!", cid)
end
function onCreatureDisappear(cid)
-- nada acontece quando o jogador sai de perto do NPC
end
function onCreatureSay(cid, type, msg)
-- verifique se o jogador falou "hi" ou "ola" para o NPC
if (string.lower(msg) == "hi" or string.lower(msg) == "ola") then
-- o NPC responde com "Olá!"
selfSay("Olá!", cid)
end
end
Esse é um exemplo simples de como um NPC pode saudar um jogador e responder a uma saudação. Na primeira função, onCreatureAppear
, o NPC saúda o jogador quando ele aparece na tela. Na segunda função, onCreatureDisappear
, nada acontece quando o jogador sai de perto do NPC. Na terceira função, onCreatureSay
, o NPC verifica se o jogador falou “hi” ou “ola” e responde com “Olá!”.
Agora, vamos criar um NPC que vende um item específico quando o jogador clica nele:
-- Script de venda de item de um NPC
local config = {
itemId = 2160, -- ID do item que o NPC vende
itemPrice = 100, -- preço do item
itemCount = 1, -- quantidade de itens que o jogador recebe quando compra
storageValue = 100, -- valor de storage que é alterado quando o jogador compra
message = "Você comprou uma poção de mana!" -- mensagem que o NPC fala quando o jogador compra
}
function onCreatureAppear(cid)
selfSay("Olá, eu vendo poções de mana por " .. config.itemPrice .. " gold coins cada.", cid)
end
function onCreatureSay(cid, type, msg)
-- verifique se o jogador falou "comprar" para o NPC
if (string.lower(msg) == "comprar") then
-- verifique se o jogador tem gold coins suficientes para comprar o item
if (getPlayerMoney(cid) >= config.itemPrice) then
-- remova o dinheiro do jogador
doPlayerRemoveMoney(cid, config.itemPrice)
-- adicione o item ao inventário do jogador
doPlayerAddItem(cid, config.itemId, config.itemCount)
-- altere o valor de storage do jogador
setPlayerStorageValue(cid, config.storageValue, 1)
-- fale uma mensagem ao jogador
selfSay(config.message, cid)
else
-- o jogador não tem gold coins suficientes, fale uma mensagem de erro
selfSay("Você não tem gold coins suficientes para comprar este item.", cid)
end
end
end
Nesse exemplo, o NPC vende uma poção de mana por 100 gold coins cada. Quando o jogador clica no NPC e fala “comprar”, o NPC verifica se o jogador tem gold coins suficientes para comprar o item. Se tiver, o NPC remove o dinheiro do jogador, adiciona o item ao inventário do jogador, altera o valor de storage do jogador e fala uma mensagem de sucesso. Se o jogador não tiver gold coins suficientes, o NPC fala uma mensagem de erro.