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
- Adicione uma escolha
Adicione uma entrada Calling com duas opções.
schemas/keeper.json"calling": { "label": "Calling", "options": ["Guard", "Scout"], "defaultValue": "Guard" }- 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.tsexport 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 }; }- Linha 4 A nova entrada.
- 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" } }- Linha 4 A entrada que você dá.
- 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" },- Linha 4 A mesma entrada.
- 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 - 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" } ]- 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.tsfunction 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); }- Linhas 1-3 Um auxiliar sem export.
- 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.tsexport 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 }; }- 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 }] }- Linha 10 O valor derivado, depois que a função do ledger rodou.
- 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 },- 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" }- Linha 15 O binding do novo campo.
layouts/keeper.json{ "kind": "text", "text": "Guard: {{element.attributes.guard}}" },- 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 - 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" ] }- Linha 8 O esquema do Lamp se junta ao esquema do Keeper.
- 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 }] }- Linhas 6-9 As entradas do filho. Um filho também precisa de um nome.
- Linha 10 O filho tem seu próprio Frame.
Crie o construtor, o Frame e o cenário dele.
rules/lamp.tsexport function construct(name: string, oil: number): { name: string; oil: number } { return { name: name, oil: oil }; }- 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}}" } ] } }- 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 } }- 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 }- Linha 14 Uma entrada oculta nunca aparece no assistente.
schemas/keeper.json"childFrameworks": { "lamp": "lamp" }- 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.tsexport 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 } } }, }; }- Linha 5 lamp é um FrameworkRef. A plataforma transforma o id do Framework da entrada em um.
- Linhas 6-9 O tipo de retorno lista os valores guardados e os espaços filhos.
- 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 } } }- Linha 8 A entrada oculta.
- 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 },- Linha 8 A entrada oculta em heal.json.
scenarios/heal.json"expectedChildren": { "lamp": { "name": "Lantern", "oil": 5 } }- 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 - 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" ] }- Linhas 8-12 O esquema do Trinket se junta aos outros dois.
- 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 } } ] }- Linha 8 Um peso com limites. A oferta o lê.
- Linhas 10-16 O Trinket tem seu próprio Frame.
- 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.tsexport function construct( name: string, weight: number, ): { name: string; weight: number } { return { name: name, weight: weight }; }- 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}}" } ] } }- 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 } }- 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" } ],- Linhas 19-23 A nova ação.
schemas/keeper.json"lists": { "pack": { "label": "Pack", "of": "trinket", "max": 4 } }- 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.tsexport function pack_trinket(self: ElementRef, trinket: ContentRef): void { list.add(self, "pack", trinket, offer.where({ weight: { lte: 3 } })); }- Linha 1 O trinket escolhido chega como um ContentRef.
- 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"] } }- Linhas 19-24 Aceito: a corda é leve o bastante, então é adicionada.
- Linhas 25-31 Recusado: a bigorna é pesada demais. O passo espera a recusa NOT_OFFERED e nenhuma escrita.
- 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",- 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" }]- Linha 21 O alias da nova ação.
- 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." }- Linhas 37-39 O botão que executa a ação.
- Linha 42 Uma região desenha uma lista.
- Linha 44 A lista a desenhar.
- 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.

