コードフレームワーク
プールと修正値
入力の間でポイント予算を共有し、コンテンツがキャラクターの数値を変えられるようにします。
プール
プールは、ポイント予算を共有する 3 つの特性のように、プレイヤーがまとめて埋める数値入力のグループを制限します。サーバーは、キャラクターを作成または編集するとき、すべてのプールを確認します。
"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 }
}
}
}
}- 23 行目 プールの数値入力です。それぞれに inputs の項目が必要です。
- 24 行目 プレイヤーが使える方式です。
- 26 行目 スコアの合計コストは、予算を超えられません。
- 27 行目 すべてのスコアは、この値以上です。
- 28 行目 すべてのスコアは、この値以下です。
- 29 行目 各スコアのコストです。min から max までのすべてのスコアに必要です。
| 方式 | 確認する内容 |
|---|---|
| standardArray | スコアが、一覧にある値になっている。順序は問いません。 |
| pointBuy | すべてのスコアが min と max の間にあり、コストの合計が予算以下である。 |
| manual | すべてのスコアが min と max の間にある。 |
| roll | サーバーが dice で count 個の値をロールし、任意の bonus を足して任意の multiplier を掛け、プレイヤーがその合計を各入力に割り当てます。 |
| budget | すべてのスコアが min と max の間にあり、スコアの合計が total 以下である。 |
プールは、方式を 1 つ以上宣言する必要があります。方式が複数あるプールでは、方式を選ぶ入力を methodInput に指定します。プリセットとシナリオはプールを満たす必要があり、シナリオで確認できます。
{
"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
}
}- 7-9 行目 これらのスコアのコストは 2、1、0 なので、合計は 3 で、予算は 6 です。
is not satisfied: pointBuy.budgetpointBuy costs has no entry for 5A pool declares at least one method.| 上限 | 値 |
|---|---|
| 1 つのフレームワークのプール数 | 4 |
| 1 つのプールの入力数 | 64 |
| min と max の間の値の数 | 64 |
| 予算 | 1000 |
budget 方式は total、min、max を取ります。すべてのスコアは min から max までの整数で、スコアの合計は total 以下です。スコアに価格はないため、技能ポイントの予算に向いており、コストの定義は要りません。
ロールしたスコア
roll メソッドでは、サーバーが値をロールするため、プレイヤーが自分で値を入力することはできません。dice と count に加え、任意のキー bonus と multiplier を取ります。dice は "3d6" や "4d6dl1" のような 1 つのダイスグループです。count はサーバーがロールする値の数で、プールの入力の数と同じです。bonus(-20 から 100)は各ダイス合計に足され、その後 multiplier(1 から 10)が掛けられるので、各値は(ダイス合計 + bonus)× multiplier になります。クトゥルフの呪いのような 3d6 × 5 の能力値は、dice "3d6" に 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 }
}
}
}- 29 行目 方式が複数あるプールでは、方式を選ぶ入力を指定します。
- 31 行目 プレイヤーはスコアを入力できます。
- 32 行目 または、サーバーが 3d6 で 3 つのスコアをロールし、入力ごとに 1 つ使います。
methodInput は、プールが使うメソッドを選ぶ文字列入力を指定します。この入力でのプレイヤーの選択によって、サーバーがどのメソッドを検証するかが決まるため、同じ値を標準配列、ポイントバイ、ロールのいずれで検証することもできます。メソッドが 1 つのプールでは methodInput は不要で、常にそのメソッドが使われます。複数のメソッドがある場合は必須で、入力の options はプールのメソッド名とちょうど一致しなければなりません。シナリオやプリセットは、入力をそのいずれかの名前に設定します。値がない場合やメソッド名でない場合は、method.missing または method.unknown で失敗します。
"method": {
"label": "Scores by",
"options": ["manual", "roll"],
"defaultValue": "manual"
},- 11 行目 プールの方式とちょうど同じで、それ以外は入れません。
ロールが出しうるすべての合計は、bonus と multiplier を適用した後で、各入力の min と max に収まらなければなりません。3d6 × 5 は 15 から 90 になるので、各能力値は 15 から 90 を許可します。ダイスは 1 つのグループで、最大 10 個、最大 100 面です。dl、dh、kl、kh で 1 から 9 個を捨てるか残しますが、少なくとも 1 個は残します。倍率は multiplier キーで書き、ダイスの中には書きません。3d6x5 は拒否されます。
The roll dice must be one group such asoutside the min/max of inputgive exactly one roll per input.足りないキーは JSON の一覧で表示されます。一覧のパスがキーを示します。ここでは pools、traits、methods、roll、dice で、メッセージは Required です。
Requiredキャラクター作成フォームでは、roll を使うプールは入力の代わりに「能力値を振る」ボタンを表示し、最初のロールの後は「すべて振り直す」を表示します。ロールはサーバーが行って記録を残すため、ブラウザーがロールすることはありません。プレイヤーは出た合計を好きな順で各入力に割り当てます。ロールは作成フォームでのみ使えます。
roll メソッドを選ぶシナリオは、実際のロールがサーバーにしか存在しないため、poolRolls で自らロールを与えます。poolRolls はプール ID を、ロールした値ごとに 1 つのスロットに対応付けます。スロットには、bonus と multiplier の適用後の最終値である total があり、任意で dice と dropped を持てますが、これらは読むためだけのものです。プールの検証はこのスロットをサーバーの記録として扱うため、シナリオの値はそれらの合計と(順不同で)一致しなければなりません。各合計は、そのロールが出しうる値でなければなりません。poolRolls がないと、roll を選ぶシナリオは roll.missing で失敗します。プリセットは roll メソッドを使えません。作成フォームは実際のロールをテストします。
{
"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] }
]
}
}- 7 行目 シナリオが roll メソッドを選びます。
- 21-27 行目 記録されたロール。各 total はダイス合計に multiplier を掛けた値で、ここでは 9 × 5 = 45 です。
is not satisfied: roll.missing修正値と効果
修正値は、キャラクターが持つコンテンツからプラットフォームが導き出す数値です。効果を適用するリストがこれに値を与えます。ルールはこれをフィールドのように読みます。この例では、身につけたお守りが Guard を上げます。
キャラクターのフレームワークが修正値を宣言します。
"modifiers": {
"mod_guard": {
"label": "Guard",
"stack": "sum",
"each": { "min": -3, "max": 3 },
"total": { "min": -5, "max": 5 },
"appliesTo": ["guard"]
}
},- 25 行目 プレイヤーに見える名前です。
- 26 行目 sum はすべての寄与を足します。max は最大のものを取ります。
- 27 行目 1 つの寄与の範囲です。
- 28 行目 合計の範囲です。
- 29 行目 修正値が反映されるレジャーのフィールドです。これは人のためのラベルで、計算はルールが行います。
リストは、そのエントリが効果を適用することを示し、効果に上限を設けられます。
"lists": {
"worn": {
"label": "Worn",
"of": "charm",
"max": 3,
"effects": true,
"distinctBy": "name",
"caps": [
{ "label": "Rings", "where": { "slot": { "eq": "Ring" } }, "max": 1 }
]
}
},- 37 行目 このリストのエントリは、修正値を変えます。
- 38 行目 リストにあるエントリと同じ名前のエントリは、SAME_NAME で拒否されます。
- 39-41 行目 slot が Ring のエントリは最大 1 つです。もう 1 つは LIST_CAP で拒否されます。
コンテンツのフレームワークは、効果の入力を指定します。選択肢と範囲は書きません。コンパイラがキャラクターの修正値から埋めるためです。
{
"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
}
}
]
}- 13-14 行目 効果とその量には、選択肢も範囲もありません。
- 16 行目 効果 1 つにつき、修正値を指定する入力と、量を持つ入力を指定します。
- 27 行目 プリセットが、修正値のラベルと量を与えます。
レジャー関数は、修正値を他のフィールドと同じように読みます。
export function guard(currentValue: number, self: ElementRef): number {
return 10 + (element.var(self, "mod_guard") as number);
}- 2 行目 mod_guard はフレームワークで宣言され、コンストラクタが返すものではありません。
シナリオで各ケースを確認します。expectedValuesAfter はステップ後の派生値を確認し、expectedModifiers は最後の修正値を確認します。
{
"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 }
}- 14 行目 Guard は 12 です。10 に指輪の 2 を足した値です。
- 20 行目 2 つ目の指輪は拒否されます。上限は 1 つです。
- 30 行目 マントの 1 を足して、Guard は 13 です。
- 36 行目 同じ名前の 2 つ目のマントは拒否されます。
- 46 行目 指輪を外すと、Guard は 11 です。
- 50 行目 残る合計は、マントの 1 です。
| 修正値のフィールド | 意味 |
|---|---|
| label | コンテンツの効果の選択肢に表示される名前です。 |
| stack | sum か max です。 |
| each, total | 1 つの寄与の範囲と、合計の範囲です。 |
| appliesTo | 修正値が反映されるフィールドの名前です。最大 8 つです。 |
| appliesWhen | 条件をラベルとして示します。実際に守らせるのはルールです。 |
まだ説明していないこと
他にも高度な機能がありますが、ここでは説明しません。行リスト、コンテンツの選択とスロット、レベル変更と再構築、戦闘の役割、記録カタログです。それぞれに専用のページが必要です。

