Модуль для упрощенного создания диалогов от Zander_driver
Это скриптовый файл, дающий новый способ создания диалогов, и не отменяя старые.
Новый способ прост и не требует от пользователя знания скриптов, помогает быстро и легко отловить все распространенные ошибки в диалогах, а так же существенно упрощает сам процесс создания и редактирования, особенно в случае больших, сложных и разветвленных диалогов, по сравнению со старыми методами.
Диалог пишется в файле ltx-конфига, и избавлен от громоздкой мишуры xml-тегов. Возможно это мое субъективное мнение, но форма написания диалога получается более наглядной.
Сохраняются все возможности диалогов созданных по-старинке: к любой фразе можно прицепить прекондишен, повесить вызов функции, выдачу/отнятие инфопоршня, проверку на наличие/отсутствие инфопоршня.
Добавлена возможность написания диалоговых блоков - своего рода готовых деталей для сборки. И диалог можно составлять как из простых фраз, так и из готовых блоков, в любом сочетании. Сложность и размеры диалоговых блоков практически не ограничены, любой диалоговый блок сам так же может быть составлен из других, более мелких диалоговых блоков. Вы можете использовать одни и те же диалоговые блоки в различных диалогах, это как детали конструктора.
Ряд возможных ошибок при составлении диалога, ранее приводивших к неинформативным или безлоговым вылетам, теперь сопровождаются более ясной информацией в логе. примеры:
1) Старые методы: При указании функции для прекондишена, допущена опечатка/функция не существует. Результат: пока не добраться до той фразы, все работает. Дойдя до проблемной фразы, уже разговаривая с нпс в игре, получаем безлоговый вылет.
Новый метод: Еще при формировании диалога, выполняется проверка существования всех вызываемых в нем функций. В случае, если функция не существует, произойдет вылет:
Dialog ; line : function . not exist
2) Старые методы: В большом разветвленном диалоге, из-за ошибки в связях между фразами, к какой-либо фразе отсутствует входящая связь. Результат: насколько я помню, довольно неинформативный вылет, никак не помогающий найти ошибку.
Новый метод: Еще при формировании диалога, проверяется его структура. В случае такой ошибки произойдет вылет:
Отсутствует входящий линк для фразы. section , phrase
3) Старые методы: По ошибке/невнимательности, модостроитель при составлении сложноразветвленного диалога, допустил нарушение чередования фраз актор/нпс. Результат: непредсказуемое поведение диалога в игре, как правило не то что ожидалось, причину приходится искать вручную.
Новый метод: Существует возможность настроить отслеживание фраз. обычный диалог - первая фраза принадлежит актору. старт-диалог - первая фраза принадлежит нпс. Если чередование фраз нарушено, Это будет обнаружено еще при формировании диалога, и произойдет такой вылет:
Нарушено чередование фраз npc/actor. section , line
указывающий точно, где именно произошло "столкновение" с фразой, принадлежащей не тому собеседнику.
Так же есть возможность отключить отслеживание чередования для отдельно взятого диалога.
1. скачать можно тут https://yadi.sk/d/XxUqwIvthfeWo
2. распаковываем архив, кидаем файл в папку gamedata/scripts
3. пользуемся новыми возможностями.
Никакого совмещения не требуется. Встроена проверка версий, так что модуль будет работать на любой версии игры. (ТЧ любой патч / ЧН / ЗП). Правда, я не тестировал на других версиях Если я ошибся и что-то не работает, пишите.
* Для того чтобы правильно работали функции отслеживания ошибок, у вас должны быть рабочие функции abort и log.
Ну для начала,
раз теперь у нас диалоги пишутся в ltx-файлах конфигов, неплохо бы завести для них отдельное место. Это не обязательное действие, просто для удобства следует выполнить следующее:
1. в папке config создаем новую папку dialogs
2. создаем в нем текстовый файл, называем его dialogs.ltx
3. открываем в папке config файл system.ltx, и в самый его конец пишем:
#include "dialogs\dialogs.ltx"
4. теперь можно в папке dialogs создавать новые файлы, писать в них свои диалоги, к примеру мы создали файл my_first_dialog.ltx, его надо подключить к игре. открываем наш файл dialogs.ltx, и в нем пишем
#include "my_first_dialog.ltx"
Но, сторонники бардака и беспорядка могут писать свои диалоги в любых файлах конфигов где угодно, только не забудьте так или иначе подключить их к system.ltx.
Как создать диалог.
1. Каждый диалог в новой системе имеет секцию конфига. таким образом, создав в файле конфига секцию
[my_first_dialog]
вы уже создали новый диалог. Правда пока он не имеет ни единой фразы, но мы решим этот вопрос позже. Пока надо сделать чтобы скрипты его увидели.
2. Идем в gamedata/config/gameplay, открываем файл dialogs.xml, Где-нибудь между уже имеющихся диалогов, добавляем в него следущее:
dialogs_new_system.my_first_dialog
3. Идем в gamedata/scripts, создаем новый текстовый файл, называем его dialogs_new_system.script
4. Открываем его, вставляем такой код:
function my_first_dialog(dlg) assembly_dialogs.construct_dialog("my_first_dialog",dlg) end
Все, игра его видит. Правда он все еще не написан.
Пишем диалог...
1. Идем в gamedata\config\dialogs, открываем наш файл my_first_dialog.ltx, где мы создали пока еще пустую, секцию диалога. Диалог в конфиге пишется при помощи команд, далее я напишу какие команды что делают.
2. НЕОБЯЗАТЕЛЬНО.
в начале можно настроить, надо ли в этом диалоге отслеживать чередование фраз.
если мы напишем
params_sequence = actor
значит это обычный диалог, где первую фразу говорит актор. система будет отслеживать чтобы собеседники говорили по очереди не нарушая порядок.
Если написать
params_sequence = npc
значит это старт-диалог, и в нем первая фраза принадлежит НПС. система так же будет отслеживать порядок фраз.
А если мы напишем
params_sequence = free
То чередование фраз отслеживаться не будет.
Если не указывать этот параметр, то по умолчанию считается что params_sequence = actor, т.е. это обычный диалог, первая фраза принадлежит актору, отслеживание активно.
3. добавляем фразы, и связи между ними
вот пример простенького диалога:
[my_first_dialog] x0 = текст_первой_фразы link0 = 1 x1 = текст_второй_фразы link1 = 2 x2 = текст_третьей_фразы
Тут все, я думаю, очевидно и понятно.
команды x<номер фразы> - создают новую фразу в диалоге. значение команды это ее текст.
Можно писать текст прямо тут, можно приводить string_id из файлов папки config/text/rus. рекомендуется второй вариант.
Диалог всегда начинается с фразы x0, она здесь обязательна.
команды link<номер фразы> = № следующей фразы - создают связи между фразами.
Теперь возьмем пример посложнее.
[my_first_dialog] x0 = текст_первой_фразы link0 = 1 x1 = текст_второй_фразы link1 = 10, 20, 30 x10 = текст_фразы_10 link10 = 11 x11 = текст_фразы_11 x20 = текст_фразы_20 link20 = 21 x21 = text link21 = 22 x22 = text x30 = text link30 = 31 x31 = text
Тут у нас актор произносит фразу х0, нпс ему отвечает фразой х1, а далее актору открывается выбор из трех фраз, х10, х20, х30, каждая из которых ведет в свою ветку диалога. Из любой фразы можно сделать неограниченное число линков, разделяя их запятыми. Этот пример так же показывает тот факт, что нумерация фраз не обязана быть сплошной. В них могут быть разрывы до сотни.
И еще вот такой пример:
[my_first_dialog] x0 = text link0 = 1 x1 = text link1 = 2 x2 = text link2 = 3 x3 = text link3 = 4 x4 = text link4 = 1, 5 x5 = text link5 = 6 x6 = text
здесь после 4-й фразы (нпс) актору открывается выбор, продолжить диалог или вернуться к началу.
Думаю, с созданием фраз и связей между ними, все уже понятно.
4. Добавляем другие команды
все команды пишутся по принципу <команда><номер фразы>
команда a - добавит вызов функции в указанной фразе (аналогично тегу
команда p - добавит прекондишен (проверку условия) в указанной фразе (аналогично тегу
команда h - добавит проверку наличия инфопоршня в указанной фразе (аналогично тегу
команда d - добавит проверку отсутствия инфопоршня в указанной фразе (аналогично тегу
команда gi - выдать актору инфопоршень в указанной фразе (аналогично тегу
команда di - отнять у актора инфопоршень в указанной фразе. (аналогично тегу
команда item - заспавнить в инвентарь актора итем указанной секции и количества. количество пишется через точку, если не указано то считается равным единице.
пример: item5 = bandage.10, medkit.5 ; заспавнит 10 бинтов и 5 аптечек пример2: item5 = antirad, wpn_pm.2 ; заспавнит 1 антирад и два ПМа.
команда remove - удалить из инвентаря актора итемы указанных секций и количества. Если у актора меньше итемов, удаляются те что есть. Т.е. проверка необходимого количества здесь не выполняется. Если такая проверка нужна, используйте команду has_item.
пример: remove5 = wpn_pm ; удалит ПМ пример2: remove5 = bandage.2, medkit.3, antirad.4, vodka.5 ; удалит 2 бинта, 3 аптечки, 4 антирада и 5 бутылок водки. если чего-то не хватает, это не помешает.
команда has_item - вставляет проверку наличия в инвентаре актора, указанных предметов в указанном количестве.
фраза не будет пропущена, если хоть какого-то из указанных итемов не хватает.
пример: has_item5 = wpn_abakan ; фраза будет доступна если у актора есть абакан пример2: has_item5 = af_medusa.10, bandage.4 ; фраза будет доступна если у актора есть 10 медуз и 4 бинта.
команда donthas_item - вставляет проверку, что в инвентаре актора указанных предметов меньше заданного количества.
пример: donthas_item5 = wpn_pm, wpn_abakan, wpn_mp5, medkit ; фраза будет доступна только если у актора нет ничего из перечисленного оружия и нет аптечек. пример2: donthas_item5 = bandage.25, medkit ; фраза будет доступна если у актора нет ни одной аптечки, и количество бинтов в его инвентаре менее 25.
пример:
[my_first_dialog] x0 = text link0 = 1 a0 = spawn.minigun x1 = text link1 = 5, 10, 15 gi1 = answer_infoportion x5 = text link5 = 6 p5 = my_script.answer_function x6 = text a6 = spawn.ammo_1, spawn.medkit, xr_motivator.kill_all_bandits x10 = text h10 = какой_то_поршень_1, какой_то_поршень_2 link10 = 11 x11 = text x15 = text link15 = 16 x16 = text link16 = 17 x17 = text
тут мы в стартовой фразе х0, вызываем скриптовую функцию spawn.minigun,
в следующей выдаем актору некий инфопоршень, затем у нас развилка на три ветки.
первая ветка начинается с фразы 5, в ней предусловие - my_script.answer_function
и если указанная функция вернет истину, то только тогда эта ветка диалога будет доступна. в следующей фразе вызываем три функции, spawn.ammo_1, spawn.medkit, xr_motivator.kill_all_bandits - для любой команды, можно указывать любое количество аргументов, разделяя их запятыми. при вызове, очередность их следования будет соблюдаться.
в другой ветке, начинающейся с фразы x10, на входе мы проверяем наличие двух каких-то инфопоршней. ветка будет доступна если у актора есть оба. третья ветка, начинается с фразы x15, доступна в любом случае и не снабжена никакими командами.
что можно сказать по итогам. К каждой фразе можно прицепить неограниченное количество каких вам угодно команд, и каждой указать любое количество аргументов через запятую. Не надо только повторять команды. к примеру такая запись:
x5 = text a5 = my_script.xxx_function link5 = 6 a5 = my_file.answer_function
неправильна, из двух команд a5, читаться будет только одна. Правильный способ записи в таком случае будет выглядеть так:
x5 = text a5 = my_script.xxx_function, my_file.answer_function link5 = 6
5. прописываем в профиле любого удобного вам НПС, новый диалог
my_first_dialog
И идем проверять как все работает.
Для начала, что такое диалоговый блок. Это некоторый фрагмент диалога, у него есть вход - начальная фраза. Некоторое число фраз, от одной до - много. и есть фразы, (от нуля до сколько захотите), которые считаются выходами диалогового блока. Я не случайно говорю "от нуля" - выходы могут и отсутствовать.
Что такое выходы - это те фразы, к которым будет подключать линки на последующие внешние для этого блока, фразы, внешний для него же, конструктор диалога.
Пока все может быть не очень понятно, наберитесь терпения. когда доберемся до конкретных примеров, все станет яснее.
Как создаются диалоговые блоки? А точно так же как и диалоги. создаем секцию, в нее пишем фразы, линки и команды. Начальная фраза - вход, выходов может быть нет, может быть есть один, может быть много.
Чем же отличается диалоговый блок, от самого диалога? А отличается он тем, что может быть подключен к другому диалогу, в любом месте.
Для создания выходов диалогового блока, существует команда exit. аргумент, который для нее указывается - это порядковый номер выхода.
Давайте уже перейдем к примерам. Создадим простенький диалоговый блок с одним выходом:
[dlg_block_test] x1 = text_phrase_1 link1 = 2 x2 = text_phrase_2 link2 = 3 x3 = text_phrase_3 link3 = 4 x4 = text_phrase_4 link4 = 1, 5 x5 = text_phrase_5 link5 = 6 x6 = text_phrase_6 link6 = 7, 8 x7 = text_phrase_7 link7 = 9 x8 = text_phrase_8 x9 = text_phrase_9 exit9 = 1
Как видим, в середине есть возможность вернуться в начало, образуя петлю, в конце есть развилка, одна ветка ведет вникуда, т.е. приводит к обрыванию диалога, другая - вроде бы тоже, но в ней объявлен пока загадочный для нас ВЫХОД. Пока получается что-то похожее на диалог, правда?
Кстати обратите внимание на еще одно отличие - в диалоговом блоке первая фраза x1, а не x0.
Ну и, не лишне будет сказать, при создании диалоговых блоков так же можно пользоваться всеми командами, описанными выше.
Теперь идем далее. Слегка усложним один из примеров диалогов, приведенных ранее.
Это не диалоговый блок, это сам диалог.
[my_first_dialog] params_sequence = actor x0 = text0 link0 = 1 x1 = text1 link1 = 10, 20, 30 x10 = text10 link10 = 11 x11 = text11 link11 = 12 x12 = text12 link12 = 40 x20 = text20 link20 = 21 x21 = text21 link21 = 22 x22 = text22 link22 = 40 x30 = text30 link30 = 31 x31 = text31 link31 = 32 x32 = text32 link32 = 40 x40 = text40 link40 = 41 x41 = text41 link41 = 42 x42 = text42 link42 = 43 x43 = text43
Ну так вот. любую фразу в диалоге можно заменить на диалоговый блок.
Делается это например так:
Берем вот этот кусок
x31 = text31 link31 = 32 x32 = text32 link32 = 40
И меняем вот так:
x31 = text31 link31 = 32 block32 = dlg_block_test, block_1 link32 = 40
вместо команды x (создание фразы) - команда block. подключение диалогового блока.
первый аргумент команды - секция конфига подключаемого блока. второй аргумент - имя блока, должно быть уникальным для данного блока. Вы ведь уже поняли что можно вставлять один и тот же блок несколько раз в разных местах диалога? имена только им надо давать разные.
Например, вставим наш блок вместо фраз 12, 22, 32:
block12 = dlg_block_test, block_1 ... block22 = dlg_block_test, block_2 ... block32 = dlg_block_test, block_3
Мы сделали это с тем диалогом что приведен выше, my_first_dialog.
Что же в итоге получится? А вот что:
Граф диалога усложнился в разы а мы всего лишь три строчки поменяли.
В любой диалог, в любом его месте, можно вставить какой угодно диалоговый блок. При желании можно практически весь диалог из блоков и строить, только начальная фраза должна оставаться фразой.
Ну и теперь самый смак. помните, что я говорил, что при создании диалогового блока доступны все те же команды что и для диалога? Так вот. команда block - тоже доступна. Вы можете составлять блоки из блоков, а те в свою очередь еще из блоков - сколько уровней вложенности вам понадобится, столько и будет.
Единственное, чего не стоит делать, это вставлять диалоговый блок внутри себя самого. Спровоцируете бесконечный цикл, который завершится переполнением стека и вылетом.
Таким образом, в код диалогового блока можно вставлять любые другие блоки, кроме него самого.