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.

![Flow Romergo: Final Accusation passa por Switch e três If / Else para diferentes finais, com o painel Logic aberto na parte inferior](../assets/logic-without-scripts-flow-nodes.webp)

*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:

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:

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 página da variável route mostra a chave, o nome, o tipo e a visibilidade do estado](../assets/logic-without-scripts-variables.webp)

*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.

![Configurações Switch com os ramos Laboratory e Habitat, que comparam a variável da rota selecionada](../assets/logic-without-scripts-switch-rules.webp)

*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:

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:

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:

![Editor de cena Romergo: três opções de escolha gravam lab, habitat e server na variável route](../assets/logic-without-scripts-choice-effects.webp)

*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.

![Editor real de design Romergo: CSS do tema Brutalism e visualização móvel do diálogo](../assets/logic-without-scripts-custom-html-css.webp)

*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.

Voltar ao blog técnico