Создание диалогов (полный обзор) — различия между версиями
Материал из S.T.A.L.K.E.R. Inside Wiki
(nRVzezMGXjciGc) |
(Отмена правки 9551 участника 212.121.219.1 (обсуждение)) |
||
Строка 1: | Строка 1: | ||
− | + | '''Статья в процессе написания...''' | |
+ | |||
+ | ===Общие данные=== | ||
+ | |||
+ | В диалогах обычно задействуются три .xml-файла: | ||
+ | |||
+ | * character_desc_x.xml | ||
+ | * dialogs_x.xml | ||
+ | * stable_dialogs_x.xml | ||
+ | |||
+ | В них нам важны, соотвественно, списки присвоения диалогов персонажам, структуры диалогов и текстовые массивы для фраз. | ||
+ | |||
+ | ===Структура фраз=== | ||
+ | |||
+ | В общем виде диалог выглядит так: | ||
+ | |||
+ | |||
+ | '''диалог 001'''<br><br>'''фраза 0'''<br> | ||
+ | ''"Так, чего хотел-то?"''<br> | ||
+ | следующие фразы: '''1'''<br><br>'''фраза 1'''<br> | ||
+ | ''"Задание для тебя есть. Возьмешься?"''<br> | ||
+ | следующие фразы: '''21, 22'''<br><br>'''фраза 21'''<br> | ||
+ | ''"Ну, давай."''<br> | ||
+ | следующие фразы: '''3'''<br><br>'''фраза 22'''<br> | ||
+ | ''"Не, иди к черту."''<br> | ||
+ | следующие фразы: '''нет'''<br> | ||
+ | действие: '''выйти из диалога'''<br><br>[фраза 3 и дальнейшее продолжение...]<br><br>'''конец диалога''' | ||
+ | |||
+ | |||
+ | В файлах эта структура записывается следующим образом: | ||
+ | |||
+ | <xml> | ||
+ | <dialog id="escape_trader_letat_gusi"> | ||
+ | <phrase_list> | ||
+ | |||
+ | <phrase id="0"> | ||
+ | <text>Так, чего хотел-то?</text> | ||
+ | <next>1</next> | ||
+ | </phrase> | ||
+ | |||
+ | <phrase id="1"> | ||
+ | <text>Задание для тебя есть. Возьмешься?</text> | ||
+ | <next>21</next> | ||
+ | <next>22</next> | ||
+ | </phrase> | ||
+ | |||
+ | <phrase id="21"> | ||
+ | <text>Ну, давай.</text> | ||
+ | <next>3</next> | ||
+ | </phrase> | ||
+ | |||
+ | <phrase id="22"> | ||
+ | <text>Не, иди к черту.</text> | ||
+ | <action>dialogs.break_dialog</action> | ||
+ | </phrase> | ||
+ | |||
+ | [...] | ||
+ | |||
+ | </phrase_list> | ||
+ | </dialog> | ||
+ | </xml> | ||
+ | |||
+ | Разберемся подробнее. | ||
+ | |||
+ | Во-первых, принадлежность фраз. | ||
+ | Они могут принадлежать или игроку, или NPC, причем: | ||
+ | |||
+ | * фраза, принадлежащая игроку, всегда идет первой, а за ней уже по древу диалога игра понимает, чья фраза кому принадлежит (например, у нас 0, 21, 22 - игрока; а 1, 3 - NPC) | ||
+ | * только у фразы, принадлежащей NPC, можно выбрать несколько вариантов ответа игрока | ||
+ | |||
+ | Впрочем, и у фразы игрока можно сделать несколько вариантов ответов, но поскольку компьютер не обладает способностью самостоятельно выбирать, то каждый из вариантов должен быть заранее определен по условиям выбора (например, по наличию у игрока какого-нибудь предмета, или количества денег, или репутации и т.д.), причем условия выбора различных вариантов никогда не должны одновременно становиться подходящими. Это мы разберем позднее. | ||
+ | |||
+ | Во-вторых, структура фразы. | ||
+ | Простейшая фраза выглядит так: | ||
+ | |||
+ | <xml> | ||
+ | <phrase id="0"> -- открывающий тег с ID фразы | ||
+ | <text>Привет.</text> -- текст фразы | ||
+ | <next>1</next> -- ссылка на следующую фразу | ||
+ | </phrase> -- закрытие тега фразы | ||
+ | </xml> | ||
+ | |||
+ | Но есть возможность строить и такие фразы (в данном случае она принадлежит NPC, а игрок на неё будет давать ответ): | ||
+ | |||
+ | <xml> | ||
+ | <phrase id="1"> | ||
+ | <give_info>propusk_given</give_info> -- выдача инфопорции (см. объяснение №1 ниже) | ||
+ | <text>Вот, держи пропуск. Теперь тебе охранник позволит войти.</text> | ||
+ | <action>dialogs.give_propusk_item</action> -- ссылка на скрипт (см. объяснение №2 ниже) | ||
+ | <next>21</next> | ||
+ | <next>22</next> | ||
+ | <next>23</next> | ||
+ | </phrase> | ||
+ | </xml> | ||
+ | |||
+ | * 1. Здесь у нас выдается [[Инфопорция|инфопорция]], которая может потом использоваться в различных местах (например, её наличие может проверяться каким-нибудь охранником - и если она есть, то он, скажем, отойдет и пропустит игрока внутрь). Важно понимать отличие инфопорции от предмета. Инфопорция - это ''факт наличия'' пропуска, а лежащий в инвентаре пропуск - ''материальный предмет''. Можно, кстати, обойтись и без инфопорций, впоследствии пользуясь скриптами, проверяющими инвентарь игрока и ищущими нужный предмет, но этот способ сложен и нам пока не понадобится. | ||
+ | |||
+ | * 2. Это скрипт, выдающий игроку предмет "пропуск" в инвентарь. Ссылка на скрипты дается в формате имя_файла.имя_функции. | ||
+ | |||
+ | Как видите, фраза, в которой некий NPC выдает игроку пропуск, позволяющий пройти дальше, достаточно сложна. | ||
+ | |||
+ | Вот еще один пример (здесь она принадлежит игроку): | ||
+ | |||
+ | <xml> | ||
+ | <phrase id="4"> | ||
+ | <has_info>propusk_given</has_info> -- проверка на наличие инфопорции propusk_given | ||
+ | <text>Да, пропуск у меня есть.</text> | ||
+ | <action>dialogs.break_dialog</action> -- ссылка на скрипт, завершающий диалог | ||
+ | </phrase> | ||
+ | </xml> | ||
+ | |||
+ | Здесь у нас фраза из диалога игрока с NPC - та, что после вопроса NPC "А пропуск у тебя есть?". Это один из вариантов ответа на неё. Особенность в том, что этот вариант не появится, если пропуска у вас на самом деле нет - она появляется только при наличии нужной инфопорции (за это отвечает строка с <has_info>). | ||
+ | |||
+ | Далее, поподробнее остановимся на задании условий появления фразы.<br> | ||
+ | Способов это сделать - два. | ||
+ | |||
+ | * проверка на наличие инфопорций | ||
+ | * проверка через скриптовую функцию | ||
+ | |||
+ | Первый метод мы видели выше, а второй - гораздо более комплексный и многофункциональный. Он запускает скрипт, который проводит проверку определенных вещей, и возвращает true/false. Простой пример - NPC требует денег (скажем, 5000 рублей), а нам нужно, чтобы ответная фраза игрока "Вот, держи свои 5000.", могла быть доступна для выбора, только если у нас действительно есть эти деньги. Для этого пишем такую фразу: | ||
+ | |||
+ | <xml> | ||
+ | <phrase id="8"> | ||
+ | <text>Вот, держи, ровно 5000.</text> | ||
+ | <precondition>dialogs.actor_have_5000</precondition> -- вызываем условие (см. объяснение №1 ниже) | ||
+ | <action>dialogs.transfer_5000</action> -- передача денег скриптом (см. объяснение №2 ниже) | ||
+ | <next>9</next> | ||
+ | </phrase> | ||
+ | </xml> | ||
+ | |||
+ | * 1. Этот скрипт который проверяет наличие денег: если было возвращено true, то фраза доступна. | ||
+ | * 2. Вызываем другой скрипт, действие - он осуществляет саму передачу денег. | ||
+ | |||
+ | Да, кстати, порядок расстановки тегов внутри фразы не играет никакой роли - они все выполняются одновременно. Единственное - <next> всегда последний. Еще одно - есть второй вид проверки на инфопорцию - он наоборот, проверяет её отсутствие. Применяется точно так же, но называется не has_info, а dont_has_info. | ||
+ | |||
+ | Итак, что у нас есть: | ||
+ | |||
+ | * '''<phrase id="..."></phrase>''' - общий тег фразы, задание номера фразы | ||
+ | * '''<nowiki><text>...</text></nowiki>''' - тег, содержащий текст фразы | ||
+ | * '''<give_info>...</give_info>''' - выдача инфопорции с именем, записанным в теге | ||
+ | * '''<has_info>...</has_info>''' - проверка на наличие инфопорции с именем, записанным в теге | ||
+ | * '''<dont_has_info>...</dont_has_info>''' - проверка на отсутствие инфопорции с именем, записанным в теге | ||
+ | * '''<precondition>...</precondition>''' - запуск скрипта, возвращающего true или false в зависимости от чего-либо (скриптовая проверка) | ||
+ | * '''<action>...</action>''' - запуск скрипта | ||
+ | * '''<next>...</next>''' - тег ссылки на следующую фразу | ||
+ | |||
+ | |||
+ | В первой версии - достаточно просто окна с формами для ввода этих параметров. А программа на основе введенных данных будет собирать фразу. Например, на основе вот таких введенных в поля данных: | ||
+ | |||
+ | Номер фразы: '''12'''<br><br>Текст: ''Да, там сейчас база военных.<br>Я не в курсе, сколько их там точно, но<br>могу сказать, не меньше, чем один два отряда.''<br><br>Выдать инфопорцию: '''info_about_military_base'''<br><br>Проверить наличие инфопорции: '''info_1'''<br>Проверить отсутствие инфопорции: '''нет'''<br>Проверить через скрипт: '''dialogs.is_npc_a_friend'''<br>Совершить действие: '''dialogs.break_dialog'''<br><br>Номера следующих фраз: '''13'''<br><br> | ||
+ | |||
+ | Нам нужна вот такая запись: | ||
+ | |||
+ | <xml> | ||
+ | <phrase id="12"> | ||
+ | <has_info>info_1</has_info> | ||
+ | <text>Да, там сейчас база военных. Я не в курсе, сколько их там точно [...]</text> | ||
+ | <precondition>dialogs.is_npc_a_friend</precondition> | ||
+ | <action>dialogs.break_dialog</action> | ||
+ | <give_info>info_about_military_base</give_info> | ||
+ | <next>13</next> | ||
+ | </phrase> | ||
+ | </xml> | ||
+ | |||
+ | ===Текстовые (строковые) массивы=== | ||
+ | |||
+ | Мы упустили один важный нюанс, критичный для крупных диалогов. | ||
+ | |||
+ | <xml> | ||
+ | <phrase id="54"> | ||
+ | <text>Здравствуй, сталкер. Чем я могу помочь тебе?</text> | ||
+ | <next>55</next> | ||
+ | </phrase> | ||
+ | </xml> | ||
+ | |||
+ | Мы записывали текст фразы в <'''text>...</text>'''. Это, безусловно, удобно, но на самом деле - может привести к проблемам. Дело в том, что таким образом можно использовать только короткие фразы. Стоит ввести в тег длинный текст, и игра... так сказать, не переварит наш диалог. | ||
+ | |||
+ | Поэтому нужно использовать не тексты, а ссылки на них. Работает это так: | ||
+ | |||
+ | [[Изображение:Text massive explain.jpg]] | ||
+ | |||
+ | То есть фразу мы записываем так: | ||
+ | |||
+ | <xml> | ||
+ | <phrase id="54"> | ||
+ | <text>my_text_1</text> | ||
+ | <next>55</next> | ||
+ | </phrase> | ||
+ | </xml> | ||
+ | |||
+ | А в игре видим нормальный текст - игра по названию my_text_1 находит его в массиве.<br> | ||
+ | Массивы (для русскоязычной версии игры) находятся в папке: | ||
+ | |||
+ | <pre>S.T.A.L.K.E.R\gamedata\config\text\rus</pre> | ||
+ | |||
+ | Нужные вам тексты фраз вы можете прописывать в любом из находящихся там .xml-файлов, их разделение на специализации - чисто формальное. Лучше всего держать тексты всех ваших диалогов в одном месте, поэтому выберите один файл - этим вы упростите работу тем, кто будет скрещивать ваш мод с другими. | ||
+ | |||
+ | Запись текста происходит в такой форме: | ||
+ | |||
+ | <xml> | ||
+ | <string id="my_text_1"> -- в заголовке указывается имя текста, которое вызывается из диалога | ||
+ | <text>Здравствуй, сталкер. Чем я могу помочь тебе?</text> -- собственно, сам текст | ||
+ | </string> | ||
+ | </xml> | ||
+ | |||
+ | ===Диалоги=== | ||
+ | |||
+ | Ну что же, структуру фраз мы полностью разобрали. Теперь попробуем собрать из них полноценный диалог. Для начала выберем жертву для экспериментов - Сидорович вполне подойдет :) | ||
+ | |||
+ | Откроем папку: | ||
+ | |||
+ | <pre>S.T.A.L.K.E.R\gamedata\config\gameplay</pre> | ||
+ | |||
+ | Сейчас нас интересует файл '''character_desc_escape.xml''', хранящий профили персонажей с локации "Кордон" (остальные файлы названы по аналогичной системе - вместо '''escape''', соответственно, идет название других уровней). Открываем его и видим профиль Сидоровича '''escape_trader''' - он находится в самом начале файла. | ||
+ | |||
+ | <xml> | ||
+ | <specific_character id="escape_trader" no_random = "1"> | ||
+ | <name>escape_trader_name</name> | ||
+ | <icon>ui_npc_u_trader</icon> | ||
+ | <bio>escape_trader_bio</bio> | ||
+ | |||
+ | <class>trader</class> | ||
+ | <community>trader</community> | ||
+ | <visual>actors\trader\trader</visual> | ||
+ | |||
+ | <rank>330</rank> | ||
+ | <reputation>23</reputation> | ||
+ | <money min="100000" max="100000" infinitive="1"/> | ||
+ | <supplies> | ||
+ | [spawn] \n | ||
+ | wpn_knife \n | ||
+ | </supplies> | ||
+ | |||
+ | <start_dialog>escape_trader_start_dialog</start_dialog> | ||
+ | <actor_dialog>escape_trader_talk_info</actor_dialog> | ||
+ | <actor_dialog>escape_trader_jobs</actor_dialog> | ||
+ | <actor_dialog>tm_trader_dialog</actor_dialog> | ||
+ | <actor_dialog>tm_trader_reward</actor_dialog> | ||
+ | <actor_dialog>escape_trader_done_blockpost_box</actor_dialog> | ||
+ | </specific_character> | ||
+ | </xml> | ||
+ | |||
+ | Подробный разбор параметров профиля мы делать не будем - для этого существует [[Редактирование_NPC|отдельная статья]]. Сейчас нас интересуют теги <actor_dialog>...</actor_dialog>, ссылающиеся на доступные у этого NPC диалоги. Добавим в этот список и ссылку на наш будущий диалог: | ||
+ | |||
+ | <xml> | ||
+ | <start_dialog>escape_trader_start_dialog</start_dialog> | ||
+ | <actor_dialog>my_dialog_1</actor_dialog> | ||
+ | <actor_dialog>escape_trader_talk_info</actor_dialog> | ||
+ | <actor_dialog>escape_trader_jobs</actor_dialog> | ||
+ | <actor_dialog>tm_trader_dialog</actor_dialog> | ||
+ | <actor_dialog>tm_trader_reward</actor_dialog> | ||
+ | <actor_dialog>escape_trader_done_blockpost_box</actor_dialog> | ||
+ | </xml> | ||
+ | |||
+ | Далее добавим в любой диалоговый файл, например, в '''dialogs_escape.xml''', следующий диалог: | ||
+ | |||
+ | <xml> | ||
+ | <dialog id="my_dialog_1"> | ||
+ | <precondition>my_script_for_dialogs.my_function_1</precondition> -- условия можно вызывать и здесь | ||
+ | <phrase_list> | ||
+ | <phrase id="0"> -- фразы Меченого | ||
+ | <text>my_text_0</text> -- "Ага, вот эти ребята..." | ||
+ | <next>1</next> | ||
+ | </phrase> | ||
+ | <phrase id="1"> -- фразы Сидоровича | ||
+ | <text>my_text_1</text> -- "Ненене!!! Нет, Меченый, нет!" | ||
+ | <next>2</next> | ||
+ | </phrase> | ||
+ | <phrase id="2"> | ||
+ | <text>my_text_2</text> -- "Я делаю особую, уличную магию. Кто хочет увидеть немного магии?" | ||
+ | <next>3</next> | ||
+ | </phrase> | ||
+ | <phrase id="3"> | ||
+ | <text>my_text_3</text> -- "Что я тебе, Толик какой-нибудь? Целый день у этих идиотов из лагеря | ||
+ | <next>4</next> разный хлам скупал, устал, хочу просто отдохнуть..." | ||
+ | </phrase> | ||
+ | <phrase id="4"> | ||
+ | <text>my_text_4</text> -- "И что купил?" | ||
+ | <next>5</next> | ||
+ | </phrase> | ||
+ | <phrase id="5"> | ||
+ | <text>my_text_5</text> -- "Я купил зеленый свитер, если ты так хочешь знать! Сорок долларов, | ||
+ | <next>6</next> между прочим." | ||
+ | </phrase> | ||
+ | <phrase id="6"> | ||
+ | <text>my_text_6</text> -- "Зеленый свитер? А ты уверен, что не купил тедди-бир?" | ||
+ | <next>7</next> | ||
+ | </phrase> | ||
+ | <phrase id="7"> | ||
+ | <text>my_text_7</text> -- "Тедди-бир?... ТЕДДИ-БИР!!! Где мой зеленый свитер, ты что делаешь, демон?!" | ||
+ | <next>8</next> | ||
+ | </phrase> | ||
+ | <phrase id="8"> | ||
+ | <text>my_text_8</text> -- "Ладно, ладно... еще один фокус. Будь добр, взгляни на телевизор | ||
+ | <next>9</next> рядом с тобой. Что он показывает?" | ||
+ | </phrase> | ||
+ | <phrase id="9"> | ||
+ | <text>my_text_9</text> -- "Билд 1154!!! Ты что делаешь, демон, ты что делаешь?! Проваливай отсюда!" | ||
+ | <next>10</next> | ||
+ | </phrase> | ||
+ | <phrase id="10"> | ||
+ | <text>my_text_10</text> -- "Ладно." | ||
+ | <action>dialogs.break_dialog</action> -- выход из диалога | ||
+ | </phrase> | ||
+ | </phrase_list> | ||
+ | </dialog> | ||
+ | </xml> | ||
+ | |||
+ | Для начала лучше попробовать свои силы в простом линейном диалоге вроде этого.<br> | ||
+ | Да, и не забудьте добавить строки с текстовыми фразами в массив.<br><br> | ||
+ | * Если вам неясно, что за ахинея написана в текстах, вам [http://ru.youtube.com/watch?v=CdTIQ6BVlvw&feature=related сюда] xD | ||
+ | |||
+ | === "Динамические диалоги" === | ||
+ | |||
+ | Выше был изложен вариант написания простых диалогов вопрос-ответ. Но в этой части статьи я расскажу как написать "динамические диалоги", которые предполагают выбор ответа не только ГГ, но и НПС с которым мы говорим. Рассчитано на опытных пользователей диалога. | ||
+ | |||
+ | Вообще урок это копия предыдущей статьи, с той лишь небольшой разницей, что у как и у ГГ, так и у НПС будет несколько вариантов ответов. Получившаяся структура диалога: | ||
+ | |||
+ | <xml> | ||
+ | <dialog id="название диалога"> | ||
+ | <phrase_list> | ||
+ | <phrase id="0"> | ||
+ | <text>слушаю</text> | ||
+ | <next>10</next> | ||
+ | </phrase> | ||
+ | <phrase id="10"> | ||
+ | <text>Привет, как дела?</text> | ||
+ | <next>1</next> | ||
+ | <next>2</next> | ||
+ | <next>3</next> | ||
+ | </phrase> | ||
+ | <phrase id="1"> | ||
+ | <text>Отлично!</text> | ||
+ | <next>11</next> | ||
+ | </phrase> | ||
+ | <phrase id="2"> | ||
+ | <text>Нормик, что хотел?</text> | ||
+ | <next>21</next> | ||
+ | </phrase> | ||
+ | <phrase id="3"> | ||
+ | <text>Плохо, иди отсюда(</text> | ||
+ | <action>dialogs.break_dialog</action> | ||
+ | </phrase> | ||
+ | <phrase id="11"> | ||
+ | <text>Это хорошо =)</text> | ||
+ | </phrase> | ||
+ | <phrase id="21"> | ||
+ | <text>Да вот {....}</text> | ||
+ | </phrase> | ||
+ | [и так далее] | ||
+ | </phrase_list> | ||
+ | </dialog> | ||
+ | </xml> | ||
+ | Ну вот как-то так. Рассмотрим наиболее интересную часть: | ||
+ | <xml> | ||
+ | <phrase id="10"> | ||
+ | <text>Привет, как дела?</text> | ||
+ | <next>1</next> | ||
+ | <next>2</next> | ||
+ | <next>3</next> | ||
+ | </xml> | ||
+ | Здесь мы предоставляем выбор дальнейшего диалога для НПС, причём в этой структуре его выбор получается абсолютно рандомным. | ||
+ | Скриншоты: [[Изображение:Dialog02.jpg|950px]] | ||
+ | |||
+ | |||
+ | [[Изображение:Dialog01.jpg|950px]] | ||
+ | |||
+ | Можно прописать установки и проверки для ответов, например, можно реализовать возможность отдавания ненужных вещей какому-нибудь нпс, тогда структура будет выглядеть примерно так(писать полностью не буду, только фразы): | ||
+ | <xml> | ||
+ | ГГ: Привет, у меня тут есть некоторые вещи, взгляни, нужно что? | ||
+ | НПС ответ1: (проверка на АК-47) Да, мне нужна эта пушка! | ||
+ | НПС ответ2: (проверка на консервы) Ух как кушать хочется, можешь мне дать консерву? | ||
+ | НПС ответ3: (без предпроверки) Есть ли у тебя водка? | ||
+ | ГГ на ответ 1 и 2: Да, держи! | ||
+ | ГГ на ответ 3: (проверка) Держи/ Извини, нету(( | ||
+ | </xml> | ||
+ | |||
+ | Ну и напоследок скажу, что можно даже управлять вероятностью ответа НПС на фразу ГГ, если прописать несколько <next>номер ответа</next>. Например, если написать: | ||
+ | <xml> | ||
+ | |||
+ | <next>1</next> | ||
+ | <next>3</next> | ||
+ | <next>3</next> | ||
+ | <next>3</next> | ||
+ | |||
+ | </xml> | ||
+ | То вероятность ответа "3" будет составлять 75% | ||
+ | |||
+ | P.S.: Это не просто увлекательная и интересная вещь, но и очень уж трудоёмкая - у меня день уходил на создание простого диалога. Для огромной модификации только на диалоги нужно человек 20-30. Но зато одних диалогов хватит чтобы впечатлить! Аналогов быть не должно, а сама игра использует этот приём только в ЗП и то в 2-х местах(на Затоне, другие локации не глядел). | ||
+ | |||
+ | ==Дополнительные сведения по диалогам== | ||
+ | |||
+ | ==Скрипт-генерируемые диалоги== | ||
+ | |||
+ | [[Категория:Конфигурационные_файлы]] |
Версия 10:13, 12 марта 2011
Статья в процессе написания...
Содержание
Общие данные
В диалогах обычно задействуются три .xml-файла:
- character_desc_x.xml
- dialogs_x.xml
- stable_dialogs_x.xml
В них нам важны, соотвественно, списки присвоения диалогов персонажам, структуры диалогов и текстовые массивы для фраз.
Структура фраз
В общем виде диалог выглядит так:
диалог 001
фраза 0
"Так, чего хотел-то?"
следующие фразы: 1
фраза 1
"Задание для тебя есть. Возьмешься?"
следующие фразы: 21, 22
фраза 21
"Ну, давай."
следующие фразы: 3
фраза 22
"Не, иди к черту."
следующие фразы: нет
действие: выйти из диалога
[фраза 3 и дальнейшее продолжение...]
конец диалога
В файлах эта структура записывается следующим образом:
<dialog id="escape_trader_letat_gusi"> <phrase_list> <phrase id="0"> <text>Так, чего хотел-то?</text> <next>1</next> </phrase> <phrase id="1"> <text>Задание для тебя есть. Возьмешься?</text> <next>21</next> <next>22</next> </phrase> <phrase id="21"> <text>Ну, давай.</text> <next>3</next> </phrase> <phrase id="22"> <text>Не, иди к черту.</text> <action>dialogs.break_dialog</action> </phrase> [...] </phrase_list> </dialog>
Разберемся подробнее.
Во-первых, принадлежность фраз. Они могут принадлежать или игроку, или NPC, причем:
- фраза, принадлежащая игроку, всегда идет первой, а за ней уже по древу диалога игра понимает, чья фраза кому принадлежит (например, у нас 0, 21, 22 - игрока; а 1, 3 - NPC)
- только у фразы, принадлежащей NPC, можно выбрать несколько вариантов ответа игрока
Впрочем, и у фразы игрока можно сделать несколько вариантов ответов, но поскольку компьютер не обладает способностью самостоятельно выбирать, то каждый из вариантов должен быть заранее определен по условиям выбора (например, по наличию у игрока какого-нибудь предмета, или количества денег, или репутации и т.д.), причем условия выбора различных вариантов никогда не должны одновременно становиться подходящими. Это мы разберем позднее.
Во-вторых, структура фразы. Простейшая фраза выглядит так:
<phrase id="0"> -- открывающий тег с ID фразы <text>Привет.</text> -- текст фразы <next>1</next> -- ссылка на следующую фразу </phrase> -- закрытие тега фразы
Но есть возможность строить и такие фразы (в данном случае она принадлежит NPC, а игрок на неё будет давать ответ):
<phrase id="1"> <give_info>propusk_given</give_info> -- выдача инфопорции (см. объяснение №1 ниже) <text>Вот, держи пропуск. Теперь тебе охранник позволит войти.</text> <action>dialogs.give_propusk_item</action> -- ссылка на скрипт (см. объяснение №2 ниже) <next>21</next> <next>22</next> <next>23</next> </phrase>
- 1. Здесь у нас выдается инфопорция, которая может потом использоваться в различных местах (например, её наличие может проверяться каким-нибудь охранником - и если она есть, то он, скажем, отойдет и пропустит игрока внутрь). Важно понимать отличие инфопорции от предмета. Инфопорция - это факт наличия пропуска, а лежащий в инвентаре пропуск - материальный предмет. Можно, кстати, обойтись и без инфопорций, впоследствии пользуясь скриптами, проверяющими инвентарь игрока и ищущими нужный предмет, но этот способ сложен и нам пока не понадобится.
- 2. Это скрипт, выдающий игроку предмет "пропуск" в инвентарь. Ссылка на скрипты дается в формате имя_файла.имя_функции.
Как видите, фраза, в которой некий NPC выдает игроку пропуск, позволяющий пройти дальше, достаточно сложна.
Вот еще один пример (здесь она принадлежит игроку):
<phrase id="4"> <has_info>propusk_given</has_info> -- проверка на наличие инфопорции propusk_given <text>Да, пропуск у меня есть.</text> <action>dialogs.break_dialog</action> -- ссылка на скрипт, завершающий диалог </phrase>
Здесь у нас фраза из диалога игрока с NPC - та, что после вопроса NPC "А пропуск у тебя есть?". Это один из вариантов ответа на неё. Особенность в том, что этот вариант не появится, если пропуска у вас на самом деле нет - она появляется только при наличии нужной инфопорции (за это отвечает строка с <has_info>).
Далее, поподробнее остановимся на задании условий появления фразы.
Способов это сделать - два.
* проверка на наличие инфопорций * проверка через скриптовую функцию
Первый метод мы видели выше, а второй - гораздо более комплексный и многофункциональный. Он запускает скрипт, который проводит проверку определенных вещей, и возвращает true/false. Простой пример - NPC требует денег (скажем, 5000 рублей), а нам нужно, чтобы ответная фраза игрока "Вот, держи свои 5000.", могла быть доступна для выбора, только если у нас действительно есть эти деньги. Для этого пишем такую фразу:
<phrase id="8"> <text>Вот, держи, ровно 5000.</text> <precondition>dialogs.actor_have_5000</precondition> -- вызываем условие (см. объяснение №1 ниже) <action>dialogs.transfer_5000</action> -- передача денег скриптом (см. объяснение №2 ниже) <next>9</next> </phrase>
- 1. Этот скрипт который проверяет наличие денег: если было возвращено true, то фраза доступна.
- 2. Вызываем другой скрипт, действие - он осуществляет саму передачу денег.
Да, кстати, порядок расстановки тегов внутри фразы не играет никакой роли - они все выполняются одновременно. Единственное - <next> всегда последний. Еще одно - есть второй вид проверки на инфопорцию - он наоборот, проверяет её отсутствие. Применяется точно так же, но называется не has_info, а dont_has_info.
Итак, что у нас есть:
- <phrase id="..."></phrase> - общий тег фразы, задание номера фразы
- <text>...</text> - тег, содержащий текст фразы
- <give_info>...</give_info> - выдача инфопорции с именем, записанным в теге
- <has_info>...</has_info> - проверка на наличие инфопорции с именем, записанным в теге
- <dont_has_info>...</dont_has_info> - проверка на отсутствие инфопорции с именем, записанным в теге
- <precondition>...</precondition> - запуск скрипта, возвращающего true или false в зависимости от чего-либо (скриптовая проверка)
- <action>...</action> - запуск скрипта
- <next>...</next> - тег ссылки на следующую фразу
В первой версии - достаточно просто окна с формами для ввода этих параметров. А программа на основе введенных данных будет собирать фразу. Например, на основе вот таких введенных в поля данных:
Номер фразы: 12
Текст: Да, там сейчас база военных.
Я не в курсе, сколько их там точно, но
могу сказать, не меньше, чем один два отряда.
Выдать инфопорцию: info_about_military_base
Проверить наличие инфопорции: info_1
Проверить отсутствие инфопорции: нет
Проверить через скрипт: dialogs.is_npc_a_friend
Совершить действие: dialogs.break_dialog
Номера следующих фраз: 13
Нам нужна вот такая запись:
<phrase id="12"> <has_info>info_1</has_info> <text>Да, там сейчас база военных. Я не в курсе, сколько их там точно [...]</text> <precondition>dialogs.is_npc_a_friend</precondition> <action>dialogs.break_dialog</action> <give_info>info_about_military_base</give_info> <next>13</next> </phrase>
Текстовые (строковые) массивы
Мы упустили один важный нюанс, критичный для крупных диалогов.
<phrase id="54"> <text>Здравствуй, сталкер. Чем я могу помочь тебе?</text> <next>55</next> </phrase>
Мы записывали текст фразы в <text>...</text>. Это, безусловно, удобно, но на самом деле - может привести к проблемам. Дело в том, что таким образом можно использовать только короткие фразы. Стоит ввести в тег длинный текст, и игра... так сказать, не переварит наш диалог.
Поэтому нужно использовать не тексты, а ссылки на них. Работает это так:
То есть фразу мы записываем так:
<phrase id="54"> <text>my_text_1</text> <next>55</next> </phrase>
А в игре видим нормальный текст - игра по названию my_text_1 находит его в массиве.
Массивы (для русскоязычной версии игры) находятся в папке:
S.T.A.L.K.E.R\gamedata\config\text\rus
Нужные вам тексты фраз вы можете прописывать в любом из находящихся там .xml-файлов, их разделение на специализации - чисто формальное. Лучше всего держать тексты всех ваших диалогов в одном месте, поэтому выберите один файл - этим вы упростите работу тем, кто будет скрещивать ваш мод с другими.
Запись текста происходит в такой форме:
<string id="my_text_1"> -- в заголовке указывается имя текста, которое вызывается из диалога <text>Здравствуй, сталкер. Чем я могу помочь тебе?</text> -- собственно, сам текст </string>
Диалоги
Ну что же, структуру фраз мы полностью разобрали. Теперь попробуем собрать из них полноценный диалог. Для начала выберем жертву для экспериментов - Сидорович вполне подойдет :)
Откроем папку:
S.T.A.L.K.E.R\gamedata\config\gameplay
Сейчас нас интересует файл character_desc_escape.xml, хранящий профили персонажей с локации "Кордон" (остальные файлы названы по аналогичной системе - вместо escape, соответственно, идет название других уровней). Открываем его и видим профиль Сидоровича escape_trader - он находится в самом начале файла.
<specific_character id="escape_trader" no_random = "1"> <name>escape_trader_name</name> <icon>ui_npc_u_trader</icon> <bio>escape_trader_bio</bio> <class>trader</class> <community>trader</community> <visual>actors\trader\trader</visual> <rank>330</rank> <reputation>23</reputation> <money min="100000" max="100000" infinitive="1"/> <supplies> [spawn] \n wpn_knife \n </supplies> <start_dialog>escape_trader_start_dialog</start_dialog> <actor_dialog>escape_trader_talk_info</actor_dialog> <actor_dialog>escape_trader_jobs</actor_dialog> <actor_dialog>tm_trader_dialog</actor_dialog> <actor_dialog>tm_trader_reward</actor_dialog> <actor_dialog>escape_trader_done_blockpost_box</actor_dialog> </specific_character>
Подробный разбор параметров профиля мы делать не будем - для этого существует отдельная статья. Сейчас нас интересуют теги <actor_dialog>...</actor_dialog>, ссылающиеся на доступные у этого NPC диалоги. Добавим в этот список и ссылку на наш будущий диалог:
<start_dialog>escape_trader_start_dialog</start_dialog> <actor_dialog>my_dialog_1</actor_dialog> <actor_dialog>escape_trader_talk_info</actor_dialog> <actor_dialog>escape_trader_jobs</actor_dialog> <actor_dialog>tm_trader_dialog</actor_dialog> <actor_dialog>tm_trader_reward</actor_dialog> <actor_dialog>escape_trader_done_blockpost_box</actor_dialog>
Далее добавим в любой диалоговый файл, например, в dialogs_escape.xml, следующий диалог:
<dialog id="my_dialog_1"> <precondition>my_script_for_dialogs.my_function_1</precondition> -- условия можно вызывать и здесь <phrase_list> <phrase id="0"> -- фразы Меченого <text>my_text_0</text> -- "Ага, вот эти ребята..." <next>1</next> </phrase> <phrase id="1"> -- фразы Сидоровича <text>my_text_1</text> -- "Ненене!!! Нет, Меченый, нет!" <next>2</next> </phrase> <phrase id="2"> <text>my_text_2</text> -- "Я делаю особую, уличную магию. Кто хочет увидеть немного магии?" <next>3</next> </phrase> <phrase id="3"> <text>my_text_3</text> -- "Что я тебе, Толик какой-нибудь? Целый день у этих идиотов из лагеря <next>4</next> разный хлам скупал, устал, хочу просто отдохнуть..." </phrase> <phrase id="4"> <text>my_text_4</text> -- "И что купил?" <next>5</next> </phrase> <phrase id="5"> <text>my_text_5</text> -- "Я купил зеленый свитер, если ты так хочешь знать! Сорок долларов, <next>6</next> между прочим." </phrase> <phrase id="6"> <text>my_text_6</text> -- "Зеленый свитер? А ты уверен, что не купил тедди-бир?" <next>7</next> </phrase> <phrase id="7"> <text>my_text_7</text> -- "Тедди-бир?... ТЕДДИ-БИР!!! Где мой зеленый свитер, ты что делаешь, демон?!" <next>8</next> </phrase> <phrase id="8"> <text>my_text_8</text> -- "Ладно, ладно... еще один фокус. Будь добр, взгляни на телевизор <next>9</next> рядом с тобой. Что он показывает?" </phrase> <phrase id="9"> <text>my_text_9</text> -- "Билд 1154!!! Ты что делаешь, демон, ты что делаешь?! Проваливай отсюда!" <next>10</next> </phrase> <phrase id="10"> <text>my_text_10</text> -- "Ладно." <action>dialogs.break_dialog</action> -- выход из диалога </phrase> </phrase_list> </dialog>
Для начала лучше попробовать свои силы в простом линейном диалоге вроде этого.
Да, и не забудьте добавить строки с текстовыми фразами в массив.
- Если вам неясно, что за ахинея написана в текстах, вам сюда xD
"Динамические диалоги"
Выше был изложен вариант написания простых диалогов вопрос-ответ. Но в этой части статьи я расскажу как написать "динамические диалоги", которые предполагают выбор ответа не только ГГ, но и НПС с которым мы говорим. Рассчитано на опытных пользователей диалога.
Вообще урок это копия предыдущей статьи, с той лишь небольшой разницей, что у как и у ГГ, так и у НПС будет несколько вариантов ответов. Получившаяся структура диалога:
<dialog id="название диалога"> <phrase_list> <phrase id="0"> <text>слушаю</text> <next>10</next> </phrase> <phrase id="10"> <text>Привет, как дела?</text> <next>1</next> <next>2</next> <next>3</next> </phrase> <phrase id="1"> <text>Отлично!</text> <next>11</next> </phrase> <phrase id="2"> <text>Нормик, что хотел?</text> <next>21</next> </phrase> <phrase id="3"> <text>Плохо, иди отсюда(</text> <action>dialogs.break_dialog</action> </phrase> <phrase id="11"> <text>Это хорошо =)</text> </phrase> <phrase id="21"> <text>Да вот {....}</text> </phrase> [и так далее] </phrase_list> </dialog>
Ну вот как-то так. Рассмотрим наиболее интересную часть:
<phrase id="10"> <text>Привет, как дела?</text> <next>1</next> <next>2</next> <next>3</next>
Здесь мы предоставляем выбор дальнейшего диалога для НПС, причём в этой структуре его выбор получается абсолютно рандомным. Скриншоты:
Можно прописать установки и проверки для ответов, например, можно реализовать возможность отдавания ненужных вещей какому-нибудь нпс, тогда структура будет выглядеть примерно так(писать полностью не буду, только фразы):
ГГ: Привет, у меня тут есть некоторые вещи, взгляни, нужно что? НПС ответ1: (проверка на АК-47) Да, мне нужна эта пушка! НПС ответ2: (проверка на консервы) Ух как кушать хочется, можешь мне дать консерву? НПС ответ3: (без предпроверки) Есть ли у тебя водка? ГГ на ответ 1 и 2: Да, держи! ГГ на ответ 3: (проверка) Держи/ Извини, нету((
Ну и напоследок скажу, что можно даже управлять вероятностью ответа НПС на фразу ГГ, если прописать несколько <next>номер ответа</next>. Например, если написать:
<next>1</next> <next>3</next> <next>3</next> <next>3</next>
То вероятность ответа "3" будет составлять 75%
P.S.: Это не просто увлекательная и интересная вещь, но и очень уж трудоёмкая - у меня день уходил на создание простого диалога. Для огромной модификации только на диалоги нужно человек 20-30. Но зато одних диалогов хватит чтобы впечатлить! Аналогов быть не должно, а сама игра использует этот приём только в ЗП и то в 2-х местах(на Затоне, другие локации не глядел).