Всем привет!
Сегодня говорим про систему world state — она позволяет делать вариативность, запоминать решения игрока и делать реплики/ответы на основе флагов.

Для меня было несколько удивительно узнать об этой системе — в играх мы обычно видим различные системы, с которыми взаимодействует игрок: различные варианты выбора, наборы умений и бонусов, деревья прокачки или сложное поведение НПЦ. Однако почти во всех играх, как оказалось, присутствует система состояния мира. Но находится она в неявном виде, поэтому существует только в логике игры.

Причина вообще делать эту систему:
Представьте что вы хотите чтобы НПЦ в диалоге отреагировал на то что вы встретили другого персонажа. Тогда вы добавите его реакцию в виде реплики "Как тебе этот чел в кроссовках?(указывает на ступеньки)".
И дадите несколько вариантов ответа:
1. Харизматичный тип.
2. У него грязные кроссовки.

Но. Если вы этого персонажа в кроссовках еще не встречали, то НПЦ не должен этого говорить. Эти реплики и ответы будут скрыты. Они будут отфильтрованы системой World state.
То есть сама реакция НПЦ и ответы игрока будут внутри игры, но не будут показываться пока World state не обновит свое значение.

Другие условия в которых это можно применить:
Если вы подойдёте к НПЦ, то некоторые реплики вы захотите разблокировать только после определённой проверки навыка, после сказанной реплики, после выполненного квеста или новой обнаруженной информации. Вы хотите создать реактивность игры на действия игрока. Или по простому - РАЗВИЛКИ В КВЕСТАХ.

Как сделал:
Создал динамическую структуру — WorldStateMap, которая может добавлять в себя новые значения.
Изначально она полностью пустая, и чем больше ты играешь в игру, тем больше она наполняется значениями, которые игра может использовать.

Для доступа к структуре у каждой реплики НПЦ и у каждого ответа игрока есть 3 поля (они добавлены в шаблонах Articy, и потом мой скрипт-конвертер их обрабатывает):

bRecordToWS — если галочка стоит, то реплика должна записать флаг.
WSFlagName — имя флага. (Пример: SETBOOL(met_person).)
NodeVisibilityCondition — условия видимости. (Пример: GETBOOL(met_person).) Каждая нода проверяется. Если условия возвращают FALSE, то тогда нода не показывается.

Сам цикл записи и получения данных из WS:
SETBOOL(met_person) --> Запись в WS --> GETBOOL(met_person) --> true/false

То есть если вы не встретили персонажа в кроссовках из примера выше, то значение будет False и реплики скроются - игра их просто пропустит. Однако если встретили, то команда SETBOOl запишет значение True в world state. Тогда, в нужный момент реплика спросит у WS через команду GETBOOL встречал ли ты этого персонажа и вернет значение True. Эта реплика будет явной и вы сможете заметить, что игра отреагировала на ваше действие.

При самой записи в WS простой парсер разбирает текст на аргументы и отправляет значения на выбор команды: (СКРИН_1)
SETBOOL
GETBOOL
SETINT
GETINT
HAS_VISITED — это специальное поле, которое позволяет затемнять уже прочитанные реплики. (СКРИН_2)
ADD_VISITED — ещё одно поле, которое записывает в список все прочитанные реплики.

СКРИН_1
СКРИН_1
СКРИН_2
СКРИН_2

Также есть поддержка NOT — это не отдельные команды, а рекурсия с перевёрнутыми значениями true/false.

Для того чтобы записать значение, диалоговый менеджер отправляет значение флага в WS (Process Query). (СКРИН_3)

СКРИН_3
СКРИН_3

В случае чтения кнопка или менеджер также обращаются в WS, который возвращает true/false. (СКРИН_4)

СКРИН_4
СКРИН_4

Также все активные проверки (навыки/сноски/сноски+навыки) ставят флаг только после успешного броска кубика. Все обычные кнопки ставят его сразу.

Отдельно упомяну, что все посещённые ответы игрока имеют свой уникальный ID, который и записывается в WS, но не в общий пул флагов, а в отдельный VisitedDialogueIDs.

В результате ответы игрока и реплики НПЦ могут скрывать или показывать друг друга. Получаем динамическую систему, которая запоминает все действия игрока, — что и нужно нарративному дизайнеру!