Ir para conteúdo

Skyen

Campones
  • Total de itens

    36
  • Registro em

  • Última visita

  • Dias Ganhos

    5

Posts postados por Skyen

  1. Nossa, na boa, Raposa. Que tutorial foda, meu velho.

     

    Acho que as duas únicas coisas que ainda não sigo do modo Raposa de programar Lua, é o modo em como declaro nomes de funções e variáveis e o espaçamento que, não consigo evitar, dou entre variáveis em uma tabela.

     

    Realmente, fica melhor assim?

     

    {x=100, y=100, z=7}
    

     

    O IPB tem um bug sério quanto identação, TAB não é reconhecido aqui, apenas espaços, e por isso quando você cola seus códigos aqui, eles acabam ficando sem identação.

     

    Eu uso Notepad++ para programar, e depois de terminar um código, uso a opção TAB para Espaço dele, é bem útil pois contorna o "bug" do fórum

     

    Acho que arrumei quase tudo o que estava bugado. Realmente esse negócio de tab me encheu o saco. Outro bug dele é que o editor convertia algumas letras maiúsculas com acentos em minúsculas e adicionava um monte de espaços em lugares que não devia. Mas, enfim, acho que tá arrumado.

     

    E quanto à tabela, eu passei muito tempo pensando nisso e decidi que assim fica mais legível porque tabelas de linha única contém poucos atributos, e o espaço adicional geralmente ajuda a quebrar uma outra regra que eu sigo mas não adicionei ali pra não complicar: Máximo de 80 caracteres na horizontal.

     

    Mas claro, como eu disse, é questão de gosto. Você usa o que te deixar mais confortável.

  2. Obs.: Eu sei que esse tutorial é quase uma trilogia, mas não desanime, tente ler até o final, você pode aprender muita coisa nova! Se você não quer ler tudo, pule a parte de escopo e identação, pois é a parte mais complexa. Recomendo voltar depois e tentar entender identação e escopo.

     

    Você está jogando Tetris quando um desconhecido te chama e pede para arrumar um script dele que não está funcionando direito. Aparentemente, os players estão ficando irritados porque o servidor está respondendo "Hell, world!" para eles, quando deveria estar respondendo "Hello, world!".

     

    Você diz "tudo bem", o cara te manda o script, e você se depara com isso:

     

    _,__,___,____=string.char,print,type,"fu"
    .."nction"if(___(__)==____)then __(_(0x48
    ,0x65,0x6c,0x6c,0x2c,0x20,0x77,0x6f,0x72,
    0x6c,0x64,0x21))  --[[Hello, world!]] end

     

    Se você sentiu um pingo de vontade de ler o código, exceto por curiosidade, seu nome é Mock.

     

    O erro mais comum que eu vejo sendo cometido por iniciantes não é nada relacionado à sintaxe, lógica da programação ou consumo excessivo de fungos pluricelulares. É a elegância (Ou melhor dizendo, a falta dela). Não me interessa se seu código deixa o Tibia 4D com Intel® Tesselated 256x Clockwise Polygoned RealExtreme™ Greener Foliage e nunca deu um bug. Se ele não for no mínimo agradável de ler, eu vou jogar fora, e sentir pena da lixeira.

     

    90% das vezes que eu digo para algum iniciante deixar o código mais bonito, eles me respondem que "só eu vou ler mesmo, cara, não vou ficar me preocupando com isso". Isso é a mesma coisa que limpar a bunda sempre com o mesmo papel porque você não é homossexual e não precisa da* * *****.

     

    Nas outras 10% das vezes, a pessoa fica offline e nunca mais aparece.

     

    Devaneios à parte, vamos ao que interessa:

     

    Por que a característica mais importante de um código é a elegância, e como deixar seu código elegante?

    Começando do princípio: Para escrever um código, o programador precisa ter na cabeça a abstração de uma solução para o problema em mente. Se você não entendeu, isso apenas significa que se você quer escrever um script que faça X coisa, você tem que ter na cabeça uma ideia para fazer a coisa X acontecer através de um código. Se você é distraído por algo, ou para de programar na metade do código, ou está com muito sono, essa ideia vai se perdendo e você tem que pensar nela novamente depois.

     

    Veja o problema do "Hello, world!" acima. Imagine que aquele código é seu, e você achou que ele estivesse funcionando, mas agora percebeu o bug e quer consertar. Só que já faz tempo que você fez o script e não tem mais em mente a ideia que usou para escrever ele à muito tempo atrás. A ideia se perdeu, e o único modo de relembrar ou descobrir a ideia de um código é, bem, lendo ele...

     

    Se você ainda tem a ideia em mente, ler um código como aquele ali em cima é muito fácil: parece óbvio para você o que ele faz, pois afinal, você acabou de escrevê-lo! Só que não é bem assim que funciona se você já não lembra de como fez o código, vai ser muito difícil ler um código grande todo mal-feito como aquele ali em cima, e o trabalho de arrumar o código é muito mais complexo.

     

    Caso você estivesse na situação de ter que arrumar o código do "Hello, world!", qual código você iria preferir arrumar? O de cima ou este aqui:

     

    print("Hell, world!")

     

    O problema piora se você não sabe como arrumar o bug e precisa de ajuda de alguém. Se você enviar um código todo mal-feito para alguém te ajudar, é bem provável que a pessoa nem vá ler seu código, dar uma desculpa e se safar de ter que ver tamanha aberração (Aos que estão se perguntando: sim, eu faço isso).

     

    Ou seja, enfie na cabeça de uma vez por todas que mesmo que seu código jamais vá ser lido por outra pessoa, é importante que você faça ele de forma elegante.

     

    É muito chato ter que enfeitar o código depois que ficou pronto? Você está fazendo algo muito errado!

    Se você faz o código todo para depois deixar bonitinho, fique sabendo que essa é uma péssima ideia. Você não tem que deixar bonito depois de pronto, e nem antes de começar, você tem que ir aplicando a elegância justamente enquanto escreve o código!

     

    Não faz sentido escrever um código feio para depois enfeitar. É a mesma coisa que parir o Hitler e deixar ele mais bonitinho com maquiagem e lacinhos. Acostume-se à escrever um código naturalmente bonito.

     

    8j3YD.png

     

    A parte que realmente interessa: Como deixar seu código bonito!

    Identação

    A primeira coisa que me vem em mente quando alguém fala em código bonito é a identação. Identação é o espaço horizontal que separa as linhas de código da borda da esquerda. Veja um exemplo de código identado abaixo. Em azul é o espaço da identação, geralmente feito com a tecla tab:

     

    XhPZM.png

     

    Além de mais bonito, fica extremamente mais simples ler um código identado. Ela é tão importante que na linguagem Python a identação não somente é obrigatória, como também é parte da sintaxe.

     

    Existem muitos iniciantes por aí que não sabem identar, mas adicionam espaços antes das linhas para copiar o código de outra pessoa e acabam fazendo tudo errado. Isso atrapalha tanto quanto um código não identado, se não piorar.

     

    Escopo

    Para aprender a identar corretamente, primeiro você deve entender o que é um escopo. A explicação abaixo não serve apenas para embelezar seu código, mas também é um conceito fundamental para programar, não apenas em Lua, mas em diversas outras linguagens de programação, então é importante que você leia mesmo se quiser fazer códigos feios (Afinal, a opção é sua, só não sei por que você chegou até aqui no tutorial se quer fazer um código feio).

     

    Escopo tem tudo à ver com variáveis locais e globais.

     

    A definição informal de escopo é: Até que ponto as variáveis locais podem ser alcançadas. Obviamente você não vai decorar isso, então vamos explicar de um jeito que você entenda:

     

    Quando você declara uma variável dessa forma em Lua, ela é uma variável global:

     

    x = 1

     

    Significa que ela pode ser acessada de qualquer lugar no seu código! Emocionante, não é?

     

    Não. Você não deveria estar fazendo isso à não ser em casos muito, muito especiais, e só quando você sabe o que está fazendo. Variáveis globais tem seus usos, mas são perigosas se você não usá-las corretamente. Isso acontece porque variáveis globais podem dar conflito com outras variáveis. E pior, em um lugar que não tem nada a ver com a paçoca.

     

    Por exemplo, você tem dois scripts completamente diferentes: Um deles é uma alavanca que abre uma porta e o outro é uma pedra que teleporta. Completamente diferentes. Exceto por uma coisa: ambos possuem a variável "pos", e o inútil escritor desses scripts cometeu o grandíssimo erro de não usar variáveis locais quando necessário. Veja:

     

    alavanca_que_abre_uma_porta.lua:

    pos = {x=100, y=100, z=7}

     

    pedra_que_teleporta.lua:

    pos = {x=200, y=200, z=8}

     

    Quando o Lua abre o primeiro script, ele registra a variável global "pos" com o valor 100x100x7. Quando o Lua abre o segundo script, ele registra novamente essa variável com o valor 200x200x8. O resultado é bem óbvio, existe apenas uma variável "pos" usada pelos dois scripts com o valor 200x200x8, que é válida para a pedra que teleporta, mas completamente inválida para a alavanca que abre uma porta!

     

    Para criar uma variável local, basta adicionar a palavra "local" antes do nome da variável. Tornando a variável "pos" local, vão existir duas variáveis locais "pos", uma para cada script, e cada uma com seu valor:

     

    alavanca_que_abre_uma_porta.lua:

    local pos = {x=100, y=100, z=7}

     

    pedra_que_teleporta.lua:

    local pos = {x=200, y=200, z=8}

     

    Problema resolvido. Agora mesmo que as variáveis possuam o mesmo nome, cada script tem a sua, e elas não irão conflitar, pois cada uma tem seu valor.

     

    Variáveis globais tem seus usos. Por exemplo, caso você precise trocar informações entre dois scripts diferentes. Porém, se precisar usar variáveis globais, escolha um nome que você tem certeza absolutíssima de que não causará conflito com nenhuma outra variável.

     

    Mas isso não é tudo o que há para falar sobre variáveis locais. Elas possuem uma propriedade muito interessante, veja:

     

    if true then
    local var = "Hello, world!"
    end
    
    print(var)

     

    O que você acha que o print vai escrever? Se você disse "Hello, world!", você errou. E errou feio.

    O print vai escrever "nil". Curioso, não?

     

    Na verdade, é algo muito óbvio. A variável "var" é local, e foi criada dentro do "if". Isso significa que ela é local dentro do if, e que fora dele, ela não existe. Quando o "if" atinge seu "end", todas as variáveis locais dentro dele são destruídas. Em outras palavras, o print não consegue encontrar a variável "var", pois ela só existe dentro do "if"!

     

    Agora vamos ver um caso diferente:

     

    if true then
    local var = "Hello, world!"
    
    if true then
    	print(var)
    end
    end

     

    O que você acha que o print escreve? Você provavelmente acertou essa, agora. A resposta é "Hello, world!". A variável local existe, sim, apenas dentro do primeiro "if". Porém, o segundo if está dentro do primeiro, então a variável var continua existindo. Ela só será destruída quando o primeiro "if" atingir seu "end".

     

    Vamos complicar as coisas um pouquinho.

     

    local x = 10
    
    if true then
    local var = "Hello, world!"
    
    if true then
    	local var = "Goodbye, world!"
    
    	print(var)
    	print(x)
    end
    
    print(var)
    end

     

    Uma variável local "x", duas variáveis locais "var", três valores diferentes, três prints. O que você acha que cada um escreve?

    A resposta é: o primeiro escreve "Goodbye, world!", o segundo escreve "10", e o terceiro escreve "Hello, world!".

     

    Epa, mas pera aí, a segunda "var" não dá conflito com a primeira, reescrevendo o valor dela?

    Não. Isso acontece porque a primeira "var" continua existindo no primeiro "if" quando a segunda é criada no segundo "if". Os prints vão escrever o valor da "var" mais próxima do escopo deles.

     

    Escopo, como disse antes, é até onde as variáveis locais são alcançadas. Imagine os escopos como degrais de uma pirâmide. Um escopo mais alto pode alcançar todos os degrais mais baixos que ele na pirâmide, mas não consegue alcançar os mais altos. Se fôssemos dar números aos escopos do código acima:

    1. Escopo global (Fora dos dois "if"s).
    2. Dentro do primeiro "if".
    3. Dentro do segundo "if".

    cXPY4.png

     

    E por que o segundo print escreveu o "x" do primeiro escopo? Porque é como se Lua fosse descendo os degraus dos escopos até achar o que procura. Se não achar, retorna "nil". Por isso, também, o primeiro print escreve a segunda "var", e não a primeira.

     

    Vamos complicar mais uma vez:

     

    if true then
    local var = "Hello, world!"
    
    if true then
    	var = "Goodbye, world!"
    
    	print(var)
    end
    
    print(var)
    end

     

    E agora, o que cada print escreve?

    O primeiro escreve "Goodbye, world!", e o segundo... também!

     

    Observe bem, a segunda "var" não tem a palavra "local" antes! Você deve estar pensando que a segunda "var" é global, mas esse não é o caso. Se eu colocar mais um print, fora dos dois "if"s, ele vai escrever "nil"!

     

    Mas que magia negra está acontecendo aqui agora?

    É bem simples. Quando a palavra "local" é usada, você está dizendo à Lua "crie uma variável local aqui!". Quando você não usa, você está dizendo "substitua o valor da variável no escopo mais próximo por este valor!", e então Lua vai procurar a variável "var" no escopo mais alto (mais próximo ao topo, onde o código está), e substituir seu valor. Se nessa descida da piramide Lua não encontrar a variável que você quer, então ela criará uma variável global!

     

    Ou seja, naquele código acima, se não existisse o primeiro "var", o segundo "var" seria global!

     

    A última coisa que você precisa saber sobre escopo é que todo todo "repeat", "while", "do", "for", "if", "elseif", "else" e "function" abre um novo escopo, e todo "end" e "until" (No caso do "repeat") fecha o escopo mais alto da "pirâmide", destruindo todas as suas variáveis locais.

     

    Voltando à identação

    Agora que você já sabe usar variávies locais em toda sua maestria... Okay, eu sei, talvez ainda esteja confuso demais e você não tenha entendido tudo, mas não se preocupe! Talvez demore um tempo para você assimilar o que é o escopo e variáveis locais, e como aproveitar isso no seu código, isso vem com a prática. Mas continue acompanhando, pois identação é uma coisa muito simples!

     

    A vantagem imediata da identação é que você consegue enxergar exatamente quais são os escopos. Fica simples ver que tal print está dentro de tal "if", já que o print está com um nível a mais de identação.

     

    Antes que você comece a sair por ai distribuindo espaços aos seus códigos, há algumas coisas a considerar sobre a identação.

     

    A identação pode ser feita com "hard tabs", espaços ou "soft tabs". A identação com um hard tab é exatamente um caractere de tab. É quando você aperta a tecla tab do teclado (Fica em cima do "caps lock", representada por duas setinhas) e o seu editor adiciona um único caractere. A identação por espaços usa a tecla de espaço ao invés do tab para adicionar o espaçamento. É praticamente inviável, já que pra adicionar uma identação adequada você teria que apertar a tecla espaço umas 12 vezes. Os soft tabs são uma mistura dos dois estilos. Quando você aperta a tecla tab, ao invés de adicionar um único caractere de tab, o editor adiciona um determinado número de espaços. É como se você apertasse a tecla de espaço várias vezes.

     

    Muitas pessoas preferem usar soft tabs, muitas outras preferem hard tabs. Isso é um debate que dá longas horas de discussão para programadores experientes. Cada um tem suas vantagens e desvantagens.

     

    Vantagens do Hard Tab:

    • Seu tamanho pode ser alterado editando as preferências do editor de texto.
    • É mais fácil controlar o nível de identação, uma vez que é composto de um único caractere.

    Desvantagens do Hard Tab:

    • Alguns editores zoam o caractere de tab, tornando a identação totalmente errada, mesmo que tenha sido feita corretamente. Esse é o caso do OTScriptLive!, muito usado para programar para Open Tibia. Se você usa OTScriptLive!, considere trocar de editor. Existem muitas alternativas ótimas, como SciTE, Notepad++, gedit...

    Vantagens do Soft Tab:

    • Já que é composto de espaços, é garantido que o código seja exibido da mesma forma em todos os editores.
    • Não tem o problema de editores que zoam a identação, como no Hard Tab.

    Desvantagens do Soft Tab:

    • Seu tamanho não é variável.
    • O arquivo fica maior, já que cada caractere usado no hard tab corresponde a quatro ou oito caracteres do soft tab (dependendo do tamanho adotado).
    • Por ser composto de espaços, é extremamente chato remover níveis de identação.

    A escolha é sua. Se você usa OTScriptLive!, recomendo trocar agora mesmo de editor, pois você não terá suporte a soft tabs e os hard tabs são destroídos pelo programa, tornando a identação correta um desastre. Você terá que fazer a identação com espaços.

     

    Eu, particularmente, prefiro hard tabs. É muito mais natural. A maioria dos projetos open source usam soft tabs para garantir que o código fique idêntico em todos os editores, e para um projeto aberto assim, com várias pessoas mexendo, até faz sentido. Mas na minha opinião, isso traz uma série de problemas.

     

    Independente de qual for sua decisão, siga sempre esta regra: Nunca, jamais, misture caracteres de tab com espaços.

     

    Chega disso, vamos logo aprender a identar!

    A identação, diferente do que você deve estar pensando, é uma coisa ridiculamente simples.

     

    Tudo se baseia em usar um espaçamento para separar os escopos. A cada escopo criado, adiciona-se um tab a mais à cada linha seguinte. A cada escopo fechado, remove-se um tab de cada linha seguinte. Veja:

     

    KEA7A.png

     

    Cada setinha representa um caractere de tab. Toda vez que um escopo novo é aberto (por um "function", "for" ou "if"), as próximas linhas recebem um tab a mais. Toda vez que um escopo é destruído (por um "end"), todas as próximas linhas, incluindo a linha do end, recebem um tab a menos.

     

    Se seguirmos essa regra, dá pra perceber que no escopo global (nível 1), as linhas terão 0 tabs. Em um escopo de nível 2, terão 1 tab, e assim por diante.

     

    Há um caso especial: "else" e "elseif". Eles funcionam como se abrissem um novo escopo, ou seja, as linhas seguintes recebem o tab adicional, porém a linha do "else" e "elseif" não. Veja:

     

    WS4em.png

     

    O "segredo" da identação é sempre adicionar mais um tab depois de "repeat", "while", "do", "for", "if", "elseif", "else" e "function" e colocar um tab a menos depois de "end" e "until".

     

    Outro ponto importante da identação é a de tabelas verticais. Quando você fizer uma tabela que se extende verticalmente, idente seus valores. Nunca coloque o caractere de abertura ({) e fechamento (}) em uma linha que contém um valor, e não idente a linha desses caracteres. Veja:

     

    3TzZr.png

     

    Isso é tudo sobre identação. Não deixe para identar depois que o código estiver pronto! Quando você pular uma linha, já adicione os tabs necessários e continue escrevendo. A maioria dos editores adicionam estes tabs automaticamente se você habilitar a opção, mas apesar de ser uma questão de gosto, não recomendo usar este recurso.

     

    Se você chegou até aqui e acha que entendeu (A parte de identação ao menos, não vou te culpar se você não entendeu sobre escopo), então você agora sabe identar! Yay!

     

    Nem tudo é identação...

    Se você achou que identação é a unica coisa que torna um código elegante, se enganou. Porém, daqui pra frente, as coisas serão bem mais simples.

     

    Código Frankstein não é legal.

    Se você usa variáveis com nomes em português, pode ir parando com Lua agora mesmo e vá programar em G-Portugol. Apesar de ter sido criada no Brasil, a sintaxe de Lua é em inglês e, portanto, não misture inglês com português. Se você não sabe inglês, já passou da hora de começar a aprender.

     

    Quem é esse pokémon?

    Use nome de váriáveis auto-explicativos, e nunca abrevie, à não ser que a abreviação seja comumente usada, como "tmp" ao invés de "temporary". Ninguém é obrigado a ficar adivinhando o que aquela sua variável "cntplr1" ou "hahahalol" significa.

     

    Como faz essa mágica?

    Eu acho comentários muito idiotas. Diversos programadores vivem dizendo "explique cada linha de código com um comentário". Isso simplesmente não faz sentido, o código está bem ali. Como disse Linus Torvalds, "Talk is cheap, show me the code". Se o negócio foi bem escrito, qualquer programador que se preze vai entender... Ou não. Existem algumas gambiarras que você precisa comentar. Quando fizer algo que não é tão óbvio assim só de ler o código, comente. Isso é comum em números mágicos, por exemplo:

     

    radius = radius + 0.5

     

    Por que aquele " + 0.5" está ali? O que ele faz de especial? Isso não dá pra descobrir apenas lendo o código, então comente e explique suas magias negras.

     

    Volte para a segunda série.

    Isso é um caso sério. Muito sério. Aprendemos na segunda série a sempre usar espaço depois de vígula, mas parece que tem gente que ainda insiste em fazer errado. Custa tanto assim fazer isso:

    doSetCreatureOutfit(cid, outfit, -1)

    Ao invés disso:

    doSetCreatureOutfit(cid,outfit,-1)

    ?

     

    Sempre. Use. Espaços. Depois. Da. Vírgula.

    Sim, eu já estou cansado disso.

     

    Maria vai com as outras.

    Se todo mundo usa o nome de variável "cid" para identificar o Creature ID de algo, siga a moda e use também. Fica confuso tentar entender um código que usa "banana" ao invés de "cid", que é o que todo mundo já está acostumado.

     

    Não use parenteses em condicionais!

    Os estadunidenses começaram com uma mania chata de colocar parenteses em condicionais, tipo isso:

     

    if (x == 10) then

     

    Parece que não entenderam muito bem que Lua é Lua, C++ é C++. Não faça isso, à não ser quando estritamente necessário pra evitar ambiguidade em uma condição muito grande. Faça do jeito que Lua foi feito para ser usado:

     

    if x == 10 then

     

    The KISS Principle.

    KISS é uma sigla inglesa para a frase "Keep It Simple, Stupid!", que significa mais ou menos isso: "Faça as coisas da forma mais simples, seu estúpido!".

     

    Nunca faça gambiarra quando não é necessário. Sempre faça as coisas da forma mais simples, pois é mais fácil de arrumar bugs e facilita a leitura.

     

    Número de linhas não indica qualidade de código!

    Esqueça essa história de que quanto menos linhas, melhor. Número de linhas nunca foi indicador de qualidade de código, então JAMAIS, e eu vou dizer denovo, JAMAIS coloque mais de uma coisa na mesma linha. É sério. Nunca faça algo assim:

     

    if x <= 0 then return false end

     

    Sempre separe cada comando em uma linha, assim:

     

    if x <= 0 then
    return false
    end

     

    Programe como se quem ler seu código fosse um serial killer com complexo de fofura.

    Não preciso explicar, apenas faça isso.

     

    Use vírgula no último elemento de uma tabela vertical.

    Veja:

     

    local messages = {
    "123",
    "456",
    "789",
    }

     

    O último elemento, "789", possui uma vírgula no final, mesmo sendo o último elemento da tabela. Sempre faça isso em tabelas verticais, tanto para manter a consistência visual, quanto para evitar que você adicione outro elemento depois e esqueça de colocar a virgula, ocasionando um erro. Não se preocupe, Lua aceita essa sintaxe, mas apenas faça isso em tabelas verticais.

     

    Linhas vazias são importantes também.

    Deixe algumas linhas em branco para separar partes do código. Elas ajudam bastante na visibilidade.

     

    E o mais importante de tudo: Siga um padrão.

    Adote um padrão de estilo e siga ele! Se você usa espaço em um lugar, mas não usa em outro, pode ir parando com isso. Sempre mantenha seu código dentro de um padrão que te deixe confortável. Não misture as coisas. Se você fez de um jeito, faça sempre desse jeito.

     

    Eis o meu padrão de estilo para a linguagem Lua. Você pode seguí-lo se quiser, ou seguir o seu próprio, mas o importante é que seu estilo tenha uma razão para cada coisa e que você se sinta confortável com ele, e use-o sempre, em todas as ocasiões, quebrando-o apenas em situações muito, muito especiais.

     

    Skyen Hasus' Lua Coding Style Guide

    Este é meu estilo de código para Lua. Todas as regras aqui foram pensadas antes de serem criadas, então ouso dizer que é um estilo consistente.

     

    Use o syntactic sugar para declarar funções.

    Faça assim:

    function foo()

    Ao invés de:

    foo = function()

     

    Não use espaços para separar o nome da função dos parênteses da lista de argumentos.

    Faça assim:

    function foo()

    Ao invés de:

    function foo ()

     

    Não use espaços no início ou no final de parenteses, chaves ou colchetes.

    Faça assim:

    function foo(bar, baz)
    x = {"a", "b"}
    x[1]

    Ao invés de:

    function foo( bar, baz )
    x = { "a", "b" }
    x[ 1 ]
    

     

    Use sempre um espaço antes e depois de operadores binários (dois valores: +, -, *, /, %, =, ==, <=, et cetera...).

    Faça assim:

    x = a + b * c

    Ao invés de:

    x=a+b*c

     

    A exceção para a regra acima são tabelas de uma linha só.

    Faça assim:

    x = {x=100, y=100, z=7}

    Ao invés de:

    x = {x = 100, y = 100, z = 7}

     

    Nunca use espaço depois de um operador unário (um só valor: único caso é o operador de negatividade, -).

    Faça assim:

    x = -a

    Ao invés de:

    x = - a

     

    Use sempre aspas para strings de uma linha só e [[]] para string de múltiplas linhas.

    Faça assim:

    msg = "And he said: \"Hello, world!\"..."

    Ao invés de:

    msg = 'And he said: "Hello, world!"...'

     

    Use a notação lower_underscore para nome de variáveis e funções. Todas as letras são minusculas e espaços são separados por underscore (_).

    Faça assim:

    function long_function_name()
    long_variable_name = 1

    Ao invés de:

    function longFunctionName()
    longVariableName = 1

     

    Use a notação CamelCase para nome de classes. (Apenas quando usar orientação à objetos!)

    Faça assim:

    Class = {}

    Ao invés de:

    class = {}

     

    Tabs tem tamanho de 8 caracteres!

    Faça assim:

    if true then
           this_tab_is_8_characters_wide = true
    end

    Ao invés de:

    if true then
       this_tab_is_4_characters_wide = true
    end

     

    Não use a notação multilinha de comentários. Use a notação de única linha em todas as linhas.

    Faça assim:

    -- Hello
    -- World

    Ao invés de:

    --[[
    Hello
    World
    ]]

     

    Finalmente o fim.

    Foi um "tutorial" bem longo, mas espero que ajude muita gente à escrever códigos mais legíveis. Se você tem alguma dúvida, ou quer ver se sua identação está correta, ou quer discutir uma regra de estilo, ou ficou confuso em alguma parte e precisa de uma explicação melhor, ou achou algum erro, ou precisa de alguma dica, poste aqui!

     

    E não menospreze a beleza de um código, porque a beleza é o fator mais importante. Algo bem escrito é mais fácil de consertar e manter do que algo mal-escrito. Acostume-se a aplicar as suas regras de estilo conforme programa, e não depois que está tudo pronto.

     

    E acima de tudo, use um bom editor de texto!

    (Sério, parem de usar OTScriptLive!)

    (E coloquem espaços depois de vírgulas!!)

     

    21nl25z.png
  3. Vou postar alguns scripts meus que estiveram engavetados. Faz algum tempo que fiz estes scripts, então não vou postar screenshots ou como configurar, mas as configurações são fáceis de entender, então divirtam-se.

     

    Edit: Cuidado, se usar muitas chuvas ao mesmo tempo ou em áreas muito grandes, é muito provável que vá dar lag no seu servidor!

    Porém, tenho planos de fazer um remake desse sistema, deixando muito melhor e praticamente sem lag numa área imensa, realmente imensa, do porte de 100000x100000 SQMs!

     

    Esse é de longe o meu melhor script para open tibia. Fiz todo usando a "orientação à objetos" de Lua, o que torna o sistema altamente customizável, podendo adicionar novos tipos de clima apenas com uma linha de comando.

     

    O Weather System é basicamente um sistema de chuva que te permite adicionar o clima que você quiser, como chuva ácida, chuva de meteoros e et cetera.

     

    Porém, o maior atrativo deste sistema é que ele não chove aonde há telhado, ou seja, os efeitos só vão acontecer no andar mais alto onde há tiles. Não vai chover embaixo de uma ponte, vai chover em cima dela. Não vai chover dentro da loja daquele NPC, vai chover no telhado da loja. Não vai chover dentro de cavernas, montanhas, ou qualquer lugar onde haja um tile por cima.

     

    Você pode criar uma chuva eterna, uma chuva que acontece de vez em quando em algum lugar, ou criar a chuva com o comando /weather com um GM.

     

    /data/lib/weather-system.lua

    -- This script is part of Weather System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    -- base (class) for weather types
    WeatherType = {
    effects = CONST_ME_NONE,
    
    interval = 10,
    
    items = {},
    chance = 0,
    
    callback = nil,
    param = {},
    }
    
    -- base (class) for weathers
    Weather = {
    type = nil,
    
    topleft = {x=0, y=0},
    bottomright = {x=0, y=0},
    
    intensity = 0,
    }
    
    -- create new instances of WeatherType
    function WeatherType:new(effects, interval, items, chance, callback, ...)
    local new_weathertype = {
    	effects = effects,
    	interval = interval,
    	items = items,
    	chance = chance,
    	callback = callback,
    	param = {...},
    }
    
    return setmetatable(new_weathertype, {__index = self})
    end
    
    -- create new instances of Weather
    function WeatherType:create(topleft, bottomright, intensity)
    if bottomright.x < topleft.x then
    	local tmp_x = topleft.x
    	topleft.x = bottomright.x
    	bottomright.x = tmp_x
    end
    
    if bottomright.y < topleft.y then
    	local tmp_y = topleft.y
    	topleft.y = bottomright.y
    	bottomright.y = tmp_y
    end
    
    local new_weather = {
    	type = self,
    	topleft = topleft,
    	bottomright = bottomright,
    	intensity = intensity,
    }
    
    return setmetatable(new_weather, {__index = Weather})
    end
    
    -- start the weather's main loop
    function Weather:start(duration)
    if duration and duration <= 0 then
    	return true
    end
    
    local area = (self.bottomright.x - self.topleft.x) * (self.bottomright.y - self.topleft.y)
    for i = 1, (area * self.intensity / 40) + 1 do
    	local pos = {}
    	pos.x = math.random(self.topleft.x, self.bottomright.x)
    	pos.y = math.random(self.topleft.y, self.bottomright.y)
    	pos.z = get_roof_tile(pos)
    
    	if is_walkable(pos) then
    		local send_effect = true
    
    		-- if the weather type for this weather has a callback, call it
    		if type(self.type.callback) == "function" then
    			send_effect = self.type.callback(self.type, pos)
    		end
    
    		-- if callback returned true, send effect, no effect otherwise
    		if send_effect then
    			doSendMagicEffect(pos, type(self.type.effects) == "table" and self.type.effects[math.random(1, #self.type.effects)] or self.type.effects)
    		end
    
    		-- create item based on chance
    		if math.random(1, 100) <= self.type.chance / 40 then
    			doCreateItem(type(self.type.items) == "table" and self.type.items[math.random(1, #self.type.items)] or self.type.items, pos)
    		end
    	end
    end
    
    if not duration then
    	addEvent(self.start, self.type.interval, self, false)
    else
    	addEvent(self.start, self.type.interval, self, duration - self.type.interval)
    end
    return true
    end
    
    -- itemids that count as void when being checked for the top-most tile
    local include = {
    434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936,
    937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964,
    3188, 3189, 3218, 3221, 3222, 3298, 3299, 3300, 3301, 3302, 3303, 3304, 3305, 3306, 3307, 3308, 3309, 4456, 4457, 4458, 4459, 4460, 4461, 4462,
    4463, 4464, 4465, 4466, 4467, 4514, 4542, 4543, 4544, 4545, 4546, 4547, 4548, 4549, 4550, 4551, 4552, 4553, 4554, 4555, 4556, 4557, 4558, 4559,
    4560, 4561, 4562, 4563, 4564, 4565, 4596, 4597, 4598, 4599, 4600, 4601, 4602, 4603, 4604, 4605, 4606, 4607, 4667, 4668, 4669, 4670, 4671, 4672,
    4673, 4674, 4675, 4676, 4677, 4678, 4679, 4680, 4681, 4682, 4683, 4684, 4685, 4686, 4687, 4688, 4689, 4690, 4713, 4714, 4715, 4716, 4717, 4718,
    4719, 4720, 4721, 4722, 4723, 4724, 4725, 4726, 4727, 4728, 4729, 4730, 4731, 4732, 4733, 4734, 4735, 4736, 4737, 4738, 4739, 4740, 4741, 4742,
    4760, 4761, 4762, 4763, 4764, 4765, 4766, 4767, 4768, 4769, 4770, 4771, 4772, 4773, 4774, 4775, 4776, 4777, 4778, 4779, 4780, 4781, 4782, 4783,
    4784, 4785, 4786, 4787, 4788, 4789, 4790, 4791, 4792, 4793, 4794, 4795, 4796, 4797, 4798, 4799, 4800, 4801, 4802, 4803, 4804, 4805, 4806, 4807,
    3226, 3227, 3228, 3229, 3230, 3231, 3232, 3233, 3234, 3235, 3236, 3237, 3238, 3239, 3240, 3241, 4514, 4515, 4516, 4517, 4518, 4519, 4520, 4521,
    4522, 4523, 4524, 4525, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5816, 5817, 5818, 5819, 5820, 5821, 5822, 5823, 5824, 5825,
    5826, 5827, 6160, 6161, 6162, 6163, 6164, 6165, 6166, 6167, 6168, 6169, 6170, 6171, 6695, 6696, 6697, 6698, 6699, 6700, 6701, 6702, 6703, 6704,
    6705, 6706, 7067, 7068, 7069, 7070, 7071, 7072, 7073, 7074, 7075, 7076, 7077, 7078, 7079, 7080, 7081, 7082, 7083, 7084, 7085, 7086, 7087, 7088,
    7089, 7090, 7107, 7108, 7109, 7110, 7111, 7112, 7113, 7114, 7115, 7116, 7117, 7118, 7119, 7120, 5771, 5772, 5773, 5774, 6211, 6212, 6213, 6214,
    6215, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 6810, 6811, 6812, 6813, 6814, 6815, 6816, 6817, 6818, 6819, 6820, 6821, 7201,
    7202, 7203, 7204, 7205, 7206, 7207, 7208, 7209, 7210, 7211, 7212, 7641, 7642, 7643, 7644, 7645, 7646, 7647, 7648, 7649, 7650, 7651, 7652, 7653,
    7709, 7710, 7656, 7657, 7658, 7659, 7660, 7661, 7662, 7663, 7664, 7654, 7833, 7834, 7666, 7667, 7668, 7669, 7835, 7671, 7672, 7836, 7837, 477,
    478, 487, 488, 8053, 8054, 8055, 8056, 8057, 8117, 8118, 8119, 8120, 8021, 8022, 8023, 8024, 8025, 8026, 8027, 8028, 8365, 8030, 8031, 8032,
    3349, 3350, 3351, 3352, 3353, 3354, 3355, 3356, 3357, 3358, 3359, 3360, 3225, 3242, 3243, 3244, 3245, 3140, 3141, 3142, 3143, 3144, 3145, 3146,
    3147, 3148, 3149, 3150, 3151, 8349, 8350, 8351, 8352, 8353, 8354, 8355, 8356, 8357, 8358, 8359, 8360, 8435, 8436, 8437, 8438, 8439, 8440, 8441,
    8442, 8443, 8444, 8445, 8446, 8447, 8448, 8449, 8450, 8451, 8452, 8453, 8454, 8455, 8456, 8457, 8458, 8460, 8461, 8462, 8463, 8464, 8465, 8466,
    8467, 8468, 8469, 8470, 8471, 9233, 9234, 9537, 9538, 9539, 9540, 9541, 9542, 9543, 9544, 9545, 9546, 9547, 9548, 9549, 9550, 9551, 9552, 9553,
    9554, 9555, 9556, 9557, 9558, 9559, 9560, 9569, 9570,
    }
    
    -- get the top-most existent tile, and returns its z position
    function get_roof_tile(pos)
    pos.stackpos = 0
    pos.z = 7
    
    local thing = getTileThingByPos(pos)
    while pos.z >= 0 do
    	if thing.uid == 0 or isInArray(include, thing.itemid) then
    		return pos.z + 1
    	end
    
    	pos.z = pos.z - 1
    	thing = getTileThingByPos(pos)
    end
    
    return 0
    end
    
    -- checks if the position is walkable
    function is_walkable(pos)
    pos.stackpos = 0
    
    if getTileThingByPos(pos).uid == 0 then
    	return false
    end
    
    local thing = getThingFromPos(pos)
    while thing.uid ~= 0 and pos.stackpos <= 252 do
    	if not isCreature(thing.uid) and (hasItemProperty(thing.uid, 3)
    	or hasItemProperty(thing.uid, 7)) then
    		return false
    	end
    	pos.stackpos = pos.stackpos + 1
    	thing = getThingFromPos(pos)
    end
    return true
    end
    
    -- simple callback for weathers to heal creatures
    function weather_heal(rain_type, pos)
    pos.stackpos = 253
    
    if getTileThingByPos(pos).uid == 0 then
    	return false
    end
    
    local thing = getThingFromPos(pos)
    if thing.uid ~= 0 then
    	doCreatureAddHealth(thing.uid, rain_type.param[1])
    	doSendMagicEffect(pos, CONST_ME_MAGIC_BLUE)
    end
    
    return true
    end
    
    -- simple callback for weathers to damage creatures
    function weather_damage(rain_type, pos)
    pos.stackpos = 253
    
    if getTileThingByPos(pos).uid == 0 then
    	return false
    end
    
    local thing = getThingFromPos(pos)
    if thing.uid ~= 0 then
    	doCreatureAddHealth(thing.uid, -rain_type.param[1])
    	doSendMagicEffect(pos, CONST_ME_MAGIC_BLUE)
    end
    
    return true
    end
    
    -- simple callback for weathers to teleport creatures
    function weather_teleport(rain_type, pos)
    pos.stackpos = 253
    
    if getTileThingByPos(pos).uid == 0 then
    	return false
    end
    
    local thing = getThingFromPos(pos)
    if isCreature(thing.uid) then
    	local topos = {x=rain_type.param[1], y=rain_type.param[2], z=rain_type.param[3]}
    	doTeleportThing(thing.uid, topos)
    end
    
    return true
    end
    
    -- import the weathers definition's file
    dofile(getDataDir() .. "/lib/weather-types.lua")
    
    
    

     

    Para criar novos tipos de chuva, use o comando "WeatherType:new" como exemplificado no script abaixo:

    /data/lib/weather-types.lua

    -- This script is part of Weather System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    -- WeatherType:new(<table|number: effect(s)>, <number: interval>, <table|number: items>, <number: chance>[, function: callback[, ...]])
    
    Rain		 = WeatherType:new(1, 15, {2016, 2017, 2018}, 60)
    DivineRain   = WeatherType:new(1, 15, {2016, 2017, 2018}, 60, weather_heal, 200)
    Storm		= WeatherType:new({1, 40}, 18, {2016, 2017, 2018}, 60)
    ThunderStorm = WeatherType:new({1, 40}, 18, {2016, 2017, 2018}, 60, weather_damage, 80)
    Blizzard	 = WeatherType:new({42, 43}, 20, {}, 0)
    AcidRain	 = WeatherType:new({8, 20}, 18, 1496, 60)
    MeteorRain   = WeatherType:new(36, 18, {1492, 1493, 1494}, 10, weather_damage, 120)
    Sandstorm	= WeatherType:new(34, 20, {}, 0)
    Snowfall	 = WeatherType:new(27, 15, {}, 0)
    Teleporter   = WeatherType:new(10, 18, {}, 0, weather_teleport, 100, 100, 7)
    Fissure	  = WeatherType:new(6, 18, {1492, 1493, 1494}, 30, weather_damage, 300)
    StaticEnergy = WeatherType:new({11, 47}, 15, {}, 0, weather_damage, 30)
    Fireworks	= WeatherType:new({28, 29, 30}, 18, {}, 0, weather_heal, 50)
    IceStorm	 = WeatherType:new(41, 20, {}, 0)
    Avalanche	= WeatherType:new(44, 20, {}, 0)
    
    
    

     

    Para criar uma chuva eterna, altere a tabela "weathers" no script abaixo.

    /data/globalevents/scripts/weather-eternal.lua

    -- This script is part of Weather System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    -- erase the two examples and add your own new locations inside this table, so they will start when the server start
    local weathers = {
    --	Storm:create({x=100, y=100}, {x=200, y=200}, 0.5),
    --	Blizzard:create({x=100, y=100}, {x=200, y=200}, 0.3),
    }
    
    -- globalevent's callback event
    function onStartup()
    for i, weather in pairs(weathers) do
    	weather:start(false)
    end
    return true
    end
    
    
    

     

    Para criar uma chuva aleatória (acontece de vez em quando em uma área), altere a tabela "weathers" no script abaixo.

    /data/globalevents/scripts/weather.lua

    -- This script is part of Weather System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    -- erase the two examples and add your own new locations inside this table, so they will be randomized
    local weathers = {
    --	Rain:create({x=100, y=100}, {x=200, y=200}, 0.5),
    --	Storm:create({x=100, y=100}, {x=200, y=200}, 0.3),
    }
    
    -- min and max durations of a weather, the value will be randomized
    local minduration = 240000
    local maxduration = 600000
    
    -- globalevent's callback event
    function onThink(interval, lastExecution, thinkInterval)
    if #weathers > 0 then
    	weathers[math.random(1, #weathers)]:start(math.random(minduration, maxduration))
    end
    return true
    end
    
    
    

     

    /data/globalevents/globalevents.xml

    <!-- Weather System -->
    <globalevent name="weather-eternal" type="start" event="script" value="weather-eternal.lua"/>
    <globalevent name="weather" interval="900" event="script" value="weather.lua"/>
    
    
    

     

    /data/talkactions/scripts/weather.lua

    -- This script is part of Weather System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    -- types of weather that can be passed as <weather type> argument to the command
    local weathers = {
    ["rain"] = Rain,
    ["divine-rain"] = DivineRain,
    ["storm"] = Storm,
    ["thunder-storm"] = ThunderStorm,
    ["blizzard"] = Blizzard,
    ["acid-rain"] = AcidRain,
    ["meteor-rain"] = MeteorRain,
    ["sandstorm"] = Sandstorm,
    ["snowfall"] = Snowfall,
    ["teleporter"] = Teleporter,
    ["fissure"] = Fissure,
    ["static-energy"] = StaticEnergy,
    ["fireworks"] = Fireworks,
    ["ice-storm"] = IceStorm,
    ["avalanche"] = Avalanche,
    }
    
    -- auxiliary function, checks if a given value is an index of a given table
    local function is_index(t, v)
    for i in pairs(t) do
    	if i == v then
    		return true
    	end
    end
    return false
    end
    
    -- talkaction's callback event
    function onSay(cid, words, param)
    local param = string.explode(param, " ")
    
    local weather
    local topleft = {}
    local bottomright = {}
    local duration = 1000
    local intensity = 0.5
    
    -- number of arguments' validation
    if #param < 3 then
    	doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Usage: " .. words .. " <weather type> <top-left position> <bottom-right position> [duration=3000] [intensity=0.5]")
    	return true
    end
    
    -- weather type's validation
    if not is_index(weathers, string.lower(param[1])) then
    	doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Invalid weather type.")
    	return true
    end
    weather = weathers[param[1]]
    
    -- top-left position's validation
    if not param[2] then
    	doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Top-left position must be specified.")
    	return true
    end
    
    -- top-left position's format validation
    local param_topleft = string.explode(param[2], "x")
    param_topleft[1] = tonumber(param_topleft[1])
    param_topleft[2] = tonumber(param_topleft[2])
    if not param_topleft[1] or param_topleft[1] < 0 or not param_topleft[2] or param_topleft[2] < 0 then
    	doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Top-left position is invalid. Use format <x>x<y> like this: 100x100. Values must be valid positive numbers.")
    	return true
    end
    topleft.x = param_topleft[1]
    topleft.y = param_topleft[2]
    
    -- bottom-right position's validation
    if not param[3] then
    	doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Bottom-right position must be specified.")
    	return true
    end
    
    -- bottom-right position's format validation
    local param_bottomright = string.explode(param[3], "x")
    param_bottomright[1] = tonumber(param_bottomright[1])
    param_bottomright[2] = tonumber(param_bottomright[2])
    if not param_bottomright[1] or param_bottomright[1] < 0 or not param_bottomright[2] or param_bottomright[2] < 0 then
    	doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Bottom-right position is invalid. Use format <x>x<y> like this: 100x100. Values must be valid positive numbers.")
    	return true
    end
    bottomright.x = param_bottomright[1]
    bottomright.y = param_bottomright[2]
    
    -- duration's validation
    param[4] = tonumber(param[4])
    if not param[4] then
    	duration = 3000
    elseif param[4] <= 0 then
    	doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Weather duration must be a valid number greater than 0.")
    	return true
    else
    	duration = param[4]
    end
    
    -- intensity's validation
    param[5] = tonumber(param[5])
    if param[5] and param[5] >= 0 and param[5] <= 1 then
    	intensity = param[5]
    elseif param[5] then
    	doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Weather intensity must be a valid number between 0 and 1.")
    	return true
    end
    
    -- create and start the weather with the specified arguments
    weather:create(topleft, bottomright, intensity):start(duration)
    
    return true
    end
    
    
    

     

    /data/talkactions/talkactions.xml

    <!-- Rain System -->
    <talkaction log="yes" words="/weather" access="3" event="script" value="weather.lua"/>
    
    
    

  4. Vou postar alguns scripts meus que estiveram engavetados. Faz algum tempo que fiz estes scripts, então não vou postar screenshots ou como configurar, mas as configurações são fáceis de entender, então divirtam-se.

     

    Esse é o remake de um dos meus melhores scripts, muito melhor que a primeira versão. Foi meu segundo script pra open tibia feito usando orientação a objetos (ao menos ao jeito OO de Lua).

     

    Quem já jogou Diablo 2 vai entender do que se trata. Esse sistema consiste em vários "teleports", chamados de "warps", espalhados no seu mapa, aonde você quiser. Cada warp tem uma identificação, por exemplo "dark cave" ou "venore". Todos os warps são interligados, o que significa que o player pode ir de um warp à outro, desde que esteja pisando em cima de um warp. Porém, ele só pode ir para outro lugar caso tenha ativado o warp desse lugar antes: Se um player nunca esteve na "dark cave" ou não ativou o warp de lá, ele não pode usar um outro warp para ir para lá.

     

    Para ativar um warp, o player deve dar "use" no warp ou pisar em cima dele.

    Para ver a lista de warps ativados e as restrições de cada warp, o player deve dar "use" no warp.

     

    Para ir de um warp para outro, o player deve satisfazer as condições de level ou mana do warp de destino e ter ativado o warp de destino antes. Além disso, deve saber a identificação do outro warp (que pode descobrir dando "use" em algum warp) e estar pisando em cima de algum warp, e então dizer "warp identificação do destino". Por exemplo, se eu estou em venore e quero ir para a dark cave, e já ativei o warp de lá antes, basta pisar em cima do warp de venore e dizer "warp dark cave".

     

    Você pode usar o item que quiser como sendo um warp. Eu usava o item de ID 9742, mas parece que ele é moveable. Apenas tenha certeza de alterar o ID do item no actions.xml e no movements.xml.

     

    Para criar um novo warp, coloque o item no mapa e abra o arquivo /data/lib/warpgate.lua, e vá até o final dele. Existem três warps de exemplo já adicionados. Apague-os e, para criar um novo, use o seguinte comando:

    warp:new(1000, "Gate 1", {x=1, y=1, z=7}, 10, 20)

    Onde o 1000 é a StorageID do warp, "Gate 1" é a identificação do warp, {x=1, y=1, z=7} é a posição do warp, 10 é a mana necessária e 20 é o level necessário. A mana e o level podem ser omitidos.

     

    Por exemplo, para configurar a dark cave com StorageID 5000:

    warp:new(5000, "Dark Cave", {x=100, y=120, z=8})

     

    Agora, ao script:

     

    /data/lib/warpgate.lua

    -- This script is part of Warp Gate System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    warp = {
    config = {
    	statusInactive = -1,
    	statusActive = 1,
    
    	talkType = TALKTYPE_ORANGE_1,
    
    	warpEffect = CONST_ME_MAGIC_GREEN,
    	stepIn = CONST_ME_MAGIC_BLUE,
    	stepOut = CONST_ME_POFF,
    
    	listPrefix = "   ",
    	listHeadline = "You can warp to:",
    	noWarp = "You must stand above a warp gate.",
    	noDestiny = "The gate you want to warp doesn't exist.",
    	noMana = "You can't support the mana consumed by the gate you want to warp.",
    	noLevel = "You can't support the level required by the gate you want to warp.",
    	inDestiny = "You can't warp the gate that you are standing.",
    	notActive = "You must find and activate your destiny's gate before warp it.",
    	inFight = "You cannot warp a gate while you are in battle.",
    },
    
    name = "",
    pos = {
    	x = nil,
    	y = nil,
    	z = nil,
    },
    mana = 0,
    level = 0,
    gates = {},
    }
    
    function warp:new(id, name, pos, mana, level)
    if self:getWarpById(id) then
    	return self:getWarpById(id)
    end
    
    local object = {id=id or #warp.gates+1, name=name, pos=pos, mana=mana, level=level}
    local index = {}
    
    setmetatable(object, {__index = self})
    
    warp.gates[id] = {}
    setmetatable(warp.gates[id], {__index = object})
    
    return object
    end
    
    function warp:getList()
    return self.gates
    end
    
    function warp:getWarpById(id)
    return self.gates[id]
    end
    
    function warp:getWarpByName(name)
    for index, warp in pairs(self.gates) do
    	if warp.name:lower() == name:lower() then
    		return warp
    	end
    end
    return false
    end
    
    function warp:getWarpByPosition(pos)
    for index, warp in pairs(self.gates) do
    	if warp.pos.x == pos.x and warp.pos.y == pos.y and warp.pos.z == pos.z then
    		return warp
    	end
    end
    return false
    end
    
    function warp:isActive(cid)
    if getPlayerStorageValue(cid, self.id) == self.config.statusActive then
    	return true
    end
    return false
    end
    
    function warp:activate(cid)
    doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "The Warp Gate is now active!")
    doSendMagicEffect(getCreaturePosition(cid), CONST_ME_MAGIC_BLUE)
    
    return setPlayerStorageValue(cid, self.id, self.config.statusActive)
    end
    
    function warp:deactivate(cid)
    return setPlayerStorageValue(cid, self.id, self.config.statusInactive)
    end
    
    warp:new(1000, "Gate 1", {x=1, y=1, z=7})
    warp:new(2000, "Gate 2", {x=2, y=2, z=7})
    warp:new(3000, "Gate 3", {x=3, y=3, z=7})
    
    
    

     

    /data/actions/scripts/warpgate.lua

    -- This script is part of Warp Gate System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    function onUse(cid, item, frompos, itemEx, pos)
    local usedwarp = warp:getWarpByPosition(pos)
    if not usedwarp then
    	return false
    end
    
    if not usedwarp:isActive(cid) then
    	usedwarp:activate(cid)
    	return true
    end
    
    local text, names = warp.config.listHeadline .. "\n", {}
    for index, gate in pairs(warp:getList()) do
    	if gate:isActive(cid) then
    		table.insert(names, gate.name)
    	end
    end
    
    table.sort(names)
    for index, value in pairs(names) do
    	text = text .. "\n" .. warp.config.listPrefix .. value
    	if warp:getWarpByName(value).mana > 0 then
    		text = text .. " [MP: " .. warp:getWarpByName(value).mana .. "]"
    	end
    	if warp:getWarpByName(value).level > 0 then
    		text = text .. " [Lv: " .. warp:getWarpByName(value).level .. "]"
    	end
    end
    
    doShowTextDialog(cid, item.itemid, text)
    return true
    end
    
    
    

     

    /data/actions/actions.xml

    <!-- Warp Gate System -->
    <action itemid="9742" script="warpgate.lua"/>
    
    
    

     

    /data/movements/scripts/warpgate.lua

    -- This script is part of Warp Gate System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    function onStepIn(cid, item, pos)
    local usedwarp = warp:getWarpByPosition(pos)
    
    if usedwarp then
    	if not usedwarp:isActive(cid) then
    		usedwarp:activate(cid)
    		return true
    	end
    	doSendMagicEffect(pos, warp.config.stepIn)
    end
    
    return true
    end
    
    function onStepOut(cid, item, pos)
    if warp:getWarpByPosition(pos) then
    	doSendMagicEffect(pos, warp.config.stepOut)
    end
    
    return true
    end
    
    
    

     

    /data/movements/movements.xml

    <!-- Warp Gate System -->
    <movevent type="StepIn" itemid="9742" script="warpgate.lua"/>
    <movevent type="StepOut" itemid="9742" script="warpgate.lua"/>
    
    
    

     

    /data/talkactions/scripts/warpgate.lua

    -- This script is part of Warp Gate System
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    function onSay(cid, words, param)
    local pos = getCreaturePosition(cid)
    local usedwarp = warp:getWarpByPosition(pos)
    local destwarp = warp:getWarpByName(param)
    
    if not usedwarp then
    	doPlayerSendCancel(cid, warp.config.noWarp)
    	doSendMagicEffect(pos, CONST_ME_POFF)
    	return true
    end
    
    if not destwarp then
    	doPlayerSendCancel(cid, warp.config.noDestiny)
    	doSendMagicEffect(pos, CONST_ME_POFF)
    	return true
    end
    
    if not destwarp:isActive(cid) then
    	doPlayerSendCancel(cid, warp.config.notActive)
    	doSendMagicEffect(pos, CONST_ME_POFF)
    	return true
    end
    
    if getCreatureCondition(cid, CONDITION_INFIGHT) then
    	doPlayerSendCancel(cid, warp.config.inFight)
    	doSendMagicEffect(pos, CONST_ME_POFF)
    	return true
    end
    
    if pos.x == destwarp.pos.x and pos.y == destwarp.pos.y and pos.z == destwarp.pos.z then
    	doPlayerSendCancel(cid, warp.config.inDestiny)
    	doSendMagicEffect(pos, CONST_ME_POFF)
    	return true
    end
    
    if getCreatureMana(cid) < destwarp.mana then
    	doPlayerSendCancel(cid, warp.config.noMana)
    	doSendMagicEffect(pos, CONST_ME_POFF)
    	return true
    end
    
    if getPlayerLevel(cid) < destwarp.level then
    	doPlayerSendCancel(cid, warp.config.noLevel)
    	doSendMagicEffect(pos, CONST_ME_POFF)
    	return true
    end
    
    doSendMagicEffect(destwarp.pos, warp.config.warpEffect)
    doCreatureSay(cid, words .. " " .. param, warp.config.talkType)
    doCreatureAddMana(cid, -destwarp.mana)
    doTeleportThing(cid, destwarp.pos)
    
    return true
    end
    
    
    

     

    /data/talkactions/talkactions.xml

    <!-- Warp Gate System -->
    <talkaction words="warp" sensitive="false" event="script" value="warpgate.lua"/>
    
    
    

  5. Vou postar alguns scripts meus que estiveram engavetados. Faz algum tempo que fiz estes scripts, então não vou postar screenshots ou como configurar, mas as configurações são fáceis de entender, então divirtam-se.

     

    Um dos scripts que eu mais me diverti fazendo é, por incrível que pareça (fazer NPC é muito chato), esse aqui. É um bardo que, de tempo em tempo, começa a cantar músicas (que você pode configurar e colocar as suas próprias músicas, quantas você quiser!).

     

    Os players podem até mesmo pedir para o NPC tocar uma música por apenas 10 moedas de ouro!

    Ótimo para colocar em castelos e tavernas.

     

    /data/npc/scripts/bard.lua

    -- This script is part of The Bard
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    dofile(getDataDir() .. "/npc/scripts/songs.lua")
    
    local price = 10
    local min_delay = 300
    local max_delay = 1000
    local min_random_delay = 30
    local max_random_delay = 300
    local max_distance = 4
    local talk_duration = 180
    
    local keywords = {
    greet    = {"hi", "hello", "hey", "greetings"},
    farewell = {"bye", "goodbye", "farewell", "cya"},
    refuse   = {"no", "not"},
    song     = {"song", "songs", "list", "yes", "ok"},
    play     = {"play", "yes", "ok"},
    }
    
    local songs = BARD_SONGS
    local notes = {18, 19, 21, 22, 23, 24}
    
    local song_lyrics = false
    local song_line = 1
    local next_line = 0
    
    local next_random_song = os.time() + math.random(min_random_delay, max_random_delay)
    
    local talk = {}
    
    function is_talking(cid)
    for i, v in pairs(talk) do
    	if i == cid then
    		return true
    	end
    end
    return false
    end
    
    function get_state(cid)
    if not is_talking(cid) then
    	return false
    end
    
    return talk[cid].state
    end
    
    function get_last(cid)
    if not is_talking(cid) then
    	return false
    end
    
    return talk[cid].last
    end
    
    function get_request(cid)
    if not is_talking(cid) then
    	return false
    end
    
    return talk[cid].request
    end
    
    function set_request(cid, song)
    if not is_talking(cid) then
    	return false
    end
    
    talk[cid].request = song
    end
    
    function start_talk(cid)
    if is_talking(cid) then
    	return false
    end
    
    talk[cid] = {}
    talk[cid].state = "start"
    talk[cid].last = os.time()
    
    return true
    end
    
    function update_talk(cid, state)
    if not is_talking(cid) then
    	return false
    end
    
    if state then
    	talk[cid].state = state
    end
    
    talk[cid].last = os.time()
    
    return true
    end
    
    function stop_talk(cid)
    if not is_talking(cid) then
    	return false
    end
    
    talk[cid] = nil
    return true
    end
    
    function say(cid, msg)
    addEvent(selfSay, math.random(min_delay, max_delay), msg, cid)
    return true
    end
    
    function start_song(song)
    song_lyrics = song
    song_line = 1
    next_line = os.time()
    end
    
    function stop_song()
    song_lyrics = nil
    song_line = 1
    next_line = 0
    
    next_random_song = os.time() + math.random(min_random_delay, max_random_delay)
    end
    
    function msgcontains(message, keyword)
    if type(keyword) == "table" then
    	for i, v in ipairs(keyword) do
    		if msgcontains(message, v) then
    			return v
    		end
    	end
    	return false
    end
    
    local message = (" " .. message .. " "):lower()
    local keyword = ("[%s%p]+" .. keyword .. "[%s%p]+"):lower()
    
    local a, b = message:find(keyword)
    if a ~= nil and b ~= nil then
    	return keyword
    end
    
    return false
    end
    
    function onCreatureAppear(cid)
    return true
    end
    
    function onCreatureDisappear(cid)
    return true
    end
    
    local song_list = {}
    local song_list_str = ""
    
    for i, v in ipairs(songs) do
    if v.title then
    	table.insert(song_list, v.title)
    	song_list_str = song_list_str .. "{" .. v.title .. "}"
    	if i < #songs - 1 then
    		song_list_str = song_list_str .. ", "
    	elseif i == #songs - 1 then
    		song_list_str = song_list_str .. " and "
    	end
    end
    end
    
    function onCreatureSay(cid, type, msg)
    if getNpcDistanceTo(cid) > max_distance then
    	return true
    end
    
    if msgcontains(msg, keywords.greet) then
    	if song_lyrics then
    		say(cid, "Sorry, I'm singing now. Wait a moment...")
    	elseif not is_talking(cid) then
    		say(cid, "Hello there, friend. Would you like to hear a {song}?")
    
    		start_talk(cid)
    	else
    		say(cid, "I'm already talking with you.")
    
    		update_talk(cid)
    	end
    elseif msgcontains(msg, keywords.song) and get_state(cid) == "start" then
    	if song_lyrics then
    		say(cid, "Sorry, I'm singing now. Wait a moment...")
    	else
    		say(cid, "Very well, " .. getCreatureName(cid) .. ". For a small fee of 10 gold coins I can sing you " .. song_list_str .. ".")
    
    		update_talk(cid)
    	end
    elseif msgcontains(msg, song_list) and get_state(cid) == "start" then
    	if song_lyrics then
    		say(cid, "Sorry, I'm singing now. Wait a moment...")
    	else
    		local song = msgcontains(msg, song_list)
    
    		for i, v in ipairs(songs) do
    			if v.title == song then
    				song = v
    				break
    			end
    		end
    
    		say(cid, song.title .. (song.artist and (" by " .. song.artist) or "") .. ", huh? Okay, that will cost you " .. price .. " gold coins. Would you like me to play that song?")
    
    		set_request(cid, song)
    		update_talk(cid, "play")
    	end
    elseif msgcontains(msg, keywords.play) and get_state(cid) == "play" then
    	if song_lyrics then
    		say(cid, "Sorry, I'm singing now. Wait a moment...")
    	else
    		local song = get_request(cid)
    
    		if getPlayerMoney(cid) > price then
    			say(cid, "Then " .. song.title .. (song.artist and (" by " .. song.artist) or "") .. " it is!")
    			doPlayerRemoveMoney(cid, price)
    
    			start_song(song)
    		else
    			say(cid, "Sorry, you don't have enough money...")
    		end
    
    		update_talk(cid, "start")
    	end
    elseif msgcontains(msg, keywords.refuse) then
    	if song_lyrics then
    		say(cid, "Sorry, I'm singing now. Wait a moment...")
    	else
    		say(cid, "Well, maybe later then, huh?")
    
    		update_talk(cid, "start")
    	end
    elseif msgcontains(msg, keywords.farewell) and is_talking(cid) then
    	say(cid, "Goodbye, " .. getCreatureName(cid) .. "!")
    
    	stop_talk(cid)
    end
    
    return true
    end
    
    function onThink()
    selfFocus(0)
    
    for cid, v in pairs(talk) do
    	if not isPlayer(cid) or os.time() > v.last + talk_duration or getNpcDistanceTo(cid) > max_distance then
    		if isPlayer(cid) then
    			say(cid, "Goodbye, then...")
    		end
    		stop_talk(cid)
    	else
    		selfFocus(cid)
    	end
    end
    
    if os.time() > next_random_song and not song_lyrics then
    	local song = song_list[math.random(1, #song_list)]
    
    	for i, v in ipairs(songs) do
    		if v.title == song then
    			song = v
    			break
    		end
    	end
    
    	start_song(song)
    end
    
    if song_lyrics then
    	selfFocus(getNpcId())
    	if os.time() >= next_line then
    		local line = song_lyrics[song_line]
    		if type(line) == "string" then
    			selfSay(line)
    			next_line = os.time()
    		elseif type(line) == "number" then
    			if line == 0 then
    				doSendMagicEffect(getThingPosition(getNpcId()), notes[math.random(1, #notes)])
    				next_line = os.time()
    			else
    				next_line = os.time() + line
    			end
    		end
    		song_line = song_line + 1
    	end
    
    	if song_line > #song_lyrics then
    		stop_song()
    	end
    end
    
    return true
    end
    
    
    

     

    /data/npc/scripts/songs.lua

    -- This script is part of The Bard
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    BARD_SEND_NOTE  = 0
    BARD_PAUSE_TINY = 1
    BARD_PAUSE_SMLL = 3
    BARD_PAUSE_NORM = 5
    BARD_PAUSE_LONG = 6
    
    BARD_SONGS = {
    {
    title = "Ragnar the red",
    artist = "Bethesda, from Skyrim",
    
    BARD_PAUSE_LONG, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Oh there once was a hero named Ragnar the Red",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Who came riding to Whiterun from ole Rorikstead",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And the braggart did swagger and brandish his blade",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "As he told of bold battles and gold he had made",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "But then he went quiet, did Ragnar the Red",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "When he met the shieldmaiden Matilda who said;",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "\"Oh, you talk and you lie and you drink all our mead",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Now I think that its time that you lie down and bleed\"",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And so then came clashing and slashing of steel",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "As the brave lass Matilda charged in full of zeal",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And the braggart named Ragnar was boastful no more",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "When his ugly red head rolled around on the floor",
    BARD_PAUSE_SMLL,
    },
    
    {
    title = "My love is like a red rose",
    artist = "Robert Burns",
    
    BARD_PAUSE_LONG, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Oh, my love's like a red, red rose",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "That's newly sprung in June;",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Oh, my love's like the melodie",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "That's sweetly play'd in tune.",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "As fair art thou, my bonnie lass,",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "So deep in love am I:",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And I will love thee still, my dear,",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Till a' the seas gang dry:",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Till a' the seas gang dry, my dear,",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And the rocks melt wi' the sun:",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I will love thee still, my dear,",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "While the sands o' life shall run.",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And fare thee well, my only love",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And fare thee well, a while!",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And I will come again, my love,",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Tho' it were ten thousand mile.",
    BARD_PAUSE_SMLL,
    },
    
    {
    title = "Begging I will go",
    
    BARD_PAUSE_LONG, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Of all the trades in England, a beggin' is the best",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "For when a beggar's tired, You can lay him down to rest.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And a begging I will go, a begging I will go.",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I got a pocket for me oatmeal, and another for me rye.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I got a bottle by me side to drink when I am dry.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And a begging I will go, a begging I will go.",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I got patches on me cloak, and black patch on me knee.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "When you come to take me home, I'll drink as well as thee.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And a begging I will go, a begging I will go.",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I got a pocket for me ... and another for me malt",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I got a pair of little crutches, you should see how I can halt.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And a begging I will go, a begging I will go.",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I sleep beneath an open tree, and there I pay no rent.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Providence provides for me, and I am well content.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And a begging I will go, a begging I will go.",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I fear no plots against me. I live an open cell.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Who would be a king then when beggars live so well.",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And a begging I will go, a begging I will go.",
    BARD_PAUSE_SMLL,
    },
    
    {
    title = "Greybeard Halt",
    artist = "Will Treaty, from Rangers Apprentice",
    
    BARD_PAUSE_LONG, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Greybeard Halt is a friend of mine",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "He lives on Redmont's hill",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Greybeard Halt never took a bath",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "And they say he never will!",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, greybeard Halt",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, I say",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, greybeard Halt",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I'll see you on your way",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Greybeard Halt, he lives with goats that's what I've heard tell",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "He hasn't changed his socks for years",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "But the goats doesn't mind the smell!",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, greybeard Halt",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, I say",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, greybeard Halt",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I'll see you on your way",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Greybeard Halt is a fighting man",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I've heard common talk",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "That greybeard Halt, he cuts his hair",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "With a carving knife and fork!",
    
    BARD_PAUSE_NORM, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, greybeard Halt",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, I say",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "Fare thee well, greybeard Halt",
    BARD_PAUSE_SMLL, BARD_SEND_NOTE, BARD_PAUSE_TINY,
    "I'll see you on your way",
    BARD_PAUSE_SMLL,
    },
    }
    
    
    

     

    /data/npc/will.xml

    <?xml version="1.0"?>
    <npc name="Will" script="bard.lua">
    <look type="132" head="39" body="117" legs="2" feet="59"/>
    </npc>
    
    
    

  6. Não usem com itens stackables.

     

    Vou postar alguns scripts meus que estiveram engavetados. Faz algum tempo que fiz estes scripts, então não vou postar screenshots ou como configurar, mas as configurações são fáceis de entender, então divirtam-se.

     

    Este é um sisteminha bem simples: Coloque uma ActionID (configurável nos .xml) em um item, e ele ficará imóvel e inutilizável, servido apenas como item de exibição. Bom para fazer lojas sem aquelas bancadas pra bloquear o caminho do player. Ele não conseguirá arrastar o item nem usar, então você pode colocar o item no meio da sala, em um lugar que todos possam chegar perto. Quando algum player dá look no item, vai ser exibido no final da descrição que o item é só de exposição.

     

    /data/actions/scripts/sample.lua

    -- This script is part of Sample Items
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    function onUse(cid, item)
    if not isPlayer(cid) then
    	return true
    end
    doPlayerSendDefaultCancel(cid, RETURNVALUE_CANNOTUSETHISOBJECT)
    return true
    end
    
    
    

     

    /data/actions/actions.xml

    <!-- Sample Items -->
    <action actionid="9000" event="script" value="sample.lua"/>
    
    
    

     

    /data/creaturescripts/scripts/login.lua

    registerCreatureEvent(cid, "SampleItems")
    
    
    

     

    /data/creaturescripts/scripts/sample.lua

    -- This script is part of Sample Items
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    local actionid = 9000
    local message = "This item is just a sample."
    
    function onLook(cid, thing, pos, dist)
    local descr
    if not isPlayer(cid) or thing.actionid ~= 9000 then
    	return true
    end
    
    descr = getItemInfo(thing.itemid).description
    if descr then
    	descr = descr .. " "
    end
    descr = (descr or "") .. message
    
    doItemSetAttribute(thing.uid, "description", descr)
    
    return true
    end
    
    
    

     

    /data/creaturescripts/creaturescripts.xml

    <!-- Sample Items -->
    <event type="look" name="SampleItems" event="script" value="sample.lua"/>
    
    
    

     

    /data/movements/scripts/sample.lua

    -- This script is part of Sample Items
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    function onRemoveItem(item, tile, lastpos, cid)
    if not isPlayer(cid) then
    	return true
    end
    doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTMOVEABLE)
    doTeleportThing(item.uid, lastpos, true)
    return true
    end
    
    
    

     

    /data/movements/movements.xml

    <!-- Sample Items -->
    <movevent type="RemoveItem" actionid="9000" event="script" value="sample.lua"/>
    
    
    

  7. Vou postar alguns scripts meus que estiveram engavetados. Faz algum tempo que fiz estes scripts, então não vou postar screenshots ou como configurar, mas as configurações são fáceis de entender, então divirtam-se.

     

    O primeiro deles é uma spell, "detect life", que funciona igual à um exiva, mas detecta todos os players perto de você (incluindo monstros e npcs na versão "ultimate").

     

    /data/spells/scripts/detect life.lua

    -- This script is part of Detect Life Spell
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    -- Radius of floors that will be detected by the spell
    local floors = 0
    
    -- Magic effect shown on detected creatures
    local effect = CONST_ME_MAGIC_BLUE
    
    -- Message sent as a warning to detected players, false to deactivate
    local warn = "You feel like you're being watched."
    
    -- Message sent when no living creature is found nearby
    local nolife = "You detected no signs of life nearby."
    
    -- If true, the message will show in detail how many creatures of each type are in each direction.
    local detailed = false
    
    local area = {
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0},
    {0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0},
    {0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
    {0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
    {0, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 0},
    {0, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 0},
    {0, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 0},
    {7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 5, 5, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3},
    {0, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 0},
    {0, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 0},
    {0, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 0},
    {0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0},
    {0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0},
    {0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0},
    {0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    }
    
    local keys = {
    [1] = "north",
    [2] = "northeast",
    [3] = "east",
    [4] = "southeast",
    [5] = "south",
    [6] = "southwest",
    [7] = "west",
    [8] = "northwest",
    }
    
    function is_index(t, index)
    for i, v in ipairs(t) do
    	if index == i then
    		return true
    	end
    end
    return false
    end
    
    local function get_message(life, keys)
    local msg
    local add = {}
    
    if life.total == 0 then
    	return false
    end
    
    for key, direction in ipairs(keys) do
    	local keymsg = ""
    	local keyadd = {}
    
    	if life[key].players == 1 then
    		if not msg then
    			msg = "There is "
    		end
    		table.insert(keyadd, "a player")
    	elseif life[key].players > 1 then
    		if not msg then
    			msg = "There are "
    		end
    		table.insert(keyadd, life[key].players .. " players")
    	end
    
    	if life[key].monsters == 1 then
    		if not msg then
    			msg = "There is "
    		end
    		table.insert(keyadd, "a monster")
    	elseif life[key].monsters > 1 then
    		if not msg then
    			msg = "There are "
    		end
    		table.insert(keyadd, life[key].monsters .. " monsters")
    	end
    
    	if life[key].npcs == 1 then
    		if not msg then
    			msg = "There is "
    		end
    		table.insert(keyadd, "a NPC")
    	elseif life[key].npcs > 1 then
    		if not msg then
    			msg = "There are "
    		end
    		table.insert(keyadd, life[key].npcs .. " NPCs")
    	end
    
    	for i = 1, #keyadd do
    		if i == #keyadd and #keyadd > 1 then
    			keymsg = keymsg .. " and "
    		elseif i ~= 1 then
    			keymsg = keymsg .. ", "
    		end
    		keymsg = keymsg .. keyadd[i]
    	end
    
    	if #keyadd > 0 then
    		table.insert(add, keymsg .. " by the " .. keys[key])
    	end
    end
    
    for i = 1, #add do
    	if i == #add and #add > 1 then
    		msg = msg .. " and "
    	elseif i ~= 1 then
    		msg = msg .. ", "
    	end
    	msg = msg .. add[i]
    end
    
    return msg .. "."
    end
    
    local function detect_life(area, keys, pos, floors, effect, warn)
    local detected = {}
    local center = {}
    
    -- Make sure the area is large enought and has a center
    if #area < 1 or #area[1] < 1 or #area % 2 == 0 or #area[1] % 2 == 0 then
    	error("The size of the area is invalid.")
    	return false
    end
    
    center.y = math.ceil(#area / 2)
    center.x = math.ceil(#area[1] / 2)
    detected.total = 0
    
    for key, value in pairs(keys) do
    	detected[key] = {
    		players = 0,
    		monsters = 0,
    		npcs = 0,
    	}
    end
    
    for z = -floors, floors do
    	for y = 1, #area do
    		-- Make sure that the size of the area doesn't vary
    		if #area[y] ~= #area[1] then
    			error("The size of the area varies.")
    			return false
    		end
    		for x = 1, #area[y] do
    			local dpos = {}
    			local key = area[y][x]
    
    			dpos.x = pos.x - center.x + x
    			dpos.y = pos.y - center.y + y
    			dpos.z = pos.z + z
    			dpos.stackpos = STACKPOS_TOP_CREATURE
    
    			local thing = getThingFromPos(dpos, false)
    			if thing.uid ~= 0 and is_index(keys, key) and not isPlayerGhost(thing.uid) then
    				if isPlayer(thing.uid) then
    					if warn then
    						doPlayerSendTextMessage(thing.uid, MESSAGE_EVENT_ADVANCE, warn)
    					end
    					detected[key].players = detected[key].players + 1
    				elseif isMonster(thing.uid) then
    					detected[key].monsters = detected[key].monsters + 1
    				elseif isNpc(thing.uid) then
    					detected[key].npcs = detected[key].npcs + 1
    				end
    				detected.total = detected.total + 1
    				doSendMagicEffect(dpos, effect)
    			end
    		end
    	end
    end
    return detected
    end
    
    function onCastSpell(cid, var)
    local pos = getThingPosition(cid)
    local life = detect_life(area, keys, pos, floors, effect, warn)
    
    if not life then
    	return false
    end
    
    local message = "There " .. (life.total == 1 and "is " or "are ") .. life.total .. " creatures nearby."
    if detailed then
    	message = get_message(life, keys)
    end
    if life.total == 0 then
    	message = nolife
    end
    
    doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, message)
    
    return true
    end
    
    
    

     

    /data/spells/scripts/ultimate detect life.lua

    -- This script is part of Detect Life Spell
    -- Copyright (C) 2011 Skyen Hasus
    --
    -- This program is free software: you can redistribute it and/or modify
    -- it under the terms of the GNU General Public License as published by
    -- the Free Software Foundation, either version 3 of the License, or
    -- (at your option) any later version.
    --
    -- This program is distributed in the hope that it will be useful,
    -- but WITHOUT ANY WARRANTY; without even the implied warranty of
    -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    -- GNU General Public License for more details.
    --
    -- You should have received a copy of the GNU General Public License
    -- along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    -- Radius of floors that will be detected by the spell
    local floors = 1
    
    -- Magic effect shown on detected creatures
    local effect = CONST_ME_MAGIC_BLUE
    
    -- Message sent as a warning to detected players, false to deactivate
    local warn = false
    
    -- Message sent when no living creature is found nearby
    local nolife = "You detected no signs of life nearby."
    
    -- If true, the message will show in detail how many creatures of each type are in each direction.
    local detailed = true
    
    local area = {
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0},
    {0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0},
    {0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0},
    {0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0},
    {0, 0, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 0, 0},
    {0, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 0},
    {0, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 0},
    {0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 5, 5, 5, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
    {0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0},
    {0, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0},
    {0, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 0},
    {0, 0, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 0, 0},
    {0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0},
    {0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0},
    {0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0},
    {0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    }
    
    local keys = {
    [1] = "north",
    [2] = "northeast",
    [3] = "east",
    [4] = "southeast",
    [5] = "south",
    [6] = "southwest",
    [7] = "west",
    [8] = "northwest",
    }
    
    function is_index(t, index)
    for i, v in ipairs(t) do
    	if index == i then
    		return true
    	end
    end
    return false
    end
    
    local function get_message(life, keys)
    local msg
    local add = {}
    
    if life.total == 0 then
    	return false
    end
    
    for key, direction in ipairs(keys) do
    	local keymsg = ""
    	local keyadd = {}
    
    	if life[key].players == 1 then
    		if not msg then
    			msg = "There is "
    		end
    		table.insert(keyadd, "a player")
    	elseif life[key].players > 1 then
    		if not msg then
    			msg = "There are "
    		end
    		table.insert(keyadd, life[key].players .. " players")
    	end
    
    	if life[key].monsters == 1 then
    		if not msg then
    			msg = "There is "
    		end
    		table.insert(keyadd, "a monster")
    	elseif life[key].monsters > 1 then
    		if not msg then
    			msg = "There are "
    		end
    		table.insert(keyadd, life[key].monsters .. " monsters")
    	end
    
    	if life[key].npcs == 1 then
    		if not msg then
    			msg = "There is "
    		end
    		table.insert(keyadd, "a NPC")
    	elseif life[key].npcs > 1 then
    		if not msg then
    			msg = "There are "
    		end
    		table.insert(keyadd, life[key].npcs .. " NPCs")
    	end
    
    	for i = 1, #keyadd do
    		if i == #keyadd and #keyadd > 1 then
    			keymsg = keymsg .. " and "
    		elseif i ~= 1 then
    			keymsg = keymsg .. ", "
    		end
    		keymsg = keymsg .. keyadd[i]
    	end
    
    	if #keyadd > 0 then
    		table.insert(add, keymsg .. " by the " .. keys[key])
    	end
    end
    
    for i = 1, #add do
    	if i == #add and #add > 1 then
    		msg = msg .. " and "
    	elseif i ~= 1 then
    		msg = msg .. ", "
    	end
    	msg = msg .. add[i]
    end
    
    return msg .. "."
    end
    
    local function detect_life(area, keys, pos, floors, effect, warn)
    local detected = {}
    local center = {}
    
    -- Make sure the area is large enought and has a center
    if #area < 1 or #area[1] < 1 or #area % 2 == 0 or #area[1] % 2 == 0 then
    	error("The size of the area is invalid.")
    	return false
    end
    
    center.y = math.ceil(#area / 2)
    center.x = math.ceil(#area[1] / 2)
    detected.total = 0
    
    for key, value in pairs(keys) do
    	detected[key] = {
    		players = 0,
    		monsters = 0,
    		npcs = 0,
    	}
    end
    
    for z = -floors, floors do
    	for y = 1, #area do
    		-- Make sure that the size of the area doesn't vary
    		if #area[y] ~= #area[1] then
    			error("The size of the area varies.")
    			return false
    		end
    		for x = 1, #area[y] do
    			local dpos = {}
    			local key = area[y][x]
    
    			dpos.x = pos.x - center.x + x
    			dpos.y = pos.y - center.y + y
    			dpos.z = pos.z + z
    			dpos.stackpos = STACKPOS_TOP_CREATURE
    
    			local thing = getThingFromPos(dpos, false)
    			if thing.uid ~= 0 and is_index(keys, key) then
    				if isPlayer(thing.uid) then
    					if warn then
    						doPlayerSendTextMessage(thing.uid, MESSAGE_STATUS_DEFAULT, warn)
    					end
    					detected[key].players = detected[key].players + 1
    				elseif isMonster(thing.uid) then
    					detected[key].monsters = detected[key].monsters + 1
    				elseif isNpc(thing.uid) then
    					detected[key].npcs = detected[key].npcs + 1
    				end
    				detected.total = detected.total + 1
    				doSendMagicEffect(dpos, effect)
    			end
    		end
    	end
    end
    return detected
    end
    
    function onCastSpell(cid, var)
    local pos = getThingPosition(cid)
    local life = detect_life(area, keys, pos, floors, effect, warn)
    
    if not life then
    	return false
    end
    
    local message = "There " .. (life.total == 1 and "is " or "are ") .. life.total .. " creatures nearby."
    if detailed then
    	message = get_message(life, keys)
    end
    if life.total == 0 then
    	message = nolife
    end
    
    doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, message)
    
    return true
    end
    
    
    

     

    /data/spells/spells.xml

    <!-- Detect Life Spell -->
    <instant name="Detect Life" words="exiva vita" lvl="1" mana="0" aggressive="0" selftarget="1" exhaustion="1000" needlearn="0" event="script" value="detect life.lua"/>
    <instant name="Ultimate Detect Life" words="exiva mas vita" lvl="60" mana="820" aggressive="0" selftarget="1" exhaustion="1000" needlearn="0" event="script" value="ultimate detect life.lua"/>
    
    
    

     

    Se quiserem usem apenas uma das versões da spell, estão bem customizáveis.

  8. O erro está aqui:

    local item3 == getPlayerItemById(cid,true,1949)
    

    == é operador de igualdade (comparar se um valor é igual ao outro), e não de atribuição (dar um valor à uma variável). Use o operador de atribuição.

     

    E, por favor, idente seus códigos.

     

    function onTextEdit(cid, item, newText)
    if item.itemid == 1949 then
    	local item3 = getPlayerItemById(cid, true, 1949)
    
    	if newText ~= getItemAttribute(item3.uid, "credito") then
    		doPlayerSendTextMessage(cid, 27, "Sua senha esta incorreta.")
    	end
    
    	setPlayerStorageValue(cid, 98123, 1)
    	doPlayerSendTextMessage(cid, 27, "Seu banco está aberto.")
    end
    
    return true
    end
    

  9. Arrumei um bug que dava quando o player que estava resolvendo o puzzle era removido forçadamente de lá (com, por exemplo, /r):

    Na onThink, troque isto:

    if target == 0 then
     return true
    end
    

     

    Por isto:

    if target == 0 then
     return true
    end
    if not isPlayer(target) then
     target = 0
     doChangeSpeed(getNpcId(), -getCreatureSpeed(getNpcId()))
     doSetCreatureOutfit(getNpcId(), outfit, -1)
     doTeleportThing(getNpcId(), pos_start)
     doSendMagicEffect(pos_device, CONST_ME_MAGIC_BLUE)
     turn_stairs(false)
     return true
    end
    

     

    Recomendo colocar a sala inteira do puzzle como no-logout zone, mas o fix acima resolve o problema do player sair do jogo no meio do puzzle.

     

    Também fiz o NPC copiar o que o player fala, só que ao contrário. Quem quiser, adicione isto no final do script:

    function onCreatureSay(cid, type, msg)
     if cid == target then
       selfSay(msg:reverse())
     end
    end
    

     

    Divirtam-se.

  10. Não quis ameaçar ninguém, mas vocês já foram rippados para saber como é?

    A pelomenos 6 meses atrás eu enviei uma mensagem particular para 8 pessoas da equipe falando sobre o ripping do meu nick, e só agora o caso foi analisado por ter sido postado neste tópico, então eu acho que vocês deviam ser mais rigidos contra o ripping, pois sendo punido com alertas, quem liga?

     

    Quanto ao que o Mock falou, eu acho que deixar livre para os membros postarem é mais democrático, porém sempre tem os engraçadinhos que querem acabar com a felicidade alheia, então eu acho que os scripts postados deviam ser analisados antes de serem enviados a público, evitando problemas com códigos maliciosos e ripping.

     

    Um jeito simples, fácil e 90% eficiente para descobrir ripping é colar pequenos trechos do código no www.google.com.br e no www.cade.com.br. Se os buscadores acharem algo, vocês analisam se pode ou não ter sido ripping.

  11. Bem, por respeito aos membros e por questões éticas, o ripping na ##### é severamente punido com um banimento imediado e permanente, e os tópicos rippados nem mesmo chegam à publico. Antes mesmo que eu entrasse como moderador da seção scripting de lá, nunca vi nenhuma reclamação contra ripping.

     

    Se vocês não manterem um controle adequado contra o ripping, então também não seremos obrigados a manter, e então vocês e seus membros poderão sentir o gostinho do constrangimento de ser rippado e nada ser feito contra isso.

     

    Garanto que é amargo. :party:

  12. http://www.xtibia.com/forum/Ripping-global...em-t117038.html

    http://www.xtibia.com/forum/action-Pocao-E...es-t115319.html

    Os tópicos foram fechados, mas por que o script não foi removido, ou no mínimo, os créditos não foram adicionados?

    Continua sendo um ripping, mesmo após ter sido fechado.

     

    http://www.xtibia.com/forum/Syken-Husus-m239105.html

    Olhe o nick deste membro. Parece extremamente parecido com o meu, não acham? Eu já reportei e nada foi feito. Tenho quase certeza que esta é uma conta-dupla do usuário esK~.

     

    Quando o Mock reporta, vocês se mechem. Por que quando eu reporto vocês ignoram? Os dois scripts acima só fóram fechados com um "toque" do Mock, porque quando eu reportei, nada foi feito.

     

    Expliquem.

  13. Poxa cara, adorei o meu script.

    Você não tem vergonha de copiar o trabalho dos outros?

    Ainda coloca o crédito para outro membro sem vergonha, que copiou meu nome.

    Para quem não me conhece, sou o scripter Skyen Hasus, moderador da seção scripting da OTS Network.

     

    Perdi o interesse neste fórum quando um tal de Syken Husus copiou meu nick, pois este fórum está decaindo cada dia mais. 90% dos scripts daqui são copiados de outros fórums.

    Já enviei mensagem particular para os administradores e moderadores. Nada foi feito, mais um motivo para dizer que este fórum está decaindo.

     

    Esteja avisando, caso eu descubra o nick na OTS Network dos engraçadinhos que estão se passando por mim aqui, não vou perdoar.

     

    Minha rixa é somente com os membros esK~ e Syken Husus, e não com este fórum, mas se os administradores e moderadores se recusarem a ouvir um membro só porque possui um cargo inferior ao deles, como fizeram com minhas mensagens, a minha rixa será contra este fórum.

     

    Querem guerra? Então que esta seja declarada, e que vença o melhor.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...