Pular para o conteúdo principal

Code frameworks

Pools e modificadores

Divida um orçamento de pontos entre entradas, e deixe o conteúdo mudar números no personagem.

Pools

Um pool limita um grupo de entradas de número que um jogador preenche juntas, como três traços que dividem um orçamento de pontos. O servidor confere todo pool quando um personagem é criado ou editado.

schemas/keeper.json
  "pools": {
    "traits": {
      "label": "Traits",
      "inputs": ["might", "wit", "grace"],
      "methods": {
        "pointBuy": {
          "budget": 6,
          "min": 1,
          "max": 5,
          "costs": { "1": 0, "2": 1, "3": 2, "4": 4, "5": 6 }
        }
      }
    }
  }
  1. Linha 23 As entradas de número do pool. Cada uma precisa de um item em inputs.
  2. Linha 24 Os métodos que o jogador pode usar.
  3. Linha 26 O custo total dos valores não pode passar do orçamento.
  4. Linha 27 Todo valor é pelo menos este.
  5. Linha 28 Todo valor é no máximo este.
  6. Linha 29 O custo de cada valor. Todo valor de min a max precisa de um.
MétodoO que confere
standardArrayOs valores são os valores listados, em qualquer ordem.
pointBuyTodo valor está entre min e max, e os custos somam no máximo o orçamento.
manualTodo valor está entre min e max.
rollO servidor rola count pontuações com dice, soma o bonus opcional, multiplica pelo multiplier opcional, e o jogador atribui os totais às entradas.
budgetTodo valor está entre min e max, e os valores somam no máximo total.

Um pool deve declarar pelo menos um método. Um pool com mais de um método nomeia em methodInput a entrada que escolhe o método. Presets e cenários devem satisfazer o pool, e um cenário mostra isso:

scenarios/new-keeper.json
{
  "id": "new-keeper",
  "framework": "keeper",
  "parameters": {
    "name": "Mira",
    "max_hp": 10,
    "might": 3,
    "wit": 2,
    "grace": 1
  },
  "expectedValues": {
    "name": "Mira",
    "max_hp": 10,
    "hp": 10,
    "might": 3,
    "wit": 2,
    "grace": 1
  }
}
  1. Linhas 7-9 Estes valores custam 2, 1 e 0, então o total é 3 e o orçamento é 6.
is not satisfied: pointBuy.budget
pointBuy costs has no entry for 5
A pool declares at least one method.
LimiteValor
Pools por Framework4
Entradas por pool64
Valores entre min e max64
Orçamento1000

O método budget recebe total, min e max. Cada valor é um número inteiro de min a max, e os valores somam no máximo total. Os valores não têm preço, então ele serve para um orçamento de pontos de perícia e não precisa de custos.

Valores rolados

O método roll faz o servidor rolar as pontuações, então um jogador não pode digitar as suas. Ele recebe dice e count, e duas chaves opcionais, bonus e multiplier. dice é um grupo de dados, como "3d6" ou "4d6dl1". count é quantas pontuações o servidor rola e é igual ao número de entradas do pool. bonus (de -20 a 100) é somado a cada total de dados e depois multiplier (de 1 a 10) o multiplica, de modo que cada pontuação é (total dos dados + bonus) × multiplier. Uma característica 3d6 × 5, como em O Chamado de Cthulhu, é dice "3d6" com multiplier 5.

schemas/keeper.json
  "pools": {
    "traits": {
      "label": "Traits",
      "inputs": ["might", "wit", "grace"],
      "methodInput": "method",
      "methods": {
        "manual": { "min": 15, "max": 90 },
        "roll": { "dice": "3d6", "count": 3, "multiplier": 5 }
      }
    }
  }
  1. Linha 29 Um pool com mais de um método nomeia a entrada que escolhe o método.
  2. Linha 31 O jogador pode digitar os valores.
  3. Linha 32 Ou o servidor rola 3 valores de 3d6, um para cada entrada.

methodInput nomeia a entrada de texto que escolhe o método de um pool. A escolha do jogador nessa entrada decide qual método o servidor verifica, então as mesmas pontuações podem ser conferidas pela lista padrão, pela compra de pontos ou por uma rolagem. Um pool com um único método não precisa de methodInput e sempre usa esse método. Com mais de um método ele é obrigatório, e as options da entrada devem ser exatamente os nomes dos métodos do pool. Um cenário ou preset define a entrada como um desses nomes. Um valor ausente ou que não seja um nome de método falha com method.missing ou method.unknown.

schemas/keeper.json
    "method": {
      "label": "Scores by",
      "options": ["manual", "roll"],
      "defaultValue": "manual"
    },
  1. Linha 11 Exatamente os métodos do pool, nada mais.

