» TeamX (Архив Форума)«


Форум TeamX » Исследования » Делимся опытом (Наследие "Техподдержки")

Переход по темам
<< Пред. След. >>
Единственная страница этой темы

 
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

>>мне вдруг тоже чертовски интересны стали последние разработки в области диалогов.

Ну, в диалогах, не в диалогах. Но наработки есть. Сегодня расскажу про одну (и постараюсь делать это более-менее регулярно). Начнём с мелочей.

ВНИМАНИЕ! Всё, о чём я (пока) буду говорить - это техническая сторона скриптинга.

Говорить будем про msg-файлы. Что это такое? Пронумерованный (необязательно последовательно) список строк. Имя должно совпадать с одним из имён существующих скриптов. Номер совпадает с номером одноимённого скрипта.

Для работы с msg-фйалом с помощью стандартных макросов необходимо определить дефайн NAME:
#define NAME номер_скрипта

После этого mstr(xxx) вернёт строку xxx.

Задача 1.

Недостаток: привязка скрипта к какому-то там номеру. Если вы поменяете положение скрипта в scripts.lst (а номер строки, где прописан скрипт и есть номер скрипта), то скрипт перестаёт работать - т.к. не находит свою msg-шку.

Решение: устранить привязку. Делаем это следующим образом. В msg на фиксированной строчке заводим определённую запись. Например, у нас есть персонаж по имени Сайег (имя из прототипа):
------------------------
{1000}{}{Сайег}
------------------------

Пишем код:
------------------------
#define LIMIT 2000 //на каком номере остановиться
variable msg_num; //глобальная вара, в которую пойдёт номер найденой msg-шки
<...>
variable i:=1300; //ищем только в своих msg-шках, игровые не трогаем
while message_str(i,1000)!=obj_name(self_obj) and i<LIMIT do i+=1;
if i==LIMIT then display_msg("ERROR: msg not found.");
else msg_num:=i;
<...>
------------------------

Всё это легко запихивается в макрос - и вуаля!

Задача 2:

Мы хотим сделать набор флоатеров для персонажа, так чтобы он выбирал и говорил случайную строку из некоторого диапазона номеров в msg-файле. Но! Мы хотим, чтобы диапазон изменялся динамически - т.е. если в msg-шке у нас 15 реплик, то он выбирал 1 из 15; 20 - 1 из 20.

Заимствуем принцип из предыдущей задачи, но вместо поиска по содержимому и номеру строки в наборе мсг-шек будем искать строку по содержимому в конкретной мсг-шке.

Решение:
Заводим такой вот msg:
------------------------
<...> //номера произвольны, главное - последовательны
{231}{}{BEGIN REPLICS}
{232}{}{Привет}
{233}{}{Хай}
{234}{}{Как дела?}
<...>
{240}{}{END REPLICS}
<...>
------------------------

Код:
------------------------
#define LIMIT 5000 //сколько строк обрабатывать

//msg_num - номер нашей msg-шки, найденный заранее

<...>
variable i:=1;
variable first; //номер строки с первой репликой
variable last; //с последней

while message_str(msg_num,i)!="BEGIN REPLICS" and i<LIMIT do i+=1;
if i==LIMIT then display_msg("ERROR: replics list not found");
else start:=i+1;

//Начинаем искать от строки first
//Ищем либо закрывающий "тэг", либо первую пустую строку
while message_str(msg_num,i)!="END REPLICS" or message_str(msg_num,i)!="Error" do i+=1;
last:=i-1; //проверки на ошибку не делаем, т.к. список не может быть бесконечным, пустая строка всегда найдётся
//Закрывающий тэг нужен "для себя", как комментарий

floater(random(first,last));
<...>
------------------------

Тормозов никаких нет, всё работает довольно шустро. Но ОБЯЗАТЕЛЬНО вводите ограничители! Бесконечный цикл подвешивает игру.

Ладно, на сегодня хватит. Следущий постинг будет взаимодействие объектов, наверное.

Отзывы?

И, да - не стесняётесь постить свои мысли/наработки.

Отправлено: 11:41 - 24 Ноября, 2004
Mynah
Модератор

Откуда: Пермь
Регистрация: Окт. 2004

Всего: 469 сообщений

Цитата:

Тормозов никаких нет, всё работает довольно шустро.


А если таких скриптов 15-20? Тоже всё ок?

С устранением привязки - рулез :)

-----
Scio me nihil scire

Отправлено: 12:52 - 24 Ноября, 2004
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

