Добавлен , опубликован

Диалоги

Содержание:
В этом уроке мы разберемся как устроен стандартный диалог в Корсарах, из каких элементов он состоит и как с этим работать.

Из чего состоит диалог?

Все диалоги лежат по пути ..\PROGRAM\DIALOGS\
Если вы откроете несколько файлов, то обязательно заметите, что все они очень похожи. Возьмем следующий кусок кода:
void ProcessDialogEvent() {
    ref NPChar, PChar;
    aref Link;
    PChar = GetMainCharacter();
    DeleteAttribute(&Dialog,"Links");

    makeref(NPChar,CharacterRef);
    makearef(Link, Dialog.Links);

    switch(Dialog.CurrentNode) {
        case "First Time":
            Dialog.Text = "Здравствуйте! Меня зовут " + NPChar.Name + ". Могу ли я узнать как вас зовут?";
            Link.l1 = "Меня зовут " + PChar.Name + ". Я капитан корабля " + PChar.Ship.Name + ".";
            Link.l1.go = "speak";
            Link.l2 = "Это не твое дело, " + NPChar.Name + "! Пока!";
            Link.l2.go = "exit";
        break;

        case "speak":
            Dialog.Text = "Что вы от меня хотели?";
            Link.l1 = "Да нет, ничего! До свидания!";
            Link.l1.go = "exit";
        break;

        case "exit":
            NPChar.Dialog.CurrentNode = NPChar.Dialog.TempNode;
            DialogExit();
        break;
    }
}
Функция ProcessDialogEvent() обрабатывает наш диалог. Строго говоря это метод обработки прерывания "разговор". Внутри нее уже доступна основная информация: диалог, персонаж с которым мы разговариваем и т.д. Но для удобства работы принято создавать переменные-ссылки («алиасы») с короткими, интуитивно понятными именами.
Справка:
Alias (с англ. — «псевдоним») — это имя, назначенное источнику данных в запросе при использовании выражения в качестве источника данных или для упрощения ввода и прочтения инструкции. Такая возможность полезна, если имя источника данных слишком длинное или его трудно вводить.
С этого приведённая в примере ф-ция и начинается:
ref NPChar, PChar;
aref Link;
PChar = GetMainCharacter();         // наш ГГ
DeleteAttribute(&Dialog,"Links");   // чистим варианты ответов от предыдущего диалога

makeref(NPChar,CharacterRef);       // НПЦ с которым разговариваем
makearef(Link, Dialog.Links);       // ссылка на атрибут NPChar.Dialog.Links
Далее идет само тело диалога. И реализовано оно посредством перехода по нодам при помощи конструкции switch.
Ноду вы можете понимать как страницу в диалоге. В процессе станет более понятно.
По умолчанию стартовой нодой является "First Time". Рассмотрим её наполнение:
case "First Time":
    // текст, который отображается как реплика НПЦ
    Dialog.Text = "Здравствуйте! Меня зовут " + NPChar.Name + ". Могу ли я узнать как вас зовут?";
    // текст, который отображается как доступный ответ ГГ
    Link.l1 = "Меня зовут " + PChar.Name + ". Я капитан корабля " + PChar.Ship.Name + ".";
    // нода, на которую ведет данная реплика
    Link.l1.go = "speak";
    // вторая реплика ГГ
    Link.l2 = "Это не твое дело, " + NPChar.Name + "! Пока!";
    // и её нода
    Link.l2.go = "exit";
break;
С текстом, который говорит нам НПЦ, думаю все понятно. А что за линки такие?
Это ссылки на другие ноды диалога. Тобишь, наши реплики ответов в диалоге.
Собственно, Link.l2 - это текст, который мы видим в окне диалога, а Link.l2.go - название ноды, на которую эта реплика ведет.
В данном примере вариантов ответа у нас два - Link.l1 и Link.l2, но, разумеется, у вас их может быть сколь угодно много.
Итак, первая реплика ведет нас к кейсу "speak". И здесь, в принципе, ничего нового не происходит:
case "speak":
    Dialog.Text = "Что вы от меня хотели?";     // реплика НПЦ
    Link.l1 = "Да нет, ничего! До свидания!";   // вариант ответа
    Link.l1.go = "exit";                        // нода
