на главную
об игре
Crusader Kings 3 01.09.2020

Дневник разработчиков Crusader Kings 3 #30

Товарищи, приветствую вас в очередном дневнике! Сегодня мы рассмотрим структуру событий в CK3, их скрипты и отличия от CK2.

Анатомия события

Основа стандартного события персонажа наверняка уже вам знакома: у нас есть название, описание, частенько портрет, и один или несколько вариантов ответа.

Но в скриптах мы кое-что изменили, чтобы улучшить функционал, читабельность и общую гигиену скрипта. Вот сравнение начала события в CK2 и CK3:

Вы сразу же заметите, что мы поменяли местами тип и ID события: теперь события создаются по имени (всё ещё определяемому вверху каждого файла) и уникальному ID, а тип задаётся внутри самого события, а не наоборот. Это значит, что теперь вы сможете читать ID даже свёрнутых событий.

Далее, мы изменили принцип работы триггеров текста. В CK2 это мы использовали этот полезный инструмент, чтобы мелкие атмосферные детали соответствовали положению игрока. Это позволяло нам делать события более универсальными и в то же время уникальными. Но конструкция могла выйти громоздкой, поскольку у нас не было возможности сделать так, чтобы триггеры были взаимоисключающими, что порой приводило к таким вот ситуациям:

Не самое жуткое зрелище в мире, но слишком громоздко для своего функционала.

В CK3 мы можем урезать эту конструкцию при помощи блока first_valid внутри блоков текста (как показано на первом изображении), выбирая первый пункт из списка, отвечающий заданным критериям. Это значит, что нам больше не нужно делать блоки текста взаимоисключающими с точки зрения триггера (что зачастую приводит к усложнению с ростом числа текстов). Вместо этого мы может просто логически упорядочить тексты по предпочтению в соответствии с довольно простыми триггерами.

Например, если у меня есть блок текста, отличающийся для француза, ашарита и персонажа старше восьми лет, и выдающий стандартный вариант во всех остальных случаях, я могу задать для него следующий скрипт:

Скрипт автоматически пойдёт по списку. Персонаж-француз увидит один вариант, ашарит не француз увидит другое, а не француз и не ашарит старше восьми лет увидит третий вариант, тогда как все остальные получат четвёртый. Так можно очень легко добавлять новые контекстозависимые варианты описаний и заголовков событий.

И ещё одно небольшое замечание. Поскольку теперь тексты хранятся внутри большого блока, стало проще сходу сортировать их: каким бы большим ни был ваш стриггеренный текст, вы можете свернуть его в своём любимом редакторе за один, а не за десяток кликов. в деле минимизации количества кликов важна каждая мелочь!

Тематика и фон событий

В формате событий CK3 отсутствует несколько важных блоков: изображение и границы.

И не без весомой на то причины! Большей частью они были включены в новую систему тематики событий, которую вы можете наблюдать в самом первом примере скрипта CK3 наверху.

Тематика определяет, какой значок будет отображаться левом верхнем углу, и помогает группировать события, объединённые общей темой. Эта система также помогает игроку понять, с чем именно связано событие. Кроме того, тематика задаёт подходящий вариант стандартого фона (который в свою очередь задаёт степень освещённости на портретах персонажей) в зависимости от ситуации и набор фоновых звуковых эффектов.

Фоны и тематические значки при необходимости можно вручную изменить, задав строку в скрипте, если нам кажется, что для данного события есть более подходящий фон. Поэтому, хотя эта система и создана с целью упростить работу по добавлению атмосферности событиям, мы всё ещё полностью контролируем процесс.

Вряд ли нужно уточнять, что тематику событий можно с лёгкостью задавать скриптами: вы можете без проблем модифицировать значок, стандартный фон, освещение модели персонажа и звуковые эффекты, а также самолично создавать и настраивать темы.

Портреты и анимации

Годнота! Портреты в новой системе, вот удивительно, чуть более динамичны, чем в CK2, и на любом событии может присутствовать от 0 до 5 портретов. Из них два полностью анимированы (располагаются слева и справа соответственно), а три других представлены головами персонажей в нижней части окна. Их можно использовать в любых сочетаниях, чтобы событие выглядело именно так, как хочется вам.

Левый и правый портреты могут использовать множество анимаций, созданных для релизной версии. Головы персонажей не анимированы, но позволяют отображать вспомогательных персонажей, упоминаемых в описании события.

On actions для всего!

