Pular para o conteúdo principal

Code frameworks

Faça o Lantern virar um jogo de verdade

Adicione uma escolha, um valor derivado, um Framework filho e uma lista ao jogo do início rápido, e prove cada passo com um cenário.

Antes de começar

Comece pelo projeto que você criou no início rápido. Cada passo indica todos os arquivos a criar ou mudar. Um trecho mostra a parte que muda, e você pode expandi-lo para ver o arquivo inteiro. Depois de cada passo, escolha Verificar e visualizar.

Quatro passos

  1. Adicione uma escolha

    Adicione uma entrada Calling com duas opções.

    schemas/keeper.json
        "calling": {
          "label": "Calling",
          "options": ["Guard", "Scout"],
          "defaultValue": "Guard"
        }
    1. Linha 11 Uma lista fixa de escolhas. O padrão deve ser uma delas.

    O construtor recebe a nova entrada e a devolve como um campo guardado.

    rules/keeper.ts
    export function construct(
      name: string,
      max_hp: number,
      calling: string,
    ): { name: string; max_hp: number; hp: number; calling: string } {
      return { name: name, max_hp: max_hp, hp: max_hp, calling: calling };
    }
    1. Linha 4 A nova entrada.
    2. Linhas 5-6 calling agora é um campo guardado.

    Os dois cenários precisam do novo valor, em parameters e em expectedValues. Comece por new-keeper.json.

    scenarios/new-keeper.json
    {
      "id": "new-keeper",
      "framework": "keeper",
      "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard"
      }
    }
    1. Linha 4 A entrada que você dá.
    2. Linha 9 O valor guardado que você espera.

    Depois faça as mesmas duas mudanças em heal.json.

    scenarios/heal.json
      "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard"
      },
    1. Linha 4 A mesma entrada.
    2. Linha 9 O mesmo valor guardado.

    Escolha Verificar e visualizar. Ele deve passar antes de você seguir.

    O projeto agora tem estes arquivos. Expanda qualquer trecho acima para ver o arquivo inteiro.

    manifest/project.json
    schemas/keeper.json
    rules/heal.ts
    rules/hurt.ts
    rules/keeper.ts
    layouts/keeper.json
    scenarios/heal.json
    scenarios/new-keeper.json
  2. Derive um valor

    Uma função do ledger deriva Guard a partir do calling. Declare-a no esquema.

    schemas/keeper.json
      "ledgerFunctions": [
        {
          "id": "guard",
          "name": "Guard",
          "field": "guard",
          "source": "rules/guard.ts"
        }
      ]
    1. Linhas 24-27 id e name identificam a função, field é o valor guardado que ela preenche e source é o arquivo de regras dela.

    Crie rules/guard.ts.

    rules/guard.ts
    function base_guard(calling: string): number {
      return calling === "Guard" ? 3 : 1;
    }
    
    export function guard(currentValue: number, self: ElementRef): number {
      const calling: string = element.var(self, "calling") as string;
      return base_guard(calling);
    }
    1. Linhas 1-3 Um auxiliar sem export.
    2. Linhas 5-7 A função do ledger lê o calling e devolve o valor derivado.

    O construtor também devolve um campo guard, com valor inicial 0. A função do ledger preenche o valor real.

    rules/keeper.ts
    export function construct(
      name: string,
      max_hp: number,
      calling: string,
    ): { name: string; max_hp: number; hp: number; calling: string; guard: number } {
      return { name: name, max_hp: max_hp, hp: max_hp, calling: calling, guard: 0 };
    }
    1. Linhas 5-6 guard faz parte do valor de retorno, começando em 0.

    Um cenário confere a função. Adicione guard aos valores esperados de new-keeper.json, e uma linha de ledger que chama a função.

    scenarios/new-keeper.json
    {
      "id": "new-keeper",
      "framework": "keeper",
      "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard",
        "guard": 3
      },
      "ledger": [{ "function": "guard", "currentValue": 0, "expected": 3 }]
    }
    1. Linha 10 O valor derivado, depois que a função do ledger rodou.
    2. Linha 12 Chama a função guard com o valor atual 0 e espera 3.

    Adicione o mesmo valor esperado a heal.json.

    scenarios/heal.json
      "parameters": { "name": "Mira", "max_hp": 10, "calling": "Guard" },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard",
        "guard": 3
      },
    1. Linha 10 O valor derivado fica em 3 neste cenário também.

    Para mostrar Guard na ficha, liste-o em requiredBindings e adicione um nó de texto que o use.

    layouts/keeper.json
          { "binding": "element.attributes.guard", "expectedType": "number" }
    1. Linha 15 O binding do novo campo.
    layouts/keeper.json
          { "kind": "text", "text": "Guard: {{element.attributes.guard}}" },
    1. Linha 32 Um nó de texto que mostra o valor.

    Escolha Verificar e visualizar. Ele deve passar antes de você seguir.

    O projeto agora tem estes arquivos. Expanda qualquer trecho acima para ver o arquivo inteiro.

    manifest/project.json
    schemas/keeper.json
    rules/guard.ts
    rules/heal.ts
    rules/hurt.ts
    rules/keeper.ts
    layouts/keeper.json
    scenarios/heal.json
    scenarios/new-keeper.json
  3. Adicione um Framework filho

    Um Keeper carrega um Lamp. O Lamp é um segundo Framework, com seu próprio esquema, construtor, Frame e cenário. Liste o esquema e o cenário dele no manifesto.

    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", "schemas/lamp.json"],
      "scenarios": [
        "scenarios/new-keeper.json",
        "scenarios/heal.json",
        "scenarios/new-lamp.json"
      ]
    }
    1. Linha 8 O esquema do Lamp se junta ao esquema do Keeper.
    2. Linhas 9-13 O cenário do Lamp se junta aos cenários do Keeper.

    Crie o esquema do Lamp.

    schemas/lamp.json
    {
      "id": "lamp",
      "name": "Lamp",
      "elementType": "item",
      "constructor": "rules/lamp.ts",
      "inputs": {
        "name": { "label": "Name" },
        "oil": { "label": "Oil", "defaultValue": 5 }
      },
      "frames": [{ "id": "card", "source": "layouts/lamp.json", "default": true }]
    }
    1. Linhas 6-9 As entradas do filho. Um filho também precisa de um nome.
    2. Linha 10 O filho tem seu próprio Frame.

    Crie o construtor, o Frame e o cenário dele.

    rules/lamp.ts
    export function construct(name: string, oil: number): { name: string; oil: number } {
      return { name: name, oil: oil };
    }
    1. Linha 1 O construtor devolve as entradas como valores guardados.
    layouts/lamp.json
    {
      "version": "1.0",
      "metadata": {
        "name": "Lamp card",
        "bindingMode": "bound",
        "frameworkRef": "lamp",
        "context": "BLADE",
        "template": "standard-blade"
      },
      "dataContract": {
        "requiredBindings": [
          { "binding": "element.attributes.name", "expectedType": "string" },
          { "binding": "element.attributes.oil", "expectedType": "number" }
        ],
        "optionalBindings": [],
        "actions": [],
        "regions": []
      },
      "layout": {
        "kind": "container",
        "children": [
          { "kind": "text", "text": "{{element.attributes.name}}" },
          { "kind": "text", "text": "Oil: {{element.attributes.oil}}" }
        ]
      }
    }
    1. Linhas 11-14 Todo campo que o Frame mostra está listado como um binding.
    scenarios/new-lamp.json
    {
      "id": "new-lamp",
      "framework": "lamp",
      "parameters": { "name": "Lantern", "oil": 5 },
      "expectedValues": { "name": "Lantern", "oil": 5 }
    }
    1. Linha 4 Cria um Lamp sozinho e confere os valores dele.

    No Keeper, uma entrada oculta guarda a referência ao Framework, e childFrameworks liga o espaço ao Framework.

    schemas/keeper.json
        "lamp": { "label": "Lamp", "defaultValue": "lamp", "hidden": true }
    1. Linha 14 Uma entrada oculta nunca aparece no assistente.
    schemas/keeper.json
      "childFrameworks": { "lamp": "lamp" }
    1. Linha 31 Liga o espaço lamp ao Framework com o id lamp.

    O construtor devolve as entradas do Lamp junto com seus próprios valores.

    rules/keeper.ts
    export function construct(
      name: string,
      max_hp: number,
      calling: string,
      lamp: FrameworkRef,
    ): {
      values: { name: string; max_hp: number; hp: number; calling: string; guard: number };
      children: { lamp: { framework: FrameworkRef; params: { name: string; oil: number } } };
    } {
      return {
        values: { name: name, max_hp: max_hp, hp: max_hp, calling: calling, guard: 0 },
        children: { lamp: { framework: lamp, params: { name: "Lantern", oil: 5 } } },
      };
    }
    1. Linha 5 lamp é um FrameworkRef. A plataforma transforma o id do Framework da entrada em um.
    2. Linhas 6-9 O tipo de retorno lista os valores guardados e os espaços filhos.
    3. Linha 12 O espaço filho: qual Framework criar e as entradas a dar a ele.

    Dê aos cenários do Keeper a entrada lamp, e espere o Lamp que o construtor cria.

    scenarios/new-keeper.json
    {
      "id": "new-keeper",
      "framework": "keeper",
      "parameters": {
        "name": "Mira",
        "max_hp": 10,
        "calling": "Guard",
        "lamp": "lamp"
      },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard",
        "guard": 3
      },
      "ledger": [{ "function": "guard", "currentValue": 0, "expected": 3 }],
      "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } }
    }
    1. Linha 8 A entrada oculta.
    2. Linha 18 O filho que o construtor cria, com seus valores.
    scenarios/heal.json
      "parameters": {
        "name": "Mira",
        "max_hp": 10,
        "calling": "Guard",
        "lamp": "lamp"
      },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard",
        "guard": 3
      },
    1. Linha 8 A entrada oculta em heal.json.
    scenarios/heal.json
      "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } }
    1. Linha 30 O filho esperado em heal.json.

    Escolha Verificar e visualizar. Ele deve passar antes de você seguir.

    O projeto agora tem estes arquivos. Expanda qualquer trecho acima para ver o arquivo inteiro.

    manifest/project.json
    schemas/keeper.json
    schemas/lamp.json
    rules/guard.ts
    rules/heal.ts
    rules/hurt.ts
    rules/keeper.ts
    rules/lamp.ts
    layouts/keeper.json
    layouts/lamp.json
    scenarios/heal.json
    scenarios/new-keeper.json
    scenarios/new-lamp.json
  4. Adicione uma lista e uma ação recusada

    Uma lista guarda conteúdo que um personagem ganha durante o jogo. O Pack guarda Trinkets, um segundo Framework cujos presets são o conteúdo. Liste o esquema dele e seus dois cenários no manifesto.

    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",
        "schemas/lamp.json",
        "schemas/trinket.json"
      ],
      "scenarios": [
        "scenarios/new-keeper.json",
        "scenarios/heal.json",
        "scenarios/new-lamp.json",
        "scenarios/new-trinket.json",
        "scenarios/pack.json"
      ]
    }
    1. Linhas 8-12 O esquema do Trinket se junta aos outros dois.
    2. Linhas 17-18 O cenário do Trinket e o cenário pack.

    Crie o esquema do Trinket. O Frame dele precisa de um id que nenhum outro Frame do jogo use.

    schemas/trinket.json
    {
      "id": "trinket",
      "name": "Trinket",
      "elementType": "item",
      "constructor": "rules/trinket.ts",
      "inputs": {
        "name": { "label": "Name" },
        "weight": { "label": "Weight", "defaultValue": 1, "min": 0, "max": 5 }
      },
      "frames": [
        {
          "id": "trinket-card",
          "source": "layouts/trinket.json",
          "default": true
        }
      ],
      "content": [
        {
          "id": "rope",
          "name": "Rope",
          "parameters": { "name": "Rope", "weight": 2 }
        },
        {
          "id": "anvil",
          "name": "Anvil",
          "parameters": { "name": "Anvil", "weight": 5 }
        }
      ]
    }
    1. Linha 8 Um peso com limites. A oferta o lê.
    2. Linhas 10-16 O Trinket tem seu próprio Frame.
    3. Linhas 17-28 Os presets que um jogador pode guardar. Os ids deles são o que um cenário escolhe.

    Crie o construtor, o Frame e o cenário dele.

    rules/trinket.ts
    export function construct(
      name: string,
      weight: number,
    ): { name: string; weight: number } {
      return { name: name, weight: weight };
    }
    1. Linha 5 Devolve os dois valores guardados.
    layouts/trinket.json
    {
      "version": "1.0",
      "metadata": {
        "name": "Trinket card",
        "bindingMode": "bound",
        "frameworkRef": "trinket",
        "context": "BLADE",
        "template": "standard-blade"
      },
      "dataContract": {
        "requiredBindings": [
          { "binding": "element.attributes.name", "expectedType": "string" },
          { "binding": "element.attributes.weight", "expectedType": "number" }
        ],
        "optionalBindings": [],
        "actions": [],
        "regions": []
      },
      "layout": {
        "kind": "container",
        "children": [
          { "kind": "text", "text": "{{element.attributes.name}}" },
          { "kind": "text", "text": "Weight: {{element.attributes.weight}}" }
        ]
      }
    }
    1. Linhas 22-23 Mostra o nome e o peso.
    scenarios/new-trinket.json
    {
      "id": "new-trinket",
      "framework": "trinket",
      "parameters": { "name": "Rope", "weight": 2 },
      "expectedValues": { "name": "Rope", "weight": 2 }
    }
    1. Linha 4 Cria um Trinket sozinho e confere os valores dele.

    Declare a ação e a lista no esquema do Keeper.

    schemas/keeper.json
      "actions": [
        { "id": "hurt", "name": "Take damage", "source": "rules/hurt.ts" },
        { "id": "heal", "name": "Heal", "source": "rules/heal.ts" },
        {
          "id": "pack_trinket",
          "name": "Pack a trinket",
          "source": "rules/pack-trinket.ts"
        }
      ],
    1. Linhas 19-23 A nova ação.
    schemas/keeper.json
      "lists": { "pack": { "label": "Pack", "of": "trinket", "max": 4 } }
    1. Linha 37 A lista Pack: seu rótulo, o Framework de conteúdo que ela guarda e seu tamanho.

    Crie o arquivo de regras da ação.

    rules/pack-trinket.ts
    export function pack_trinket(self: ElementRef, trinket: ContentRef): void {
      list.add(self, "pack", trinket, offer.where({ weight: { lte: 3 } }));
    }
    1. Linha 1 O trinket escolhido chega como um ContentRef.
    2. Linha 2 A oferta decide o que pode ser guardado. Só trinkets que pesam 3 ou menos.

    Uma oferta pode recusar, então teste os dois resultados. Crie scenarios/pack.json.

    scenarios/pack.json
    {
      "id": "pack-trinkets",
      "framework": "keeper",
      "parameters": {
        "name": "Mira",
        "max_hp": 10,
        "calling": "Guard",
        "lamp": "lamp"
      },
      "expectedValues": {
        "name": "Mira",
        "max_hp": 10,
        "hp": 10,
        "calling": "Guard",
        "guard": 3
      },
      "expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } },
      "actions": [
        {
          "action": "pack_trinket",
          "parameters": {},
          "choose": { "trinket": "rope" },
          "expectedWrites": [{ "op": "list.add", "key": "pack", "value": "rope" }]
        },
        {
          "action": "pack_trinket",
          "parameters": {},
          "choose": { "trinket": "anvil" },
          "expectedRefusal": "NOT_OFFERED",
          "expectedWrites": []
        }
      ],
      "expectedLists": { "pack": ["rope"] }
    }
    1. Linhas 19-24 Aceito: a corda é leve o bastante, então é adicionada.
    2. Linhas 25-31 Recusado: a bigorna é pesada demais. O passo espera a recusa NOT_OFFERED e nenhuma escrita.
    3. Linha 33 Depois do último passo, o Pack guarda só a corda.

    Um Frame que mostra uma lista usa a version 2.0. Mude a versão, declare a ação e uma região no dataContract, e desenhe o botão e a região.

    layouts/keeper.json
      "version": "2.0",
    1. Linha 2 Frames com uma lista usam a version 2.0.
    layouts/keeper.json
        "actions": [
          { "name": "hurt", "functionRef": "hurt" },
          { "name": "heal", "functionRef": "heal" },
          { "name": "pack_trinket", "functionRef": "pack_trinket" }
        ],
        "regions": [{ "name": "pack", "listBinding": "element.lists.pack" }]
    1. Linha 21 O alias da nova ação.
    2. Linha 23 Declara a região da lista.
    layouts/keeper.json
          {
            "kind": "button",
            "label": "Pack a trinket",
            "actionRef": "pack_trinket"
          },
          {
            "kind": "region",
            "id": "pack_list",
            "listBinding": "{{element.lists.pack}}",
            "emptyText": "Your pack is empty."
          }
    1. Linhas 37-39 O botão que executa a ação.
    2. Linha 42 Uma região desenha uma lista.
    3. Linha 44 A lista a desenhar.
    4. Linha 45 Mostrada enquanto a lista está vazia.

    Escolha Verificar e visualizar. Ele deve passar antes de você seguir.

    O projeto agora tem estes arquivos. Expanda qualquer trecho acima para ver o arquivo inteiro.

    manifest/project.json
    schemas/keeper.json
    schemas/lamp.json
    schemas/trinket.json
    rules/guard.ts
    rules/heal.ts
    rules/hurt.ts
    rules/keeper.ts
    rules/lamp.ts
    rules/pack-trinket.ts
    rules/trinket.ts
    layouts/keeper.json
    layouts/lamp.json
    layouts/trinket.json
    scenarios/heal.json
    scenarios/new-keeper.json
    scenarios/new-lamp.json
    scenarios/new-trinket.json
    scenarios/pack.json

O que você criou

Seu Keeper agora tem uma escolha, um valor derivado, um Framework filho, uma lista com uma ação recusada e cenários que provam cada um. As receitas mostram mudanças menores, uma a uma.