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.
"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 }
}
}
}
}- Linha 23 As entradas de número do pool. Cada uma precisa de um item em inputs.
- Linha 24 Os métodos que o jogador pode usar.
- Linha 26 O custo total dos valores não pode passar do orçamento.
- Linha 27 Todo valor é pelo menos este.
- Linha 28 Todo valor é no máximo este.
- Linha 29 O custo de cada valor. Todo valor de min a max precisa de um.
| Método | O que confere |
|---|---|
| standardArray | Os valores são os valores listados, em qualquer ordem. |
| pointBuy | Todo valor está entre min e max, e os custos somam no máximo o orçamento. |
| manual | Todo valor está entre min e max. |
| roll | O servidor rola count pontuações com dice, soma o bonus opcional, multiplica pelo multiplier opcional, e o jogador atribui os totais às entradas. |
| budget | Todo 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:
{
"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
}
}- Linhas 7-9 Estes valores custam 2, 1 e 0, então o total é 3 e o orçamento é 6.
is not satisfied: pointBuy.budgetpointBuy costs has no entry for 5A pool declares at least one method.| Limite | Valor |
|---|---|
| Pools por Framework | 4 |
| Entradas por pool | 64 |
| Valores entre min e max | 64 |
| Orçamento | 1000 |
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.
"pools": {
"traits": {
"label": "Traits",
"inputs": ["might", "wit", "grace"],
"methodInput": "method",
"methods": {
"manual": { "min": 15, "max": 90 },
"roll": { "dice": "3d6", "count": 3, "multiplier": 5 }
}
}
}- Linha 29 Um pool com mais de um método nomeia a entrada que escolhe o método.
- Linha 31 O jogador pode digitar os valores.
- 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.
"method": {
"label": "Scores by",
"options": ["manual", "roll"],
"defaultValue": "manual"
},- 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 asoutside the min/max of inputgive 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.
RequiredNo 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.
{
"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] }
]
}
}- Linha 7 O cenário escolhe o método roll.
- Linhas 21-27 As rolagens registradas. Cada total é a soma dos dados vezes o multiplier, aqui 9 × 5 = 45.
is not satisfied: roll.missingModificadores 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.
"modifiers": {
"mod_guard": {
"label": "Guard",
"stack": "sum",
"each": { "min": -3, "max": 3 },
"total": { "min": -5, "max": 5 },
"appliesTo": ["guard"]
}
},- Linha 25 O nome que os jogadores veem.
- Linha 26 sum soma todas as contribuições. max pega a maior.
- Linha 27 A faixa de uma contribuição.
- Linha 28 A faixa do total.
- 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.
"lists": {
"worn": {
"label": "Worn",
"of": "charm",
"max": 3,
"effects": true,
"distinctBy": "name",
"caps": [
{ "label": "Rings", "where": { "slot": { "eq": "Ring" } }, "max": 1 }
]
}
},- Linha 37 Os itens desta lista mudam modificadores.
- Linha 38 Um item com o mesmo nome de um que já está na lista é recusado com SAME_NAME.
- 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.
{
"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
}
}
]
}- Linhas 13-14 O efeito e a quantidade dele não têm opções nem faixa.
- Linha 16 Um efeito: qual entrada nomeia o modificador, e qual guarda a quantidade.
- 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.
export function guard(currentValue: number, self: ElementRef): number {
return 10 + (element.var(self, "mod_guard") as number);
}- 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.
{
"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 }
}- Linha 14 Guard 12: 10 mais os 2 do anel.
- Linha 20 Um segundo anel é recusado: o teto permite 1.
- Linha 30 Guard 13 com o 1 da capa.
- Linha 36 Uma segunda capa com o mesmo nome é recusada.
- Linha 46 Guard 11 depois que o anel sai.
- Linha 50 O total que sobra é o 1 da capa.
| Campo do modificador | Significado |
|---|---|
| label | O nome que aparece nas escolhas de efeito do conteúdo. |
| stack | sum ou max. |
| each, total | A faixa de uma contribuição e do total. |
| appliesTo | Até 8 nomes dos campos que o modificador alimenta. |
| appliesWhen | Uma 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.