Все события происходят по какой-то причине, и в СК2 (особенно на ранних этапах её жизни) причиной выступала система Mean Time To Happen, позволяющая нам примерно задать количество дней, через которое произойдёт событие. К сожалению, когда приходится балансировать сотни событий между собой, гибкость этой системы из преимущества превращается в недостаток, потому что становится тяжело управлять частотой событий без жёстких триггеров. К тому же, из-за работы с чистыми вероятностями и пагубного влияния на производительность это становилось причиной странных статистических отклонений.

По мере работы над СК2 мы стали всё чаще прибегать к вызову событий через on_action — это небольшие крючочки в коде, прицепленные к особенностям или регулярным проверкам, которые и вызывают события (и эффекты, если необходимо). На это уходит несколько больше времени в плане баланса, но такой подход даёт нам гораздо больше контроля над тем, как и когда вызываются события. Настраивать их тоже намного проще, да и в вопросе производительность всё работает супербыстро.

В СК3 для вызова событий мы используем исключительно on_action. В коде их очень много, и они позволяют нам выдавать события, когда в мире происходит что-то конкретное (например, рождается ребёнок), или с определённым интервалом (например, каждые пять лет). Можно настроить так, чтобы они срабатывали при каждом on_action, или внести в список возможных событий (в этом случае применяются весовые модификаторы, которые работают как обычно).

Главное улучшение по сравнению с on_action в СК2 (помимо более продуманной и логичной системы их использования, что по мне тоже очень здорово, но я также уважаю мнение других людей с более интересной жизнью, чем моя, и более высокой планкой для "здоровского") — это скриптованные on_action! Это позволяет нам (и вам, конечно!) создавать и подвязывать on_action прямо в скриптах, а не полагаться лишь на заранее прописанные в коде on_action. Скриптованные on_action работают в точности как те, что прописаны в коде — как совокупность событий и эффектов с весом или без, только вызывается откуда-то из общедоступного скрипта, а не из закрытого кода.

Для примера предположим, что я сделал новый набор событий о примирении после гражданской войны: соседи вновь учатся жить мирно после того, как сражались по разные стороны баррикад, такое вот всё — и я хочу привязать их к окончанию гражданской войны. Всё, что мне для этого нужно сделать, это лишь добавить что-то такое в соответствующий эффект окончания войны:

Потому я создаю файл с таким содержанием в нужной папке:

Везде, где у вы можете описать эффект, вы также можете описать ссылку на новый (или уже написанный) on_action.

Блок immediate и вы

Большое нововведение в скриптах СК, которым мы очень часто пользуемся в блоке immediate — скриптованный список!

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

Например, если я хочу взять каждого озлобленного старика среди моих вассалов и придворных, то пишу что-то такое:

Затем я хочу выбрать двух самых озлобленных среди них, чтобы использовать в событии или где угодно ещё:

Готово! Я могу ссылаться на этих двух персонажей в локализации, применять к ним эффекты, делать их портретными персонажами и т.д. — что угодно. Вы могли заметить наш классный alternative_limit: он проверяет условия, если limit прям над ним не сработает.

Таким образом мы сводим к минимуму необходимое обслуживание скриптов и пресекаем возможные баги в зародыше. Но это скрипт всё ещё может вызывать тревогу, ведь как насчёт вызова двух отдельных списков, у которых одинаковые условия, которые сами являются часть отдельного скриптованного триггера?

Эту проблему можно решить различными способами, но давайте я покажу вам нашу новую возможность — эффект ordered_in_list:

Ordered_in_list берёт список и, используя систему под названием script_maths (о которой, надеюсь, удастся поговорить в другой раз), присваивает числовое значение каждому элементу списка. Затем он применяет любые эффекты из своей группы как обычно, по умолчанию к элементу с наибольшим значением из всего списка (хотя здесь мы можем сказать ему применить эффекты к любому количеству элементов списка). Тут мы сортировали относительно небольшой набор элементов по довольно ограниченному ряду условий, но эта сортировка может быть сколь угодно сложной и всеобъемлющей.

Из других связанных с блоком immediate новостей: теперь легче сохранять скоупы (изначально цели событий, которые вы можете отчасти увидеть в примерах выше) и переменные, и использовать эту группу для настройки музыки для максимальной драмы. Стандартный функционал блока immediate (выполняется до того, как будет показано окно события) не изменился, и видимые эффекты, выполненные в immediate, будут появляться с припиской "Произошло:" во всех вариантах ответа на события.

Варианты ответа: Даём ИИ личность и подвергаем игроков стрессу

И наконец, варианты ответа. Они похожи на свои аналоги из CK2, в видимом событии их не меньше одного, и каждому из них требуется текстовое описание, но в целом вы вольны добавлять любые эффекты по своему усмотрению.

