Welcome to U.A.C. [O.S.A.]
login / register
Status: Guest
Архивы форума | iddqd.ru
Wolf 3D
ПравилаПравила ПоискПоиск
18+
Простая и функциональная библиотека диалогов
   Список разделов - Местечко мапперов и моддеров - Простая и функциональная библиотека диалоговОтветить
АвторСообщение
ZZYZX
- UAC Commissar -
Next rank: = UAC Commissar = after 16 pointsМодератор форума
6284

Doom Rate: 1.65

Posts quality: +1630
Ссылка на пост №1 Отправлено: 21.09.15 04:38:26
Отличный креатив или рецензия на ресурс (+40)
Трэд на зандронуме: http://zandronum.com/forum/showthread.php?tid=6433

http://www.mediafire.com/download/srh9kga10dyzeap/zzconv.pk3
Код отдельно здесь: http://pastebin.com/0kcRVmHx (библиотека), http://pastebin.com/RfXDhpgy (использование)

Скриншот:
Скрытый текст:



Собственно, суть.
Я тут недавно глянул на то, как люди пишут диалоговые либы на ACS, и стало мне очень страшно жить.
Поэтому я потратил полтора часа времени и накодил либу, от которой мне жить не страшно. Смею надеяться, что кому-нибудь она действительно будет полезна. Впрочем, пофиг.

Пример использования:
#include "zcommon.acs"
#import "zzconv.inc"

script 1 (void) // starting dialogue
{
    // don't switch if dialogue has been started already
    Conversation_Init("Spawn a Cacodemon?\\n(or a cyberdemon if you have the BFG)\\n(мяу, кстати)\nYes[default,target_named=test(666,41,0)]\nNo[target=11]\nGIMME A CYBERDEMON!!![can_activate=13,target=14]");
}

script "test" (int arg1, int arg2, int arg3) // spawn caco named
{
    ACS_ExecuteAlways(10, 0, arg1, arg2, arg3);
}

script 10 (int arg1, int arg2, int arg3) // spawn caco
{
    print(i:arg1, c:' ', i:arg2, c:' ', i:arg3); // should output 666 41 0
    SpawnSpot("TeleportFog", 1);
    SpawnSpotFacing("Cacodemon", 1);
}

script 11 (void)
{
    Conversation_Init("Maybe spawn a Pain Elemental then?\nYes[target=12]\nNo[default]");
}

script 12 (void) // spawn pain
{
    SpawnSpot("TeleportFog", 1);
    SpawnSpotFacing("PainElemental", 1);
}

script 13 (void)
{
    SetResultValue(!!CheckInventory("BFG9000"));
}

script 14 (void)
{
    SpawnSpot("TeleportFog", 1);
    SpawnSpotFacing("Cyberdemon", 1);
}


На практике это выглядит так:
http://i.imgur.com/wrpEh0A.png (у игрока нет бфг, скрипт 13 возвращает 0)
http://i.imgur.com/fYpYbx2.png (у игрока есть бфг, скрипт 13 возвращает 1)

Каждый скрипт должен выполняться от имени игрока, который просматривает диалог. В мультиплеере, соответственно, должно быть CLIENTSIDE. Не проверял, если что, могу допилить.

Одновременно может отображаться только один диалог.
Переключение диалогов возможно либо насильственно (т.е. так: ConvInternal_Active = false; Conversation_Init(...); ), либо в результате нажатия на вариант выбора в диалоге (там первая команда делается автоматически).

Запустить новый диалог при наличии активного другого диалога НЕВОЗМОЖНО. Исключение — запуск диалога из target-скрипта (см. про доступные параметры ниже).
Это сделано из-за того, что даже PROP_TOTALLYFROZEN не фильтрует +use, т.е. если в гуе поменять BT_USE на что-нибудь другое, то это можно и убрать. Но об интерфейсе позже.

А сейчас подробности по синтаксису строки, пихаемой в Conversation_Init.
Выглядит она так:
"заголовок диалога\nвариант выбора 1[параметры]\nвариант выбора 2[параметры]"