На Йцукеновском Cel 1700 всё нормально (по его словам) на схожем скрипте. На моём Athlon XP 3000+ - тоже :-) На моём бывшем 166... Не должно, по идее.

Это всё выполняется один раз, в map_enter. А msg-шку можно и в LVARу запомнить, если что.

Отправлено: 13:01 - 24 Ноября, 2004
Mynah
Модератор

Откуда: Пермь
Регистрация: Окт. 2004

Всего: 469 сообщений

Если кому-то интересно - могу выложить свои наработки. В частности obj_hear_obj и obj_see_obj.

Мои функции вроде бы работают не так глючно как бисовские :)

-----
Scio me nihil scire

Отправлено: 4:20 - 26 Ноября, 2004
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

>>Если кому-то интересно...

Для того эта тема и создана. Выкладывайте все, без всякого "если кому-то надо". Надо.

P.S. Егор планирует отдельный форум для таких вещей.

Отправлено: 4:37 - 26 Ноября, 2004
Alan Killenger
Пользователь

Откуда: Россия, Ижевск
Регистрация: Июль 2004

Всего: 404 сообщения

Даже не знаю, прав ли я.. или опять глючу :).

----------
procedure set_script_number begin
 variable V_index;
 V_index:=C_identify_start;
 while not(V_index>C_identify_end) do begin
   if message_str(V_index,C_identify)==obj_name(self_obj) then begin
     set_local_var(LV_script,V_index);
     V_index:=C_identify_end+1;
   end else begin
     V_index:=V_index+1;
   end
 end
 if local_var(LV_script)==0 then begin
   display_msg("Error: *.msg file not found for "+obj_name(self_obj)+".");
 end else begin
   display_msg("Соответствующий номер найден: "+local_var(LV_script)+".");
 end
end
----------

Искомого *.msg файла заранее не существует.

Эксперимент #1:
----------
 #define C_identify 0
 #define C_identify_start 1000
 #define C_identify_end 1300
----------
Результат: после одной-двух секунд раздумий, появляется надпись: "Error: *.msg file not found for Алан."

Эксперимент #2:
----------
 #define C_identify 0
 #define C_identify_start 1000
 #define C_identify_end 1400
----------
Результат: после одной-двух секунд раздумий, Fallout (как и маппер) вылетает с надписью: ".. память не может быть read."

Догадываетесь, чего не позволяет нам делать коварная win2k? Объясните, кто догадался :).

(Отредактировал(а) Raven - 17:01 - 25 Янв., 2005)

Отправлено: 2:39 - 27 Ноября, 2004
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

Посмотри вот это:
if message_str(V_index,C_identify)

И вот это:
#define C_identify 0

Понял, нет? Ты пытаешся читать нулевые строчки в мсг-шках. А строчки там нумеруются от единицы. Понимаешь? Естественно, Фол может вылететь - ведь непонятно в какую область памяти ты лезешь с такими запросами.

Вторая потенциальная трабла (вызывающая тормоза :-) - диапазон (1000-1300). Строчки 1-1304 заняты под оригинальные скрипты Фола, поэтом искать в них *свою* мскг-шку бессмысленно. В идеале диапазон должен быть: строчка с первым добавленным тобою скриптом (1305) - последняя строчка в scripts.lst + небольшой запас.

>>Догадываетесь, чего не позволяет нам делать коварная win2k? Объясните, кто догадался.

Так это - загадка, типа, была - если так, то хорошо :-)

P.S. Всю процедуру перебора проще записать так:
-------------------------------------------
#define LIM 1500
#define search(line,what) while not message_str(i,line)!=what and i<LIM do i+=1; \
                         if i==LIM then display_mg(what + " not found")
<...>
variable i:=1300;
search(1,obj_name(self_obj));
set_local_var(0,i);
<...>
-------------------------------------------


(Добавление от 5:03 - 27 Ноября, 2004.)


Наработка номер 2.

Проблема:

Есть generic-скрипт, определяющий поведение многих объектов сразу (НПС-крестьян в моём случае).

Нужно выдавать каждой копии скрипта уникальное имя из списка, чтобы все эти крестьяне были не так безлики. Трабла в том, что так как они являются копиями одного и того же скрипта, то и совпадает у них практически всё - и SID, и имя/описание из scrname.msg, и реакция на talk/description, и данные прототипа...

Как быть?

Отступление:

Полагаю, все знают про импорт/экспорт переменных. Так вот, стандартный метод - это миф. Это недопонимание принципов работы БИСовскими скриптерамИ, перенесённое сюда.