Тут вы заметите два главных нововведения: значительно более частое использование ai_chance и, во многих случаях, stress_impact.

ai_chance, как и в CK2 определяет примерную вероятность того, что NPC выберет этот вариант. В CK3 мы куда чаще используем этот блок вместе с ai_value_modifiers (создающими личность каждого персонажа исходя из значений, которые в свою очередь по большей части определяются их свойствами), чтобы персонажи по возможности действовали в соответствии со своим типом личности. Это достигается путём назначения разных весовых коэффициентов вариантам события на основе подходящих ai_value_modifiers. Этот блок всё ещё учитывает и другие триггеры, поэтому ИИ может выбирать варианты, исходя из свойств, состояния войны, соперничества и т.д.

Меж тем Stress_impact используется с новой механикой стресса, о которой мы упоминали несколько дневников назад! Не волнуйтесь, если забыли. Вкратце, стресс является мерой негативных эффектов, которые ваш персонаж получает, когда действует вопреки своему типу личности (скажем, сострадательный персонаж не любит пытать людей).

Мы проверяем его здесь, заполняя блок stress_impact любыми свойствами личности, связанными с выбором того или иного варианта, и используя заскриптованные значения stress_impact. Благодаря магии автоматизации мы можем сочетать любое количество источников получения и потери стресса в блоке stress_impact. И на варианте выбора события будет показано итоговое значение изменения стресса.

Вы также можете добавлять стресс как обычный эффект за пределами блока stress_impact. В этом случае суммирования не будет. При желании вы можете даже добавить несколько блоков влияния стресса, которые просто будут сочетать отдельные модификаторы стресса, а можете вообще игнорировать этот блок. Как вам угодно.

И на этом мы завершаем очередной дневник. Я пару часов послежу за темой, чтобы ответить на возможные вопросы. Жду нашей следующей встре...

А что, о триггерах не расскажете?

Ха, попались! Поднимите руки все, кто заметил мой незаметный спойлер триггеров на одном из первых скриншотов. Вы выиграли ровно один интернет-балл!

Всем остальным предлагаю ещё раз взглянуть на скриншот:

Напомню, в CK2 заскриптованные триггеры позволяли объединять длинные списки требований в ссылке, а затем при проверке ссылаться уже на неё.

Например, у меня в событии есть два места, где нужно проверить более 20 условий: событие будет нормально работать, если я дважды запишу этот скрипт, но что если кто-нибудь потом обновит один набор триггеров и забудет про второй? Тут же полезут баги. А что если мне нужно проверять эти триггеры больше двух раз или в нескольких событиях или, вот уж полный кошмар, в нескольких событиях в разных файлах?

Легко представить, что мы очень быстро получим полный бардак. Но мы не хотим использовать менее сложные триггеры, потому что это портит игру и делает её менее интересной (да и какое может быть CK без абсурдно большого числа указаний?), а в частности ещё и потому, что это лишь упрощает проблему, но не решает её.

Вместо этого мы создали скриптованный триггер, представляющий собой список триггеров, записанных один раз в файле, к которым потом можно обращаться из других файлов. Затем, как только мы захотим обратиться к этим триггерам, мы вызываем заскриптованный триггер, проверяющий контент. В любой момент, когда нужно обновить или починить триггеры, достаточно один раз починить заскриптованный триггер, что в большинстве случаев автоматически чинит все места, где он вызывается. Экономит кучу времени на обслуживание и значительно снижает количество возможных багов!

Но в CK2 эти заскриптованные триггеры нужно было хранить в особой папке в директории /common, отдельно от событий (и других скриптов), которые к ним обращались. Возможно, это не кажется большой проблемой, но нам постоянно приходилось прыгать туда-сюда во время создания и поддержки заскриптованных триггеров. И на практике вы видели лишь огромный список заскриптованных триггеров, некоторые из которых могли использоваться всего в паре мест.

В CK3 мы улучшаем эту систему, добавляя подстановку скриптованных триггеров. Это значит, что заскриптованные триггеры можно писать прямо в файле, который их использует, если они больше нигде не используются. Для более утилитарных скриптованных триггеров, используемых в нескольких файлах, всё ещё есть старая система с отдельной папкой. Это позволяет нам куда тщательнее разделять крупные и мелкие скриптованные триггеры (да и скриптованные эффекты тоже), что значительно упрощает нам (и вам!) процесс создания сложных скриптов, с которые всё равно будет довольно просто работать в дальнейшем.

И вот теперь мы наконец подходим к концу дневника. До встречи на следующей неделе!

Оригинал на английском

Комментарии: 0
Ваш комментарий