Pular para o conteúdo principal

Code frameworks

Início rápido: seu primeiro code framework

Escreva um jogo pequeno com um personagem, duas ações e uma ficha. Depois verifique e crie um personagem no The Forge.

Antes de começar

  • Você precisa de um plano pago para criar um Jogo.
  • Crie um jogo privado no The Forge. Escolha “CRIE SEU PRÓPRIO JOGO”, depois “Editor manual”, e crie o jogo.
  • Escreva os arquivos em qualquer editor e cole no editor de código-fonte, ou digite direto nele.

O jogo

Lantern Keepers tem um personagem, o Keeper. Um Keeper tem um nome, um HP máximo e o HP atual. Duas ações mudam o HP: Take damage e Heal. Heal nunca passa do máximo.

Passo 1: o manifesto

O manifest dá nome ao projeto e lista todos os Frameworks e arquivos de cenário. Mantenha o projectId que o editor criou para o seu jogo. O valor deste exemplo é só um marcador.

manifest/project.json
{
  "formatVersion": "fabled.game-project/v1",
  "projectId": "game-your-game-id",
  "projectKind": "GAME",
  "compilerProfile": "native-game-v1",
  "name": "Lantern Keepers",
  "entryFramework": "keeper",
  "frameworks": ["schemas/keeper.json"],
  "scenarios": ["scenarios/new-keeper.json", "scenarios/heal.json"]
}
  1. Linha 3 Mantenha o projectId que o editor criou para o seu jogo. Este é só um marcador.
  2. Linhas 4-5 GAME é um jogo completo com um personagem jogável. native-game-v1 é o único perfil.
  3. Linha 7 O Framework que um jogador cria primeiro.
  4. Linhas 8-9 Todo arquivo de Framework e todo arquivo de cenário está listado aqui.

Passo 2: o Framework

Um arquivo de Framework declara o que o jogador informa (entradas), o que o Keeper pode fazer (ações) e qual Frame o desenha. O construtor e cada ação apontam para um arquivo de regras.

schemas/keeper.json
{
  "id": "keeper",
  "name": "Keeper",
  "elementType": "character",
  "constructor": "rules/keeper.ts",
  "inputs": {
    "name": { "label": "Name" },
    "max_hp": { "label": "Maximum HP", "defaultValue": 10 }
  },
  "actions": [
    { "id": "hurt", "name": "Take damage", "source": "rules/hurt.ts" },
    { "id": "heal", "name": "Heal", "source": "rules/heal.ts" }
  ],
  "frames": [
    { "id": "sheet", "source": "layouts/keeper.json", "default": true }
  ]
}
  1. Linhas 2-5 Quem é o Framework: seu id, seu nome de exibição, o tipo de Element que ele cria e o arquivo de regras que cria um.
  2. Linhas 6-9 As entradas que o jogador preenche. Cada chave corresponde a um parâmetro do construtor.
  3. Linhas 10-13 As ações. Cada uma aponta para um arquivo de regras.
  4. Linhas 14-16 Os Frames que desenham um Keeper. Exatamente um é o padrão.

Passo 3: o construtor

O construtor transforma as entradas nos valores iniciais. Seu tipo de retorno lista todos os campos guardados. Um novo Keeper começa com o HP cheio.

rules/keeper.ts
export function construct(
  name: string,
  max_hp: number,
): { name: string; max_hp: number; hp: number } {
  return { name: name, max_hp: max_hp, hp: max_hp };
}
  1. Linhas 1-4 As entradas e os campos que o construtor retorna.
  2. Linha 5 Os valores iniciais. Um novo Keeper começa com o HP cheio.

Passo 4: as ações

Uma ação recebe primeiro o Element, depois suas próprias entradas. Ela só muda valores por meio do ledger. Take damage soma um valor negativo. Quando um jogador usa o botão de uma ação com entradas, como amount, o Fabled pede esses valores em uma caixa de diálogo.

rules/hurt.ts
export function hurt(self: ElementRef, amount: number): void {
  ledger.add(self, "hp", 0 - amount);
}
  1. Linha 2 ledger.add muda hp por um valor. Somar um número negativo subtrai.

Heal lê dois valores e depois define o novo HP. Ele não faz nada quando o Keeper já está com o HP cheio, então esse caso não escreve nada.

rules/heal.ts
export function heal(self: ElementRef, amount: number): void {
  const hp: number = element.var(self, "hp") as number;
  const max_hp: number = element.var(self, "max_hp") as number;
  if (hp < max_hp) {
    ledger.set(self, "hp", math.min(max_hp, hp + amount));
  }
}
  1. Linhas 2-3 Leia o HP atual e o máximo. element.var dá um valor que você converte com as number.
  2. Linha 4 Não faça nada quando o Keeper já está com o HP cheio, então esse caso não escreve nada.
  3. Linha 5 Defina hp como o menor valor entre o máximo e o HP curado.