Итак, как работает импорт/экспорт. Любая переменная, объявленная как export попадает в некоторый общий namespace - из какого бы скрипта она не экспортировалась. После этого любой другой скрипт может её импортировать. Скрипт карты трогать не нужно.

Решение:

Всё-таки наши объекты кое-чем различаются - указателями. Вот и воспользуемся этим.

--------------------------
//Объявляем набор вар для указателей
export variable begin
ptr_1;
ptr_2;
ptr_3;
end

procedure start begin
end

procedure map_enter_p_proc begin
if not ptr_1 then ptr_1:=self_obj;
else if not ptr_2 then ptr_2:=self_obj;
else if not ptr_3 then ptr_3:=self_obj;
end

procedure talk_p_proc begin
if self_obj==ptr_1 then display_msg("NPC 1");
else if self_obj==ptr_2 then display_msg("NPC 2");
else if self_obj==ptr_3 then display_msg("NPC 3");
end
--------------------------

Фокус в том, что все "внешние" вары хранятся в общем нэймспэйсе и сохраняются/берутся оттуда. Поэтому если мы ставим ptr_1, то для всех остальных объектов, импортировавших (или экспортировавших!) эту вару, она будет уже занята.

Это - самый простой (и в плане возможностей, и в плане реализации) вариант. Можно брать имена из msg, сделать его для неограниченного числа НПС с повторениями имён и т.д.

Естественно, я описываю идею вообще, не какой-то частный случай. Можно, например, запоминать указатель на последнего чара, говорившего с чузом. А следующий будет справшивать "Я видел, ты там с ... болтал, ну что, как дела у него?".

Отправлено: 4:04 - 27 Ноября, 2004
Alan Killenger
Пользователь

Откуда: Россия, Ижевск
Регистрация: Июль 2004

Всего: 404 сообщения

Что-то не спешат скриптологи делиться опытом, видать коммерческая ценность . Три эксперимента, которые достаточно явно показывают все, что я знаю о номерах реплик в *.msg файлах:

Цитата:

display_msg(message_str(1304,0));
display_msg(message_str(1304,999));
display_msg(message_str(1304,-998));
display_msg(message_str(1304,998));
display_msg(message_str(1304,-999));



Эксперимент #1.
Цитата:

{0}{}{реплика 0}
{999}{}{реплика 999}
{-998}{}{реплика -998}
{998}{}{реплика 998}
{-999}{}{реплика -999}


Цитата:

•реплика 0
•реплика 999
•реплика -998
•реплика 998
•реплика -999



Эксперимент #2.
Цитата:

{0}{}{реплика 0}
{999}{}{реплика 999}
{-998}{}{реплика -998}
#{1.5}{}{реплика 1.5}
{998}{}{реплика 998}
{-999}{}{реплика -999}


Цитата:

•Error
•реплика 999
•реплика -998
•Error
•Error


Два раза проверял - именно так.

Эксперимент #3.
Цитата:

{0}{}{реплика 0}
{999}{}{реплика 999}
{-998}{}{реплика -998}
#{998}{}{неиспользуемая реплика 998}
{998}{}{реплика 998}
{-999}{}{реплика -999}


Цитата:

•реплика 0
•реплика 999
•реплика -998
•реплика 998
•реплика -999



P.S. Пошлите кто-нибудь please на мыло любой исходник с использованием таких вещей как spawn или расскажите об этом в "Скриптах".

3.12.04

Выложил работающий скриптик, в котором реализована наработка Raven'а относительно не фиксированных номеров реплик.

4.12.04

Ссылка исправлена.

(Отредактировал(а) Alan Killenger - 21:09 - 4 Дек., 2004)

-----
hit me, nail me, make me god

Отправлено: 20:10 - 29 Ноября, 2004
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

spawn, exec и fork

По порядку. Синткаксис:
spawn("путь_к_скрипту/файл_скрипта.int");
Запускает скрипт на исполнение (взывает в нём процедуру start). Пока выполняется child-скрипт, родительский простаивает. По прекращению выполнения происходит возврат в исходный скрипт. Т.е. это call для скриптов, а не процедур.

exec фнфлогичен spawn, но возвращение происходит только по команде exit. Если она опущена - Фол вылетает.

fork - тот же spawn, но вызываемый скрипт работает параллельно основному.

Теперь почему это всё нафиг не надо: скрипты, вызванные таким образом, не привязываются к какому-либо объекту (self_obj==0). Естественно, обработчики в них вызваны никогда не будут. И пропадает весь смысл использования скрипта. Кроме того, в вызваном скрипте выполняется только процедура start (хотя можно делать call), по её выполнению скрипт прекраает работу.

