meubk 257 Postado Abril 6, 2011 Share Postado Abril 6, 2011 Especificações Nome: Maze Minigame Eventos: Movement Testado em: The Forgotten Server 0.3.6 PL1 8.54 (linux-build) Autor: Skyen Hasus Copyright © 2011 Skyen Hasus Licensa: GNU General Public License v3 <http://www.gnu.org/licenses/gpl.html> Observações: Optei por fazer um minigame de labirinto por ser um clássico mundialmente conhecido, e pelo algoritmo de geração ser complexo, mas não difícil, além de poder ser totalmente aplicado em um jogo como o Tibia. O script não foi feito sob Programação Funcional, que é o paradigma mais comum usado em scripts para Open Tibia, e sim sob Orientação à Objetos. A classe do minigame é a Maze, contida em data/movements/lib/libmaze.lua Os labirintos gerados são únicos, uma vez que usam de pseudo-randomismo (math.random) para serem gerados pelo computador. Todos os labirintos gerados tem solução, pois nenhum bloco do labirinto fica isolado. Qualquer tamanho para o labirinto é aceito, porém, quanto maior o labirinto, mais o algoritmo vai demorar para gerar o labirinto. Um labirinto 50x50 foi gerado sem travamentos em minha máquina, com estas especificações: Sistema Operacional: GNU/Linux Ubuntu 10.10 - Maverick Meerkat Processador: Intel® Pentium® Dual CPU E2180 @ 2.0 GHz Memória RAM: 2.0 GB O código está todo comentado e foi previamente testado para evitar problemas. Instalação e configuração O script é instalado apenas copiando os conteúdos do arquivo zipado para suas respectivas pastas, e modificando o arquivo movements.xml para incluir o conteúdo do arquivo zipado. O script requer uma leve modificação no mapa, como mostra a imagem abaixo: O script já vem pré-configurado para o mapa acima. A área maior é a área onde será gerado o labirinto. Na imagem acima o tamanho dela é de 15x15. Este valor já vem pré-configurado no script, na linha 46 do maze.lua. maze:set_size(15, 15) Esta área deve estar preenchida com o No-Logout Zone. Os tiles destacados em verde na parte de cima devem ter a ActionID 1001 (Este valor pode ser alterado desde que o script maze.lua e o movements.xml também sejam alterados). O ActionID 1001 é o chaveamento para iniciar o labirinto. O tile destacados em vermelho na parte de baixo deve ter a ActionID 1002 (Este valor pode ser alterado desde que o script maze.lua e o movements.xml também sejam alterados). O ActionID 1002 é o chaveamento para encerrar o labirinto. Caso você deseje alterar algum dado, estas são as configurações necessárias: Relocation Position: Posição para onde serão teleportados os itens e players não relacionados ao minigame quando for executada uma limpeza do labirinto. maze:set_relocation_pos(x, y, z) Top-Left Position: Posição superior-esquerda do primeiro tile da área do labirinto. maze:set_topleft_pos(x, y, z) Exit: Posição da célula do labirinto que será usada como saída. Vale lembrar que esta célula deve estar na parte inferior do labirinto (Isto pode ser alterado mudando a linha 290 do libmaze.lua) e não é contada como uma posição em SQM do Tibia, e sim como número da célula. maze:set_exit(x, y) Wall ID: ItemID da parede do labirinto que será gerada. maze:set_wall(itemid) Para adicionar uma nova entrada no labirinto, usar: maze:add_entrance({ tile = {x=?, y=?, z=?}, init = {x=?, y=?, z=?}, exit = {x=?, y=?, z=?}, }) tile: Posição do tile com o ActionID 1001, usado para ativar o labirinto. init: Posição que o player daquele tile será teleportado assim que o minigame começar. exit: Posição que o player daquele tile será teleportado assim que o minigame acabar. Bugs conhecidos e suas soluções: Estes bugs não podem ser corrigidos via script. Abaixo seguem suas descrições e soluções. Se o player fizer logout dentro da área do labirinto, ao fazer login o mesmo não conseguirá sair de dentro. Para isso, marque toda a área do labirinto como No-Logout Zone. Se o mundo for No-PVP, os players podem passar por cima de outros (update). Caso um player já esteja sobre um dos tiles com ActionID, se outro player entrar em cima dele, o tile receberá um ID não-correspondente. Para solucionar o problema existe um patch de correção do The Forgotten Server que impossibilita a passagem de um player sobre outro em mundos No-PVP onde os tiles possuem ActionID. Arquivos /data/movements/movements.xml <!-- Maze minigame --> <movevent type="StepIn" actionid="1001;1002" event="script" value="maze.lua"/> <movevent type="StepOut" actionid="1001;1002" event="script" value="maze.lua"/> /data/movements/lib/libmaze.lua -- libmaze.lua -- This file is part of Maze minigame -- -- 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/>. -- Por questões de compatibilidade com diversas distribuições, mantive esta -- biblioteca na mesma pasta do script. -- Criação da classe Maze (Orientação à Objetos) Maze = {} -- Método útil para comparar posições function Maze:compare_pos(pos1, pos2) return pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z end -- Inicializa uma nova instância de Maze (Orientação à Objetos) function Maze:new(obj) local obj = obj or {} if not obj.entrances then obj.entrances = {} end if not obj.players then obj.players = {} end return setmetatable(obj, {__index=self}) end -- Métodos de configuração function Maze:add_entrance(template) table.insert(self.entrances, template) end function Maze:add_player(entrance, cid) self.players[cid] = entrance end function Maze:rem_players() for cid, _ in pairs(self.players) do self.players[cid] = nil end end function Maze:set_relocation_pos(x, y, z) self.relpos = {x=x, y=y, z=z} end function Maze:set_topleft_pos(x, y, z) self.topleft = {x=x, y=y, z=z} end function Maze:set_exit(x, y) self.exit = {x=x, y=y} end function Maze:set_wall(id) self.wall_id = id end function Maze:set_size(width, height) self.size = {width=width, height=height} end function Maze:set_area(template) self.area = template end -- Métodos para ler a configuração function Maze:get_entrances() return self.entrances end function Maze:get_entrance(pos) for _, entrance in ipairs(maze:get_entrances()) do if self:compare_pos(pos, entrance.tile) then return entrance end end return false end function Maze:get_players() return self.players end function Maze:get_relocation_pos() return self.relpos end function Maze:get_topleft_pos() return self.topleft end function Maze:get_exit() return self.exit end function Maze:get_wall() return self.wall_id end function Maze:get_size() return self.size end function Maze:get_area() return self.area end -- Métodos úteis para o desenvolvimento do script function Maze:get_top_left(pos) return {x=pos.x-1, y=pos.y-1, z=pos.z, stackpos=pos.stackpos} end function Maze:get_top_right(pos) return {x=pos.x+1, y=pos.y-1, z=pos.z, stackpos=pos.stackpos} end function Maze:get_bottom_left(pos) return {x=pos.x-1, y=pos.y+1, z=pos.z, stackpos=pos.stackpos} end function Maze:get_bottom_right(pos) return {x=pos.x+1, y=pos.y+1, z=pos.z, stackpos=pos.stackpos} end function Maze:get_top(pos) return {x=pos.x, y=pos.y-1, z=pos.z, stackpos=pos.stackpos} end function Maze:get_bottom(pos) return {x=pos.x, y=pos.y+1, z=pos.z, stackpos=pos.stackpos} end function Maze:get_left(pos) return {x=pos.x-1, y=pos.y, z=pos.z, stackpos=pos.stackpos} end function Maze:get_right(pos) return {x=pos.x+1, y=pos.y, z=pos.z, stackpos=pos.stackpos} end -- Método para transformar uma posição do Tibia em células do labirinto function Maze:to_maze(value) return (value-1)/2 end -- Método que verifica se todos os players estão em suas posição e se não -- há nenhum player dentro do labirinto function Maze:is_available() local start = self:get_topleft_pos() local size = self:get_size() for _, entrance in ipairs(self:get_entrances()) do local player = getTopCreature(entrance.tile) if player.uid == 0 or not isPlayer(player.uid) then return false end end for x = start.x, start.x+size.width do for y = start.y, start.y+size.height do local player = getTopCreature({x=x, y=y, z=start.z}) if isCreature(player.uid) then return false end end end return true end -- Método para pegar uma lista de células vizinhas function Maze:get_neighbors(x, y) local neighbors = { {x=x, y=y-1}, {x=x, y=y+1}, {x=x-1, y=y}, {x=x+1, y=y}, } return neighbors end -- Método para determinar se uma posição está dentro de uma área function Maze:is_valid(x, y) local size = self:get_size() local width = self:to_maze(size.width) local height = self:to_maze(size.height) return x >= 1 and x <= width and y >= 1 and y <= height end -- Método para geração de uma área limpa para o labirinto function Maze:generate_area() local size = self:get_size() -- Verifica se a área do labirinto é valida if not ((size.width-1)%2 == 0 and (size.height-1)%2 == 0) then print("Warning: Invalid size for maze area generation!") return false end -- Gera a área e suas respectivas células limpas local area = {} for x = 1, self:to_maze(size.width) do area[x] = {} for y = 1, self:to_maze(size.height) do -- Gera uma nova célula limpa area[x][y] = { visited = false, top = true, bottom = true, left = true, right = true, } end end self:set_area(area) return true end -- Método recursivo para caminhar pela área do labirinto, gerando os caminhos function Maze:handle_cell(x, y) -- Pega a área e tamanho do labirinto e copia em váriaveis locais -- para otimização do código local area = self:get_area() local size = self:get_size() -- Antes de mais nada, marca a célula atual como visitada area[x][y].visited = true; -- Pega uma lista de células vizinhas local nb = self:get_neighbors(x, y) local used = {false, false, false, false} -- Converte o tamanho do labirinto de número de tiles -- para número de células local width = self:to_maze(size.width) local height = self:to_maze(size.height) -- Enquanto a célula atual tiver vizinhas não visitadas, inicie um novo -- caminho pelo labirinto, partindo de uma célula aleatória while not (used[1] and used[2] and used[3] and used[4]) do local c = math.random(1, 4) used[c] = true -- Verifica se a célula vizinha escolhida é válida e ainda não -- foi visitada if self:is_valid(nb[c].x, nb[c].y) and not area[nb[c].x][nb[c].y].visited then -- Abre as paredes entre as duas células if c == 1 then area[x][y].top = false area[nb[c].x][nb[c].y].bottom = false elseif c == 2 then area[x][y].bottom = false area[nb[c].x][nb[c].y].top = false elseif c == 3 then area[x][y].left = false area[nb[c].x][nb[c].y].right = false elseif c == 4 then area[x][y].right = false area[nb[c].x][nb[c].y].left = false end -- Salva as modificações na área e faz a recursão self:set_area(area) self:handle_cell(nb[c].x, nb[c].y) end end -- No fim de tudo, salva a área do labirinto gerado self:set_area(area) end -- Gera um novo labirinto function Maze:generate_maze() local size = self:get_size() local centerx = math.floor(math.ceil(size.width/2)/2) local centery = math.floor(math.ceil(size.height/2)/2) self:handle_cell(centerx, centery); local area = self:get_area() local exit = self:get_exit() area[exit.x][exit.y].bottom = false self:set_area(area) end -- Método útil para limpar a área do labirinto dentro do jogo function Maze:clean(callback, wall, winner) winner = winner or false local start = self:get_topleft_pos() local size = self:get_size() -- Faz uma varredura pela área for x = start.x, start.x + size.width-1 do for y = start.y, start.y + size.height-1 do local pos = {x=x, y=y, z=start.z, stackpos=1} -- Enquanto existirem itens na posição, continue -- a enviá-los para a função callback while getThingFromPos(pos, false).uid ~= 0 do local thing = getThingFromPos(pos, false) if wall and thing.itemid == self:get_wall() then doRemoveThing(thing.uid) else callback(self, thing, winner) end pos.stackpos = pos.stackpos + 1 end end end end -- Método para aplicar uma área de labirinto gerada em uma área do Tibia -- Mesmo que uma área de labirinto seja criada e gerada, nada aparecerá no -- Tibia caso se esta função não for chamada function Maze:apply_maze() local pos = self:get_topleft_pos() local wall = self:get_wall() local area = self:get_area() local size = self:get_size() -- Faz uma varredura pela área for x = 1, self:to_maze(size.width) do for y = 1, self:to_maze(size.height) do -- Pega a célula da posição atual local cell = area[x][y] local rawpos = {x=pos.x+x*2-1, y=pos.y+y*2-1, z=pos.z} rawpos.stackpos = 1 -- Cria as paredes fixas (que não precisam ser geradas) -- em seus respectivos lugares local cpos = self:get_top_left(rawpos) if getThingFromPos(cpos, false).uid == 0 then doCreateItem(wall, cpos) end local cpos = self:get_top_right(rawpos) if getThingFromPos(cpos, false).uid == 0 then doCreateItem(wall, cpos) end local cpos = self:get_bottom_left(rawpos) if getThingFromPos(cpos, false).uid == 0 then doCreateItem(wall, cpos) end local cpos = self:get_bottom_right(rawpos) if getThingFromPos(cpos, false).uid == 0 then doCreateItem(wall, cpos) end -- Cria as paredes geradas em seus respectivos lugares local cpos = self:get_top(rawpos) if cell.top and getThingFromPos(cpos, false).uid == 0 then doCreateItem(wall, cpos) end local cpos = self:get_bottom(rawpos) if cell.bottom and getThingFromPos(cpos, false).uid == 0 then doCreateItem(wall, cpos) end local cpos = self:get_left(rawpos) if cell.left and getThingFromPos(cpos, false).uid == 0 then doCreateItem(wall, cpos) end local cpos = self:get_right(rawpos) if cell.right and getThingFromPos(cpos, false).uid == 0 then doCreateItem(wall, cpos) end end end end /data/movements/scripts/maze.lua -- maze.lua -- This file is part of Maze minigame -- -- 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/>. -- Ao carregar o script, carregar também a biblioteca dofile(getDataDir() .. "movements/lib/libmaze.lua") -- ----------------------------------------------------------------------------- -- Criação e configuração de um novo labirinto: -- ----------------------------------------------------------------------------- local maze = Maze:new() maze:add_entrance({ tile = {x=1000, y=1016, z=7}, init = {x=1000, y=1018, z=7}, exit = {x=1000, y=1014, z=7}, }) maze:add_entrance({ tile = {x=1012, y=1016, z=7}, init = {x=1012, y=1018, z=7}, exit = {x=1012, y=1014, z=7}, }) maze:set_relocation_pos(1006, 1012, 7) maze:set_topleft_pos(999, 1017, 7) maze:set_exit(4, 7) maze:set_wall(1483) maze:set_size(15, 15) -- ----------------------------------------------------------------------------- -- Gera uma nova semente aleatória, garantindo que cada labirinto seja único math.randomseed(os.time()) -- Função callback para limpeza do labirinto local function clear_thing(maze, thing, winner) local entrances = maze:get_entrances() local players = maze:get_players() if isPlayer(thing.uid) and players[thing.uid] then if winner then doPlayerSendTextMessage(thing.uid, 25, getPlayerName(winner) .. " completed the maze!") end doTeleportThing(thing.uid, entrances[players[thing.uid]].exit) else doTeleportThing(thing.uid, maze:get_relocation_pos()) end end -- Função callback principal do evento StepIn function onStepIn(cid, item, position) if not isPlayer(cid) then return true end doTransformItem(item.uid, item.itemid+1) if item.actionid == 1001 and maze:is_available() then if not maze:generate_area() then return false end maze:generate_maze() maze:clean(clear_thing, true) maze:apply_maze() for id, entrance in ipairs(maze:get_entrances()) do local player = getTopCreature(entrance.tile) doTeleportThing(player.uid, entrance.init) maze:add_player(id, player.uid) end elseif item.actionid == 1002 then local entrances = maze:get_entrances() local players = maze:get_players() doTeleportThing(cid, entrances[players[cid]].exit) doPlayerSendTextMessage(cid, 25, "You completed the maze!") maze:clean(clear_thing, false, cid) maze:rem_players() end return true end -- Função callback principal do evento StepOut function onStepOut(cid, item) if not isPlayer(cid) then return true end doTransformItem(item.uid, item.itemid-1) return true end ------ Isso aew, obrigado a todos que participou do concurso e este foi o grande vencedor. Link para o comentário Compartilhar em outros sites More sharing options...
Demonbholder 420 Postado Abril 6, 2011 Share Postado Abril 6, 2011 (editado) Como esperado do grande Skyen, um grande scripter, um exemplo disso é esse script. Até mais, e parabéns novamente. Editado Abril 6, 2011 por Demonbholder Link para o comentário Compartilhar em outros sites More sharing options...
tinfer3 6 Postado Abril 7, 2011 Share Postado Abril 7, 2011 Skyen Hasus não é scripter,é programador,ele tem grande conhecimentos em várias linguagens e códigos,como LUA. Link para o comentário Compartilhar em outros sites More sharing options...
masterzs 14 Postado Abril 8, 2011 Share Postado Abril 8, 2011 Muito Bom, Parabéns. Link para o comentário Compartilhar em outros sites More sharing options...
infernity 9 Postado Junho 3, 2011 Share Postado Junho 3, 2011 (editado) Mano teria como da uma explicada melhor como funciona o script? ele da um erro! [Error - MoveEvents Interface] data/movements/scripts/maze.lua Description: (luaDoCreateItem) Tile Not Found me explica melhor essa parte: Relocation Position: Posição para onde serão teleportados os itens e players não relacionados ao minigame quando for executada uma limpeza do labirinto. maze:set_relocation_pos(x, y, z) Top-Left Position: Posição superior-esquerda do primeiro tile da área do labirinto. maze:set_topleft_pos(x, y, z) Exit: Posição da célula do labirinto que será usada como saída. Vale lembrar que esta célula deve estar na parte inferior do labirinto (Isto pode ser alterado mudando a linha 290 do libmaze.lua) e não é contada como uma posição em SQM do Tibia, e sim como número da célula. maze:set_exit(x, y) Wall ID: ItemID da parede do labirinto que será gerada. maze:set_wall(itemid) Para adicionar uma nova entrada no labirinto, usar: maze:add_entrance({ tile = {x=?, y=?, z=?}, init = {x=?, y=?, z=?}, exit = {x=?, y=?, z=?}, }) _____________________________________________________________________________________________________________________________________ Ajudei? verdinha :button_ok: Editado Junho 3, 2011 por infernity Link para o comentário Compartilhar em outros sites More sharing options...
hugajuga 0 Postado Julho 7, 2011 Share Postado Julho 7, 2011 como começo o labirinto? Link para o comentário Compartilhar em outros sites More sharing options...
elitevini 1 Postado Julho 26, 2011 Share Postado Julho 26, 2011 Kra funcionou aqui mais eu queria sabe se ele num deveria limpa todos os wall q aparecerem quando eles pisarem na saida ? pq aqui não ta limpando eles pisam no tile de exit e o labirinto só muda não limpa tudo! vlw pelo sistema Link para o comentário Compartilhar em outros sites More sharing options...
Administrador Administrador 1435 Postado Abril 10, 2015 Administrador Share Postado Abril 10, 2015 Tópico movido para Lixeira Pública por conter link offline. Link para o comentário Compartilhar em outros sites More sharing options...
Posts Recomendados