Przejdź do głównej treści

Frameworki kodowe

Pule i modyfikatory

Dziel budżet punktów między dane wejściowe i pozwól treściom zmieniać liczby na postaci.

Pule

Pula ogranicza grupę liczbowych danych wejściowych, które gracz wypełnia razem, na przykład trzy cechy dzielące budżet punktów. Serwer sprawdza każdą pulę, gdy postać jest tworzona lub edytowana.

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. Wiersz 23 Liczbowe dane wejściowe puli. Każde potrzebuje wpisu w inputs.
  2. Wiersz 24 Metody, których może użyć gracz.
  3. Wiersz 26 Łączny koszt wyników nie może przekroczyć budżetu.
  4. Wiersz 27 Każdy wynik jest co najmniej tyle.
  5. Wiersz 28 Każdy wynik jest co najwyżej tyle.
  6. Wiersz 29 Koszt każdego wyniku. Każdy wynik od min do max potrzebuje kosztu.
MetodaCo sprawdza
standardArrayWyniki to wymienione wartości, w dowolnej kolejności.
pointBuyKażdy wynik mieści się między min a max, a koszty sumują się do najwyżej budżetu.
manualKażdy wynik mieści się między min a max.
rollSerwer rzuca count wartości kością dice, dodaje opcjonalny bonus, mnoży przez opcjonalny multiplier, a gracz przypisuje sumy do pól.
budgetKażdy wynik mieści się między min a max, a wyniki sumują się do najwyżej total.

Pula musi deklarować co najmniej jedną metodę. Pula z więcej niż jedną metodą wskazuje w methodInput dane wejściowe, które wybierają metodę. Presety i scenariusze muszą spełniać pulę, a scenariusz to pokazuje:

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. Wiersze 7-9 Te wyniki kosztują 2, 1 i 0, więc suma to 3, a budżet to 6.
is not satisfied: pointBuy.budget
pointBuy costs has no entry for 5
A pool declares at least one method.
LimitWartość
Pule na framework4
Dane wejściowe na pulę64
Wartości między min a max64
Budżet1000

Metoda budget przyjmuje total, min i max. Każdy wynik jest liczbą całkowitą od min do max, a wyniki sumują się do najwyżej total. Wyniki nie mają ceny, więc metoda pasuje do budżetu punktów umiejętności i nie potrzebuje kosztów.

Rzucane wyniki

Metoda roll sprawia, że wartości rzuca serwer, więc gracz nie może wpisać własnych. Przyjmuje dice i count oraz dwa opcjonalne klucze, bonus i multiplier. dice to jedna grupa kości, na przykład "3d6" lub "4d6dl1". count to liczba wartości rzucanych przez serwer i równa się liczbie pól w puli. bonus (od -20 do 100) jest dodawany do każdej sumy kości, a potem multiplier (od 1 do 10) ją mnoży, więc każda wartość to (suma kości + bonus) × multiplier. Cecha 3d6 × 5, jak w Zewie Cthulhu, to dice "3d6" z 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. Wiersz 29 Pula z więcej niż jedną metodą wskazuje dane wejściowe, które wybierają metodę.
  2. Wiersz 31 Gracz może wpisać wyniki.
  3. Wiersz 32 Albo serwer rzuca 3 wyniki z 3d6, po jednym na dane wejściowe.

methodInput wskazuje pole tekstowe, które wybiera metodę puli. Wybór gracza w tym polu decyduje, którą metodę sprawdza serwer, więc te same wartości można zmierzyć standardową tablicą, zakupem punktów albo rzutem. Pula z jedną metodą nie potrzebuje methodInput i zawsze używa tej metody. Przy więcej niż jednej metodzie jest wymagane, a options pola muszą być dokładnie nazwami metod puli. Scenariusz lub preset ustawia to pole na jedną z tych nazw. Brakująca wartość lub wartość niebędąca nazwą metody kończy się błędem method.missing lub method.unknown.

schemas/keeper.json
    "method": {
      "label": "Scores by",
      "options": ["manual", "roll"],
      "defaultValue": "manual"
    },
  1. Wiersz 11 Dokładnie metody puli i nic więcej.

Każda suma, jaką może dać rzut, po uwzględnieniu bonus i multiplier, musi mieścić się w min i max każdego pola. 3d6 × 5 daje od 15 do 90, więc każda cecha dopuszcza od 15 do 90. Kości to jedna grupa: do 10 kości o co najwyżej 100 ściankach, a dl, dh, kl lub kh odrzuca albo zachowuje od 1 do 9 z nich, przy czym zostaje co najmniej jedna kość. Mnożnik zapisz kluczem multiplier, nigdy w kościach: 3d6x5 jest odrzucane.

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

Brakujący klucz pojawia się jako lista JSON. Ścieżka na liście wskazuje klucz, tutaj pools, traits, methods, roll, dice, a komunikat brzmi Required.

Required

