Maze Minigame
Poketibia Script :: OPTS - Open Poketibia Server :: Zona do Scripter :: Actions, Talkactions & Moveevents
Página 1 de 1
Maze Minigame
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:
Sistema Operacional: GNU/Linux Ubuntu 10.10 - Maverick Meerkat
Processador: Intel® Pentium® Dual CPU E2180 @ 2.0 GHz
Memória RAM: 2.0 GB
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.
Top-Left Position: Posição superior-esquerda do primeiro tile da área do labirinto.
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.
Wall ID: ItemID da parede do labirinto que será gerada.
Para adicionar uma nova entrada no labirinto, usar:
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.
Arquivos
/data/movements/movements.xml
/data/movements/lib/libmaze.lua
------
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.
- Código:
maze:set_relocation_pos(x, y, z)
Top-Left Position: Posição superior-esquerda do primeiro tile da área do labirinto.
- Código:
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.
- Código:
maze:set_exit(x, y)
Wall ID: ItemID da parede do labirinto que será gerada.
- Código:
maze:set_wall(itemid)
Para adicionar uma nova entrada no labirinto, usar:
- Código:
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
- Código:
<!-- 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
- Código:
-- 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
------
Poketibia Script :: OPTS - Open Poketibia Server :: Zona do Scripter :: Actions, Talkactions & Moveevents
Página 1 de 1
Permissões neste sub-fórum
Não podes responder a tópicos
|
|