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

Глобальный квест — общий прогресс всех игроков

Серверный ивент: «вместе уничтожить 10 000 зомби», «общими усилиями собрать 50 000 алмазов», «построить городскую стену». Все игроки видят один и тот же счёт. Когда он достигает цели — задача засчитывается всем.

InkQuest не имеет встроенного понятия «глобального квеста»: у каждого игрока — собственный трекер. Но Minecraft позволяет хранить scoreboard-счёт под именем виртуального игрока (любая строка, не связанная с реальным аккаунтом). Если все участники аккумулируют свой вклад в одну такую переменную — получится общий прогресс.


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

  • Серверные ивенты с коллективной целью. Создаёт чувство общего дела — лучшего, чем личные ачивменты.
  • Гонки фракций. Две команды соревнуются, чей счёт быстрее достигнет цели.
  • Постоянная серверная кампания. Долгосрочные многоэтапные ивенты, тянущиеся неделями.
  • «Стена-страх» в Survival. Виден общий счётчик («осталось 47 минут до Ночи»), создаёт давление.

Как это работает в одном предложении

condition.success смотрит на scoreboard виртуального игрока #GLOBAL через тип global_score. Каждый участник через on.tick переносит свой личный вклад в этот общий счёт. Когда #GLOBAL достигает цели — success срабатывает у каждого участника независимо.


Шаг 1. Создаём квест с глобальным условием

data/legion_fall/quests/main.json:

{
  "version": 3,
  "variant": 0,
  "title": "Падение легиона",
  "description": "Тьма наступает. Каждый удар по нечисти приближает конец войны.",
  "tasks": {
    "kill_zombies": {
      "title": "Сразить 10 000 зомби",
      "condition": {
        "success": {
          "type": "global_score",
          "objective": "zombies_killed_total",
          "to": 10000
        }
      },
      "on": {
        "load": {
          "functions": ["legion_fall:init"]
        },
        "tick": {
          "functions": ["legion_fall:accumulate"]
        },
        "success": {
          "functions": ["legion_fall:reward"]
        }
      }
    }
  },
  "stages": [["kill_zombies"]]
}