W formularzu tworzenia postaci pula z roll pokazuje zamiast danych wejściowych przycisk Rzuć wartości, a po pierwszym rzucie Przerzuć wszystko. Serwer rzuca i zapisuje wynik, więc przeglądarka nigdy nie rzuca. Gracz przypisuje potem sumy do danych wejściowych w dowolnej kolejności. Rzucanie jest dostępne tylko w formularzu tworzenia.

Scenariusz wybierający metodę roll sam podaje rzuty w poolRolls, bo prawdziwe rzuty istnieją tylko na serwerze. poolRolls przypisuje identyfikatorowi puli po jednym slocie na każdą rzuconą wartość. Slot ma total, czyli końcową liczbę po bonus i multiplier, oraz opcjonalnie dice i dropped, które służą tylko do odczytu. Sprawdzanie puli traktuje wtedy sloty jak zapis serwera, więc wartości scenariusza muszą być tymi sumami, w dowolnej kolejności. Każda suma musi być taka, jaką rzut może dać. Bez poolRolls scenariusz wybierający roll kończy się błędem roll.missing. Preset nie może używać metody roll. Formularz tworzenia testuje prawdziwy rzut.

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. Wiersz 7 Scenariusz wybiera metodę roll.
  2. Wiersze 21-27 Zapisane rzuty. Każdy total to suma kości razy multiplier, tutaj 9 × 5 = 45.
is not satisfied: roll.missing

Modyfikatory i efekty

Modyfikator to liczba, którą platforma wylicza z treści, jakie ma postać. Zasila go lista, która stosuje efekty. Twoje reguły czytają go jak pole. Ten przykład pozwala noszonym amuletom podnosić Guard.

Framework postaci deklaruje modyfikator.

schemas/keeper.json
  "modifiers": {
    "mod_guard": {
      "label": "Guard",
      "stack": "sum",
      "each": { "min": -3, "max": 3 },
      "total": { "min": -5, "max": 5 },
      "appliesTo": ["guard"]
    }
  },
  1. Wiersz 25 Nazwa, którą widzą gracze.
  2. Wiersz 26 sum dodaje każdy wkład. max bierze największy.
  3. Wiersz 27 Zakres jednego wkładu.
  4. Wiersz 28 Zakres sumy.
  5. Wiersz 29 Pole ledgera, które zasila modyfikator. To etykieta dla ludzi. Matematykę robią Twoje reguły.

Lista mówi, że jej wpisy stosują efekty, i może je ograniczać.

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. Wiersz 37 Wpisy tej listy zmieniają modyfikatory.
  2. Wiersz 38 Wpis o tej samej nazwie co wpis na liście zostaje odrzucony z SAME_NAME.
  3. Wiersze 39-41 Najwyżej 1 wpis ze slotem Ring. Kolejny zostaje odrzucony z LIST_CAP.

Framework treści nazywa swoje dane wejściowe efektów. Pomiń ich wybory i zakresy, bo kompilator uzupełnia je z modyfikatorów postaci.

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. Wiersze 13-14 Efekt i jego wartość nie mają opcji ani zakresu.
  2. Wiersz 16 Jeden efekt: które dane wejściowe nazywają modyfikator, a które przechowują wartość.
  3. Wiersz 27 Preset podaje etykietę modyfikatora i wartość.

Funkcja ledgera czyta modyfikator jak każde inne pole.

rules/guard.ts
export function guard(currentValue: number, self: ElementRef): number {
  return 10 + (element.var(self, "mod_guard") as number);
}
  1. Wiersz 2 mod_guard jest zadeklarowany we frameworku, a nie zwracany przez konstruktor.

Scenariusz potwierdza każdy przypadek. expectedValuesAfter sprawdza wartość pochodną po kroku, a expectedModifiers sprawdza modyfikatory na końcu.

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. Wiersz 14 Guard 12: 10 plus 2 od pierścienia.
  2. Wiersz 20 Drugi pierścień zostaje odrzucony: limit pozwala na 1.
  3. Wiersz 30 Guard 13 z 1 od płaszcza.
  4. Wiersz 36 Drugi płaszcz o tej samej nazwie zostaje odrzucony.
  5. Wiersz 46 Guard 11 po zdjęciu pierścienia.
  6. Wiersz 50 Pozostała suma to 1 od płaszcza.
Pole modyfikatoraZnaczenie
labelNazwa widoczna w wyborach efektów treści.
stacksum lub max.
each, totalZakres jednego wkładu i sumy.
appliesToDo 8 nazw pól, które zasila modyfikator.
appliesWhenWarunek, jako etykieta. Egzekwują go Twoje reguły.

Jeszcze nieopisane

Istnieją też inne zaawansowane części, które nie są tu opisane: listy wierszy, wybory treści i sloty, awansowanie i przebudowy, role bojowe oraz katalogi rekordów. Każda potrzebuje własnej strony.

Dalej