Параметры можно не писать. Без параметров получается постоянно активный пункт диалога, при выборе которого происходит тупо выход из диалога без последствий.
Если вы пишете параметры, то писать их надо не абы как, а через запятую. Пример выше.
Доступные параметры:
default: помечает вариант выбора как "по умолчанию". Он будет выделен в самом начале. Крайне не рекомендую совмещать can_activate с default.
can_activate, can_activate_named: указывает скрипт, который будет вызываться для проверки, можно ли выбрать определённый вариант. Первый принимает число, второй название. Если не указано, то вариант всегда активен.
target, target_named: указывает скрипт, который будет вызываться при выборе этого варианта. Если указан can_activate, и он возвращает 0, то данный скрипт не выполняется.
visible, visible_named: указывает скрипт, который будет вызываться для проверки видимости этого варианта. Статично (проверяется ровно один раз, при инициализации экрана. В то время как can_activate вызывается каждый тик).

Для каждого скрипта можно указывать аргументы (например: target_named=test(666,41,0), или 12(666) для номерного скрипта). Пример выше.
Пробелов между запятыми, скобками и аргументами НЕ ДОЛЖНО БЫТЬ. В ацс дорог каждый опкод, и вообще мне лень было писать Trim :)
Аргументы могут быть ТОЛЬКО числами. Fixed не принимается.

Если указать параметры несколько раз (это касается named и не-named вариантов тоже), то будет использован только последний.

Теперь о гуе. Гуй находится в библиотеке скриптом номер 999 c названием zzconv_gui.
Выглядит он как крутящаяся в цикле клиентсайд-фигня, которая отображает текущий диалог (тот, который последний был вызван функцией Conversation_Init), если ConvInternal_Active == true.
Этот же скрипт фризит игрока и обрабатывает его ввод. Но я не знаю, насколько надёжен фриз на клиентской стороне. Это надо тестировать, а сейчас четыре часа ночи. Главное, что как минимум в сингле это работает, да.

Собственно, как выглядит этот скрипт.
Во-первых, он следит за состоянием кнопки use. Если во время открытия диалога была нажата кнопка use, скрипт будет ждать, пока её отожмут, перед тем как предпринимать активные действия. Это связано с тем, что сам диалог часто открывается по кнопке use.
Во-вторых, он следит за переменной ConvInternal_Active. Если она true, значит в данный момент активен какой-то диалог. Игрок фризится и происходит отрисовка диалога. Если же false, то игрок перестаёт фризиться и ничего не отрисовывается.
При отрисовке диалога используется массив ConvInternal_Strings. Массив этот генерируется функцией Conversation_Init, как и всё остальное, что является массивом и начинается с ConvInternal, да.
Ещё при отрисовке диалога используется переменная ConvInternal_Count. Она не является массивом, однако тоже генерируется функцией Conversation_Init. Ибо Джашин воистину!..
Эта переменная (ConvInternal_Count) символизирует собой фактическое количество строк во всех массивах ConvInternal.
Текущий вариант (тот, на котором курсор) хранится в ConvInternal_CurrentItem. Если -1, значит ничего не выбрано. Если больше ConvInternal_Count, то, в принципе, тоже ничего не выбрано, но лучше так не делать.
По дефолту текущий вариант прикольно подсвечивается жёлтым цветом и мигающей хреновиной.
Ах да, и если функция Conversation_CheckActiveChoice(индекс) возвращает false (напоминаю, она вызывает скрипт, который мы указывали в can_activate), то итем будет отображаться серым и его будет невозможно выделить. А даже если и выделится, то не выполнится.

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

Мышь реализована. Активируется при попытке пошевелить мышью. Деактивируется при попытке пошевелить клавиатурой.

Кажется, всё. За остальным можно ковырять руками скрипт. И даже нужно.

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

Рейтинг сообщения: +1, отметил(и): Keilin Fox
2 2 1
VladGuardian
= Commissar =
Next rank: - UAC Commissar - after 263 points
5537

Doom Rate: 1.28