Todo total que a rolagem pode dar, depois do bonus e do multiplier, deve caber em min e max de cada entrada. 3d6 × 5 dá de 15 a 90, então cada traço permite de 15 a 90. Os dados são um único grupo: até 10 dados com até 100 lados, e dl, dh, kl ou kh descarta ou mantém de 1 a 9 deles, mantendo pelo menos um dado. Escreva um multiplicador com a chave multiplier, nunca dentro dos dados: 3d6x5 é recusado.

The roll dice must be one group such as
outside the min/max of input
give exactly one roll per input.

Uma chave que falta aparece como uma lista JSON. O caminho da lista nomeia a chave, aqui pools, traits, methods, roll, dice, e a mensagem diz Required.

Required

No formulário de criação de personagem, um pool com roll mostra um botão Rolar pontuações no lugar das entradas, e Rolar tudo de novo depois da primeira rolagem. O servidor rola e guarda um registro, então o navegador nunca rola. O jogador atribui então os totais às entradas em qualquer ordem. A rolagem só é oferecida no formulário de criação.

Um cenário que escolhe o método roll fornece as rolagens ele mesmo em poolRolls, porque as rolagens reais só existem no servidor. poolRolls associa o id de um pool a um slot para cada pontuação rolada. Um slot tem um total, o número final depois do bonus e do multiplier, e opcionalmente dice e dropped, que servem só para leitura. A verificação do pool trata então os slots como o registro do servidor, de modo que as pontuações do cenário devem ser esses totais, em qualquer ordem. Cada total deve ser um que a rolagem pode produzir. Sem poolRolls, um cenário que escolhe roll falha com roll.missing. Um preset não pode usar o método roll. O formulário de criação testa a rolagem real.

scenarios/new-keeper.json
{
  "id": "new-keeper",
  "framework": "keeper",
  "parameters": {
    "name": "Mira",
    "max_hp": 10,
    "method": "roll",
    "might": 60,
    "wit": 45,
    "grace": 75
  },
  "expectedValues": {
    "name": "Mira",
    "max_hp": 10,
    "hp": 10,
    "might": 60,
    "wit": 45,
    "grace": 75,
    "method": "roll"
  },
  "poolRolls": {
    "traits": [
      { "total": 45, "dice": [3, 4, 2] },
      { "total": 60, "dice": [5, 4, 3] },
      { "total": 75, "dice": [6, 5, 4] }
    ]
  }
}
  1. Linha 7 O cenário escolhe o método roll.
  2. Linhas 21-27 As rolagens registradas. Cada total é a soma dos dados vezes o multiplier, aqui 9 × 5 = 45.
is not satisfied: roll.missing

Modificadores e efeitos

Um modificador é um número que a plataforma deriva do conteúdo que um personagem possui. Uma lista que aplica efeitos o alimenta. Suas regras o leem como um campo. Este exemplo deixa amuletos vestidos aumentarem Guard.

O Framework do personagem declara o modificador.

schemas/keeper.json
  "modifiers": {
    "mod_guard": {
      "label": "Guard",
      "stack": "sum",
      "each": { "min": -3, "max": 3 },
      "total": { "min": -5, "max": 5 },
      "appliesTo": ["guard"]
    }
  },
  1. Linha 25 O nome que os jogadores veem.
  2. Linha 26 sum soma todas as contribuições. max pega a maior.
  3. Linha 27 A faixa de uma contribuição.
  4. Linha 28 A faixa do total.
  5. Linha 29 O campo do ledger que o modificador alimenta. Isto é um rótulo para pessoas. Suas regras fazem a conta.

A lista diz que seus itens aplicam efeitos, e pode limitá-los.

schemas/keeper.json
  "lists": {
    "worn": {
      "label": "Worn",
      "of": "charm",
      "max": 3,
      "effects": true,
      "distinctBy": "name",
      "caps": [
        { "label": "Rings", "where": { "slot": { "eq": "Ring" } }, "max": 1 }
      ]
    }
  },
  1. Linha 37 Os itens desta lista mudam modificadores.
  2. Linha 38 Um item com o mesmo nome de um que já está na lista é recusado com SAME_NAME.
  3. Linhas 39-41 No máximo 1 item com o slot Ring. Outro é recusado com LIST_CAP.

O Framework de conteúdo nomeia suas entradas de efeito. Deixe de fora as escolhas e as faixas delas, porque o compilador as preenche a partir dos modificadores do personagem.

