Code frameworks
Escrevendo regras
Escreva constructors, ações e funções de ledger em um subconjunto pequeno e seguro de TypeScript.
Três tipos de arquivo de regras
| Tipo | Formato | O que pode fazer |
|---|---|---|
| Constructor | export function construct(...inputs): { fields } | Lê suas entradas e retorna os valores iniciais. Nunca escreve. |
| Ação | export function id(self: ElementRef, ...inputs): void | Muda valores com ledger.set e ledger.add. |
| Função de ledger | export function id(currentValue: T, self: ElementRef): T | Lê valores e retorna um valor derivado. Nunca escreve. |
Um arquivo de construtor exporta uma função. Um arquivo de ação ou de ledger pode exportar várias, se cada nome exportado for um id declarado para esse arquivo: um id de ação ou um id de função de ledger. Os nomes dos campos guardados usam minúsculas, dígitos e sublinhados.
Uma função de ledger também pode devolver texto. Declare currentValue e o resultado como string, e dê à entrada de ledger do cenário um currentValue de texto.
export function last_band(currentValue: string, self: ElementRef): string {
const roll: number = element.var(self, "last_roll") as number;
const target: number = element.var(self, "last_target") as number;
if (roll === 0) {
return "No roll yet";
}
if (roll <= math.floor(target / 5)) {
return "Flawless";
}
if (roll <= math.floor(target / 2)) {
return "Strong";
}
if (roll <= target) {
return "Pass";
}
return "Miss";
}- Linha 1 Entra texto, sai texto.
export function hurt(self: ElementRef, amount: number): void {
ledger.add(self, "hp", 0 - amount);
}- Linha 1 O Element vem primeiro como self, depois as próprias entradas da ação. Uma ação retorna void.
Um arquivo de regras também pode conter funções auxiliares. Escreva-as sem export e chame-as pelo nome no mesmo arquivo. O arquivo continua a exportar exatamente uma função, ou apenas as ações ou funções de ledger que declara.
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);
}- Linhas 1-3 Uma função auxiliar: sem export, e chamada pelo nome no mesmo arquivo.
- Linha 7 A chamada à função auxiliar.
Um arquivo de regras não pode importar código. Um arquivo de ação exporta uma função, a menos que várias ações o partilhem. Uma exportação a mais, sozinha, falha com:
export function hurt(self: ElementRef, amount: number): void {
ledger.add(self, "hp", 0 - amount);
}
export function hurt_twice(self: ElementRef, amount: number): void {
ledger.add(self, "hp", 0 - amount - amount);
}functions but only the action hurt uses itPara partilhar uma função auxiliar entre ações, faça essas ações partilharem um arquivo. Dê a cada exportação o nome da ação que ela executa, declare cada exportação como id de ação e deixe a função auxiliar sem export. As ações podem pertencer a Frameworks diferentes, e uma ação não pode chamar outra. Cada função leva uma cópia de todo o arquivo no jogo compilado, então mantenha os arquivos partilhados curtos.
Uma função auxiliar só pode calcular e devolver um valor. Ela não pode chamar ledger.set nem ledger.add. Uma função auxiliar que escreve é recusada, mesmo em um arquivo que ações compartilham:
function wound(self: ElementRef): void {
ledger.add(self, "hp", -1);
}effectful action helpers are not supported until call-target provenance is enforcedDeixe a função auxiliar devolver o valor, e deixe cada ação fazer a sua própria escrita. Estas duas ações compartilham uma função auxiliar pura:
function clamp(value: number, low: number, high: number): number {
return math.max(low, math.min(high, value));
}
export function hurt(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;
ledger.set(self, "hp", clamp(hp - amount, 0, max_hp));
}
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;
ledger.set(self, "hp", clamp(hp + amount, 0, max_hp));
}- Linhas 1-3 Uma função auxiliar pura: sem export, e só devolve um valor.
- Linha 8 A ação escreve o valor que a função auxiliar devolveu.
- Linha 14 A segunda ação reutiliza a função auxiliar.
Um constructor que preenche espaços filhos retorna { values, children } em vez de um record simples. O início rápido mostra o record simples, e a página Schemas de Framework mostra a forma com filhos.
O subconjunto de TypeScript
As regras rodam do mesmo jeito em qualquer lugar e sempre terminam. Por isso a linguagem é pequena.
| Permitido | Não permitido |
|---|---|
| Tipos explícitos, const e let | import, classes, arrow functions, new, this |
| if e else, for of, return | loops while, for (;;), switch, break, continue, código async, exceções |
| Literais de número, texto, booleano, record e array | Template strings, spread, optional chaining, ??, comentários de bloco (use //) |
| Aritmética, comparação estrita (=== e !==), operadores booleanos, ternários | == e !=, **, ++ e +=, typeof, mudar objetos no lugar, valores padrão de parâmetros |
| Chamadas a funções do host, funções auxiliares do mesmo arquivo e casts com as dos valores que elas retornam | Chamadas de método como "a".toUpperCase(), Math, Date, JSON, fetch e outros globais |
Termine cada instrução com ponto e vírgula. Cada um destes erros é detectado quando você verifica o projeto.
export function heal(self: ElementRef, amount: number): void {
while (amount > 0) {
ledger.add(self, "hp", 1);
amount = amount - 1;
}
}while loops are not allowedexport function heal(self: ElementRef, amount: number): void {
ledger.set(self, "hp", Math.min(10, amount));
}'Math' is a host global and not part of the TypeScript subsetimport { clamp } from "./clamp";import is not allowedUma função com tipo de retorno deve retornar um valor em todos os caminhos.
export function hurt(self: ElementRef, amount: number): number {
ledger.add(self, "hp", 0 - amount);
}a non-void function must return a value on every reachable pathMais erros que a verificação detecta
Cada linha cita parte de uma mensagem e diz o que escrever no lugar.
| Mensagem | O que escrever no lugar |
|---|---|
| block comments are not allowed | Use comentários de linha com //. |
| switch is not allowed | Use if e else. |
| break is not allowed | Mude o loop, ou coloque o resto do corpo dele dentro de um if. |
| continue is not allowed | Coloque o resto do corpo do loop dentro de um if. |
| classic for loops are not allowed | Use for (const item of list). |
| loose equality is not allowed | Use ===. |
| loose inequality is not allowed | Use !==. |
| exponentiation (**) is not allowed | Use math.pow. |
| increment (++) is not allowed | Escreva count = count + 1. |
| compound assignment is not allowed | Escreva count = count + value. |
| typeof is not allowed | Compare com === contra um literal. |
| class is not allowed | Use funções simples. |
| arrow functions are not allowed | Escreva uma função auxiliar no topo do arquivo. |
| async is not allowed | Remova async. As regras rodam em um único passo. |
| template literals are not allowed | Use literais de texto. |
| spread/rest (...) is not allowed | Escreva os itens um a um. |
| optional chaining (?.) is not allowed | Compare com null primeiro. |
| nullish coalescing (??) is not allowed | Use um ternário que compara com null. |
| throw is not allowed | Saia da regra sem escrever. |
| try/catch is not allowed | Remova try e catch. |
| new is not allowed | Escreva literais no lugar. |
| this is not allowed | Use o parâmetro self. |
| method calls are not allowed | Chame só funções do host e funções auxiliares do mesmo arquivo. |
| default parameter values (=) are not allowed | Dê um valor a cada parâmetro onde a regra é usada. |
| assignment targets must be local variables | Monte um valor novo em vez de mudar um objeto. |
| functions but only the action | Remova export das funções auxiliares. |
| constructors must be pure and cannot read or write host state | Leia e escreva em ações e funções de ledger. |
| is not available until | Uma função de ledger não pode escrever. Use uma ação. |
| effectful action helpers are not supported | Faça a função auxiliar devolver um valor e escreva-o na ação. |
Chamadas ao host
As regras chegam ao Fabled por chamadas ao host. Você as escreve como elas são, como ledger.add(...). Você nunca as importa.
Nestas assinaturas, HostValue é qualquer valor que uma chamada ao host pode devolver, então converta-o com as antes de usá-lo. AbiValue é um número, texto, booleano ou null, ou uma lista ou um registro desses. Um parâmetro opcional sticky é de uso do próprio Fabled, então deixe-o de fora.
element.var(ref: ElementRef, name: HostValue, sticky?: AbiValue): HostValue;
ledger.set(ref: ElementRef, key: string, value: AbiValue): void;
ledger.add(ref: ElementRef, key: string, value: number): void;
math.floor(x: number): number;
math.ceil(x: number): number;
math.round(x: number): number;
math.max(...values: number[]): number;
math.min(...values: number[]): number;
math.abs(x: number): number;
math.pow(base: number, exponent: number): number;
math.sqrt(x: number): number;
list.add(ref: ElementRef, key: string, item: ContentRef, offer: ContentOffer): void;
list.remove(ref: ElementRef, key: string, item: ContentRef): void;
list.count(ref: ElementRef, key: string): number;
offer.where(where: ContentWhere, opts?: { maxItems?: number; flag?: { by: string; map: Record<string, string> } }): ContentOffer;| Chamada | Onde | O que faz |
|---|---|---|
| element.var | Ações, funções de ledger | Lê um valor guardado de um Element. Converta o resultado com as number, as string ou as boolean. |
| ledger.set | Ações | Define um campo guardado com um valor. |
| ledger.add | Ações | Soma um número a um campo guardado. Some um número negativo para subtrair. |
| math.floor | Em qualquer lugar | Arredonda para baixo. |
| math.ceil | Em qualquer lugar | Arredonda para cima. |
| math.round | Em qualquer lugar | Arredonda para o número inteiro mais próximo. |
| math.max | Em qualquer lugar | Devolve o maior dos seus números. |
| math.min | Em qualquer lugar | Devolve o menor dos seus números. |
| math.abs | Em qualquer lugar | Devolve o tamanho de um número sem o sinal. |
| math.pow | Em qualquer lugar | Eleva uma base a um expoente. |
| math.sqrt | Em qualquer lugar | Devolve a raiz quadrada. |
| list.add | Ações | Adiciona o item escolhido a uma lista. A oferta decide o que pode ser adicionado. Recusado com NOT_OFFERED, ALREADY_IN_LIST ou LIST_FULL. |
| list.remove | Ações | Remove o item escolhido de uma lista. Recusado com NOT_IN_LIST. |
| list.count | Ações | Devolve quantos itens uma lista guarda. Use em uma proteção antes de list.add. |
| offer.where | Ações | Cria a oferta para list.add: os itens cujos campos combinam com condições que você escreve como literais. |
Uma chamada de lista nomeia uma lista do Framework por uma chave literal, e o item é um ContentRef, uma entrada que guarda o item que o jogador escolhe. Uma oferta recebe só condições literais, como offer.where({ weight: { lte: 3 } }). Cada campo aceita eq, ne, gte, lte, in ou has. Uma oferta não pode ler o Element. Para depender de um valor do personagem, teste com if antes de list.add.
export function pack_trinket(self: ElementRef, trinket: ContentRef): void {
if (list.count(self, "pack") < 2) {
list.add(self, "pack", trinket, offer.where({ weight: { lte: 3 } }));
}
}- Linha 2 Pare quando o pacote já guarda dois itens.
- Linha 3 A oferta permite apenas itens que pesam 3 ou menos.
export function unpack_trinket(self: ElementRef, trinket: ContentRef): void {
list.remove(self, "pack", trinket);
}- Linha 2 Remova o item escolhido. Uma ação de lista que só remove precisa de um passo aceito.
Nomes de campo são escritos por extenso
Em uma ação, o nome de um campo deve ser um texto escrito no código, como "hp". Ele não pode vir de um valor. Uma regra como esta falha:
export function hurt(self: ElementRef, skill: string): void {
const value: number = element.var(self, skill) as number;
ledger.set(self, "hp", value);
}Action accesses require literal keys on self.Então, sem uma lista de campos, uma ação não pode testar qualquer perícia pelo nome. Escreva uma ação para cada perícia, e deixe cada uma ler o seu próprio campo. A receita Rolar contra uma perícia mostra uma ação curta para copiar. A exceção é uma entrada que lista os seus campos. Dê à entrada da ação uma lista "fields", como ["lore", "library_use"], e a ação pode passar essa entrada como nome, como em ledger.add(self, skill, amount). A verificação trata a ação como se tocasse em todos os campos da lista, e o servidor só aceita uma escrita no campo que o jogador escolheu.
Esta ação aumenta a perícia que o jogador escolher:
{
"id": "improve",
"name": "Improve a skill",
"source": "rules/improve.ts",
"inputs": {
"skill": { "label": "Skill", "fields": ["lore", "library_use"] }
}
}- Linha 25 A entrada skill escolhe um destes campos pelo nome. Uma lista precisa de pelo menos dois.
export function improve(self: ElementRef, skill: string): void {
ledger.add(self, skill, 1);
}- Linha 2 skill contém o nome do campo que o jogador escolheu.
Cada nome em fields deve ser um campo armazenado do Framework que uma ação possa escrever. Um campo que uma função de ledger deriva, ou um modificador, é recusado. Se o campo tiver uma entrada de esquema com o mesmo nome e um label, o diálogo mostra esse rótulo; caso contrário, mostra o identificador, como library_use. Por exemplo, inputs.library_use.label pode chamar a opção Uso de biblioteca. O valor selecionado, os parâmetros dos cenários e os marcadores do registro, como {skill}, continuam usando o identificador do campo. Qualquer lista nativa de opções de texto cujos valores sejam todos nomes de campos escalares graváveis usa os mesmos rótulos.
fields "skill_total" is not a writable state fieldSó campos declarados
Uma ação só pode escrever os campos que o constructor retorna. A verificação mostra o nome do campo que não encontrou.
Action "keeper.heal" calls ledger.set(self, "mana", ...), and "mana" is not declared on this schema. Declared scalar fields: hp, max_hp, name.