Пример:
------------------
<...>
spawn("scripts/fork.int");
display_msg("back to main");
<...>
------------------

forks.ssl:
------------------
procedure start begin
 display_msg("spawned script");
end
------------------

Вывод:
spawned script
back to main

(Отредактировал(а) Raven - 10:32 - 30 Ноября, 2004)

Отправлено: 3:31 - 30 Ноября, 2004
Ray
Модератор

Откуда: Донецк,Украина
Регистрация: Янв. 2004

Всего: 746 сообщений

[deleted]

Ой не в тему... Читай ФАК. Там всё описано.
WG

Отправлено: 15:19 - 5 Дек., 2004
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

Обещанный рассказ про atoi. С опозданием в неделю :-(

Atoi - это процедура преобразующая string-число в int-число.

Проблема:

Для тех, кто не смотрел исходники компилятора скриптов Фола (точнее, klngon Academy, однодвижковой игры) - коммент из parse.c:
----------------------------------
/*
* Parser for SSL (Startrek Scripting Language).
*
* All variables are "typeless"; their type is defined by how
* they are used.  All types are promoted to the "highest" type
* in an expression, where the order is from lowest to highest:
* int, float, string.
*
* So,
* if you do 2 + "foo", you get back "2foo".
* if you do 2 + 2.4,   you get 4.4
* if you do 2 + 4,     you get 6 (hopefully :)
* if you do 2 + 2.4 + "foo", you get "4.4foo", since
* expressions are parsed left to right.
* if you do 2 + (2.4 + "foo") you get "22.4foo", due
* to the parentheses.
* etc.
*/
----------------------------------

Т.е.:
----------------------------------
variable a:="1234";
a+=1;
display_msg("a="+a);
----------------------------------
выведет "a=12341".

Это ставит крест на хранении в msg-файлах числовых значений. А делать так было бы удобно, т.к. тогда можно хранить в msg PID-ы предметов, номера/значения квестовых GVAR, номера "домашних" тайлов - т.е. изменяющиеся в процессе модинга величины - что упростило бы разработку, избавив модера от необходимости отслеживать и перекомпилировать всё, что использует ГВАРу/PID.

Кроме того, можно было бы хранить там количество экспы, денег, PID-ы предетов выдаваемых в качестве квестовых наград; временные таблицы для создания НПС, двигающихся по карте "по расписанию"; значения скилов/параметров, необходимых для выдачи/решения квеста - т.е. упростить итоговую балансировку мода.

Принцип работы:

Итак, у нас есть строки. Что мы можем с ними делать? Только склейку? Неправильный ответ. Мы можем их сравнивать:
"abc"=="abc" //true
"abc">"abc" //false
"b">"a"     //true
"b">"abczz" //true
"abc">"abd" //false
"b">"A"     //false

Принцип понятен? Строки сравниваются посимвольно, как только найдено различие - сравниваются ASCII-коды символов. Символ с бОльшим номером делает больше всю строку.

Что нам это даёт? Рассмотрим строку "123". Что мы можем сказать?
"123">"1". И "123"<"2"
Уловили? Мы только что выявили первый символ. Запомнили его. Смотрим дальше.
"123">"11", "123">"12", "123"<"13".
Нашли второй. Прибавили к первому.
"123">"121","123">"122","123"=="123". Нашли строку. Параллельно с конструированием строки мы можем собирать число - поразрядно.

Пишем код:
----------------------------------
procedure atoi(var str) begin
 var tmp_str:=""; //инициализация обязательна, иначе tmp_str="0"
 var value; //числовое значение
 //""+0=="0"
 if (str>tmp_str+0 and str<tmp_str+1) or str==tmp_str+0 then begin
   tmp_str+=0; //дополняем строку. "123"+0=="1230"
   value:=value*10+0; //"дописываем" ноль справа
 end
 //тоже для остальных цифр
 <...>
----------------------------------

Всё это должно быть загнано в цикл:
----------------------------------
procedure atoi(var str) begin
 var tmp_str:=""; var value;
 var i;  //для ограничения на длину преобразуемой строки

 while str!=tmp_str and i<16 do begin
   if (str>tmp_str+0 and str<tmp_str+1) or str==tmp_str+0 then begin
     tmp_str+=0;
     value:=value*10+0;
   end
   <...>
   i+=1;
 end
 if i!=16 then return value; //строка >16 символов либо содержит нецифровые символы
 else return -1;
end
----------------------------------

Теперь чуть-чуть укоротим, введя двойной цикл:
----------------------------------
procedure atoi(var str) begin
 var tmp_str:=""; var value; var i;
 var j;  //для ограничения на длину преобразуемой строки

 while (str!=tmp_str) and j<16 do begin
   //относительно сложное выражение. Надеюсь, разберётесь :-)
   while not ((str>tmp_str+i and str<tmp_str+(i+1)) or str==tmp_str+i) and i<9 do i+=1;
   //при i=9 предыдущее выражение не работает (str>...+9 and str<...+10)
   //случай с девяткой обрабатываем отдельно
   if i==9 and not (ostr>str+9 or ostr==str+9) then i:=10;
   //дописываем tmp_str и value
   if i<10 then begin tmp_str+=i; value:=value*10+i; end
   //в строке попался нечисловой символ
   else return -1;
   i:=0;j+=1;
 end
 return val;
end
----------------------------------
После удаления комментариев получится меньше 10 строк :-)

