Фреймворки на коде
Рецепт: бросок против навыка
Проверьте процентный бросок против значения навыка и покажите полосу успеха.
Цель
Пусть игрок бросает d100 против навыка Lore и видит полосу: Flawless, Strong, Pass или Miss. Чем ниже бросок, тем лучше.
Как бросок попадает в правило
Правила не умеют бросать кубики. Игрок бросает на листе и вводит число в действие. Правило проверяет число и записывает его. Игровой цикл состоит из четырёх шагов.
- Нажмите на значок кубиков
На листе написано Roll: [[1d100]]. Нажатие бросает d100.
- Прочитайте бросок в журнале
Результат появляется в журнале бросков стола и в чате. В персонажа пока ничего не записано.
- Запустите действие и введите бросок
Нажмите кнопку Lore check. Fabled запросит бросок в диалоговом окне. Введите число из журнала.
- Полоса обновляется
Правило записывает бросок и цель. Функция журнала выводит полосу, и лист её показывает.
Что менять
Добавьте в схему поле ввода Lore, действие и функцию журнала. У поля ввода конструктора могут быть границы.
"lore": {
"label": "Lore",
"description": "The percentile a Lore check must roll under.",
"defaultValue": 40,
"min": 1,
"max": 99
}- Строки 13-14 Эти границы действуют при создании персонажа. На бросок они не действуют.
{
"id": "check_lore",
"name": "Lore check",
"source": "rules/check-lore.ts",
"inputs": { "roll": { "label": "Roll", "min": 1, "max": 100 } }
}- Строки 21-23 Действие проверки Lore и его файл правил.
- Строка 24 Вход Roll: метка и границы. Диалог показывает их, а сервер обеспечивает их соблюдение.
"ledgerFunctions": [
{
"id": "last_band",
"name": "Last band",
"field": "last_band",
"source": "rules/band.ts"
}
]- Строки 32-35 Функция журнала выводит last_band из двух полей, которые пишет действие.
Конструктор возвращает новые поля с временными значениями. Функция журнала заменяет last_band, когда элемент строится.
export function construct(
name: string,
max_hp: number,
lore: number,
): {
name: string;
max_hp: number;
hp: number;
lore: number;
last_roll: number;
last_target: number;
last_band: string;
} {
return {
name: name,
max_hp: max_hp,
hp: max_hp,
lore: lore,
last_roll: 0,
last_target: 0,
last_band: "",
};
}- Строки 10-12 Поля, которые пишет действие и читает функция журнала.
- Строки 19-21 Начальные значения: броска ещё не было.
Границы входа удерживают бросок между 1 и 100. Действие игнорирует число с дробной частью, а иначе записывает бросок и цель.
export function check_lore(self: ElementRef, roll: number): void {
if (math.floor(roll) === roll) {
const lore: number = element.var(self, "lore") as number;
ledger.set(self, "last_target", lore);
ledger.set(self, "last_roll", roll);
}
}- Строка 1 Бросок это входное значение, которое вводит игрок.
- Строка 2 Игнорируйте число с дробной частью. Границы входа уже удерживают его между 1 и 100.
- Строки 4-5 Запишите цель и бросок. Полоса следует из них.
Одна функция журнала превращает их в полосу. Её может использовать каждый навык, потому что каждое действие навыка пишет те же два поля.
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";
}- Строки 2-3 Прочитайте два поля, которые записало действие.
- Строки 4-6 До первого броска сообщите об этом.
- Строки 7-15 Чем ниже бросок, тем лучше. Каждая полоса это доля цели.
- Строка 16 Выше цели это промах.
Для другого навыка скопируйте check-lore.ts, измените имя функции и поле в вызове element.var. Каждому навыку нужно своё действие, потому что имя поля пишется в коде явно.
Покажите на листе кубики, полосу и кнопку. Укажите полосу в requiredBindings, а действие в псевдонимах.
{ "binding": "element.attributes.last_band", "expectedType": "string" }- Строка 15 Полоса это текст.
{ "name": "check_lore", "functionRef": "check_lore" }- Строка 21 Псевдоним, который использует кнопка.
{ "kind": "text", "text": "Roll: [[1d100]]" },
{
"kind": "text",
"text": "Last check: {{element.attributes.last_band}}"
},
{ "kind": "button", "label": "Lore check", "actionRef": "check_lore" }- Строка 35 Значок кубиков. Нажатие бросает кубики и показывает результат в журнале бросков.
- Строки 36-39 Полоса, которую вывела функция журнала.
- Строка 40 Кнопка, которая открывает диалог для броска.
Проверьте
Проверьте каждую полосу и бросок вне диапазона. Каждый шаг перечисляет две записи. expectedValuesAfter проверяет полосу после действия.
{
"action": "check_lore",
"parameters": { "roll": 6 },
"expectedWrites": [
{ "op": "ledger.set", "key": "last_target", "value": 40 },
{ "op": "ledger.set", "key": "last_roll", "value": 6 }
],
"expectedValuesAfter": { "last_band": "Flawless" }
},- Строка 17 Число, которое ввёл игрок.
- Строки 18-21 Две записи по порядку.
- Строка 22 Производная полоса после действия.
Последним шагом идёт бросок 40,5. Он ничего не записывает, поэтому защита от дробей доказана. Бросок 500 не может быть шагом: он вне границ входа, и сценарий отклоняется.
{
"action": "check_lore",
"parameters": { "roll": 40.5 },
"expectedWrites": [],
"expectedValuesAfter": { "last_band": "Miss" }
}- Строка 53 Число с дробной частью.
- Строка 54 Записей нет.
- Строка 55 Полоса остаётся такой, какой её оставил прежний шаг.
Укажите файл сценария в манифесте. Добавьте новые поля в ожидаемые значения остальных сценариев.
{
"formatVersion": "fabled.game-project/v1",
"projectId": "game-your-game-id",
"projectKind": "GAME",
"compilerProfile": "native-game-v1",
"name": "Lantern Keepers",
"entryFramework": "keeper",
"frameworks": ["schemas/keeper.json"],
"scenarios": [
"scenarios/new-keeper.json",
"scenarios/heal.json",
"scenarios/lore.json"
]
}- Строка 12 Новый файл сценария.

