×

Langue

Fermer
Atelier 801
  • Forums
  • Dev Tracker
  • Connexion
    • English Français
      Português do Brasil Español
      Türkçe Polski
      Magyar Română
      العربية Skandinavisk
      Nederlands Deutsch
      Bahasa Indonesia Русский
      中文 Filipino
      Lietuvių kalba 日本語
      Suomi עברית
      Italiano Česky
      Hrvatski Slovensky
      Български Latviešu
      Estonian
  • Langue
  • Forums
  • /
  • Transformice
  • /
  • Modules
  • /
  • [Tutorial] Submodos
[Tutorial] Submodos
Bolodefchoco
« Sénateur »
1527648900000
    • Bolodefchoco#0095
    • Profil
    • Derniers messages
    • Tribu
#1
  4

Muitos amigos EN têm me perguntado como fazer um sistema similar ao #grounds (de por vários submodos dentro do jogo), então resolvi fazer o tópico. Vou fazer da melhor que eu acho melhor, mas há diversas formas (talvez até mais fáceis, porém que tem performance / flexibilidades piores) (imo)


Eu acho interessante adicionar uma tabela inicial com os dados do módulo, como nome, autor, versão, etc.
Exemplo: https://github.com/Lautenschlager-id/Grounds/blob/master/src/Module.lua

Iremos, nesse tutorial, criá-la apenas com o índice _NOME, que será o nome do module atual (padrão ou submodo x)



Code Lua

1
2
3
local modulo = {
_NOME = "primeiro"
}

Agora iremos criar a tabela modo que será primordial. Ela irá conter todos os modos de jogo do seu module! No momento, só iremos criar ela.

Exemplo: https://github.com/Lautenschlager-id/Grounds/blob/master/src/Modes.lua



Code Lua

1
local modo = {}

Agora criaremos um evento novo que será executado toda vez que o modo de jogo for alterado.

Exemplo: https://github.com/Lautenschlager-id/Grounds/blob/master/src/ModeChanged.lua



Code Lua

1
2
3
4
5
eventEmMudancaDeModo = function()
-- Aqui você reseta todas as informações globais (não relacionadas só ao módulo)
-- como, por exemplo, remover senhas, textareas, imagens, shaman, score...
-- Leia o spoiler 1 para uma ajuda que poderá ajudar nisso ^
end

1

Para saber quais textareas, popups e imagens estão ativas no modulo atual, recomendo criar um mini-handler recriando as funções.

Inicialmente criamos uma tabela global para armazenar as ids dos "objetos" citados acima.
Code Lua

1
2
3
4
5
system.objetos = {
textarea = {},
popup = {},
imagem = {}
}

Depois criamos um escopo para recriar as funções necessárias
Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
do -- Cria um novo escopo, onde não há acesso fora do bloco 
--- Imagens

local addImage = tfm.exec.addImage -- copia a função addImage do Transformice
tfm.exec.addImage = function(...) -- e recriamos ela
local id = addImage(...)

if id then -- caso não tenha dado algum problema
system.objetos.imagem[id] = true -- dizemos que aquela ID está ativa no módulo
end

return id
end

local removeImage = tfm.exec.removeImage
tfm.exec.removeImage = function(id)
removeImage(id)

-- Caso a imagem esteja ativa, deixará de estar, afinal as IDs são únicas
system.objetos.imagem[id] = nil
end

--- Textareas
local addTextArea = ui.addTextArea
ui.addTextArea = function(id, ...)
addTextArea(id, ...)

system.objetos.textarea[id] = true
end
-- Aqui não faremos o ui.addRemoveTextArea porque o mesmo ID pode ser compartilhado
-- por diversos ratos, então não há uma forma eficiente de saber se ainda estão abertas para alguém.

--- Popups
local addPopup = ui.addPopup
ui.addPopup = function(id, ...)
addPopup(id, ...)

system.objetos.popup[id] = true
end
-- Mesmo caso da textarea
end

Para utilizar as informações no eventEmMudancaDeModo, utilize da seguinte forma:
Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- É recomendável que você remova as imagens caso seu script não use imagens,
-- o mesmo para textareas e popups. Loops podem ser pesados.
eventEmMudancaDeModo = function()
-- Deletamos as imagens ativas
for id in next, system.objetos.imagem do
tfm.exec.removeImage(id)
end