Posts quality: +1899
Ссылка на пост №2 Отправлено: 21.09.15 08:59:14
Кажется опечатка, исправь:
SetResultValue(!!CheckInventory("BFG9000"));

(два отрицания подряд)
4 10 23
ZZYZX
- UAC Commissar -
Next rank: = UAC Commissar = after 16 pointsМодератор форума
6284

Doom Rate: 1.65

Posts quality: +1630
Ссылка на пост №3 Отправлено: 21.09.15 09:03:51
Это не опечатка, это приведение любого ненулевого значения к 1.
2 2 1
VladGuardian
= Commissar =
Next rank: - UAC Commissar - after 263 points
5537

Doom Rate: 1.28

Posts quality: +1899
Ссылка на пост №4 Отправлено: 21.09.15 10:01:57
ZZYZX:
Это не опечатка, это приведение любого ненулевого значения к 1.

Воу, буду знать, спасибо! :ogo:
Я понял - это типа "униформизация" логического значения:
0 -> 0
any -> 1
Изящно, никогда бы не догадался сам до такого.
4 10 23
Shadowman
= UAC Marshal =
Next rank: UAC General after 161 points
8039

Doom Rate: 2.09

Posts quality: +1768
Ссылка на пост №5 Отправлено: 21.09.15 11:44:43
Это целых 6 скриптов ушло только на 1 диалог из 3 опций? А сколько тогда уйдет скриптов на сложный диалог с большим количеством текста?
Что-то эта библиотека мне совсем не кажется простой... Может, она универсальна, не спорю, но назвать ее простой я не могу.
1 7 2
VladGuardian
= Commissar =
Next rank: - UAC Commissar - after 263 points
5537

Doom Rate: 1.28

Posts quality: +1899
Ссылка на пост №6 Отправлено: 21.09.15 11:57:09
Передачей параметров (числовых значений) в скрипты можно объединить несколько скриптов в один.
Что является хорошей практикой в программировании вообще.

Вот эти два прямо просятся:
script 12 (void) // spawn pain
{
    SpawnSpot("TeleportFog", 1);
    SpawnSpotFacing("PainElemental", 1);
}

script 14 (void) // spawn cyber
{
    SpawnSpot("TeleportFog", 1);
    SpawnSpotFacing("Cyberdemon", 1);
}

Преобразуются в один:
const char* MonsterName[] = {"Zombieman", "Sergeant", "Imp", ...};
script 12 (int monster)
{
    SpawnSpot("TeleportFog", 1);
    SpawnSpotFacing(MonsterName[ monster ], 1);
}

Скрипты 1 и 11 тоже ну ооочень просятся слиться в один.
4 10 23
ZZYZX
- UAC Commissar -
Next rank: = UAC Commissar = after 16 pointsМодератор форума
6284

Doom Rate: 1.65

Posts quality: +1630
Ссылка на пост №7 Отправлено: 21.09.15 11:59:02
Shadowman:
Это целых 6 скриптов ушло только на 1 диалог из 3 опций? А сколько тогда уйдет скриптов на сложный диалог с большим количеством текста?

