|
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
|
|
|
|