Ну вот и всё. Теперь если msg:
----------------------------------
{214}{}{2000}
----------------------------------
,то код
----------------------------------
give_exp(atoi(mstr(214));
----------------------------------
даст Чузу 2000 экспы.

Учитывая мои предыдущие наработки (идея присвоения мсг-шкам уникальных строк-меток), можно подумать о создании этакого ini-файла, где могут быть прописаны:
1. экспа за каждый квест
2. изменения кармы за квесты
3. значения квестовых GVAR (взял/сделал/сдал и т.д.)
4. награды за квесты

В общем, эдакие const GVAR-ы. Всё в одном месте, перекомпиляции не требуется. Балансировка упрощается.

P.S. Mynah, ты говорил про hear/see? Запость, не стесняйся.




=================================================



Диалог между NPC.

Никогда не задумывались как можно организовать floater-дилог между двумя НПС? Рассказываю.

Решение:

Итак, у нас два объекта. К ним привязан скрипт (один и тот же). Различаются они по указателям.Каждый знает указатель другого (импорт/экспорт - предыдущие наработки). Что такое с точки зрения скриптера floater-диалог? Это последовательность вызовов floater-ов, разнесённая по времени (floater над первым НПС, пауза в секунду, floater над другим НПС ...). Т.е. очевидно, что реализуется он с помощью add_timer_event. Начинаем писать скрипт:

msg:
------------------------------
{1}{}{Phrase1}
{2}{}{Phrase2}
{3}{}{Phrase3}
{4}{}{Phrase4}
------------------------------

Код:
------------------------------
export var begin
ptr_chat_1;
ptr_chat_2;
end

procedure start begin
end

procedure map_enter_p_proc begin
 if not ptr_chat_1 then ptr_chat_1:=self_obj;
 else ptr_chat_2:=self_obj;
end

procedure talk_p_proc begin
 var i:=1; //i==началу блока реплик - пред. наработки
 
 while (mstr(i)!="Error") do begin
   //добавляем события по таймеру ч/з 0,1,2,3 секунды
   add_timer_event(self_obj,i-1,i);
   i+=1;
 end
end
 
procedure timed_event_p_proc begin
 var who; //кто говорит
 //если номер реплики чётный - говорит первый чар, иначе - второй
 if fixed_param%2 then who:=ptr_chat_1;
 else who:=ptr_chat_2;

 float_msg(who,mstr(fixed_param),0);
end
------------------------------

Всё. Лучше, конечно, добавить код для поиска начала блока фраз - для унификации.

С этим скриптом появляется один "баг" - сколько раз ткнули в НПС - столько диалогов и удет. Одновременно. Как можно исправить? Завести вару, ставить её один в начале диалога, сбрасывать в конце. Маленькая трабла - в конце значит после последнего вызова timed_event, а не после добавления событий (которое происходит сразу и очень быстро). Т.е. мы должны добавить 4 таймерных события для вывода фраз + ещё одно для сброса переменной. Как их различать? Ведь номер фразы (спасибо Алану) может быть любым - и отрицательным, и положительным, и нулевым - т.е. принять некоторые фиксированные значения параметра за "установить/сбросить флаг" мы, вообще говоря, не можем.

Выход: "сборные" параметры. Как пакеты в локальных сетях. Т.е. мы "забираем" один разряд передаваемого значения под "флаг", определяющий тип значения.

Например, флаг "0" озачает, что параметр - номер строки в msg для floater. Если пришло 3130 - значит надоы вывести строку 313, пришло 120 - выводим строку 12, 900 - строку 90. А флаг "1" будет означать сброс/установку вару "диалог уже идёт".

------------------------------
export var is_chatting;
<...>
//первый разряд числа
#define flag  (fixed_param%10)
//число без первого разряда
#define param (fixed_param/10)

procedure timed_event_p_proc begin

 if flag==0 then begin

   if param%2 then float_msg(ptr_chat_1,mstr(param),0)
   else float_msg(ptr_chat_2,mstr(param),0);

 end else if flag==1 then is_chatting:=param;

end

procedure talk_p_proc begin
 var i:=1;
 //если уже болтаем - ничего не делать
 if is_chatting then return 0;
 
 //устанавливаем флаг
 add_timer_event(self_obj,0,11);

 //передаём номера строк с добавленным нулём справа
 while (mstr(i)!="Error") do begin
   add_timer_event(self_obj,i-1,i*10+0);
   i+=1;
 end

 //добавляем событие снятия флага по окончанию разговора
 add_timer_event(self_obj,i,01);
end
------------------------------

Всё. Вообще, у timed_event/add_timer_event чрезвычайно мощный потенциал. Подумайте: ведь, зная указатель на объект, с помощью add_timer_event мы можем "приказать" ему что угодно - устанавливать/читать LVAR-ы, вызывать процедуры, проигрывать анимацию, начинать диалог, двигаться. Это - основа межобъектного взаимодействия. И, ИМХО, новая парадигма в Ф-скриптинге :-)

