Перейти к содержанию

Повторяемый квест

Квест, который можно проходить много раз: ежедневное задание, фарм-миссия, испытание на время. Один и тот же квестовый файл, новый прогресс каждый раз.

В InkQuest для этого есть одно поле — repeatable: true. Завершённый квест можно выдать повторно, и его прогресс автоматически сбросится.


Зачем нужен этот паттерн

  • Ежедневки и фарм. «Принеси 10 кожи», «убей босса в шахте» — повторяемый источник опыта и наград.
  • Тренировка/обучение. Туториальный квест, который игрок может перепройти, если что-то забыл.
  • Серверные ивенты с многоразовым участием. «Победи дракона выходного дня» — каждые выходные новая попытка.
  • Ачивменты с прогрессом. «Пройди 100 раз» — каждое прохождение засчитывается отдельной попыткой.

Шаг 1. Создаём квест с флагом repeatable

data/daily/quests/courier.json:

{
  "version": 3,
  "variant": 0,
  "title": "Доставка из таверны",
  "description": "Каждый день нужен курьер. Награда — серебро.",
  "repeatable": true,
  "tasks": {
    "deliver": {
      "title": "Доставить посылку",
      "condition": {
        "success": { "type": "predicate", "predicate": "daily:package_delivered" }
      },
      "on": {
        "success": { "functions": ["daily:pay_silver"] }
      }
    }
  },
  "stages": [["deliver"]]
}

Один флаг — и квест становится перевыдаваемым.


Шаг 2. Описываем предикат завершения

data/daily/predicates/package_delivered.json — проверяет scoreboard-флаг игрока, который ставит командный блок в сундуке-цели:

{
  "condition": "minecraft:entity_scores",
  "entity": "this",
  "scores": {
    "daily.courier_delivered": { "min": 1 }
  }
}

