Сповіщення
Очистити все

Створення діалогів (повний огляд)


Ранг:
Майстер
Роль:
Гість
Записи:
752
Приєднався:
7 місяців тому
 

Загальні дані

У діалогах зазвичай використовуються три .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 знаходить його в масиві.
Масиви (для російськомовної версії гри) перебувають у папці:

STALKER\gamedata\config\text\ukr

Потрібні вам тексти фраз ви можете прописувати в будь-якому з .xml-файлів, що знаходяться там, їх поділ на спеціалізації - чисто формальний. Найкраще тримати тексти всіх ваших діалогів в одному місці, тому виберіть один файл - цим ви спростите роботу тим, хто схрещуватиме ваш мод з іншими.

Запис тексту відбувається у такій формі:

 
<string  id = "my_text_1" > -- у заголовку вказується ім'я тексту, яке викликається з діалогу
	 <text > Привіт, сталкер. Чим я можу допомогти тобі? </text > -- власне, сам текст
 </string >
 

Діалоги

Що ж, структуру фраз ми повністю розібрали. Тепер спробуємо зібрати із них повноцінний діалог. Для початку виберемо жертву для експериментів - Сидорович цілком підійде

Відкриємо папку:

STALKER\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 > 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 > t 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 > -- "Зелений светр? А ти впевнений , що не купив тедді
			 - бір ? "
		
		 > 
			<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 >
 

Для початку краще спробувати свої сили у простому лінійному діалозі на кшталт цього.
Так, і не забудьте додати рядки з текстовими фразами до масиву.

"Динамічні діалоги"

Вище було викладено варіант написання простих діалогів питання-відповідь. Але в цій частині статті я розповім як написати "динамічні діалоги", які передбачають вибір відповіді не лише РР, а й НПС з яким ми говоримо. Розраховано на досвідчених користувачів діалогу.

Взагалі урок це копія попередньої статті, з тією лише невеликою різницею, що як і ГР, так і НПС матиме кілька варіантів відповідей. Структура діалогу, що вийшла:

 
<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 > 1 </next >
 <next > 3 </next >
 < next > 3 < /next >
 <next > 3 </next >
 
 

То ймовірність відповіді "3" становитиме 75%

PS: Це не просто захоплююча і цікава річ, але й дуже трудомістка - у мене день йшов на створення простого діалогу. Для величезної модифікації лише на діалоги потрібно людина 20-30. Зате одних діалогів вистачить щоб вразити! Аналогів не повинно бути, а сама гра використовує цей прийом тільки в ЗП і то в 2-х місцях (на Затоні, інші локації не дивився).

Приклад реалізації динамічного діалогу

А тепер я(Кім) покажу, як виглядає вищеописаний діалог у грі(точніше у файлах конфігурації). Насправді це не так вже й складно і такий діалог можна зробити за 15 хвилин (я сам зробив за 5 хвилин). Діалоги такого типу активно використовуються все в тому ж Oblivion Lost. Почнемо. Сам діалог:

<dialog id="dinamic_dialog_1_test"> 
<dont_has_info>dialog_done</dont_has_info>
<phrase_list>
<phrase id="0">
<text>Привіт. Я маю деякі речі з того, що ти просив. Вибери, що тобі потрібніше.</text>
<next>3</next>
<next>4</next>
<next>5</next>
</phrase>
<phrase id="3">
<precondition>dialogs_gr .is_has_ak_74</precondition>
<text>Так, мені потрібна ця гармата!</text>
<next>31</next>
</phrase>
<phrase id="31">
<text>Бери, ти мені допоміг, я тобі
.
_
_
_
_
_ .is_has_conserva</precondition>
<text>Ух як їсти хочеться, можеш мені дати консерву?</text>
<next>41</next>
</phrase>
<phrase id="41">
<text>На, їж Здоров'я
!
_
_
_
_
_ Чи ти маєш горілку?</text>
<next>51</next>
</phrase>
<phrase id="51">
<text>Вибач, нічого немає</text>
<action>dialogs.break_dialog</action>
</phrase>
<phrase id="52">
<precondition>dialogs_gr.is_has_vodka</precondition>
<text>На, похмелись.</text>
<action>dialogs_gr.give_vodka</action>
<give_info>dialog_done</give_info >
<action>dialogs.break_dialog</action>
</phrase>
</phrase_list>
</dialog>

Скрипти для нього:

function give_conserva (first_speaker, second_speaker)     dialogs.relocate_item_section_from_actor     ( 
first_speaker, second_speaker, "conserva") end
function
is_has_conserva() true 74 (first_speaker, second_speaker)
dialogs .relocate_item_section_from_actor(first_speaker, second_speaker, "wpn_ak74") end function is_has_ak_74() if db.actor:object("wpn_ak74") повторювати true end return false end s.relocate_item_section_from_actor(first_speaker, second_speaker, " vodka") end function is_has_vodka() if db.actor:object("vodka") then return true end return false end




 
 









 
 








Як бачите – нічого складного.


   
Цитата