Arquitetura da missão
Não há scripts. Há lógica: como os nós e variáveis estão organizados em Romergo
Quando se fala em lógica no editor de jogos, geralmente pensa-se rapidamente em scripts. Algum autor escreve JavaScript em algum lugar, Python em outro, e em outros estuda a própria linguagem do motor. Isso dá liberdade quase ilimitada, mas junto traz erros de sintaxe, ciclos travados, plugins incompatíveis e código que, seis meses depois, até mesmo o autor tem medo de abrir.
No Romergo eu segui um caminho diferente. Não há scripts arbitrários dentro da missão. Não é possível inserir uma função, fazer uma solicitação de rede ou obter acesso direto ao player. Em vez disso, a história é construída a partir de um conjunto limitado, mas suficientemente expressivo: nós lógicos, variáveis, condições e efeitos.
Isso não significa que no Romergo só se pode fazer ramificações simples. É possível lembrar as decisões do jogador, contar pontos, abrir e ocultar opções, escolher diferentes rotas nos capítulos seguintes, criar eventos aleatórios e desenvolver interfaces interativas. Apenas, em vez do comando "execute este código", o autor descreve "nesse estado, deve acontecer isso".
O gráfico já é um programa
A lógica mais básica Romergo está diretamente no mapa do capítulo. As cenas são conectadas por transições, e nós especiais decidem para onde a jogabilidade continuará.
Se simplificarmos bastante, uma pequena missão se parece com isto:
Início
↓
Conversa com o guarda
↓
Tem passe?
├─ sim → Arquivo fechado
└─ não → Caminho alternativo
Esta já é uma lógica executável. Player entra no nó inicial, mostra a cena, lê o estado do histórico e escolhe a transição apropriada. O autor vê a mesma rota como um mapa compreensível, e não como um conjunto de if, goto e identificadores de serviço.

*Fragmento do Flow real em Builder: a cena Final Accusation transmite a decisão para Switch, então vários If / Else verificam o estado e encaminham a história para diferentes finais. O painel de lógica aberto na parte inferior mostra todo o conjunto disponível: Dead End, Checkpoint, If / Else, Switch, Random e Teleport.*
Atualmente, há seis nós principais no painel de lógica:
- **Fim do jogo** interrompe a rota atual. Isso pode ser derrota, final ruim ou apenas um desfecho intencionalmente fechado.
- **Checkpoint** salva o estado neste ponto e inicia uma nova história de retorno. O jogador poderá voltar ao checkpoint, mas não poderá retroceder além dele.
- **Se / Senão** verifica uma regra. A saída verdadeira segue pela condição que corresponde, a saída falsa permanece como rota alternativa.
- **Interruptor** é adequado quando há mais de duas opções. Seus ramos são verificados de cima para baixo: a primeira condição correspondente é acionada, e o ramo sem condição se torna a opção padrão.
- **Aleatório** escolhe uma das saídas conectadas. A aleatoriedade mantém a semente e o passo no estado de progresso, então é possível restaurar o salvamento sem lançar o jogador de forma caótica para outra rota.
- **Teleporte** por si só não decide nada. Ele ajuda a quebrar uma longa conexão visual e a conectar cuidadosamente partes distantes de um grande mapa.
Início e Fim emolduram o capítulo, cenas comuns mostram o conteúdo e nós lógicos os transformam em uma rota. Já nesse nível é possível montar uma história linear, uma bifurcação, vários finais, um evento aleatório e um ponto seguro de retorno — sem uma única linha de script.
Uma variável é a memória da história
Um único grafo não é suficiente se a história precisar se lembrar do que aconteceu antes. Para isso, na missão existem variáveis de três tipos simples:
booleanarmazena um fato: se a chave foi encontrada, se o herói mentiu, se o alarme foi acionado;numberarmazena um número: confiança, saúde, quantidade de pistas ou pontos de reputação;stringarmazena um valor escolhido: nome da rota, facção ou código de solução.
Cada variável tem uma chave, uma legenda compreensível para o autor e um valor inicial. Uma variável numérica pode ter, adicionalmente, mínimo e máximo descritos, e uma variável de serviço — ser oculta no painel de status do jogador ou mostrada apenas quando uma condição for cumprida.
O ponto principal aqui é que as variáveis pertencem à progressão de toda a missão, e não a uma cena específica. Se o jogador obteve um passe no primeiro capítulo, no quarto capítulo pode-se verificar a mesma sinalização. Ao passar pelo Fim para o próximo capítulo, o estado não é reiniciado. Ele é preservado junto com as opções escolhidas, efeitos aplicados, checkpoint e etapa atual de aleatoriedade.
Isso resulta em um ciclo simples do autor:
a escolha do jogador grava um valor
↓
a variável armazena o estado
↓
a condição o lê mais tarde
↓
o gráfico, cena ou interface reage
Por exemplo, na primeira cena, a escolha “Mostrar a foto encontrada” pode definir portrait_revealed = true. Mais tarde, o nó **Se / Senão** verificará esse sinalizador e abrirá uma nova cena de conversa. Ainda mais tarde, a interface do terminal mostrará uma entrada adicional apenas para os jogadores que revelaram a foto.
Uma variável conecta três partes diferentes da quest. Para isso, não é necessário passar manualmente o valor entre cenas ou capítulos: eles leem um estado de progresso compartilhado.