break;
И последний кейс - "exit". Он выводит нас из диалога:
case "exit":
    // помещаем в текущую ноду ссылку на открывающую ноду диалога
    NPChar.Dialog.CurrentNode = NPChar.Dialog.TempNode;
    // ф-ция выхода из диалога
    DialogExit();
break;
Закрытие окна диалога осуществляется вызовом ф-ции DialogExit(). Но есть еще кое-что.
Здесь важно запомнить, что разговор с любым персонажем всегда инициируется с Dialog.CurrentNode.
Поэтому перед завершением разговора важно поместить туда открывающую ноду - по умолчанию это "First Time".
Иначе при следующем начале разговора вы попадете в ту ноду, на которой закончили - т.е. в ноду выхода из диалога.
Как правило, при создании персонажа, в NPChar.Dialog.TempNode помещают ссылку на начальную ноду диалога.
Отсюда использование конструкции NPChar.Dialog.CurrentNode = NPChar.Dialog.TempNode; ведь в TempNode уже лежит наш "First Time".
Стоит также отметить, что ноды могут называться как угодно. В том числе и открывающая - совсем не обязательно должна называться "First Time", главное чтобы вы правильно указали её название при инициации персонажа.

Разделение мух и котлет

В файлах диалогов оригинальной игры (или серьезных аддонов с переводом на другие языки) вас ожидает следующая картина:
// ..\PROGRAM\DIALOGS\Alan Milds_dialog.c
case "Meeting":
    dialog.snd = "Voice\ALMI\ALMI006";
    d.Text = DLG_TEXT[14] + NPChar.name + " " + NPChar.lastname + DLG_TEXT[15];
    Link.l1 = DLG_TEXT[16] + NPChar.name + DLG_TEXT[17];
        if(CheckAttribute(Pchar, "Quest.Story_OxbayCaptured"))
        {
            Link.l1.go = "no trade";
        }
        else
        {
            Link.l1.go = "trade";
        }
    Link.l2 = DLG_TEXT[18] + NPChar.name + DLG_TEXT[19];
    Link.l2.go = "quest lines";
    Link.l3 = DLG_TEXT[20];
    Link.l3.go = "exit";
break;
Куда делся текст и что это за переменные DLG_TEXT[14] ?
Во-первых, текст никуда не делся, он лежит в массиве DLG_TEXT.
Массив этот объявляется в заголовочном файле (*.h), который, как правило, имеет такое же название и лежит в той же папке. Подключается он в самом начале файла с диалогом:
#include "DIALOGS\Alan Milds_dialog.h"
Таким образом, вы всегда можете узнать где данный файл находится и как называется.
Наполнение самого файла - обычный массив строк:
// ..\PROGRAM\DIALOGS\Alan Milds_dialog.h
string DLG_TEXT[50] = {
"Будем торговать по-крупному или вам нужна всякая мелочевка, капитан?",
"Я хочу продать груз, который привез.",
"Хочу прикупить пару вещей себе лично.",
"Вообще не будем. Всего хорошего.",
"Здравствуйте, ",
". Мой магазин к вашим услугам.",
"Благодарю, меня зовут ",

// ...
}
Используется такой подход для того, чтобы можно было легко подключать диалоги на разных якыках, а также облегчения самого процесса перевода.
Обратите внимание, что нумерация элементов массива начинается с нуля. То есть строка "Будем торговать по-крупному или вам нужна всякая мелочевка, капитан?" будет находиться в DLG_TEXT[0].

С теоретическим примером разобрались.
Если у вас остались какие-либо вопросы - вы можете задать их в коментариях или разделе Q/A.
В следующем уроке мы разберем живой пример из игры и посмотрим какие дополнительные функции используются в диалогах.

Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...