Pasta Enciclopedia com todos os links de outros tutoriais !
http://www.xtibia.com/forum/Pasta-Data-Enc...r-E-t99746.html
Tutorial de Lua (Intermediario)
Bem aqui meu primeiro tuto como colaborador.
Esse tutorial tenta melhorar seus conhecimentos sobre lua, facilitar os proximos scripts e até melhor entender alguns
scripts mais avançados.
Aqui terá algumas noções de for,while,repeat,vetores e o addevent que não vem do Lua mais achei apropriado explica-lo aqui.
O problema é que para saber addevent tem que saber criar funções, e para isso é bom saber sobre escopos. O que sempre é ensinado após
ou antes dos loopings. Então vamos fazer isso tudo junto.
Escopo e variaveis locais
Para melhor explicar os loopings, é bom você entender o que seria escopo e as variaveis locais. Praticamente escopo só serve para variaveis
locais mesmo (pelo menos não lembro outro motivo pra ele). Veja esse exemplo
if item.uid == 2300 then --inicio escopo 1 local escopo1 = 2500 queststatus = getPlayerStorageValue(cid,2300) if queststatus == -1 then --inicio escopo 2 local escopo2 = 3000 if playerCap >= itemWeight then --inicio escopo3 local escopo3 = 2000 doPlayerSendTextMessage(cid,24,"You have found a demon helmet.") doPlayerAddItem(cid,2493,1) setPlayerStorageValue(cid,2300,1) else doPlayerSendTextMessage(cid,24,"You have found a demon helmet. Weighing 29.50 oz it is too heavy.") end --fim escopo 3 else doPlayerSendTextMessage(cid,24,"The chest is empty.") end --fim escopo 2 end --fim escopo 1
No caso, usei essas variaveis locais, para tenta explicar o que é escopo. Cada variavel local indica o escopo existente. Escopo é um espaço
do script, normalmente entre condicionais ou loopings. Os comentarios indicam inicio e fim de cada escopo. No caso de otserver, escopo não chega
a ser muito util, porém ajuda nas outras linguagens e ensina como usar as variaveis locais.
A importancia da variavel local, é fazer ela valer apenas no escopo, para não haver confusões de 2 scripts correntes usarem a mesma variavel
e ferrar seu servidor.
Laços de Repetição (looping)
Os loopings, são condicionais usadas para facilitar certas ações, e não deixar os script com um tamanho gigantesco, os mais usados são o
for,while e repeat.
Em tese eles fazem a mesma coisa, que é repetir tudo que está dentro do escopo, porém cada uma tem seu jeito de fazer.
For
O for talvez seja o looping mais usado. O que ele faz é repetir todo seu escopo o numero de vezes mandando.
Vou dar a sintaxe dele.
for variavel = numero inicial, numero final do comandos end
Em português estruturado :
para variavel = numero inicial, numero final faça comandos fim
Um exemplo pratico :
for i= 1,24 do doPlayerAddItem(cid,2150,1) end
Esse script acima vai adicionar 24x o item 2150 ao player. Então no caso você economizo no minimo 21 linhas. Claro que essa estrutura
não serve so para isso.
Lembrando que esse i não é obrigatorio, ele é a chamada variavel de controle. Até hoje não sei porque todo mundo usa i como variavel
de controle do for.
Você também pode fazer um for mais util do que esse, ao final do script mostrarei outros exemplo.
while
O meu looping favorito de se usar, também é muito utilizado. Ele repete o seu escopo, enquanto a condicional for verdadeira.
Exemplo :
while (variavel == 60) do comandos end
Ou :
enquanto (worldtime > 60) faça comandos fim
Ai no caso enquanto a variavel for igual a 60 ele irá executar todos os comandos dentro dele. O while se assemelha um pouco ao if, pois deve
se usar sinais matematicos tem o and e o or.
Eu achei um exemplo na função :
function getTibiaTime() local worldTime = getWorldTime() local hours = 0 while (worldTime > 60) do hours = hours + 1 worldTime = worldTime - 60 end return {hours = hours, minutes = worldTime} end
Então enquanto o worldtime for menor que 60, ele irá pegar a hours e somar + 1, depois pegará o worldtime e diminuirá em 60
Ou seja ele irá contar as horas em 3 linhas. O que será uma tarefa impossivel sem um looping. E veja que faze-la no for também seria
dificil, já que teriamos que saber que horas seria. O que seria anormal, pois para que saber a hora, se ja sabemos ???
repeat
Esse ultimo looping e não menos importante, ele faz as repetições até que aconteça algo.
repeat comandos until ( variavel == 1)
Ou :
repita comandos atéque (variavel == 1)
Aqui nos meu ot ainda não achei nenhum com repeat, porém pode ser que ache algum util para ele. Dá para usar o exemplo do while aqui
repeat hours = hours + 1 worldtime = worldtime - 60 until (worltime >60)
Lembrando que o repeat não tem end, se vc colocar vai dar erro xD.
Aqui você viu os três loopings mais importantes, e sem duvidas isso ajudará a compreender alguns scripts e até diminuir outros.
Agora quando usar cada cabe a você decidir, já que cada um tem vantagens e desvantagens.
Um exemplo é esse desafio. Adicionar Magic Plate armors no player até a capacidade acabar ou no maximo 30 delas.
com for :
for i=1,30 do if getPlayerFreeCap(cid) >= 85.00 then doPlayerAddItem(cid,2472,1) end end
com while :
while (cap >= 85.00) and (mag <=30) do cap = getPlayerFreeCap(cid) mag =mag+1 doPlayerAddItem(cid,2472,1) end
com repeat :
repeat cap = getPlayerFreeCap(cid) doPlayerAddItem(cid,2472,1) mag =mag+1 until (cap >= 85.00) and (mag <=30)
Veja que no for eu nem tentei (até pq n lembrava maneira) de colocar até a capacidade acabar. Porém para adicionar um
numero exato de itens ou sequencia ele é melhor que os outros.
Porém o while e o repeat se dão melhor ao adicionar até a capacidade acabar.
Agora que você ja entende, ou pelo menos ficou menos confuso, vou tentar explicar vetores.
Vetores e Matrizes
Alguns devem se perguntar que escremento é esse ? Entretanto, devem conhecer pelo nome array.
Agora o vetor é uma variavel que armazena varias variaveis. Por exemplo imagine uma tabela do excel, as linhas seriam os vetores.
Ou seja cada linha pode guardar varios dados.
Já a matriz seria colunas e linhas, porém no otserver dificilmente ou nunca vc irá usar matriz, até porque LUA não prescisa
declarar variavel e acho que matriz nem existe no LUA.
Voltando aos vetores, existem algumas vantagens, ao usa-los. Muitos dizem que ocupa menos memoria ram, é verdade, porém
ao menos que você tenha 2 mb de ram, não irá fazer diferença nenhuma.
Eu acho a principal vantagem é poder deixar mais organizado e até facilitar a compreensão de scripts.
Por exemplo vou mostrar um script de quando eu era iniciante.
--function by ta4e-- --for tibia 7.92-- function onUse(cid, item, frompos, item2, topos) presente = math.random(1,11) pos = getPlayerPosition(cid) --surprise bag blue-- if presente == 1 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,2687,10) --cookie doRemoveItem(item.uid,1) elseif presente == 2 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6394,1) --cream cake doRemoveItem(item.uid,1) elseif presente == 3 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6280,1) --party cake doRemoveItem(item.uid,1) elseif presente == 4 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6574,1) --bar of chocolate doRemoveItem(item.uid,1) elseif presente == 5 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6578,1) -- party hat doRemoveItem(item.uid,1) elseif presente == 6 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6575,1) -- red baloon doRemoveItem(item.uid,1) elseif presente == 7 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6577,1) -- green baloon doRemoveItem(item.uid,1) elseif presente == 8 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6569,3) -- candy doRemoveItem(item.uid,1) elseif presente == 9 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6576,1) -- firework rocket doRemoveItem(item.uid,1) elseif presente == 10 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,6572,1) -- party trumpet doRemoveItem(item.uid,1) elseif presente == 11 then doSendMagicEffect(pos,27) doPlayerAddItem(cid,2114,1) -- piggy bank doRemoveItem(item.uid,1) end return 1 end
Enormee e grotesco. Agora veja o que a SVN fez (roubou meu script ) diminuiu xD
local bluePresent = {2687, 6394, 6280, 6574, 6578, 6575, 6577, 6569, 6576, 6572, 2114} local redPresent = {2152, 2152, 2152, 2153, 5944, 2112, 6568, 6566, 2492, 2520, 2195, 2114, 2114, 2114, 6394, 6394, 6576, 6576, 6578, 6578, 6574, 6574} function onUse(cid, item, fromPosition, itemEx, toPosition) local count = 1 if item.itemid == 6570 then local randomChance = math.random(1, 11) if randomChance == 1 then count = 10 elseif randomChance == 2 then count = 3 end doPlayerAddItem(cid, bluePresent[randomChance], count) elseif item.itemid == 6571 then local randomChance = math.random(1, 22) if randomChance > 0 and randomChance < 4 then count = 10 end doPlayerAddItem(cid, redPresent[randomChance], count) end doSendMagicEffect(fromPosition, CONST_ME_GIFT_WRAPS) doRemoveItem(item.uid, 1) return TRUE end
Só por curiosidade esses true e false são as variaveis boolenanas. Onde true vale 1 e false 0.
Esse ainda funciona com a red. Tirando a parte da red surprise bag, os dois funcionam da mesma forma, porém para quem entende
scripts o segundo é bem mais facil.
Bem a sintaxe da matriz é muito facil.
nome da matriz = {primeiro item,segundo item}
Para usar deve se usar como se fosse a variavel mais o numero da "coluna".
Por exemplo
local itens = {"boots of haste","giant sword"} if math.random (1,3) == 1 then doPlayerAddItem(cid,itens[1],1) else doPlayerAddItem(cid,itens[2],1) end
Veja se a sorte do player for 1 ele adiciona o item boots of haste, se for 2 ou 3 ele adiciona a giant sword. Claro que esse
scripts não iria funcionar. Porém é so um exemplo para melhor explicar o bendito vetor.
Porém acho que é mais comum você observar o vetor sendo usado para indicar varios itens ao inves de apenas um.
local useWorms = FALSE local waterIds = {493, 4608, 4609, 4610, 4611, 4612, 4613, 4614, 4615, 4616, 4617, 4618, 4619, 4620, 4621, 4622, 4623, 4624, 4625} function onUse(cid, item, fromPosition, itemEx, toPosition) if isInArray(waterIds, itemEx.itemid) == TRUE then if itemEx.itemid ~= 493 then if useWorms == FALSE or useWorms == TRUE and doPlayerRemoveItem(cid, ITEM_WORM, 1) == TRUE then if math.random(1, (100 + (getPlayerSkill(cid, SKILL_FISHING) / 10))) <= getPlayerSkill(cid, SKILL_FISHING) then doPlayerAddItem(cid, ITEM_FISH, 1) end doPlayerAddSkillTry(cid, SKILL_FISHING, 1) end end doSendMagicEffect(toPosition, CONST_ME_LOSEENERGY) return TRUE end return FALSE end
Nesse caso o autor do script ele diminui varias linhas usando o vetor.
if isInArray(waterIds, itemEx.itemid) == TRUE then
No Português Estruturado :
Se estiver no vetor (nome do vetor, uid ) == verdadeiro faça
Ou seja tudo que tiver no vetor waterIds e for itemEx (item2) vai ser considerado.
O vetor tem essas duas utilidades nos otserver, se você observar bem o seu uso vem sendo aumentado a algum tempo. E
não domina-lo é um erro para grandes scripts.
Um bom exemplo é meu script da arena que na versão 3.0 os três scripts de movements tinham 1200 linhas, na 4.0 com uso
de umas funções e if melhorados diminui para 400. E na futura versão 4.0 Virarão torno de 80 linhas, com uso dos arrays e for.
Funções
Criar funções é uma coisa maravilhosa, se você costuma sempre incrementar algo no seu otserver é muito util saber criar algumas delas.
Por exemplo todos esses doPlayerAddItem e doSetPlayerStorageValue são funções. Quem leu o segundo tutorial entendeu as funções de otserver.
E pelo que me lembre expliquei que podemos dividir as funções em dois grupos. As retornam valores e as que não.
Porém na verdade 99% das funções retornam valores, porém torno de 60% são erros. Por exemplo se vc manda o otserver adicionar
um item que não existe ele retorna uma mensagem de erro. Porém essa mensagem é inutil para o script, só mostra que você esqueceu
ou é burro (xD).
Um exemplo de função que retorna o seu dinheiro no player.
function getPlayerMoney(cid) return ((getPlayerItemCount(cid, ITEM_CRYSTAL_COIN) * 10000) + (getPlayerItemCount(cid, ITEM_PLATINUM_COIN) * 100) + getPlayerItemCount(cid, ITEM_GOLD_COIN)) end
(Observe as frescuras da SVN de tentar deixar o script bom para iniciantes, e depois eles usam variaveis que só tem nas sources.)
Se você gosta de frescura pode "socar" variaveis no global.lua com nomes para ser ids. Eu ja odeio isso xD.
Voltando ao script, ao usar essa função, ele irá retornar a quantidade de din din que vc tem
Sendo usada assim.
dinheiro = getPlayerMoney(cid) doCreatureSay(cid,dinheiro,TALKTYPE_ORANGE2)
Ou do jeito que você quiser, veja que essa função retorna algo. Espero que entendam essa parte.
Agora a vantagem de fazer uma função ? Pode tirar a repetição de alguma coisa. Por exemplo :
if item.uid == 2300 then queststatus = getPlayerStorageValue(cid,2300) if queststatus == -1 then if playerCap >= itemWeight then doPlayerSendTextMessage(cid,24,"You have found a demon helmet.") doPlayerAddItem(cid,2493,1) setPlayerStorageValue(cid,2300,1) else doPlayerSendTextMessage(cid,24,"You have found a demon helmet. Weighing 29.50 oz it is too heavy.") end else doPlayerSendTextMessage(cid,24,"The chest is empty.") end
Cada bloco desse é uma quest do meu otserver, e pelo que contei aqui tenho 90. Imagine que lindo para eu me achar ?
Criei so por diversão uma função.
function doQuest(cid, item, count, storage, value, take) local itemWeight = getItemWeightById(item, 1) local playerCap = getPlayerFreeCap(cid) if getPlayerStorageValue(cid, storage) == -1 then if playerCap >= itemWeight then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, 'You have found a ' .. getItemNameById(item) .. '.') container = doPlayerAddItem(cid, item, count) setPlayerStorageValue(cid, storage, value) else doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, 'You have found a ' .. getItemNameById(item) .. ' weighing ' .. itemWeight .. ' oz it\'s too heavy.') end else doPlayerSendTextMessage(cid,24,'The ' ..take.. ' is empty.') end end
E agora cada linha de quest minha fica.
if item.uid == 2300 then doQuest(cid,2493,1,2300,1,'chest') end
Melhor ? O unico problema é que no TFS 0.3.0+ ela da um erro (O bau abre como chest, porém a quest é feita normalmente.)
Essa é uma função media, porém se você leu todos meus tutoriais ja entende ela facinho.
Mais vou explicar como fazer passo a passo ela.
function doQuest(cid, item, count, storage, value, take)
Aqui você indica como ela será usada no script. Os nomes não importam, o que importa é vc usar eles em todo o script o mesmo.
A ideia aqui é pegar tudo que vc prescisa.
Item : É o id do item que o bau irá dar.
count : A quantidade desse item.
storage : É o storage value adicionado.
value : O valor do storage adicionado.
take : O nome do lugar que vc pega.
Claro que eu fiz uma que atendesse todas minhas necessidades. O value e o take podem ser facilmente excluidos.
Depois que declarei todos os requesitos devemos passa a parte de como fazer.
local itemWeight = getItemWeightById(item, 1)local playerCap = getPlayerFreeCap(cid)
if getPlayerStorageValue(cid, storage) == -1 then
if playerCap >= itemWeight then
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, 'You have found a ' .. getItemNameById(item) .. '.')
container = doPlayerAddItem(cid, item, count)
setPlayerStorageValue(cid, storage, value)
else
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, 'You have found a ' .. getItemNameById(item) .. ' weighing ' .. itemWeight .. ' oz it\'s too heavy.')
end
else
doPlayerSendTextMessage(cid,24,'The ' ..take.. ' is empty.')
end
.. variavel.. é a chamada junção de string. Ele junta a variavel entre .. e adiciona a frase.
Veja que usei todas as variaveis, usadas na função aqui dentro.
Uma das regras da programação é : A maquina é burra, você tem que dar passo a passo o que ela deve fazer.
Ou seja não esqueça de nenhum passo nas funções. Um erro gravissimo é o cara esquecer de meter no cid no começo. Se vc esquecer
quando for rodar no otserver, vai dar milhões de erros, e tudo porque você esqueceu o cid.
Lembrando novamente que não importam os nomes, eu poderia fazer isso.
function doQuest(macaco, lixo, quantidade, seila, tipo, ultima)
Se eu colocasse esses nomes no motor dele, iria funcionar normalmente.
E também você pode usar funções suas dentro de funções suas, que irão funcionar normalmente.
O lugar mais adequado de coloca-las é no global.lua. Se vc usa the forgotten server coloque-as no funcitons.lua.
Porém se você for mais alienado pode criar um arquivo para suas variaveis. É só adicionar no global.lua essa linha
dofile('data/meuarquivo.lua')
Isso se ele tiver na pasta data (de preferencia dentro dela.)
Mais uma inutilidade que você aprende aqui.
Depois dessa volta enorme que demos aqui. É hora de explicar o bendito addEvent.
addEvent e stopEvent
Bem essa parte tem até certos creditos ao tutorial do noobinhu, pois eu não sabia que tinha stopEvent. Porém eu vou explicar da
minha maneira. Também ao meu grande amigo Soulblaster que foi que me ensino a base sobre isso.
O dificil do addEvent é explicar como o cara deve fazer a função. Já que vcs devem saber, o resto é facil.
A função do addEvent é fazer rodar uma função num certo tempo. Ao contrario do que muitos pensam (até eu), colocar uma função depois do
addEvent não fará ela rodar após ele.
exemplo :
addEvent(lever1, 5000) doSendMagicEffect(xadrexpos,12)
Esse doSend... rodará antes dos 5 segundos. Ou seja, tudo que tenha que rodar em 5 segundos deverá estar na função.
Isso vale também para varios addEvents.
addEvent(tp1, 1000,cid) addEvent(tp2, 1000,cid) addEvent(tp3, 1000,cid) addEvent(tp4, 1000,cid)
No caso acima todos irão rodar juntos, já que ele não espera um acabar para rodar o proximo.
addEvent(tp1, 1000,cid) addEvent(tp2, 2000,cid) addEvent(tp3, 3000,cid) addEvent(tp4, 4000,cid)
Esse seria o efeito certo.
Agora que dei os macetes, explicarei a sintaxe.
addEvent(função,tempo em milessegundos,parametros)
Milessegundos é 1000 = 1 segundo e assim vai, os parametros são os das funções. Se houver apenas 1 parametro coloque-o direto. Se houver varios
deve ser colocar entre chaves.
addEvent(tp1, 1000,{cid=cid, topos= topos}
Ou fazer o que muitos fazem :
p = {cid=cid,topos=topos} addEvent (tp1, 1000,p}
Esse p também é opcional, e não sei pq o usam sempre...
Isso é mais simples, e você deve usar todos os parametros da função.
Lembra que falei que o nome não importa na função ? Aqui meio que importa já que você deve declarar tudo. É mais ou menos como se fosse
usa-la em um script.
Exemplo :
p = {cid = cid, item = 2493, count = 1, storage = 2300, value = 1, take = "chest"} addEvent (doQuest, 3000 , p)
Facil ? Eu acho que sim.
O stopEvent é simples também
stopEvent (função )
Nunca usei, será util se você colocar o addEvent em um looping ou sei lá.
E digo mais que no tempo que comecei tivesse um tuto porco desse, eu acho que seria m scripter bem melhor.
Bem espero que esse tutorial seja util ao todos que leram, qualquer duvida ou dificuldade reportem, se gostarem não deixem de comentar.
Não sei se ficou muito bom (até pq são 02:43 mins da madrugada, e como não bebo e não tem coca-cola, tomei uns 30 copo de leite). E aguardem os proximos!!!