Есть ещё кое-что - экспрот/импорт процедур. Но об этом - в следующих сериях. Не переключайте канал :-)

(Отредактировал(а) Raven - 12:47 - 7 Дек., 2004)

Отправлено: 4:50 - 7 Дек., 2004
Alan Killenger
Пользователь

Откуда: Россия, Ижевск
Регистрация: Июль 2004

Всего: 404 сообщения

Быть может удобнее не добавлять с одного маха все таймерные события, а делать их вызов последовательным?
Цитата:

..
variable self_sing:=false;
..
procedure talk_p_proc begin
 if not(self_sing) then begin
   self_sing:=true;
   add_timer_event(self_obj,30,/*позиция песни*/);
 end
end
..
procedure timed_event_p_proc begin
 if self_sing then
   self_sing:=sing_song(fixed_param,/*задержка*/);
end
..
procedure sing_song(variable P_pos,variable P_delay) begin
 if /*нет конца песни*/ then begin
   float_msg(self_obj,mstr(P_pos),0);
   add_timer_event(self_obj,P_delay,P_pos+1);
   return true;
 end else
   return false;
end
..


Таким образом переменная self_sing автоматически примет значение false, когда песня(или *float диалог) будет закончена. Этот прием дает большие возможности в плане развития событий: например, если чуз убежал от криттера на 30 хексов разумно прекратить диалог; или если чуз повернулся к NPC спиной диалог пойдет иначе.

-----
hit me, nail me, make me god

Отправлено: 12:29 - 8 Дек., 2004
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

Да, так будет гораздо гибче. Я показываю самые простые решения, as-is, саму идею. Реализация в каждом конкретном случае за вами. Но вот такие предложения/дополнения, разумеется, приветствуются. А вторая часть с is_chatting флагом была добавлена ради озвучивания идеи об "упаковке" нескольких значений в вару. Знание этого пригодится при прочтении наработки о взаимодействии двух скриптов ч/з timed_event.

Отправлено: 15:07 - 8 Дек., 2004
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

Say-режим.

Что это вообще такое.

В Фоле есть набор команд, начинающихся с say, которые не описаны в API и не использованы в игре. Например:
saystart
saysend
sayoption
sayreply
и т.д.

После не слишком длительной целенаправленной возни, мне удалось с ними разобраться. Итак, что это такое. Это - режим организации диалога. да-да, такого же, что вызывается с помощью start_gdialog, но куда как более гибкого. А именно:
1. мы можем задавать координаты, размеры и бэкграунд reply- и option-окон (потенциально возможно разные диалоги - с компами, с людьми, с предметами - оформлять по разному)
2. мы можем задавать шрифт, цвет шрифта, подсветку при наведении, выравнивание, отступы
3. для организации простых диалоговых веток (без if-проверок, установок флагов и т.п.) не требуется создавать отдельные ноды
4. самое главное. Этот режим можно инициализировать в любой момент - во время боя, из инвентаря etc. После его использования инвентарь *обновляется*. Т.е. можно сделать, например, свою карту, которая при использовании из инвентаря вызовет say-режим, а по его окончании - исчезнет из инвентаря. Проблема, считаемая нерешимой.