*A variável route em um cartão possui uma chave estável, um nome compreensível, um tipo de valor e a visibilidade do estado. As conexões com escolhas e ramificações estão abaixo, na mesma página.*
Escolhas escrevem, ramificações leem
Em uma cena comum, a maneira mais clara de mudar o estado é adicionar um efeito à opção de escolha. No editor visual, isso se parece com uma atribuição:
"Confiar em Mary" → ally = "mary"
"Ir sozinho" → ally = "none"
Depois disso, Switch pode direcionar o jogador para a cena desejada de acordo com o valor de ally. A página de variáveis mostra separadamente quais escolhas registram a variável e quais ramificações a leem. É um pequeno detalhe, mas em uma grande quest ele substitui a busca tediosa por dezenas de cenas.

*Os ramos Switch são verificados de cima para baixo. Neste exemplo, a rota Laboratory é acionada em route = lab, e Habitat em route = habitat.*
As condições suportam comparações comuns:
- igual e diferente;
- maior, maior ou igual;
- menor, menor ou igual;
- o valor existe ou ainda não foi criado.
No caso simples, o autor escolhe a variável, o operador e o valor diretamente nas configurações do If / Else ou Switch. Para uma lógica mais complexa, o runtime geral entende grupos **todas as condições**, **qualquer condição** e negação.
Tal regra pode ser descrita estruturalmente:
{
"op": "all",
"conditions": [
{ "op": "gte", "key": "evidence", "value": 3 },
{ "op": "eq", "key": "alarm_active", "value": false }
]
}
Isso se assemelha a um pequeno script, mas não é essencialmente um. Aqui não é possível chamar uma função ou alterar algo aleatório. O Romergo já conhece todas as operações permitidas, verifica a estrutura e a executa da mesma forma na visualização Builder e no Player publicado.
Efeitos: pequenos comandos sem linguagem de programação
A condição lê o estado, e o efeito o altera. Runtime Romergo suporta várias operações atômicas:
setgrava um valor específico;incedecincrementam ou decrementam um número;togglealterna uma flag lógica;clampmantém um número dentro de um limite especificado.
No inspetor comum de seleção, o cenário principal é intencionalmente simples: escolher uma variável e gravar um novo valor através de set. Na lógica avançada da interface, condições e efeitos podem ser definidos como estruturas verificáveis. Por exemplo, a transição entre estados do terminal pode simultaneamente registrar uma decisão, adicionar uma pista e acionar o alarme:

