Runs, combates e replay
Fluxo autoritativo
- Crie a run com
POST /api/v1/runs, informandosettingId,modeIde a revisão publicada. Informe também a seed quando o cliente precisar reproduzir uma partida externa. O modo é explícito porque seleciona todo o grafo de regras, inclusive o sistema de zonas de cartas. - Leia
GET /api/v1/runs/{runId}/sessione guarde sequência, steps, revisão e fingerprint. O agregado completo continua emGET /runs/{runId}, mas não precisa ser reenviado a cada jogada. - Consuma mapa e comandos da sessão; consultas individuais continuam disponíveis.
- Envie uma mutação ao gateway
/commandscom as versões observadas. - Para encontro ativo, use o gateway de combate associado à run.
- Após o recibo, sincronize a sessão na sequência confirmada e publique a revisão inteira. Consulte sessão interativa para concorrência e retries.
O contrato v1 é a única superfície HTTP exposta. Clientes que precisam de retry, reconexão ou replay devem usar os gateways de comando descritos aqui. Não existem rotas alternativas para criar combate, executar ação, avançar IA, encerrar turno ou forçar resultado fora da run.
Retry da criação
O POST de criação aceita commandId UUID. Guarde o mesmo ID e request durante
retries; um novo ID representa outra tentativa, inclusive com a mesma seed.
O retry retorna a criação original (sequência 1), não o estado atual: para
continuar, consulte o GET da run. Mesmo ID com inputs diferentes retorna 400.
Sem chave, requests com seed/inputs idênticos também recuperam a criação;
sem seed nem chave, cada POST cria uma tentativa nova. eligibility e
creationReceipt são outputs da engine, nunca listas de permissões enviadas
pelo cliente. Veja captura e concessões.
Exemplo: avançar uma run
POST /api/v1/runs/{runId}/commands
Content-Type: application/json
{
"commandId": "a4c5ba8f-6af4-4d84-96fb-f979c3ad598b",
"expectedSequence": 3,
"expectedStep": 7,
"type": "ADVANCE_NODE",
"payload": { "targetNodeId": "combat-2" }
}
Reenviar exatamente esse request deve devolver o recibo já persistido. Enviar
o mesmo commandId com payload diferente é inválido. Se outra decisão tiver
vencido a corrida, a resposta é 409 e informa a sequência e o step atuais.
Encontro e combate
START_ENCOUNTER e RESOLVE_COMBAT são comandos de run porque modificam
simultaneamente a progressão.
O início do encontro separa a identidade da instância (instanceId) da definição
de conteúdo (definitionId). Cada participante declara lado e controlador;
recursos iniciais opcionais são um dicionário genérico por instância:
POST /api/v1/runs/{runId}/commands
Content-Type: application/json
{
"commandId": "54dc2f17-37cb-4e77-91ca-8464367f49e6",
"expectedSequence": 1,
"expectedStep": 1,
"type": "START_ENCOUNTER",
"payload": {
"participants": [
{
"instanceId": "player", "definitionId": "player_warrior",
"sideId": "player", "controllerBinding": { "kind": "Player" }
},
{
"instanceId": "enemy_1", "definitionId": "enemy_goblin",
"sideId": "opposition", "controllerBinding": { "kind": "AI", "policyId": "gambit" }
}
],
"initialResourceValues": {
"player": { "energy": 3 }
}
}
}
IDs de instância devem ser únicos, mas várias instâncias podem compartilhar a mesma definição. Uma referência ou override desconhecido rejeita o comando inteiro.
Dentro do encontro, use:
GET /api/v1/combats/{combatId}para o read model;GET /api/v1/combats/{combatId}/legal-actionspara todos os comandos legais do ator ativo, já acompanhados do preview canônico;GET /cards/evaluationspara projetar toda a mão sem N+1;GET /cards/{cardInstanceId}/evaluationpara custos, alvos, upgrades, cálculos e fontes contextuais de uma carta;POST /commandscomPLAY_CARD,EXECUTE_ACTIONouEND_TURN;GET /resolutions/{commandId}para retomar a fila visual durável.
O payload de PLAY_CARD aceita cardInstanceId, actorId, targetIds e
costOptionId opcional. A definição da carta nunca é enviada pelo cliente: a
engine compila o container e aplica seus upgrades a partir da revisão fixada
na run. EXECUTE_ACTION fica reservado às habilidades configuradas do ator.
Inspeção de cartas
A avaliação individual e a avaliação em lote usam os mesmos compiladores,
resolvedores de upgrade e o mesmo LegalActionResolver empregado pelo gateway,
pelas políticas de IA e por PLAY_CARD. isPlayable considera condição, custo, alvo, ator ativo, fase
e orçamento de ações. Quando os alvos selecionados tornam a jogada legal, a
resposta também inclui previewSteps, calculations e previewApplications
produzidos pela mesma transação pura de PLAY_CARD, sem alterar a run.
O nível é uma regra fixada no game mode. Resolved expõe legalidade, valores e
traces de buckets; Full, usado pelo sandbox, acrescenta ator, candidatos a
alvo, status, relíquias, modificadores e políticas que podem influenciar a
carta. Disabled bloqueia a projeção. A Godot deve tratar essa resposta como o
único read model de regras de carta e limitar-se a apresentação e input.
Transformações permanentes de cartas
Fora do encontro ativo, uma atividade CardUpgrade pode oferecer UPGRADE_CARD
e, com opt-in no JSON, REMOVE_CARD_TRANSFORMATION e
REPLACE_CARD_TRANSFORMATION. Todos usam o gateway /runs/{runId}/commands.
Remove envia cardInstanceId/transformationId; Replace também envia upgradeId.
A engine reconstrói e valida a composição inteira antes de persistir.
GET /runs/{runId}/cards/{cardInstanceId}/upgrade-options retorna operações
realmente executáveis na revisão fixada da run, incluindo categoria, slot e
transformação selecionada. /available-commands expõe as mesmas opções por
tipo de comando. Não consultar um catálogo mutável para inferir compatibilidade.
O histórico preserva operações removidas/substituídas e é reexecutável em branches.
Schemas autorais, snapshots de bundle, limites e configuração da atividade estão
em Transformações permanentes.
Ações legais, IA e intents
O endpoint /legal-actions devolve candidatos em ordem determinística. Cada
candidato contém o comando que pode ser reenviado ao gateway, applications,
cálculos, steps e resolutionFingerprint produzidos pelo executor canônico.
O campo costs contém os custos resolvidos (resourceId, amount e demais
metadados de ResolvedCardCost) dessa mesma avaliação. Custos alternativos
permanecem em candidatos distintos; a interface deve enviar a escolha exata,
sem somar alternativas nem recalcular modificadores localmente. A prévia vale
para a versão observada, não é uma reserva de recursos: o gateway revalida o
comando com expectedSequence e expectedStep.
Habilidades que não pertencem ao componente abilities do ator nunca aparecem
e também são rejeitadas se enviadas manualmente.
Uma política de IA recebe exatamente essa coleção e apenas filtra/ordena seus itens. Gambits são definições fixadas pela revisão da run; não possuem cache, CRUD, loader ou fallback próprios. Predicados usam o runtime comum de fórmulas, e seletores de alvo têm desempate ordinal explícito. A ação escolhida é avaliada novamente pela mesma fronteira antes de integrar o commit automático.
Buckets configurados com grouping podem retornar groups no rastro de cálculo. Cada entrada traz a chave, o total bruto (raw), o valor após a curva (transformed), o peso (weight) e a contribuição efetiva (effective). parameters informa os parâmetros efetivos quando disponíveis. Em AddPercent, os valores do grupo são frações, por exemplo 0.30 representa 30%.
Os intents no snapshot contêm previewApplications, previewCalculations,
previewFingerprint, decisionFingerprint e previewUncertain. O game mode
escolhe se eles são recalculados na publicação ou ficam travados até a ativação
do ator, além do comportamento Fail, Recompute ou Hide se um intent travado
deixar de ser legal.
A ordem de ativação também pertence ao conteúdo fixado da run. O campo
resolvedMode.combatRules.turnOrder descreve a estratégia e o boundary de
recálculo; combat.turnOrderState expõe a ordem, scores e, quando aplicável,
rolls de iniciativa ou gauges de ATB. A API não consulta configuração global do
servidor para tomar essa decisão.
Resolução e trace
Cada resolução informa initialCombatStateHash, finalCombatStateHash e
resolutionFingerprint. rootSequence aponta para o único commit que contém
o comando e todos os seus frames. Cada frame contém quatro visões complementares:
effectSteps: ordem, condição/chance, alvo, proveniência e hashes de cada efeito;calculations: buckets e contribuições numéricas usados pelo frame;applications: mudanças concretas de recurso, status, carta ou modifier.cardZoneSteps: entradas, saídas e ordenação de cartas em zonas, com IDs de fluxo/etapa e hashes. Movimentos disparados por efeitos também aparecem emapplications[].cardZoneSteps; a engine não interpreta o propósito da zona.
O início do encontro também gera um frame combat.initialized, portanto
movimentos de zonas configurados para encounter.started, regeneração,
relíquias e status de abertura não ficam invisíveis ao cliente.
Nos frames de ativação, newlyPlayableCardDefinitionIds e
affectedCardInstanceIds são projeções neutras; cardZoneSteps permanece o
trace completo. Campos de descarte/exílio só têm conteúdo no caminho sem grafo.
Da mesma forma, destination/disposition é None quando há um grafo: o
destino real pertence a cardZoneResolutionFlowId e aos seus passos.
Esses dados são diagnóstico e apresentação; o estado final persistido continua
sendo a autoridade.
Comandos de economia e recompensas
Cartas, recompensas, lojas e preparação não possuem rotas próprias de mutação.
Envie-os ao gateway da run com o payload correspondente:
| Tipo | Payload |
|---|---|
INVOKE_CARD_ZONE_GAMEPLAY_FLOW | { "flowId": "run.draw", "requestedCount": 1 } |
INVOKE_CARD_ZONE_FLOW | { "flowId": "tool.create-in-hand", "cardDefinitionIds": ["basic_attack"] } |
CREATE_CARD_SELECTION | { "selectionId": "basic_reward" } |
PICK_CARD_REWARD | { "selectionInstanceId": "...", "cardIds": ["..."] } |
REROLL_CARD_REWARD | { "selectionInstanceId": "...", "lockedCardIds": [] } |
CREATE_SHOP | { "shopId": "basic_shop" } |
BUY_SHOP_ITEM | { "shopInstanceId": "...", "itemId": "..." } |
CREATE_PREPARATION | { "preparationId": "basic_preparation" } |
APPLY_PREPARATION_OPTION | { "preparationInstanceId": "...", "optionId": "..." } |
As coleções /card-selections, /shops e /preparations permanecem apenas
como read models. Alterações de conteúdo seguem o fluxo administrativo de
draft e publicação, que produz uma nova revisão imutável.
INVOKE_CARD_ZONE_FLOW é uma ferramenta de modo experimental/sandbox/dev:
exige capabilityPolicy.allowCardZoneCheats e só executa fluxos do grafo
revisionado com allowedInvocations: ["Tool"]. O payload pode trazer
cardInstanceIds para selecionar instâncias, cardDefinitionIds para criação
e actorId quando o fluxo depende de um ator. Em um passo Create,
cardDefinitionId: "$input" consome exatamente as definições enviadas; a
engine injeta requestedCardCount a partir do tamanho real da lista. O
cliente não escolhe zona de destino nem ordem diretamente: isso permanece na
definição JSON do fluxo. Fluxos de lifecycle ou resolução de carta não podem
ser invocados como ferramenta.
INVOKE_CARD_ZONE_GAMEPLAY_FLOW é a mutação genérica para um modo com zonas
configuradas. Não exige cheats e executa apenas fluxos declarados com
allowedInvocations: ["GameplayCommand"] e playerInvokable: true.
Fluxos internos de recompensa e preparação não podem ser chamados
diretamente pela API. O jogador envia flowId,
requestedCount opcional e cardInstanceIds de cartas visíveis; não pode
enviar cardDefinitionIds nem actorId. O fluxo JSON decide origem,
destino, seleção, capacidade e ordem. Uma execução sem transição não produz
commit. O comando é determinístico, atômico e auditável como qualquer outro
comando da run.
DRAW_CARDS, DISCARD_CARDS, MOVE_CARDS, ADD_CARDS_TO_HAND e
SHUFFLE_DISCARD não são comandos publicados: seus nomes embutem propósitos de
pilha que a engine não pode presumir. Gameplay usa
INVOKE_CARD_ZONE_GAMEPLAY_FLOW; ferramentas usam INVOKE_CARD_ZONE_FLOW.
Recuperação e auditoria
GET /api/v1/runs/{runId}/card-zones é a projeção genérica das zonas de cartas
do grafo selecionado pelo modo. Cada zona tem zoneId, proprietário, escopo,
metadados de apresentação, contagem e cartas visíveis. contentsVisible e
orderVisible indicam o que a interface pode mostrar: quando a ordem é oculta,
as cartas são devolvidas em ordem de identidade, não na ordem real do fluxo;
quando o conteúdo é oculto, apenas a contagem é devolvida. A Godot deve usar
presentation.slot para decidir onde desenhar a zona e allowsCardPlay para
identificar as cartas jogáveis, sem inferir que draw,
hand, discard ou exhaust tenham significado especial para a engine. O
topologyHash permite comparar snapshots sem reconstruir as regras no cliente.
Fluxos também podem usar selection.targetZoneCount ou
selection.targetZoneCountFormula. Nesse caso, a engine seleciona somente a
quantidade ausente para a zona de destino alcançar a ocupação configurada. Isso
permite preparar qualquer zona no começo da run ou do encontro sem codificar o
conceito de “mão” no executor. O começo da run pode receber a variável
initialPlayableCardCount; demais fronteiras usam valores e fórmulas declarados
no próprio fluxo publicado.
As projeções específicas /deck e /hand não fazem parte do contrato: clientes
devem consumir /card-zones ou o campo cardZones do estado da run. Assim um
modo pode trocar nomes, quantidade e finalidade das zonas sem alterar a API.
Use os seguintes recursos depois de reconectar ou para suporte:
| Objetivo | Endpoint |
|---|---|
| Estado completo | GET /api/v1/runs/{runId} |
| Histórico de comandos | GET /api/v1/runs/{runId}/journal |
| Commits e timeline | /commits, /commits/{sequence} e /timeline |
Carregamento de replay
GET /api/v1/runs/{runId}/timeline?afterSequence=0&limit=25 retorna cabeçalhos
compactos de comandos/frames, throughSequence, nextCursor, hasMore e
historicalInspectionAllowed. Passe o mesmo throughSequence nas páginas
seguintes para não incluir comandos adicionados depois de abrir o histórico.
Não há StateAfter, Resolution ou cálculos completos nesta listagem.
Para um comando autorizado, consulte
GET /api/v1/runs/{runId}/commits/{sequence}/presentation: resumo canônico,
recursos, quantidade de cartas/reliquias, revisão e transcrição registrada.
O documento completo em /commits/{sequence} é uma inspeção técnica distinta.
Nenhuma dessas leituras reexecuta regras ou substitui POST /verify.
validationKind=StructuralChain não significa replay semanticamente verificado.
A demo carrega a primeira página antes das seguintes; cache e pedidos pendentes
são limitados e cancelados ao sair/trocar contexto.
| Replay semântico | POST /api/v1/runs/{runId}/verify |
| Auditoria de encontro | /api/v1/combats/{combatId}/journal e /verify |
O journal é a fonte de auditoria; eventos servem como projeção. Uma verificação de replay reexecuta comandos usando seed, versão da engine e conteúdo fixado, comparando hashes em vez de confiar no estado enviado pelo cliente.
Core volátil: consultas de apresentação
CardPresentationFacts.referencedRules publica nomes, descrições e contribuições
de status, modificadores e receitas de condensação referenciados pelos efeitos,
obtidos da mesma revisão imutável da carta. Não executa regras nem consome RNG.
Os atributos em explanations incluem scale, offset, operation,
resourceField (para recursos), applied e contributionValue. Este último é
o valor consumido pelo cálculo canônico, não uma estimativa da interface.
O cliente pode apresentar coeficientes simples e filtrar atributos não aplicados;
não deve reconstruir a pipeline, aplicar mitigação ou sortear crítico.
Fórmulas arbitrárias usam uma explicação genérica, sem inventar uma decomposição.
GET /api/v1/runs/{runId}/cards/{cardInstanceId}/transformation-preview aceita
operation=Apply|Remove|Replace, upgradeId e, quando necessário, transformationId.
Retorna compatibilidade, diagnósticos, custos e containers efetivos before/after,
com revisão, sequência e step capturados. Consultar não compra a transformação.
Quando compatível, inclui também beforePresentation e afterPresentation: fatos
canônicos de apresentação (CardPresentationFacts) com custos, efeitos e valores
base compostos. A interface usa esses fatos para mostrar as cartas Antes/Depois;
não interpreta patches nem calcula os valores a partir dos containers efetivos.
A mutation continua no gateway de comandos, com controle de versão e idempotência.
As evaluations de combate incluem previewScope e procs. O escopo captura hashes
do snapshot, alvos e opção de custo. Sorteios previstos continuam determinísticos;
dependsOnRandomInputs significa que a previsão não deve ser reutilizada após
qualquer alteração do contexto. GET não avança RNG. procs agrupa impactos,
consumo de stacks e continuações gerados pelo executor canônico; não é outra
autoridade de gameplay. Zonas ocultas não autorizam inspeção de suas cartas.