Я хз, что такое сложный диалог с большим количеством текста. Примерно 12-13 скриптов на такой уйдёт. Ну, может 20.
Но это не проблема, т.к. я бы такие вещи раскладывал по разным acs-файлам (scripts/dial_npc1.inc, scripts/dial_npc2.inc, scripts/dialogue.acs = #include), где npc1/npc2 соответственно условные разделители типов неписей, при взаимодействии с которыми будут данные диалоги. И ещё я бы однозначно юзал named-скрипты и окончательно положил конец любому пересечению диалоговых скриптов между собой.

VladGuardian:
Передачей параметров (числовых значений) в скрипты можно объединить несколько скриптов в один.
Что является хорошей практикой в программировании вообще.

Хорошей практикой в программировании является не закидывать в один скрипт абсолютно не связанные друг с другом вещи, как некоторые любят делать (в данном случае не шадовмен).
А я регулярно видел, как таким образом абузятся аргументы и в итоге получается нечитаемый атас.
Впрочем, почитав твой пример, ты наверное таки прав. Придётся допиливать в синтаксис конфига вещи навроде 12(0), 12(1), 12(2)...

Впрочем, я могу убрать три скрипта и сделать один (script=666), и в качестве аргумента этому скрипту будет передаваться либо 0 (вариант выбран), либо 1 (проверка активности), либо 2 (проверка видимости).
Или вообще сделать тупо один скрипт ВООБЩЕ. Который будет вызываться с двумя параметрами. Идентификатор варианта выбора (который можно будет задавать) и причина вызова (аналогично тому что выше).
Но тут будет в разы больше возможностей накосячить, как мне кажется. Зато весь отдельно взятый диалог будет управляться одним скриптом.
2 2 1
Shadowman
= UAC Marshal =
Next rank: UAC General after 161 points
8039

Doom Rate: 2.09

Posts quality: +1768
Ссылка на пост №8 Отправлено: 21.09.15 12:06:41
ZZYZX:
Я хз, что такое сложный диалог с большим количеством текста.

Как пример - те же диалоги Кейна в Тристане. При том, что в них должны быть заранее прописаны все опции, активирующиеся по ходу прохождения игры и выполнения игроком разных квестов, и соблюдены условия, при которых высвечиваются разные ответы.
1 7 2
ZZYZX
- UAC Commissar -
Next rank: = UAC Commissar = after 16 pointsМодератор форума
6284

Doom Rate: 1.65

Posts quality: +1630
Ссылка на пост №9 Отправлено: 21.09.15 12:07:56
Shadowman:
При том, что в них должны быть заранее прописаны все опции, активирующиеся по ходу прохождения игры

Здесь бы это решилось visible-скриптом. А что там в этом скрипте понаписано и как именно он определяет, докуда мы дошли — уже не моя забота. В этом и крутость либы!

Shadowman:
и соблюдены условия, при которых высвечиваются разные ответы.

Аналогично. Два взаимоисключающих варианта в той же самой проверке visible-скрипта.

Аргументы к скриптам придётся вводить :diatel:
2 2 1
VladGuardian
= Commissar =
Next rank: - UAC Commissar - after 263 points
5537

Doom Rate: 1.28

Posts quality: +1899
Ссылка на пост №10 Отправлено: 21.09.15 12:30:44
ZZYZX:
Хорошей практикой в программировании является не закидывать в один скрипт абсолютно не связанные
друг с другом вещи, как некоторые любят делать (в данном случае не шадовмен).

Совершенно согласен. У меня наблюдается другая тенденция делать более "универсальные" методы (внутри классов), а также скрипты "помощнее" :x (передачей двух параметров в скрипт, например, меня не удивишь)
Что действительно приводит к заметному ухудшению читабельности, и "размытому" коду, по внешнему виду которого не сразу догадаешься, что именно он делает :shock:
Так что надо наверное останавливаться на какой-то золотой середине.

Но тем не менее, стараться немножко сократить количество скриптов.
Например, ничто не мешает объединить скрипты отвечающие за включение и выключение одной и той же лампочки в комнате.
Вот конкретный работающий пример:
script "RoomLightSwitch" (int sector) //---вкл/выкл света в комнатах
{
  if (GetSectorLightLevel(sector) == ROOM_LIGHT_OFF)
    Light_Fade(sector,ROOM_LIGHT_ON,9);
  else
    Light_Fade(sector,ROOM_LIGHT_OFF,9);
  AmbientSound("Swtch1",127);
}

P.S. Извиняюсь за программистский оффтоп.
4 10 23
ZZYZX
- UAC Commissar -
Next rank: = UAC Commissar = after 16 pointsМодератор форума
6284

Doom Rate: 1.65

Posts quality: +1630
Ссылка на пост №11 Отправлено: 21.09.15 15:32:15
Да это не программистский оффтоп. Во-первых, слова оффтоп нет. А во-вторых, сама эта тема про программирование! И в разделе о скриптинге (в том числе).
В общем я чуть позже сделаю просто передачу статических аргументов в скрипт, таким способом: target_named=dial1_option1(0),can_activate_named=dial1_option1(1). И дальше уже люди смогут писать в меру испорченности, а универсальность только возрастёт.
2 2 1
ZZYZX
- UAC Commissar -
Next rank: = UAC Commissar = after 16 pointsМодератор форума
6284

Doom Rate: 1.65

Posts quality: +1630
Ссылка на пост №12 Отправлено: 26.09.15 23:34:38
Добавил передачу аргументов. Добавил описание в первый пост.

Кстати, код благополучно не пашет в зандронуме.
Завёл тикет.
https://zandronum.com/tracker/view.php?id=2472


Код благополучно не пашет в зандронуме и старой гоззе (1.8.6) после добавления аргументов к скриптам.

Код благополучно пашет в зандронуме и старой гоззе. Минимальное требование — версия 2.7.0.

Добавлено спустя 1 день 17 часов 4 минуты 16 секунд:

Допилил мыш! В оффлайне работает хорошо, в онлайне — умеренно адекватно.
2 2 1
UsernameAK
- Lance Corporal -
Next rank: = Lance Corporal = after 10 points
150

Doom Rate: 1.79

Posts quality: +13
Ссылка на пост №13 Отправлено: 05.11.15 10:57:11
Spawn a Cacodemon?
(or a cyberdemon if you have the BFG)
(мяу, кстати)

Yes[default,summon=Cacodemon,summonfog=TeleportFog]
No[goto=2]
GIMME A CYBERDEMON!!![if_has_class=BFG9000,summon=Cyberdemon,summonfog=TeleportFog]


Maybe spawn a Pain Elemental then?

Yes[summon=PainElemental,summonfog=TeleportFog]
No[default]


вполне можно сделать так, еще и в отдельном файле, но будет работать на глуме :(
1
ZZYZX
- UAC Commissar -
Next rank: = UAC Commissar = after 16 pointsМодератор форума
6284

Doom Rate: 1.65

Posts quality: +1630
Ссылка на пост №14 Отправлено: 05.11.15 19:06:39
Ты забыл кучу параметров кроме summon=...
В частности тэг куда суммонить. А ведь суммонить ещё можно и по координатам.

USDF не просто так придумали. И подобные системы.
Но моя именно что проще.
2 2 1
UsernameAK
- Lance Corporal -
Next rank: = Lance Corporal = after 10 points
150

Doom Rate: 1.79

Posts quality: +13
Ссылка на пост №15 Отправлено: 06.11.15 14:27:32
ZZYZX:
Но моя именно что проще.

она была бы проще если бы все можно было бы слить в один скрипт

Добавлено спустя 13 минут 6 секунд:

в 4 скрипта вжал

#include "zcommon.acs"
#import "zzconv.inc"

script 1 (void) // starting dialogue
{
    // don't switch if dialogue has been started already
    Conversation_Init("Spawn a Cacodemon?\\n(or a cyberdemon if you have the BFG)\\n(мяу, кстати)\nYes[default,target_named=ConversationSpawn(19)]\nNo[target=3]\nGIMME A CYBERDEMON!!![can_activate=2,target_named=ConversationSpawn(114)]");
}
script "ConversationSpawn" (int monster)
{
	Thing_SpawnFacing(1, monster, false, 0);
}
script 2 (void) {
	SetResultValue(!!CheckInventory("BFG9000"));
}
script 3 (void) {
	Conversation_Init("Maybe spawn a Pain Elemental then?\nYes[target_named=ConversationSpawn(115)]\nNo[default]");
}
1
ZZYZX
- UAC Commissar -
Next rank: = UAC Commissar = after 16 pointsМодератор форума
6284

Doom Rate: 1.65

Posts quality: +1630
Ссылка на пост №16 Отправлено: 06.11.15 15:02:52
UsernameAK:
она была бы проще если бы все можно было бы слить в один скрипт

Можешь слить. Нахрена???
Это усложнение.
2 2 1
Страница 1 из 1Перейти наверх
   Список разделов - Местечко мапперов и моддеров - Простая и функциональная библиотека диалогов