*O efeito é anexado diretamente à opção de resposta: três escolhas registram em route os valores lab, habitat e server. O campo da variável e o novo valor permanecem separados mesmo no inspetor estreito.*
[
{ "op": "set", "key": "terminal_decision", "value": "cut-power" },
{ "op": "inc", "key": "evidence", "amount": 1 },
{ "op": "toggle", "key": "alarm_active" }
]
Essa abordagem tem uma limitação importante: não é uma calculadora de fórmulas arbitrárias. Não é possível escrever evidence * trust / 2, executar um ciclo ou criar sua própria função. O comportamento complexo é montado a partir de várias etapas explícitas, e a expansão da lógica ocorre por meio de novas operações verificáveis runtime, e não pela execução de código desconhecido.
Para o autor, isso é um pouco menos ilimitado, mas a história continua previsível. Os mesmos efeitos podem ser testados antes da publicação, salvos, sincronizados entre os jogadores e reproduzidos após o carregamento do progresso.
Por que ainda não há outro Flow dentro do nó lógico
Apesar de toda a previsibilidade, a abordagem atual tem uma desvantagem honesta: para alguém sem experiência em desenvolvimento, a forma "variável — operador — valor" já pode parecer programação. E quando aparecem grupos de condições e vários efeitos, a estrutura declarativa permanece segura para Player, mas não necessariamente se torna simples para o autor.
Parece natural um dia chegar aos nós visuais e ao próprio interior da lógica. Em vez de uma lista de condições, você poderia conectar blocos **E**, **OU**, **NÃO**, comparar variáveis e alterar valores. No papel, isso parece mais amigável do que texto ou JSON.
Mas então eu imagino um cenário real: o autor abre um nó lógico no mapa do capítulo, e dentro dele encontra outro Flow com seus próprios nós e conexões. Resulta em um gráfico dentro de outro gráfico. É preciso entender onde você está agora, como voltar ao nível da história e em qual dos dois Flow procurar o erro. Para um iniciante, essa profundidade pode se mostrar mais assustadora do que a forma atual.
Portanto, por enquanto, é usado um compromisso. A rota da história permanece visual, enquanto as condições e efeitos dentro dos nós são mostrados em campos explícitos e compactos. Isso funciona, mas eu não considero a interface totalmente resolvida. Talvez, mais tarde, surja uma combinação mais clara de modelos prontos, configuração passo a passo e blocos visuais — algo que realmente reduza a complexidade, e não apenas a transfira para outro editor.
A lógica não vive apenas no mapa
As cenas de interface usam o mesmo estado. Um widget, mensagem, opção de resposta, tarefa, notificação ou fluxo de mídia só pode aparecer quando showWhen coincidir. O valor da métrica pode ser vinculado a uma variável, e a transição entre os estados do widget pode ser iniciada após um atraso, sob condição ou após a ação do jogador.
Por exemplo, a tela do computador de bordo pode se comportar assim:
1. Primeiro, apenas o terminal bloqueado é visível.
2. Após a escolha do jogador, o efeito define terminal_unlocked = true.
3. A condição abre o esquema da nave e novos botões.
4. Clicar no botão muda o estado do widget e aumenta evidence.
5. Quando evidence >= 3, uma notificação oculta aparece e um novo ramo da missão se torna disponível.
O mapa do capítulo, as escolhas comuns e a interface interativa não criam três sistemas separados. Eles leem e modificam um único objeto de estado. Portanto, uma pista encontrada no diálogo pode desbloquear um elemento da interface, e uma ação dentro da interface pode alterar a cena subsequente.
É exatamente aqui que a lógica declarativa começa a se sentir como programação de verdade. Só que, em vez de arquivos, funções e um barramento de eventos, o autor trabalha com entidades compreensíveis da história: fatos, escolhas, condições e transições.
Por que eu intencionalmente não adiciono JavaScript aleatório
A capacidade de inserir um script parece o caminho mais curto para qualquer nova mecânica. Mas assim que a missão publicada obtém o direito de executar código arbitrário, todo o modelo de produto muda.
É necessário decidir quais APIs de navegador estão disponíveis para este código, como limitar a rede e o armazenamento, o que fazer com loops infinitos, como transportar a quest offline, como sincronizá-la em um jogo cooperativo e como garantir que o projeto antigo continue funcionando após a atualização Player.
Uma regra declarativa é muito mais entediante do que uma função arbitrária — e é exatamente por isso que é mais confiável. Ela pode ser validada antes da execução. É possível entender quais variáveis ela lê e escreve. É possível salvar com segurança o progresso no meio da cadeia. É possível executar a quest da mesma forma no navegador, na build offline e no Player incorporado.
Um vocabulário limitado aqui não interfere na lógica, mas define seus limites. Se surgir uma operação realmente útil, é melhor adicioná-la ao runtime geral e oferecer a todos os autores um comportamento testado uniforme, do que forçar cada projeto a inventar seu próprio motor.
Para a sobremesa — seu próprio HTML e CSS
Ao mesmo tempo, o Romergo ainda deixa espaço para código verdadeiro onde ele é mais seguro: na criação de cenas comuns.
No projeto, você pode criar seu próprio tema de aparência no HTML e CSS e usá-lo em diferentes capítulos. HTML define a localização das áreas preparadas — janela de falas, retrato, nome do personagem, texto, pergunta e botões de escolha. CSS permite alterar significativamente essas áreas: bordas, fundo, forma dos cartões, espaçamento, tipografia e reação ao tamanho da tela.
Os temas prontos **Standard**, **Minimalism**, **Brutalism**, **Romance** e **Neon** mostram o alcance confirmado sem layout manual. Ao criar um tema personalizado, o Romergo copia o modelo selecionado: depois disso, seu HTML e CSS podem ser editados e verificados imediatamente na visualização real Player.