-- Deletamos as textareas ativas para todos os ratos
for id in next, system.objetos.textarea do
ui.removeTextArea(id)
end

-- Como é impossível deletar uma popup, movemos elas até o inferno (céu, no caso)
for id in next, system.objetos.popup do
ui.addPopup(id, "", nil, -1500, -1500)
end

-- Apesar de termos usado o removeImage, iremos recriar/resetar nossa tabela novamente
system.objetos = {
textarea = {},
popup = {},
imagem = {}
}

--- Suas coisas aqui
end

Você também pode:

  • Desativar as teclas bindadas pros jogadores, assim como o mouse;
  • Remover cor do nome;
  • Definir o score para 0;
  • Desativar a neve;
  • Resetar as opções de desativação (AutoScore, AutoShaman, etc...)

      Code Lua

      1
      2
      3
      for k,v in next,{"AutoScore","WatchCommand","AutoNewGame","AutoShaman","AllShamanSkills","MortCommand","DebugCommand","MinimalistMode","AfkDeath","PhysicalConsumables","AutoTimeLeft"} do
      tfm.exec["disable" .. v](false)
      end

  • Resetar o espelhamento automático de mapas;
  • Redefinir o número máximo de jogadores na sala;
  • Remover a senha...



Agora que temos nosso evento criado, faremos uma outra função para obter o modo e executar o evento caso o modo realmente exista. Um getter.

Exemplo: https://github.com/Lautenschlager-id/Grounds/blob/master/src/GameMode.lua



Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- Você também poderá criar um timer para que não trocas de modo
-- não sejam feitas diversas vezes por minuto...
local obterModulo = function(nomeModulo, executarEvento)
-- not not é uma boa prática para transformar qualquer valor em
-- um boolean, deixando a variável bem mais leve do que salvar
-- o módulo inteiro nela (sem not not)
local existe = not not modo[nomeModulo]

if existe then
modulo._NOME = nomeModulo

-- Só queremos reiniciar tudo quando for uma troca de modo,
-- não quando for a primeira vez que o script for executado.
if executarEvento then
eventEmMudancaDeModo()
end
end

return existe
end

Agora uma das partes mais importantes: a definição do caractere que será usado para definir o nome do módulo.

Exemplo: https://github.com/Lautenschlager-id/Grounds/blob/master/src/RoomSettings.lua



Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
-- Criaremos uma tabela para informar o caractere e a função que ele exercerá.
-- Leia o spoiler 2 para uma função que irá adicionar administradores a partir do nome do módulo.

local opcoesSala = {}
opcoesSala[1] = {
-- Meu padrão é #, mas usarei & para não confundir com algumas coisas.
caractere = "&", -- caractere que será usado para definir o modo
-- A função sempre receberá um valor, que é o caractereVALOR_AQUI
executar = function(nomeModulo)
if nomeModulo then
-- Primeira vez que será executado, então o evento não será disparado
-- O módulo só será definido caso ele exista. Caso contrário, o módulo
-- padrão (module._NOME inicial) será ativo.
obterModulo(nomeModulo, false)
end
end
}
-- Aqui você poderá criar mais opções com caracteres diferentes para seu módulo.
-- Evite: letras, números, caracteres que não estão no teclado, +, %, $

-- E agora criaremos uma função para gerenciar essa tabela.

-- Precisaremos detectar se a sala é um cafofo ou uma #sala.
-- Para isso, verificaremos se o segundo byte do nome da sala é diferente
-- de 3 (aquele unicode estranho no nome dos cafofos)
local ehSala = string.byte(tfm.get.room.name, 2) ~= 3

-- Também coletaremos os parâmetros no nome da sala (se for uma sala).
-- O padrão encontrado na linha de código abaixo é definido por:
--[[
Pode ter um * no começo da sala, mas pode não ter;
Há de ter um # logo em seguida;
Em seguida, terá o nome do módulo principal;
Depois uma sequência de números (obrigatório, de 0 até infinito, pelo Transformice)
Depois, em (.*), significa que tudo após a sequência de números será coletado e
será colocado na variável _atributosSala_
]]
local atributosSala = ehSala and string.match(tfm.get.room.name, "%*?#" .. modulo._NOME .. "%d+(.*)")