Время при переходе в say-режим останавливается (как с инвентарём/диалогом). Минус - исчезает курсор :-( Обратно его можно вызвать переместив мышку на display-экранчик. Он сменится на скроллинг-курсор и появится обратно.

Как скриптится.

Есть два типа процедур - подготовительные и управляющие. К подоготовительным относятся, например:

sayreplywindow(x,y,width,height,"path/to/pcx_file.pcx");
Задаёт координаты, размер и картинку окна. Путь - относительно Data. Т.е., создаём data/pcx, кладём туда pcx-картинку и пишем путь в скрипте.

setfont(num);
num - номер шрифта (1-5 с пропусками, см. корень master.dat)

sayborder(x,y);
Отступы для выводимого в окно текста - по вертикали и горизонтали - с обеих сторон.

Ладно, пока хватит, потом раскажу об остальных.

Теперь про управляющие. Вначале надо перейти в режим командой saystart. После этого вплоть до saysend  
sayreply(str node_name, str text);
Второй параметр - выводимый текст (окно создаётся автоматом в соответствии с параметрами, заданными sayreplywindow).
Первый - это имя этого "нода". Потом его можно будет вызывать наравне с процедурами из sayoption.

sayoption(str text,str/proc node);
Выводит вариант ответа в option-окно (параметры задаются в sayoptionwindow, аналогично reply).
Внимание! Если ответ на фразу один, то option окно не создаётся и ответ не выводится. Для продолжения диалога при этом надо кликнуть на reply-окно. Причём, результат будет браться из того самого написанного, но не выводящегося на экран sayoption.
Первый - текст. Второй - имя нода из sayreply (в кавычках) *или* имя вызываемой процедуры (без кавычек).

Простой пример:
-----------------------------
procedure start begin end

procedure critter_p_proc begin
end

procedure talk_p_proc begin
sayreplywindow(20,10,200,100,"111.pcx");
sayoptionwindow(230,10,200,100,"111.pcx");
setfont(5);
sayborder(10,10);

saystart;
sayreply("NodeHi","Hello");
sayoption("Hi. How it goes?","NodeHow");
sayoption("Bye","NodeClose");
sayreply("NodeHow","Fine, thanks");
sayoption("Well, gotta get moving.","NodeClose");
sayoption("Bye","NodeClose");
sayreply("NodeClose","Bye");
sayoption("Bye","Empty");
sayend;
end
-----------------------------

Как видите, никаких предобъявлений, обилия процедур и т.п. Процедуры понадобятся, если захотите организовать выдачу вещей, установку вар и т.п. if-ы работают.

Компилируем, привязываем, смотрим. Можете пока pcx-ину прикрутить, поиграться с расположением окон, проверить работу с отдельными процедурами и т.п.

Остальное - завтра :-)

(Отредактировал(а) Raven - 23:29 - 14 Янв., 2005)

Отправлено: 15:41 - 14 Янв., 2005
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

Извините, обещанного продолжения не будет.

Отправлено: 12:07 - 15 Янв., 2005
Tehnokrat
Модератор

Откуда: Новосибирск
Регистрация: Окт. 2003

Всего: 489 сообщений

Это ещё в честь чего? Я между-прочим ждал. Может всё-таки напишешь кратенько, в формате доки WG?

-----
Прошлое можно узнать, но нельзя изменить. Будущее можно изменить, но нельзя узнать.

Отправлено: 23:48 - 15 Янв., 2005
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

>>Это ещё в честь чего?

Есть повод.

>>Я между-прочим ждал. Может всё-таки напишешь

Как накатаю - пошлю на мыло.

Отправлено: 0:36 - 16 Янв., 2005
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

Оказалось, что ограничение на число ГВАР -  миф. Всё, что мы допишем в vault13.gam, может быть использовано в игре.

Что думаете по этому поводу?

Отправлено: 16:16 - 20 Янв., 2005
YikxX
Пользователь

Откуда: NCR :)
Регистрация: Февр. 2004

Всего: 304 сообщения

Хм, я например всегда думал, что после резервных можно еще своих гвар кучу понаписать. И уж точно больше, чем мне будет нужно.

-----
Обломись! Я подложил туда носок...

Отправлено: 17:00 - 20 Янв., 2005
Raven
Пользователь

Откуда: Владик
Регистрация: Февр. 2004

Всего: 408 сообщений

Ну так правильно думал :-)  А я, помню, считал ограничение на число ГВАР потенциальной проблемой - и со мной могие соглашались :-) Вот так вот и рождаются мифы, блин :-)