*Aqui não há concept art: à esquerda está mostrado o verdadeiro CSS do tema integrado Brutalism, à direita — o resultado do mesmo código na pré-visualização do telefone. O interruptor Desktop / Mobile permite verificar imediatamente as regras responsivas, e o tema personalizado começa a funcionar a partir de uma cópia do modelo selecionado.*
Mas a borda permanece a mesma. No HTML personalizado não há <script>, manipuladores de eventos, formulários, iframe ou recursos externos. CSS não pode carregar url() ou @import externos. As áreas obrigatórias da cena devem permanecer no lugar; se o modelo as perdeu, o Player retorna ao estilo padrão seguro.
Ou seja, HTML e CSS são responsáveis por **como a história se parece**, enquanto nós, variáveis, condições e efeitos são responsáveis por **como ela funciona**.
Eu gosto exatamente dessa separação. O autor pode tornar a missão visualmente diferente das outras, mas sua execução ainda faz parte do runtime geral verificável. A camada externa pode ser radicalmente redesenhada e reconstruída, sem transformar cada tema em um programa separado.
Código sem código — não é falta de lógica
O Romergo não tem o objetivo de substituir a linguagem de programação por imagens. O gráfico não é adequado para matemática complexa, e o conjunto de efeitos atômicos não se tornará um motor de automação universal.
No entanto, para uma história interativa, muitas vezes são necessárias outras coisas: lembrar um fato, alterar um contador, abrir uma opção, escolher um caminho, mostrar o estado da interface, salvar um ponto de retorno e transmitir todas essas decisões para o próximo capítulo.
Para isso, os nós e variáveis não acabam sendo uma versão simplificada de scripts, mas uma linguagem mais direta da própria história. O autor descreve não comandos para o computador, mas relações de causa e efeito do mundo: o jogador fez uma escolha, o estado mudou, a história reagiu.
E se você quiser um pouco de código de verdade, HTML e CSS ainda esperam pela sobremesa.