local definirOpcoesSala = function()
if atributosSala then -- Automaticamente verifica se é sala
-- Verificaremos por um "&letras_e_underscores"
-- E executaremos a função relacionada ao &
opcoesSala[1].executar(string.match(atributosSala, opcoesSala[1].caractere .. "([%a_]+)"))

-- Para lidar com mais de um caractere, leia o spoiler 2
end
end

2

Para que possamos colocar mais de um caractere no atributos da sala, devemos adicionar mais uma tabela em opcoesSala. Faremos um sistema de administradores com isso.

Primeiro, crie uma tabela que conterá o nome de todos os administradores da sala (não o seu, amiguinho):

Code Lua

1
2
3
-- Perceba que eu utilizo a tabela _system_, que é uma tabela do Transformice.
-- Use o que você quiser, variável, campo de uma tabela, whatever
system.administrador = {}

Agora criaremos o caractere e a função dos administradores.
Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- Crie uma função para normalizar o nome do jogador
local normalizarNomeJogador = function(nomeJogador)
-- Tudo minúsculo, a primeira letra será maiúscula, apenas.
return nomeJogador:lower():gsub("%a", string.upper, 1)
end

opcoesSala[2] = {
-- Utilizarei o caractere @, mas você poderá optar por qualquer um (evite o #)
caractere = "@",
executar = function(nomeJogador)
if nomeJogador then
-- Verificamos se o nick tem mais de duas letras, já que
-- o mínimo é 3, pelo Transformice.
if #nomeJogador > 2 then -- >= 3
-- tabela[nome] = true
system.administrador[normalizarNomeJogador(nomeJogador)] = true
end
end
end
}

E a função está pronta! Para adicionarmos ela em definirOpcoesSala faça:

Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--- Antes do opcoesSala[1]

-- Coleta o nickname + tag do jogador
-- O padrão encontrado na linha de código abaixo é definido por:
--[[
Pode ter um + no começo do nome, mas pode não ter;
Há uma sequência de letras maiúscula/minúscula/números/underline(_);
Logo após há de ter um caractere # e números (que são a tag do jogador).

A pattern pode ser melhor escrita por:
(%+?[A-Z][a-z0-9_]+#%d%d%d%d)
]]
for nomeJogador in string.gmatch(atributosSala, opcoesSala[2].caractere .. "(%+?[a-zA-Z0-9_]+#%d+)") do
opcoesSala[2].executar(nomeJogador)
end

E seu sistema de administradores está pronto! Todos os jogadores informados na sala serão adicionados na tabela system.administradores.

Para lidar com mais caracteres, você poderá fazer um loop da seguinte forma:
Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
--- Depois de opcoesSala[1]

-- só execute algo se precisar.
if #opcoesSala > 2 then -- "> 1" caso seu sistema não tenha admins
-- Todos os caracteres numa string, exceto o 1 e o 2 (modo e adm).
local caracteres = ""
for id, opcao in next, opcoesSala do
if id > 2 then -- "> 1" caso seu sistema não tenha admins
caracteres = caracteres .. opcao.caractere
end
end

-- Agora faça um loop com todos de uma vez
for caractere, valor in string.gmatch(atributosSala, "([" .. caracteres .. "])([^" .. caracteres .. "]+)") do
-- Usamos uma tabela numérica porque utilizamos
-- opcoesSala[1] e opcoesSala[2] fora daqui.
for id, opcao in next, opcoesSala do
if opcao.caractere == caractere then
-- %S+ significa que é um valor sem espaços.
-- Ou seja: "va lor" terá o retorno "va"
opcao.executar(string.gmatch(valor, "%S+"))

-- paramos o loop 2 porque ele encontrou o caractere desejado
break
end
end
end
end

Resultado final com admins e outros caracteres

Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
local definirOpcoesSala = function()
if atributosSala then
for nomeJogador in string.gmatch(atributosSala, opcoesSala[2].caractere .. "(%+?[a-zA-Z0-9_]+#%d+)") do
opcoesSala[2].executar(nomeJogador)
end

opcoesSala[1].executar(string.match(atributosSala, opcoesSala[1].caractere .. "([%a_]+)"))


if #opcoesSala > 2 then
local caracteres = ""
for id, opcao in next, opcoesSala do
if id > 2 then
caracteres = caracteres .. opcao.caractere
end
end

for caractere, valor in string.gmatch(atributosSala, "([" .. caracteres .. "])([^" .. caracteres .. "]+)") do
for id, opcao in next, opcoesSala do
if opcao.caractere == caractere then
opcao.executar(string.gmatch(valor, "%S+"))

break
end
end
end
end
end
end



Agora você poderá criar seus modulos.

Exemplo: https://github.com/Lautenschlager-id/Grounds/tree/master/src/mode



Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
-- Utilize as funções no formato NOME = function(parametros)

-- Primeiramente, crie o módulo padrão (modulo._NOME inicial)
modo.primeiro = {
tempo = 0,

printar = function(onde, quem, mensagem)
print(string.format("[#%s] [%s] %s", onde, quem, mensagem))
end,
-- Em todos os módulos, crie duas funções (obrigatórias): inicio / reset

reset = function()
-- Aqui você reseta todas as informações locais (apenas relacionadas ao módulo)
-- como, por exemplo, resetar uma variável, o dinheiro dos jogadores, o tempo, etc...
-- É o _eventEmMudancaDeModo_ do módulo

modo.primeiro.tempo = 10000
end,

inicio = function()
-- Aqui você põe todas as informações locais iniciais (apenas relacionadas ao módulo)
-- como, por exemplo, as opções de desativação (AutoScore, AutoShaman, etc...)

tfm.exec.disableAutoShaman()

-- Utilize modo.primeiro.FUNCAO para acessar a função da tabela.
-- Colocar modo.primeiro numa variável não mudará a performance.
modo.primeiro.printar("chat", "jogo", "essa coisa começou rsrsrsr")

modo.primeiro.tempo = 10000
end,

-- Agora você pode por os eventos oficiais
eventLoop = function(tempo)
modo.primeiro.tempo = tempo
end,
eventNewGame = function()
modo.primeiro.printar("tribo", "jogo", "biiiirl vem jogar galera")
end
}

-- Crie quantos módulos você quiser.
-- Cuidado, quanto maior seu código, mais tempo ele poderá levar para carregar.

Paciência, leitor. Estamos perto! Agora sim, a parte mais importante e primordial do sistema: a função inicializadora.

Exemplo: https://github.com/Lautenschlager-id/Grounds/blob/master/src/Initialize.lua



Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
-- Criaremos uma tabela com o nome de todos os eventos
local nomeEventos = {"eventLoop", "eventNewGame", "eventPlayerDied", "eventPlayerGetCheese", "eventPlayerVampire", "eventPlayerWon", "eventPlayerLeft", "eventEmotePlayed", "eventKeyboard", "eventMouse", "eventPopupAnswer", "eventTextAreaCallback", "eventChatCommand", "eventChatMessage", "eventSummoningStart", "eventSummoningEnd", "eventSummoningCancel", "eventNewPlayer", "eventPlayerRespawn", "eventColorPicked"}

-- E uma outra manterá todos os eventos oficiais do modo atual
local eventos = {}

-- Criaremos uma função vazia para os eventos não usados
-- Por padrão, chamamos de foo
foo = function() end

iniciar = function(reiniciar)
-- Reiniciamos a tabela de eventos toda vez que a função for chamada
eventos = {}

-- Agora iremos criar um loop com todas as funções
for _, evento in next, nomeEventos do

-- Põe na tabela de eventos a função respectiva que está presente no modo
-- como eventos["eventLoop"] = modo.primeiro.eventLoop
-- Se não existir, a função será a _foo_, que não faz nada.
eventos[evento] = modo[modulo._NOME][evento] or foo
end

-- Se _reiniciar_ for verdadeiro, iremos reiniciar o módulo
if reiniciar then
if modo[modulo._NOME].reset then
modo[modulo._NOME].reset()
end
end

-- Por fim, inicializamos o módulo
modo[modulo._NOME].inicio()

-- Por boas práticas, faça um eventNewPlayer pra todos os jogadores
table.foreach(tfm.get.room.playerList, eventNewPlayer)
end

--- Agora faremos as 3 coisas mais importantes:
-- Criar todos os eventos oficiais
for _, evento in next, nomeEventos do
_G[evento] = eventos[evento]
end

-- Iniciar as opções de sala
definirOpcoesSala()

-- Iniciar nosso módulo pela primeira vez
iniciar(false) -- sem reiniciar

3

Para criar eventos que são ativos em qualquer módulo, você poderá criar mais um pedaço de sistema.

Veja:

Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
local eventosOficiais = {}

-- Um eventChatCommand que funciona em qualquer módulo
eventosOficiais.eventChatCommand = function(nome, comando)
if comando == "modulos" then
local modulos = ""
for nomeModulo in next, modo do
modulos = modulos .. nomeModulo .. "\n"
end

tfm.exec.chatMessage("Os módulos nessa sala são:\n" .. modulos, nome)
end
end

Depois, basta editar a inicialização dos eventos oficiais (acima do definirOpcoesSala()), assim:
Code Lua

1
2
3
4
5
6
7
8
9
for _, evento in next, nomeEventos do
_G[evento] = function(...)
if eventosOficiais[evento] then
eventosOficiais[evento](...)
end

eventos[evento](...)
end
end


Tcharam! Tá pronto o sorvetinho

http://i63.tinypic.com/nqpflg.png



Script sem comentários


Dernière modification le 1527711660000
Error_404
« Consul »
1527649020000
    • Error_404#0000
    • Profil
    • Derniers messages
#2
  2
Melhor tópico!!
Transforato
« Citoyen »
1527650160000
    • Transforato#3036
    • Profil
    • Derniers messages
    • Tribu
#3
  2
Boa explicação pena que não manjo nada desse mundo de lua.
Infectsoul
1527651480000
    • Infectsoul#2048
    • Profil
    • Derniers messages
    • Tribu
#4
[Modéré par Kiwrimai, raison : Removido a pedido do autor.]
Bolodefchoco
« Sénateur »
1527651720000
    • Bolodefchoco#0095
    • Profil
    • Derniers messages
    • Tribu
#5
  1
Ratufufu a dit :
Parabéns, tópico bem explicado.

menina vc n sabe quantas vezes tive que apagar os nomes em ingles e escrever em pt. TOC
Infectsoul
1527651900000
    • Infectsoul#2048
    • Profil
    • Derniers messages
    • Tribu
#6
[Modéré par Kiwrimai, raison : Removido a pedido do autor.]
Ninguem
« Consul »
1527708060000
    • Ninguem#0095
    • Profil
    • Derniers messages
    • Tribu
#7
  1
Eu faço um esquema mais simples, mas que tem umas limitações:
exemplo:
Code Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function jogoUm()
a = "teste"
function eventNewGame()
print(a)
end
end

function jogoDois()
a = "okay"
function eventNewGame()
print(a)
end

function eventChatCommand(player, cmd)
print("%s digitou o comando %s":format(player, cmd))
end
end

jogoUm()

Eu uso isso no Circuit, daí posso rodar scripts diferentes dependendo da sala que for. É bom para este caso, pois eu não preciso adaptar o script para um formato, basta eu copiar um script completo e colocar dentro de uma função com o nome dela.

http://img.atelier801.com/b0a4f3a7.png

O ruim é que você entrega o controle quase que total do script para o submodo, não tem como ter dois submodos ao mesmo tempo (o que seria útil, para ter um acima para interpretar comandos globais), isso pode ser resolvido com um controle dos próprios mini scripts, removendo o controle deles para eventChatCommand, por exemplo. Aí você pode fazer um eventChatCommand por fora para gerenciar.

Dernière modification le 1527708300000
Bolodefchoco
« Sénateur »
1527709740000
    • Bolodefchoco#0095
    • Profil
    • Derniers messages
    • Tribu
#8
  1
o problema é que usar funções pra isso é um pouco mais pesado do que usar as tabelas. Se você fizer o teste, verá.

Também, é como você disse, dar o controle total pode ser bem ruim.

Prefiro o método das tabelas. Possibilita vários handlers, é mais leve e flexível também.
Viniciusdara
« Consul »
1527724800000
    • Viniciusdara#0000
    • Profil
    • Derniers messages
    • Tribu
#9
  1
Quero um submodo do Ben10
Bolodefchoco
« Sénateur »
1527724920000
    • Bolodefchoco#0095
    • Profil
    • Derniers messages
    • Tribu
#10
  1
Viniciusdara a dit :
Quero um submodo do Ben10

çcpkdlsvçodfsbopdkfoçzsv tá na seção
  • Forums
  • /
  • Transformice
  • /
  • Modules
  • /
  • [Tutorial] Submodos
© Atelier801 2018

Equipe Conditions Générales d'Utilisation Politique de Confidentialité Contact

Version 1.27