Ir para conteúdo

caotic

Infante
  • Total de itens

    1599
  • Registro em

  • Última visita

  • Dias Ganhos

    7

Histórico de Reputação

  1. Upvote
    caotic deu reputação a Tonynh em 2º CAMPEONATO MUNDIAL DE JV CHEQUERS   
    AEUHAEUHAEUHAEUHAUEHAUHEAUHEAUHEUHAEUH RI DO GSOUZA KRL
     
    +bônus
    gui bailando

  2. Upvote
    caotic deu reputação a LuckOake em Comprando Pontos De Exp   
    @Skymagnum
     
    Valeu, cara. Tô tentando melhorar ao máximo. Eu vou seguir seu conselho e estudar o return, mas nessa linha:
     

    if not pay(cid, tonumber(param) * exp_cost) and not tonumber(param) or tonumber(param) > maxim then doPlayerSendCancel(cid, "Você não tem dinheiro, ou não digitou número ou quis comprar muita exp o máximo permitido é " .. maxim .. " pontos.")
     
    Precisava especificar mais o erro, por isso não reduzi assim.
     
    E sim, o XTibia quebra a identação se você usar o espaçamento com a tecla TAB. Com a barra de espaço não quebra mesmo.
     
    @Newtonnotwen
     
    Identação é o espaçamento que vem antes das funções para melhor organização do script.
     
    Sem identação:
     

    function onSay(cid, words) if isPlayer(cid) then doPlayerAddItem(cid, 1234, 1) end return true end
     
    Com identação:
     

    function onSay(cid, words) if isPlayer(cid) then doPlayerAddItem(cid, 1234, 1) end return true end
     
    Geralmente é usada a tecla TAB no bloco de notas ou em qualquer editor de texto que você usar, mas como o XTibia quebra a identação do TAB, o povo às vezes identa com barra de espaço mesmo.
  3. Upvote
    caotic deu reputação a LuckOake em Comprando Pontos De Exp   
    Obrigado, galera, tô fazendo o possível pra evoluir e tentar virar um Vodkart da vida heaueahuaheuhae
     
    @milbradt
     
    Bom, ai eu teria que adaptar o script pra storage dos pontos, nada muito complexo.
  4. Upvote
    caotic deu reputação a LuckOake em Comprando Pontos De Exp   
    Olá olá galere do Ékz. Eu, na necessidade de treinar scripting, fico pensando no que fazer. Pesquisando scripts em lugares, vi um que comprava EXP, mas é daqueles simples que ninguém acha graça que é tipo:
     

    Fulano: !buyexp Fulano ganhou 15k de XP.
     
    Pesquisei e não achei nenhum mais legal. Então eu fiz um que compra Exp por cada ponto. Exemplo, se você configurar pra cada Exp point ser 2 gold coins, se eu comprar 20, pagarei 40 gold coins.
     
    Compreenderam? Vamos ao script.
     
    talkactions.xml:
     

    <talkaction words="!buyexp" event="script" value="buyexp.lua"/>
     
    buyexp.lua:
     
     
     
    Exemplo:
     

    Fulano: !buyexp 100 Você comprou 100 pontos de experiência por 100 gold coins.
     
    Obrigado
  5. Upvote
    caotic deu reputação a Mock em Map auto-explorer 1.01   
    simples, diminue o delay na config.
  6. Upvote
    caotic deu reputação a Skyen em Warp Gate   
    Wow! Gostei bem mais do seu! Como fez essa janelinha de seleção?
  7. Upvote
    caotic deu reputação a Skyen em Warp Gate   
    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"/>
  8. Upvote
    caotic deu reputação a Skyen em Detect Life   
    Isso é bem simples de fazer, mas vai dar muito lixo no mapa. Suponha que encontre 18 criaturas, vai adicionar 18 marcas no mapa...
    Bem, se mesmo assim quiser, aqui estão as versões modificadas:
     
    /data/spells/scripts/detect life.lua

    -- This script is part of Detect Life Spell -- Copyright (C) 2011 AbouTibia, 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(cid, 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) doPlayerAddMapMark(cid, dpos, MAPMARK_FLAG, getCreatureName(thing.uid)) end end end end return detected end function onCastSpell(cid, var) local pos = getThingPosition(cid) local life = detect_life(cid, 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 AbouTibia, 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) doPlayerAddMapMark(cid, dpos, MAPMARK_FLAG, getCreatureName(thing.uid)) end end end end return detected end function onCastSpell(cid, var) local pos = getThingPosition(cid) local life = detect_life(cid, 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
     

  9. Upvote
    caotic deu reputação a Skyen em getPlayerMaxSkill(cid, skillid) e getPlayerMaxMagLevel(cid)   
    Que eu saiba, o intervalo teórico de spells e magic level é de 0 à 255 (inteiro sem sinal de 8 bits). Não sei se na prática isso fica diferente, mas creio que todas as classes deveriam ter o mesmo limite (limite de um uint8)...
     
    Quais valores sua função retornou para as classes padrões do Tibia?
  10. Upvote
    caotic deu reputação a Skyen em formatTimeBySeconds(seconds)   
    O raciocínio não é meu, essa é a técnica padrão pra trabalhar com conversão de data e hora, que é utilizada à muitos anos. Apenas expliquei como funciona, porque acho que todo programador vai ter esse problema um dia, e todos deveriam saber resolver de forma eficiente. (Eu já fiz conversão de data e hora com while também!)
     
    Agora pode parecer coisa besta, mas conforme você vai melhorando, começa a perceber que vários problemas teriam uma solução mais rápida, e começa a pensar na otimização do seu código. Um exemplo de experiência própria: estou fazendo uma biblioteca (que daqui uns dias vai ficar pronta e vou postar aqui), e entre uma das utilidades dela, possui uma função para gerar círculos à partir de uma posição central e o raio do círculo.
     
    A ideia inicial é pegar o raio, duplicar, e fazer dois fors percorrendo todas as posições dentro da área formada pelo dobro do raio. Para um círculo de raio 5, por exemplo, a função rodaria os loops 100 vezes ((5*2)*(5*2)). Pode não parecer, mas esse é um numero muito grande em questão de desempenho. Imagine um círculo de raio 500, seriam 1000000 iterações!! Seu servidor travaria por vários segundos (talvez até minutos), e provavelmente seria morto pelo sistema operacional por usar memória demais dependendo do que você estiver fazendo.
     
    Para quem não se importa com otimização, isso é suficiente. Mas quando você começa a se preocupar com otimização, algumas soluções bem mais fáceis e rápidas surgem em mente. Para resolver o problema acima, eu apenas espelhei um dos quadrantes do círculo. Afinal, ele é simétrico! Todos os quadrantes são iguais, apenas invertidos. Meu código ficou 4 vezes mais rápido, pois agora para um círculo de raio 5, eu percorro uma área apenas 5*5!
     
    Depois disso, pensei em uma outra otimização bem mais complexa, onde eu efetivamente só passo pelas posições que o circulo vai realmente ocupar. Ou seja, se meu círculo tem área 5, só vão ser executados 5 loops. Isso faz muita diferença, principalmente quando você possui 300 jogadores fazendo várias coisas. Cada milisegundo é importante.
  11. Upvote
    caotic deu reputação a SmiX em Promotion System 1.0 - By SmiX   
    Oi galerinha, tudo bem? Queria dizer que é uma honra fazer scripts para todos vocês, aproveitando quero pedir avaliação de todos sobre o sistema. Bom, me espelhando no RPG perdido, utimamamente, pelos otservidores criei esse sistema. Que funciona basicamente assim: Há 5 tipos de promotions para cada vocação, cada uma com o seu nível, como podem ver a baixo a tabela de Promotions existentes no sistema:
     
    ////////////////////////////////////////////////////////////////// |
    Sorcerer -> Pyromantic -> Incendiary -> Cloud Walker -> Skyer Mage |
    Druid -> Lightbringer -> God Hands -> Bloody Priest -> Death Disciple |
    Paladin -> Hunter -> Head Hunter -> Ninja -> Strong Assassin |
    Knights -> Barbarians -> Hellborn -> Gladiators -> Imperial Gladiators |
    ////////////////////////////////////////////////////////////////// |
     
    O player pode trocar de vocação dando certo(s) item(s) ao npc(configurável). Vale lembrar que a cada nível de vocação é multiplicado pelo quantidade de itens que o player tem que entregar; ou seja, se o player estiver no nível 1 e a quantidade de itens a ser entregue for igual a "5" na próxima troca de vocação ele terá que entregar "10" itens e na terceira troca "15" e assim por diante.
     
    Vamos a instalação:
    (Substitua todo) data/XML/vocations.xml:

    <?xml version="1.0" encoding="UTF-8"?> <vocations> <vocation id="0" name="None" description="none" needpremium="0" gaincap="5" gainhp="5" gainmana="5" gainhpticks="6" gainhpamount="1" gainmanaticks="6" gainmanaamount="1" manamultiplier="4.0" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="0" attackable="no"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.5" club="2.0" sword="2.0" axe="2.0" distance="2.0" shielding="1.5" fishing="1.1" experience="1.0"/> </vocation> <vocation id="1" name="Sorcerer" description="a sorcerer" needpremium="0" gaincap="25" gainhp="15" gainmana="10" gainhpticks="6" gainhpamount="5" gainmanaticks="3" gainmanaamount="5" manamultiplier="1.1" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="1"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.5" club="2.0" sword="2.0" axe="2.0" distance="2.0" shielding="1.5" fishing="1.1" experience="1.0"/> </vocation> <vocation id="2" name="Druid" description="a druid" needpremium="0" gaincap="25" gainhp="15" gainmana="5" gainhpticks="3" gainhpamount="5" gainmanaticks="6" gainmanaamount="5" manamultiplier="3.0" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="4"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="3" name="Paladin" description="a paladin" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="4" name="Knight" description="a knight" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="5" name="Pyromantic" description="a pyromantic" needpremium="0" gaincap="25" gainhp="15" gainmana="10" gainhpticks="6" gainhpamount="5" gainmanaticks="3" gainmanaamount="5" manamultiplier="1.1" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="1"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.5" club="2.0" sword="2.0" axe="2.0" distance="2.0" shielding="1.5" fishing="1.1" experience="1.0"/> </vocation> <vocation id="6" name="Lightbringer" description="a lightbringer" needpremium="0" gaincap="25" gainhp="15" gainmana="5" gainhpticks="3" gainhpamount="5" gainmanaticks="6" gainmanaamount="5" manamultiplier="3.0" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="4"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="7" name="Hunter" description="a hunter" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="8" name="Barbarian" description="a barbarian" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="9" name="Incendiary" description="a Incendiary" needpremium="0" gaincap="25" gainhp="15" gainmana="10" gainhpticks="6" gainhpamount="5" gainmanaticks="3" gainmanaamount="5" manamultiplier="1.1" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="1"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.5" club="2.0" sword="2.0" axe="2.0" distance="2.0" shielding="1.5" fishing="1.1" experience="1.0"/> </vocation> <vocation id="10" name="God Hands" description="a God Hands" needpremium="0" gaincap="25" gainhp="15" gainmana="5" gainhpticks="3" gainhpamount="5" gainmanaticks="6" gainmanaamount="5" manamultiplier="3.0" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="4"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="11" name="Head Hunter" description="a Head Hunter" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="12" name="Hellborn" description="a Hellborn" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="13" name="Cloud Walker" description="a Cloud Walker" needpremium="0" gaincap="25" gainhp="15" gainmana="10" gainhpticks="6" gainhpamount="5" gainmanaticks="3" gainmanaamount="5" manamultiplier="1.1" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="1"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.5" club="2.0" sword="2.0" axe="2.0" distance="2.0" shielding="1.5" fishing="1.1" experience="1.0"/> </vocation> <vocation id="14" name="Bloody Priest" description="a Bloody Priest " needpremium="0" gaincap="25" gainhp="15" gainmana="5" gainhpticks="3" gainhpamount="5" gainmanaticks="6" gainmanaamount="5" manamultiplier="3.0" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="4"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="15" name="Ninja" description="a Ninja" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="16" name="Gladiators" description="a Gladiators" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="17" name="Skyer Mage" description="a Skyer Mage" needpremium="0" gaincap="25" gainhp="15" gainmana="10" gainhpticks="6" gainhpamount="5" gainmanaticks="3" gainmanaamount="5" manamultiplier="1.1" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="1"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.5" club="2.0" sword="2.0" axe="2.0" distance="2.0" shielding="1.5" fishing="1.1" experience="1.0"/> </vocation> <vocation id="18" name="Death Disciple" description="a Death Disciple" needpremium="0" gaincap="25" gainhp="15" gainmana="5" gainhpticks="3" gainhpamount="5" gainmanaticks="6" gainmanaamount="5" manamultiplier="3.0" attackspeed="2000" soulmax="100" gainsoulticks="120" fromvoc="4"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="19" name="Strong Assassin" description="a Strong Assassin" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> <vocation id="20" name="Imperial Gladiators" description="a Imperial Gladiators" needpremium="1" gaincap="25" gainhp="15" gainmana="5" gainhpticks="2" gainhpamount="10" gainmanaticks="4" gainmanaamount="10" manamultiplier="3.0" attackspeed="2000" soulmax="200" gainsoulticks="15" fromvoc="4" lessloss="30"> <formula meleeDamage="1.0" distDamage="1.0" wandDamage="1.0" magDamage="1.0" magHealingDamage="1.0" defense="1.0" magDefense="1.0" armor="1.0"/> <skill fist="1.1" club="1.1" sword="1.1" axe="1.1" distance="1.4" shielding="1.1" fishing="1.1" experience="1.0"/> </vocation> </vocations>
     
    Agora em: data/npc/setPromotion.xml

    <?xml version="1.0" encoding="UTF-8"?> <npc name="Cap. Oxinar" script="setPromotion.lua" walkinterval="1" floorchange="0" access="5" level="1" maglevel="1"> <health now="150" max="150"/> <look type="158" head="50" body="113" legs="113" feet="113" corpse="2212"/> <parameters> <parameter key="message_greet" value="Hello |PLAYERNAME|, you want to change your vocation? talk: {info}."/> </parameters> </npc>
     
    Agora crie: data/npc/scripts/setPromotion.lua

    local keywordHandler = KeywordHandler:new() local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) local talkState = {} function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end function onThink() npcHandler:onThink() end function creatureSayCallback(cid, type, msg) if(not npcHandler:isFocused(cid)) then return false end local config = { [1] = {itemNeeded = 2160, count = 1, new={"Sorcerer", "Pyromantic", "Incendiary", "Cloud Walker", "Skyer Mage"}}, [2] = {itemNeeded = 2160, count = 2, new={"Druid", "Lightbringer", "God Hands", "Bloody Priest", "Death Disciple"}}, [3] = {itemNeeded = 2160, count = 1, new={"Paladin", "Hunter", "Head Hunter", "Ninja", "Strong Assassin"}}, [4] = {itemNeeded = 2160, count = 1, new={"Knight", "Barbarians", "Hellborn", "Gladiators", "Imperial Gladiators"}}, } --------------- local novoc = {17,18,19,20} local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid local msg, x = string.lower(msg), config[getPlayerStorageValue(cid, 1893)] if(msgcontains(msg, 'info'))then if getPlayerStorageValue(cid, 1893) < 1 then setPlayerStorageValue(cid, 1893, getPlayerVocation(cid)) end if isInArray(novoc, getPlayerVocation(cid)) then selfSay("Sorry, you're on your last level of his vocation.", cid) talkState[talkUser] = 0 return true else selfSay("--Info-- \nYou can go from ["..getPlayerVocationName(cid).."] to ["..x.new[getPlayerStorageValue(cid, 1894)+1].."]", cid) talkState[talkUser] = 1 return true end elseif(msgcontains(msg, 'set vocation')and talkState[talkUser] == 1) then if(doPlayerRemoveItem(cid, x.itemNeeded, x.count*getPlayerStorageValue(cid, 1894))) then doPlayerSetVocation(cid, getPlayerVocation(cid)+4) selfSay("Congratulations, your new vocation was changed to: "..getPlayerVocationName(cid).."", cid) setPlayerStorageValue(cid, 1894, getPlayerStorageValue(cid, 1894)+1) else selfSay("Sorry, you need a "..x.count*getPlayerStorageValue(cid, 1894).." "..getItemNameById(x.itemNeeded)..""..(x.count == 1 and "" or "s")..".", cid) talkState[talkUser] = 0 return true end end return true end npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) npcHandler:addModule(FocusModule:new())
     
    Lembrando, eu não balanciei as vocações, deixei isso a seu gosto.
    Só isso, fui...
  12. Upvote
    caotic deu reputação a Slicer em formatTimeBySeconds(seconds)   
    o raciocinio tb poderia ter vindo dessa funçao aki q vem como padrao na lib.. ^^

    string.timediff = function (diff) local format = { {"week", diff / 60 / 60 / 24 / 7}, {"day", diff / 60 / 60 / 24 % 7}, {"hour", diff / 60 / 60 % 24}, {"minute", diff / 60 % 60}, {"second", diff % 60} } local t = {} for k, v in ipairs(format) do local d, tmp = math.floor(v[2]), "" if(d > 0) then tmp = (k < table.maxn(format) and (table.maxn(t) > 0 and ", " or "") or " and ") .. d .. " " .. v[1] .. (d ~= 1 and "s" or "") table.insert(t, tmp) end end return t end
  13. Upvote
    caotic deu reputação a Tonynh em 2º CAMPEONATO MUNDIAL DE JV CHEQUERS   
    +bônus
    pra divertir a moçada, eu acho auhahu
     
    http://i.imgur.com/1SFtI.png
  14. Upvote
    caotic deu reputação a Tonynh em 2º CAMPEONATO MUNDIAL DE JV CHEQUERS   
    Tem do leo tbm
    Leo segurando uma Naja
     

  15. Upvote
    caotic deu reputação a Oneshot em getPlayerMaxSkill(cid, skillid) e getPlayerMaxMagLevel(cid)   
    Então, Raposa, lá no cliente do Tibia, realmente os valores são variáveis em uint8. É por isso mesmo que as skills "quebram", quando passam de 255, ou seja, quando um jogador tivesse skill 256, o cliente mostraria nível 0 na skill.
     
    Nas sources do maldito TFS, não sei se é o mesmo para o Open Tibia original, o sistema já é bem diferente. A experiência de uma skill, também chamada de skill tries, é declarada como uma variável uint64 e o nível da skill uma variável uint32. O mesmo ocorre para magic level.
     
    Esse limite de skill acontece porque chega uma hora que a fórmula de skill tries necessárias passa o valor máximo de uint64, reiniciando do 0, é nessa hora que o sistema entende que já não é possível mais avançar tal skill.
     

     
    Estes são os valores máximos para as vocações padrões, sem modificações nos multiplicadores do vocations.xml
     

    Sorcerer fist fighting = 110 club fighting = 69 sword fighting = 69 axe fighting = 69 distance fighting = 70 shielding = 109 fishing = 445 magic level = 389 Druid fist fighting = 110 club fighting = 79 sword fighting = 79 axe fighting = 79 distance fighting = 80 shielding = 109 fishing = 445 magic level = 389 Paladin fist fighting = 232 club fighting = 232 sword fighting = 232 axe fighting = 232 distance fighting = 440 shielding = 428 fishing = 445 magic level = 110 Knight fist fighting = 435 club fighting = 435 sword fighting = 435 axe fighting = 435 distance fighting = 132 shielding = 428 fishing = 445 magic level = 34
     
    Os multiplicadores de skill ou o manaMultiplier são inversamente proporcionais ao nível máximo que uma vocação pode atingir. Se você diminuir o multiplicador, o nível máximo aumenta e vice-versa.
     

  16. Upvote
    caotic deu reputação a LuckOake em isPar(Number)   
    A função é até bem útil, mas o Skymagnum já tinha feito ela de uma maneira mais simplificada.
     
    http://www.xtibia.com/forum/topic/194266-getnumbertypen/
  17. Upvote
    caotic deu reputação a Skyen em formatTimeBySeconds(seconds)   
    FYI, essa função é custosa (demora mais que o método ideal pra ser executada, ou seja, é lenta) por causa dos whiles. Imagine o seu exemplo com o os.time(). Só para calcular os anos, o while rodou 43 vezes.
     
    Existe um método melhor para converter esse tipo de dados, usando módulo (em Lua, é o operador %). O módulo retorna o resto de uma divisão, ou seja:

    8 % 3 => 2
    Significa que 8 dividido por 3 resta 2.
     
    Usando isso, podemos converter um número de segundos em minutos, horas, ou o que for, usando uma operação matemática, que pode ser executada "instantaneamente".
     
    Supondo que temos 100 segundos, que equivalem à 1 minuto e 40 segundos. Se dividirmos 100 por 60, obteremos 1.666... A parte fracionária desse número (0.666...) equivale aos outros 40 segundos. Se pegarmos somente e parte inteira (1), já temos o número de minutos! Mas agora precisamos ajustar o número de segundos que sobraram. Para isso, usamos o módulo para pegar o resto da divisão. Se pegarmos o resto da divisão de 100 por 60, dá 40. Com isso, podemos fazer o código que pega o número de minutos do os.time(), por exemplo.

    local seconds = os.time() local minutes = math.floor(seconds / 60) -- Pega o número de minutos em "seconds" seconds = seconds % 60 -- Ajusta os segundos que sobraram
     
    Para pegar o número de horas em tantos minutos, dias em tantas horas, meses em tantos dias, anos em tantos meses e et cetera, a ideia é exatamente a mesma.

    local seconds = os.time() local minutes, hours, days, months, years minutes = math.floor(seconds / 60) seconds = seconds % 60 hours = math.floor(minutes / 60) minutes = minutes % 60 days = math.floor(hours / 24) hours = hours % 24 months = math.floor(days / 30) days = days % 30 years = math.floor(months / 12) months = months % 12
     
    Edit: Só por curiosidade, o os.time() retorna o tempo do sistema, que geralmente é medido em número de segundos decorridos desde o "epoch" (no tempo POSIX). Ou seja, o número de segundos desde 1 de Janeiro de 1970. Por isso dá 43 anos. Estamos em 2013: 2013-1970=43!
     

  18. Upvote
    caotic recebeu reputação de LuckOake em Manipulando Arquivos Remotos(XML)   
    Ola galerinha.
    Eu sempre percebi que usam muito o arquivo de texto para arquivo remotos.
    Mesmo que não apresenta muita diferença entre a manipulação do XML E TXT e interessante aprender a manipular os dois corretamente.
     
    O XML e mais usado para configurações e o arquivo de texto e mais usado para "guardar".
    Existe outros arquivos remotos como a própria extensão lua onde suas tables e funções podem ser acessadas remotamente.
    Um exemplo e sua pasta lib.
     
    A melhor formula de procurar valores em um arquivo xml e usando funções para manipulação de strings.
    Primeiro precisamos achar o arquivo xml.
    Vamos declarar um variável local com o diretório do arquivo.
    Aplicamos o modulo "r"(Obs: Existem outros módulos mais por enquanto utilizaremos este)
     

    local dir = io.open("data/banana/tangerina.xml", "r")
     
    Acessamos a pasta "banana" e abrimos arquivos tangerina.xml.
     
    O arquivo XML tinha salvo este texto.
     

    <fruta="Banana" quem gosta="Beeki"/> <fruta="Tangerina" quem gosta="Vodkart"/> <fruta="Uva" quem gosta="LuckOake"/> <fruta="Mamão" quem gosta="OneShot"/> <fruta="Bosta" quem gosta="Stigal"/>
     
    Então se consideramos a configuração "beeki gosta da fruta Banana".
    Então queremos ver em um script lua qual fruta gosta o beeki:
     

    for i in events:read("*a"):gmatch('fruta="(.-)" quem gosta="Beeki"') do
     
    Não se assuste irei explicar.
     
    O for serve para que ela se repita ou seja ela pode procurar outros valores(Se existir outros valores)
    O string.gmatch procuras as linhas.
     

    for i in events:read("*a"):gmatch('O QUE VEM ANTES DO QUE PROCURA(.-)O QUE VEM DEPOIS QUE VOCÊ PROCURA(Esta parte finaliza a busca)') do
     
    O sinal (.-) significa o valor que você quer procurar.
     
    Alguns exemplos:
     
    Quero todas as frutas no xml!
     

    for i in events:read("*a"):gmatch('fruta="(.-)"') do
     
    Quem gosta de tangerina?
     

    for i in events:read("*a"):gmatch('fruta="Tangerina" quem gosta = "(.-)"') do
     
    Quero os nomes das pessoas do xml!
     

    for i in events:read("*a"):gmatch('quem gosta="(.-)"') do
     
     
    Diquinhas(dicas):
     
     
     
    E protinho fim de tutorial.
    Não sou bom em organização de tópicos
    Obrigado aos usuários que foram acessados remotamente(Eu não pedi autorização)
  19. Upvote
    caotic deu reputação a 20cm em formatTimeBySeconds(seconds)   
    Bom, o que ela faz é retornar o valor em horas, minutos e segundos baseado em um tempo de segundos...
    Por exemplo:
     
     
    Espero que tenham entendido
    A função:

    http://pastebin.com/Mkqf7gJe
     
    Observações:
    Ela retorna um array com, respectivamente, as horas, os minutos e os segundos do tempo que você inseriu
     
    EXEMPLO USANDO 3665 SEGUNDOS

    time = 3665 print(horas: "..formatTimeBySeconds(time)[1].." / minutos: "..formatTimeBySeconds(time)[2].." / segundos: formatTimeBySeconds(time)[3])
    Resultado no console: horas: 1 / minutos: 1 / segundos: 5
    Preferi deixar assim porque você nem sempre precisa saber as horas, ou os segundos de um tempo.
     
    Coisas legais que descobri:
    os.time() tem 43 anos, 19 dias (vai aumentando conforme o tempo passa) - ou, caso prefira, 377.150 horas!
  20. Upvote
    caotic recebeu reputação de Yan Oliveira em Manipulando Arquivos Remotos(XML)   
    Ola galerinha.
    Eu sempre percebi que usam muito o arquivo de texto para arquivo remotos.
    Mesmo que não apresenta muita diferença entre a manipulação do XML E TXT e interessante aprender a manipular os dois corretamente.
     
    O XML e mais usado para configurações e o arquivo de texto e mais usado para "guardar".
    Existe outros arquivos remotos como a própria extensão lua onde suas tables e funções podem ser acessadas remotamente.
    Um exemplo e sua pasta lib.
     
    A melhor formula de procurar valores em um arquivo xml e usando funções para manipulação de strings.
    Primeiro precisamos achar o arquivo xml.
    Vamos declarar um variável local com o diretório do arquivo.
    Aplicamos o modulo "r"(Obs: Existem outros módulos mais por enquanto utilizaremos este)
     

    local dir = io.open("data/banana/tangerina.xml", "r")
     
    Acessamos a pasta "banana" e abrimos arquivos tangerina.xml.
     
    O arquivo XML tinha salvo este texto.
     

    <fruta="Banana" quem gosta="Beeki"/> <fruta="Tangerina" quem gosta="Vodkart"/> <fruta="Uva" quem gosta="LuckOake"/> <fruta="Mamão" quem gosta="OneShot"/> <fruta="Bosta" quem gosta="Stigal"/>
     
    Então se consideramos a configuração "beeki gosta da fruta Banana".
    Então queremos ver em um script lua qual fruta gosta o beeki:
     

    for i in events:read("*a"):gmatch('fruta="(.-)" quem gosta="Beeki"') do
     
    Não se assuste irei explicar.
     
    O for serve para que ela se repita ou seja ela pode procurar outros valores(Se existir outros valores)
    O string.gmatch procuras as linhas.
     

    for i in events:read("*a"):gmatch('O QUE VEM ANTES DO QUE PROCURA(.-)O QUE VEM DEPOIS QUE VOCÊ PROCURA(Esta parte finaliza a busca)') do
     
    O sinal (.-) significa o valor que você quer procurar.
     
    Alguns exemplos:
     
    Quero todas as frutas no xml!
     

    for i in events:read("*a"):gmatch('fruta="(.-)"') do
     
    Quem gosta de tangerina?
     

    for i in events:read("*a"):gmatch('fruta="Tangerina" quem gosta = "(.-)"') do
     
    Quero os nomes das pessoas do xml!
     

    for i in events:read("*a"):gmatch('quem gosta="(.-)"') do
     
     
    Diquinhas(dicas):
     
     
     
    E protinho fim de tutorial.
    Não sou bom em organização de tópicos
    Obrigado aos usuários que foram acessados remotamente(Eu não pedi autorização)
  21. Upvote
    caotic deu reputação a Skyen em Qual é a coisa mais importante em um código? E por que é a elegância?   
    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.
     



     
    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:
     

     
    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:

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




     
    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:
     

     
    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:
     

     
    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:
     

     
    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!!)
     

  22. Upvote
    caotic deu reputação a Oneshot em getSpectatorsFromArea   
    Como eu acho que a função getSpectators é um pouco complicadinha de mexer, pois precisa de vários parâmetros, digamos, complicados, fiz ela um pouco mais versátil, precisando apenas da posição superior-esquerda e da posição inferior-direita.
     

    function getSpectatorsFromArea(fromPosition, toPosition, multifloor) local lenght = ((math.max(fromPosition.x, toPosition.x) - math.min(fromPosition.x, toPosition.x)) * 0.5) + 1 local width = ((math.max(fromPosition.y, toPosition.y) - math.min(fromPosition.y, toPosition.y)) * 0.5) + 1 if multifloor == true then local ret = {} for f = math.min(fromPosition.z, toPosition.z), math.max(fromPosition.z, toPosition.z) do local tmp = getSpectators({x = math.min(fromPosition.x, toPosition.x) + lenght, y = math.min(fromPosition.y, toPosition.y) + width, z = f}, lenght, width, false) for k = 1, #tmp do table.insert(ret, tmp[k]) end end return ret end return getSpectators({x = math.min(fromPosition.x, toPosition.x) + lenght, y = math.min(fromPosition.y, toPosition.y) + width, z = fromPosition.z}, lenght, width, false) end
     
    Caso você use a opção multifloor, a função retorna uma tabela com todos os spectators do intervalo dos floors das posições.
     

    getSpectators({x = 100, y = 100, z = 7}, {x = 200, y = 200, z = 11}, true)
     
    No caso acima retornaria todos os spectators do floor 7 ao floor 11
     
    Abraços.
  23. Upvote
    caotic recebeu reputação de Skailord em Como ajeitar a outfice da vocação   
    Este script já existe.
     
    http://www.xtibia.com/forum/topic/202276-vocation-outfit-v10/
  24. Upvote
    caotic deu reputação a MaXwEllDeN em Repeatfunction(Func, Stopfunc, Sin, Result, Quant, ...)   
    Bem como o Demonbholder disse, dá pra simplificar MUITO usando loadstring, sintaxe do loadstring:

    loadstring("print 'oi'")()
     
    Exemplo de uso:

    function repeatFunction(func, qnt, cond, vars) for a = 1, qnt do if loadstring(vars .. " return ".. cond)() then loadstring("local cid = _G['cid'] ".. func)() end end return true end repeatFunction("print('oi')", 5, "a > 0", "a = 2")
  25. Upvote
    caotic deu reputação a Skyen em Weather System   
    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"/>
  • Quem Está Navegando   0 membros estão online

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