Passo 5: o Frame

O Frame desenha a ficha. O texto mostra valores com bindings no estilo {{element.attributes.name}}. Cada botão indica um alias de ação que o contrato de dados declara.

layouts/keeper.json
{
  "version": "1.0",
  "metadata": {
    "name": "Keeper sheet",
    "bindingMode": "bound",
    "frameworkRef": "keeper",
    "context": "BLADE",
    "template": "standard-blade"
  },
  "dataContract": {
    "requiredBindings": [
      { "binding": "element.attributes.name", "expectedType": "string" },
      { "binding": "element.attributes.hp", "expectedType": "number" },
      { "binding": "element.attributes.max_hp", "expectedType": "number" }
    ],
    "optionalBindings": [],
    "actions": [
      { "name": "hurt", "functionRef": "hurt" },
      { "name": "heal", "functionRef": "heal" }
    ],
    "regions": []
  },
  "layout": {
    "kind": "container",
    "children": [
      { "kind": "text", "text": "{{element.attributes.name}}" },
      {
        "kind": "text",
        "text": "HP: {{element.attributes.hp}} / {{element.attributes.max_hp}}"
      },
      { "kind": "button", "label": "Take damage", "actionRef": "hurt" },
      { "kind": "button", "label": "Heal", "actionRef": "heal" }
    ]
  }
}
  1. Linhas 3-9 metadata dá nome ao Frame e diz a qual Framework ele pertence.
  2. Linhas 10-22 dataContract lista os campos que o Frame lê e as ações que seus botões podem chamar.
  3. Linhas 23-34 layout é a árvore que é desenhada. O texto mostra campos com chaves duplas, e os botões indicam um alias de ação.

Passo 6: os cenários

Um cenário cria um Keeper e confere seus valores iniciais.

scenarios/new-keeper.json
{
  "id": "new-keeper",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 }
}
  1. Linha 4 As entradas para criar o Keeper.
  2. Linha 5 Todo valor que o construtor deve retornar.

Este cenário sofre dano, usa Heal além do máximo e usa Heal de novo com o HP cheio. Cada passo lista as escritas exatas que a ação deve fazer. O último passo não lista nenhuma.

scenarios/heal.json
{
  "id": "heal-after-damage",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10 },
  "actions": [
    {
      "action": "hurt",
      "parameters": { "amount": 6 },
      "expectedWrites": [{ "op": "ledger.add", "key": "hp", "value": -6 }]
    },
    {
      "action": "heal",
      "parameters": { "amount": 9 },
      "expectedWrites": [{ "op": "ledger.set", "key": "hp", "value": 10 }]
    },
    { "action": "heal", "parameters": { "amount": 3 }, "expectedWrites": [] }
  ]
}
  1. Linhas 7-11 Passo um: sofrer 6 de dano. A ação deve fazer exatamente uma escrita, um ledger.add de -6.
  2. Linhas 12-16 Passo dois: curar 9. O resultado fica limitado ao máximo, então hp é definido como 10.
  3. Linha 17 Passo três: curar de novo com o HP cheio. A proteção pula a escrita, então nenhuma escrita é esperada.

Coloque no The Forge

  • Abra o seu jogo no The Forge.
  • Na etapa “Jogabilidade”, escolha “Avançado: código-fonte e versões” para abrir o editor de código-fonte.
  • Se o jogo ainda não tem projeto, escolha “Escrever por conta própria” para começar com exemplos editáveis. Não escolha “Começar a criar meu jogo”, que é o caminho com IA. Troque os arquivos de exemplo pelos arquivos acima: use “Adicionar arquivo” para novos caminhos e “Remover arquivo” para arquivos de que você não precisa. Mantenha o projectId que o editor colocou no manifest.
  • Escolha Verificar e visualizar. Corrija cada problema listado e verifique de novo.
  • Escolha Salvar projeto.
  • Escolha Preparar meu jogo. Isso cria Frameworks, Frames e conteúdo privados a partir da sua versão salva.
  • Escolha Criar um personagem neste jogo e teste Take damage e Heal na ficha.

Quebre de propósito

Mude o campo que Heal escreve de hp para mana. O compilador para e aponta o problema.

export function heal(self: ElementRef, amount: number): void {
  const hp: number = element.var(self, "hp") as number;
  const max_hp: number = element.var(self, "max_hp") as number;
  if (hp < max_hp) {
    ledger.set(self, "mana", math.min(max_hp, hp + amount));
  }
}

A verificação informa:

Action "keeper.heal" calls ledger.set(self, "mana", ...), and "mana" is not declared on this schema. Declared scalar fields: hp, max_hp, name.

Volte o nome para hp e a verificação passa. As regras só podem escrever campos que o construtor retorna.

Próximos passos