Главное:

  • "type": "global_score" — счёт фиксированного holder'а, а не контекстного игрока. При загрузке задачи каждым участником счёт не сбрасывается — это принципиальное отличие от score.
  • "player" не указан — используется дефолт "#GLOBAL". Можно задать любое имя (SERVER, EVENT_KARMA, #тьма) — главное, чтобы оно совпадало с тем, куда on.tick-функция аккумулирует вклад.

Шаг 2. Заводим scoreboards в функции on.load

Загружается один раз при первой выдаче задачи у каждого игрока.

data/legion_fall/functions/init.mcfunction:

scoreboard objectives add zombies_personal minecraft.killed:minecraft.zombie
scoreboard objectives add zombies_killed_total dummy
scoreboard players set @s zombies_personal 0

Три строки: 1. Личный счётчик убийств зомби (статистика Minecraft). 2. Общий счётчик (dummy — управляем вручную). 3. Сброс личного счёта при старте квеста, чтобы предыдущие убийства не зачлись.

Команды scoreboard objectives add ... безопасно вызывать многократно — если objective уже есть, ничего не сломается.


Шаг 3. Накопление в on.tick

data/legion_fall/functions/accumulate.mcfunction:

scoreboard players operation GLOBAL zombies_killed_total += @s zombies_personal
scoreboard players set @s zombies_personal 0

Каждый тик личные убийства игрока прибавляются к общему счёту и сразу обнуляются. Так каждое убийство учитывается ровно один раз.


Шаг 4. Награда в on.success

data/legion_fall/functions/reward.mcfunction:

give @s minecraft:netherite_helmet
tellraw @s {"text":"Легион пал. Спасибо за участие!","color":"gold"}

Эта функция вызовется у каждого участника, как только GLOBAL-счётчик достигнет 10000.


Шаг 5. Раздаём квест всем участникам

В функции #minecraft:load (или при заходе нового игрока на сервер):

execute as @a run quest give @s legion_fall:main

Или вручную: /execute as @a run quest give @s legion_fall:main.


Проверка

  1. Выдай квест двум игрокам (или себе + alt-аккаунт).
  2. Убей нескольких зомби каждым игроком.
  3. Проверь общий счёт: /scoreboard players get GLOBAL zombies_killed_total — он должен расти.
  4. Для быстрой проверки добавь руками: /scoreboard players add GLOBAL zombies_killed_total 10000.
  5. На следующем тике задача завершится у обоих игроков одновременно. ✅

Что важно понимать

Каждый игрок — отдельный трекер

InkQuest хранит активную задачу для каждого участника независимо. Условие score смотрит на общий объект scoreboard. Когда GLOBAL zombies_killed_total достигает цели, следующий тик каждого участника увидит выполненное условие — задача завершится у каждого из них, и хук on.success сработает у каждого индивидуально.

Новый игрок видит уже накопленный прогресс

Если выдать квест посреди ивента — игрок сразу увидит «9 500 / 10 000». Прогресс хранится в scoreboard, а не в данных квеста, поэтому он «общий по построению».

Только участники вносят вклад

Хук on.tick срабатывает у тех, кто отслеживает квест. Если квест нужно сделать обязательным для всех — выдавай его автоматически (см. шаг 5), либо в on.success стартового квеста по входу на сервер.

condition.failure тоже глобален

Если задать failure через тот же score (например, «если за 30 минут не успели»), провал прилетит всем одновременно — что и нужно для ивента. См. Задача с двумя исходами про эту механику.


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

⚠️ Не используй score вместо global_score

Если ты поставишь тип score и захочешь смотреть на счёт по holder'у через player — этого поля у score больше нет. Используй global_score: он именно для этого и создан. Тип score всегда смотрит только на счёт контекстного игрока и при reset: true (дефолт) записывает from в scoreboard при каждом load — что сбросило бы общий счёт при входе каждого нового игрока.

Дроп квеста не сбросит scoreboard

Это разные слои хранения. Данные квеста живут в сейве мира, scoreboard живёт сам по себе. При перезапуске ивента обнуляй objective явно:

/scoreboard players reset #GLOBAL zombies_killed_total

on.success срабатывает у каждого игрока

Если функция должна выполниться один раз на мир (например, открыть портал, заспавнить босса) — защити её через scoreboard-флаг:

# Первой строкой — поднимаем счётчик
scoreboard players add GLOBAL boss_spawned 1
# Остальная логика — только если первый
execute if score GLOBAL boss_spawned matches 1 run summon minecraft:wither ~ ~ ~
execute if score GLOBAL boss_spawned matches 1 run tellraw @a {"text":"Босс пробудился!","color":"red"}

Первый сработавший игрок поднимет счётчик с 0 до 1 и выполнит логику; остальные поднимут его выше 1 и не пройдут условие.

Игроки офлайн не вносят вклад в реальном времени

Их on.tick не работает, пока они не зайдут на сервер. Это не мешает — оффлайн-участник просто «догонит» остальных, когда вернётся, или присоединится к уже частично заполненному прогрессу.


Расширения

Видимый счётчик в sidebar

%%Есть progress bar, так что счёт уже видно%%

Чтобы все игроки видели общий прогресс — выведи его на боковую панель:

scoreboard objectives modify zombies_killed_total displayname "Зомби убито"
scoreboard objectives setdisplay sidebar zombies_killed_total

Теперь справа у каждого игрока висит «Зомби убито: GLOBAL 9234». Это нативная Minecraft-механика, InkQuest тут не нужен.

Гонка фракций

%%Лучше сделать пустую обязательную задачу, а фракции опциональными, в success условие score (как сейчас), а в failed условие optionals с min 1. Получится универсальнее и расширяемо на любое число команд%%

Два виртуальных игрока (FACTION_RED, FACTION_BLUE), два условия global_score:

"condition": {
  "success": {
    "type": "global_score",
    "objective": "war_score",
    "player": "FACTION_RED",
    "to": 10000
  },
  "failure": {
    "type": "global_score",
    "objective": "war_score",
    "player": "FACTION_BLUE",
    "to": 10000
  }
}

Выдай этот квест игрокам красной фракции, в on.tick накапливай в FACTION_RED. Параллельный квест с обратным расположением полей — для синей. Победит та, что первой набирает 10 000.

Многошаговый ивент

Несколько обязательных задач в одном квесте, каждая со своим GLOBAL-счётчиком — этапы прогрессии всей серверной кампании.

"stages": [
  ["build_wall"],
  ["fill_granaries"],
  ["defend_against_horde"]
]

Игроки коллективно проходят по этапам: построили стену → заполняем амбары → защищаемся от орды. Каждый этап со своим глобальным счётчиком и своей наградой в on.success.

«Уникальный» вклад

Если каждый игрок должен внести вклад не более одного раза (например, «голосование 100 игроков») — не используй накопление через on.tick. Вместо этого: в специальной функции (например, по нажатию кнопки) ставится тег voted, и тогда:

execute as @a[tag=voted,tag=!counted] run function event:count_vote

Где count_vote инкрементит GLOBAL и ставит counted. Так дублирования не будет.

%%Тут хотелось бы ещё содержимое event:count_vote увидеть%%


См. также