schemas/charm.json
{
  "id": "charm",
  "name": "Charm",
  "elementType": "item",
  "constructor": "rules/charm.ts",
  "inputs": {
    "name": { "label": "Name" },
    "slot": {
      "label": "Slot",
      "options": ["Ring", "Cloak"],
      "defaultValue": "Ring"
    },
    "effect_1": { "label": "Effect" },
    "amount_1": { "label": "Amount" }
  },
  "effects": [{ "target": "effect_1", "amount": "amount_1" }],
  "frames": [
    { "id": "charm-card", "source": "layouts/charm.json", "default": true }
  ],
  "content": [
    {
      "id": "ring-of-guard",
      "name": "Ring of Guard",
      "parameters": {
        "name": "Ring of Guard",
        "slot": "Ring",
        "effect_1": "Guard",
        "amount_1": 2
      }
    },
    {
      "id": "ring-of-calm",
      "name": "Ring of Calm",
      "parameters": {
        "name": "Ring of Calm",
        "slot": "Ring",
        "effect_1": "None",
        "amount_1": 0
      }
    },
    {
      "id": "cloak-of-guard",
      "name": "Cloak of Guard",
      "parameters": {
        "name": "Cloak of Guard",
        "slot": "Cloak",
        "effect_1": "Guard",
        "amount_1": 1
      }
    },
    {
      "id": "cloak-spare",
      "name": "Cloak of Guard",
      "parameters": {
        "name": "Cloak of Guard",
        "slot": "Cloak",
        "effect_1": "Guard",
        "amount_1": 1
      }
    }
  ]
}
  1. Linhas 13-14 O efeito e a quantidade dele não têm opções nem faixa.
  2. Linha 16 Um efeito: qual entrada nomeia o modificador, e qual guarda a quantidade.
  3. Linha 27 O preset dá o rótulo do modificador e a quantidade.

Uma função de ledger lê o modificador como qualquer outro campo.

rules/guard.ts
export function guard(currentValue: number, self: ElementRef): number {
  return 10 + (element.var(self, "mod_guard") as number);
}
  1. Linha 2 mod_guard é declarado no Framework, não devolvido pelo construtor.

O cenário prova cada caso. expectedValuesAfter confere o valor derivado depois de um passo, e expectedModifiers confere os modificadores no fim.

scenarios/wear.json
{
  "id": "wear-charms",
  "framework": "keeper",
  "parameters": { "name": "Mira", "max_hp": 10 },
  "expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "guard": 10 },
  "actions": [
    {
      "action": "wear",
      "parameters": {},
      "choose": { "charm": "ring-of-guard" },
      "expectedWrites": [
        { "op": "list.add", "key": "worn", "value": "ring-of-guard" }
      ],
      "expectedValuesAfter": { "guard": 12 }
    },
    {
      "action": "wear",
      "parameters": {},
      "choose": { "charm": "ring-of-calm" },
      "expectedRefusal": "LIST_CAP",
      "expectedWrites": []
    },
    {
      "action": "wear",
      "parameters": {},
      "choose": { "charm": "cloak-of-guard" },
      "expectedWrites": [
        { "op": "list.add", "key": "worn", "value": "cloak-of-guard" }
      ],
      "expectedValuesAfter": { "guard": 13 }
    },
    {
      "action": "wear",
      "parameters": {},
      "choose": { "charm": "cloak-spare" },
      "expectedRefusal": "SAME_NAME",
      "expectedWrites": []
    },
    {
      "action": "remove_charm",
      "parameters": {},
      "choose": { "charm": "ring-of-guard" },
      "expectedWrites": [
        { "op": "list.remove", "key": "worn", "value": "ring-of-guard" }
      ],
      "expectedValuesAfter": { "guard": 11 }
    }
  ],
  "expectedLists": { "worn": ["cloak-of-guard"] },
  "expectedModifiers": { "mod_guard": 1 }
}
  1. Linha 14 Guard 12: 10 mais os 2 do anel.
  2. Linha 20 Um segundo anel é recusado: o teto permite 1.
  3. Linha 30 Guard 13 com o 1 da capa.
  4. Linha 36 Uma segunda capa com o mesmo nome é recusada.
  5. Linha 46 Guard 11 depois que o anel sai.
  6. Linha 50 O total que sobra é o 1 da capa.
Campo do modificadorSignificado
labelO nome que aparece nas escolhas de efeito do conteúdo.
stacksum ou max.
each, totalA faixa de uma contribuição e do total.
appliesToAté 8 nomes dos campos que o modificador alimenta.
appliesWhenUma condição, como rótulo. Suas regras a aplicam.

Ainda não coberto

Existem outras partes avançadas que não são cobertas aqui: listas de linhas, escolhas e slots de conteúdo, subida de nível e reconstruções, papéis de combate e catálogos de registros. Cada uma precisa da sua própria página.

Próximos passos