Отправлено: 17:27 - 20 Янв., 2005
Tehnokrat
Модератор

Откуда: Новосибирск
Регистрация: Окт. 2003

Всего: 489 сообщений

Никогда не слышал про какие-либо ограничения на число GVAR.

-----
Прошлое можно узнать, но нельзя изменить. Будущее можно изменить, но нельзя узнать.

Отправлено: 0:06 - 21 Янв., 2005
binyan
Пользователь

Откуда: Israel
Регистрация: Март 2005

Всего: 292 сообщения

Я может конечно и археолог, но не могу , когда люди подолгу мучаются. Я вот например полчаса потратил на то, чтобы сообразить в чем дело. Смотрим первый пост:
Цитата:


//Начинаем искать от строки first
//Ищем либо закрывающий "тэг", либо первую пустую строку
while message_str(msg_num,i)!="END REPLICS" or message_str(msg_num,i)!="Error" do i+=1;
last:=i-1; //проверки на ошибку не делаем, т.к. список не может быть бесконечным, пустая строка всегда найдётся
//Закрывающий тэг нужен "для себя", как комментарий



Там где   )!="END REPLICS" or message_str должно быть AND вместо or, потому как пустая строка у нас не "Всегда найдётся". Проверено.




-----
...выражая озадаченность, граничащую с озабоченностью..

Отправлено: 21:43 - 17 Авг., 2006
Oleg1969
Пользователь

Откуда: Саратов
Регистрация: Май 2006

Всего: 168 сообщений

ЭЭЭЭ... А что, оскудела земля Русская? Тихо то как тута... Мож чем хозяева поделятся...

-----
За сим прощаюсь, Олег.

Отправлено: 21:00 - 24 Сент., 2006
Dweller
Пользователь

Откуда: Е-бург
Регистрация: Июль 2007

Всего: 29 сообщений

Как ни пробывал я избавиться от привязки скрипта к номеру, добился только вылета Фола. Путем экспериментов понял, что вылетает из-за строчки message_str(i,1000)!=obj_name(self_obj).  Помогите, кто разобрался!

-----
Не нужна мне эта подпись

Отправлено: 2:12 - 30 Июля, 2007
Wasteland Ghost
Маленькое Злое Привидение

Откуда: Россия, Самара
Регистрация: Дек. 2002

Всего: 2251 сообщение

Включи дебаг-режим. Патч с объяснениями есть в разделе загрузок. Сообщения из лог-файла запость сюда. Тогда и посмотрим.

У меня были такие проблемы со скриптом фиксита. Фол отказывается нормально грузить "чужой" для данного скрипта мсг большого размера с большими номерами строк. При малых размерах мсг всё нормально. При больших размерах мсг у меня грузился нормально при повторном обращении, но через некоторое время Фол слетал.

Отправлено: 12:13 - 2 Авг., 2007
Ray
Модератор

Откуда: Донецк,Украина
Регистрация: Янв. 2004

Всего: 746 сообщений

Цитата:
Фол отказывается нормально грузить "чужой" для данного скрипта мсг большого размера с большими номерами строк. При малых размерах мсг всё нормально. При больших размерах мсг у меня грузился нормально при повторном обращении, но через некоторое время Фол слетал


А можешь попробовать с этим изменением?

0x96F8A
оригинальное значение: 000F85A500
изменить: FF7400EB02

-----
Не бывает невозможных задач – бывает мало времени.

Отправлено: 21:32 - 2 Авг., 2007
Wasteland Ghost
Маленькое Злое Привидение

Откуда: Россия, Самара
Регистрация: Дек. 2002

Всего: 2251 сообщение

Попробовала. Ничего не изменилось:
Код:
 
Error loading message file text\english\dialog\test0.msg at offset 3d03.
Error loading script dialog message file!
ERROR: message_str: can't find message file: List: 2!
Error: No message file EXISTS!: index 2, line 500


Уменьшаю файл вдвое -- работает отлично.

Эх, плакал Фиксит с расширенной сортировкой и апгрейдами...

Отправлено: 11:10 - 9 Авг., 2007
Ray
Модератор

Откуда: Донецк,Украина
Регистрация: Янв. 2004

Всего: 746 сообщений

Отправь, пожалуйста, на мыло этот скрипт. Буду мудрить...

-----
Не бывает невозможных задач – бывает мало времени.

Отправлено: 11:51 - 9 Авг., 2007
 

Переход по темам
<< Пред. След. >>
Единственная страница этой темы


Powered by Ikonboard 2.1.9 RUS
Modified by RU.Board Team
© 2000 Ikonboard.com