data/daily/functions/init.mcfunction (вызови один раз через #minecraft:load):

scoreboard objectives add daily.courier_delivered dummy

В сундуке-цели поставь командный блок: scoreboard players set @p daily.courier_delivered 1.


Шаг 3. Описываем функцию награды

data/daily/functions/pay_silver.mcfunction:

give @s minecraft:iron_ingot 8
tellraw @s {"text":"Заказ выполнен. Серебро в кармане.","color":"gold"}
scoreboard players set @s daily.courier_delivered 0

Последняя строка важна: при следующем заходе на квест scoreboard-флаг должен начинаться с нуля, иначе квест завершится моментально на старте.


Шаг 4. Выдаём первый раз и проверяем повтор

/quest give @s daily:courier

Игрок проходит, квест уходит в «Завершённые → success». Награда выдана.

Чтобы выдать тому же игроку повторно:

/quest give @s daily:courier

InkQuest видит, что квест уже завершён + repeatable: true → сам сбрасывает старый прогресс, потом создаёт новую запись. Игрок получает чистый квест: задача deliver снова active, награды можно получить заново.

Если попробуешь перевыдать активный (незавершённый) повторяемый квест — система откажет. Это защита от случайного стирания текущего прогресса. Сначала пусть игрок завершит или явно дропнет.


Проверка

  1. Выдай повторяемый квест: /quest give @s daily:courier.
  2. Заверши вручную: /scoreboard players set @s daily.courier_delivered 1.
  3. На следующем тике квест уходит в «Завершённые».
  4. Выдай ещё раз: /quest give @s daily:courier — квест снова появляется в «Активных» с чистым прогрессом. ✅
  5. Попробуй выдать ещё раз, не завершая — должен прийти отказ (already_tracked).

Чем repeatable отличается от обычной выдачи

Без repeatable команда /quest give для завершённого квеста выдаёт ошибку «квест уже завершён». С repeatable: true — эта ошибка не появляется, InkQuest сначала сбрасывает старый прогресс, потом создаёт новый.

Состояние квеста Без repeatable С repeatable: true
не выдан выдаст выдаст
активен ошибка «уже отслеживается» ошибка «уже отслеживается»
завершён success/failure/skipped ошибка «уже завершён» сбросит и выдаст заново

Команды, Quest Scroll и любые внешние вызовы работают одинаково — логика одна.


Расширения

Ежедневный квест

Если квест должен выдаваться раз в сутки автоматически — повесь раздачу на циклическую функцию датапака.

data/daily/functions/distribute.mcfunction:

execute as @a run quest give @s daily:courier

Триггерь её, когда время в мире переходит на утро. Самый простой способ — через тиковую функцию + проверку времени:

data/daily/functions/tick.mcfunction:

execute if score #day_phase daily.state matches 0 if predicate daily:is_morning run function daily:distribute
execute if score #day_phase daily.state matches 0 if predicate daily:is_morning run scoreboard players set #day_phase daily.state 1
execute if predicate daily:is_night run scoreboard players set #day_phase daily.state 0

Предикат data/daily/predicates/is_morning.json:

{
  "condition": "minecraft:time_check",
  "value": { "min": 0, "max": 6000 },
  "period": 24000
}

Предикат data/daily/predicates/is_night.json:

{
  "condition": "minecraft:time_check",
  "value": { "min": 13000, "max": 23000 },
  "period": 24000
}

Регистрируем тиковую функцию в data/minecraft/tags/functions/tick.json:

{ "values": ["daily:tick"] }

Заведи scoreboard для фазы дня (раз в #minecraft:load):

scoreboard objectives add daily.state dummy
scoreboard players set #day_phase daily.state 0

Каждое утро всем онлайн-игрокам автоматически выдастся (или перевыдастся) daily:courier. Логика «фазы дня» гарантирует, что раздача случится один раз за сутки, а не каждый тик утра.

Кулдаун между прохождениями

Если квест повторяемый, но не «раз в сутки», а «не чаще раза в час» — заведи scoreboard с временем последнего прохождения. В on.success:

scoreboard players set @s daily_courier_cooldown 72000

В тиковой функции — декрементируй:

execute as @a[scores={daily_courier_cooldown=1..}] run scoreboard players remove @s daily_courier_cooldown 1

Раздачу повторной попытки сделай отдельной командой или предметом, который проверяет cooldown:

execute as @s unless score @s daily_courier_cooldown matches 1.. run quest give @s daily:courier

Перевыдача через свиток (бесконечный источник заданий)

Свитки квестов и повторяемые квесты — идеальная пара. Игрок копит/покупает свитки daily:courier, и каждый свиток даёт ему новую попытку. Логика проверки «выдан/завершён» уже встроена в свиток.

Цепочка повторяемых квестов

Можно сделать целую серию ежедневок, связанных через after: пройди A → откроется B → пройди B → откроется C. И всё это — повторяемое, чтобы цикл можно было прокрутить заново.

⚠️ Тут важно: after смотрит на статус success зависимости. Если первый квест в цепочке — repeatable, и игрок прошёл его дважды, у него всё равно один статус: success. Зависимый квест откроется один раз. Если хочешь, чтобы цикл перезапускался полностью — дропни все квесты цепочки скриптом перед новым запуском.

Тротлинг через require.tags

Если хочешь, чтобы повторная выдача требовала специальный тег (например, «купил билет на сегодня»):

"repeatable": true,
"require": {
  "tags": ["daily.ticket"]
}

Тег ставится покупкой/действием, при выдаче квеста снимается функцией. Игрок не сможет «спамить» повторами без билета.


Подводные камни

  • Сброс при повторной выдаче — атомарный. Снаружи это выглядит как мгновенное «обнулил и выдал заново»; визуальных артефактов нет.
  • repeatable — серверное поле. Клиенту не передаётся, в UI квестовой книги никак не отображается. Если хочешь «значок повторяемости» — добавь его в title через стилизацию ("§b[Ежедневка] Доставка").
  • Все данные предыдущего прохождения теряются. Статус задач, прогресс scoreboard-условий, флаги on.load — всё обнуляется. Если тебе нужна «история прохождений» — храни её отдельно: счётчик в scoreboard, добавляемый в on.success (scoreboard players add @s courier_completed 1).
  • on.unload сработает при сбросе. Перевыдача = drop + give, поэтому on.unload старой задачи сработает перед on.load новой. Если в on.unload есть побочные эффекты — учитывай это.
  • Predicate'ы продолжают «срабатывать» при перевыдаче. Если scoreboard-флаг успеха не сбрасывается в on.success (или другим способом), новая итерация квеста завершится сразу на первом же тике. Сбрось флаг — либо в функции on.success, либо в функции, которая раздаёт квест.

См. также