Malandrinus 624 Опубликовано 23 Июня 2009 (изменено) 1. Кладём ролик в папку gamedata\textures\sleep\. Собственно, в любую папку, лишь бы она была в каталоге textures. Просто в sleep лежат ролики сна в оригинале.имя файла любое, формат ogm, а разрешение ролика вроде можно сделать любое. Я не стал заморачиваться и просто скопировал существующий ролик сна с монолитом.2. Озвучку ролика кладем по той-же схеме в папку в корневом каталогу gamedata\sounds\. У меня папка называется my_sleep. Имена файлов для левого и правого канала должны выглядеть такmy_sleep_sound_l.oggиmy_sleep_sound_r.oggдлина звуков может быть разной и вообще говоря не совпадать с длиной ролика.3. Правим файл \gamedata\config\ui\ui_movies.xml. Добавляем туда что-то в этом роде: <my_dream> <play_each_item>1</play_each_item> <global_wnd x="0" y="0" width="1024" height="768"> <auto_static x="0" y="0" width="1024" height="768" stretch="1"> <window_name>back</window_name> <texture>intro\intro_back</texture> </auto_static> </global_wnd> <item type="video"> <sound>my_sleep\my_sleep_sound</sound> <pause_state>on</pause_state> <function_on_stop>sleep_manager.stopper</function_on_stop> <video_wnd x="0" y="0" width="1024" height="768" stretch="1"> <texture x="0" y="1" width="512" height="286">sleep\dream_sarcofag</texture> </video_wnd> </item> </my_dream> Название тега my_dream - это моё имя для сна. Если я верно всё понимаю, то вся эта фигня описывает окно, в котором играется видеоролик.4. Теперь собственно добавляем сон. Правим файл gamedata\config\misc\dream.ltx.Там есть секции с именами [regular_dream#] где # - это номера снов. Сейчас там есть regular_dream1, regular_dream2 и regular_dream3. Добавляем секцию:[regular_dream4] dream = my_dream probability = 40 type = happyпараметры:dream - это ранее заданное имя сна (в нашем случае my_dream).probability - ясное дело, что нужно для вычисления вероятности появления именно этого сна. Но я пока не понял по какому алгоритму она вычисляется на основе этого параметра. Я просто сделал число побольше, дабы протестировать побыстрее.type - может быть nightmare, normal и happy. По названию вроде понятно, что это. Но на что влияет пока не ясно.5. Ну и завершающий этап. В этом же файле в секцию [dreams] к параметру regular добавляем к списку новый сон regular_dream4. Вроде такогоregular = regular_dream1, regular_dream2, regular_dream3, regular_dream4Тестируем... Опа! Новый сон. Поскольку я звуки взял случайно, то при этом в левом ухе у меня орет кот, а в правом играет мрачный эмбиент =)Ну и естественно работать будет только при установленном АМК. Что там именно сделано, для того чтобы сон вообще был, я пока не разбирался. У любого клиентского объекта имеется метод give_game_newsОн и выводит сообщения на экран. Экспериментом выяснил, что сообщение можно вывести совершенно от любого клиентского объекта, хоть от куска колбасы (хвала дизайнерскому гению компания-разработчик (признана нежелательной)*! Ну почему не сделать просто глобальную функцию?). Однако, следуя традиции и здравому смыслу, мы таки воспользуемся для этого объектом, соответствующим ГГ, т.е. актёром. local sactor = alife():actor() --получаем серверный объект актера local cactor = level.object_by_id(sactor.id) --по его ID получаем клиентский объект актера cactor:give_game_news( "Свободу тушканам!\\nТушканчики всех стран, объединяйтесь!", -- строка сообщение, может быть многострочной "ui\\ui_npc_monster", --текстура, из которой берется аватара "отправителя" Frect():set(0,215,163,105), --координаты и размеры вырезаемого из текстуры изображения math.random(1000, 3000), --начало показа сообщения с текущего момента (в миллисекундах) 5000 --длительность показа сообщения (в миллисекундах) )Доп. комментарии к аргументам give_game_news:имя текстуры - это имя файла DDS без расширения, адресуемого относительно папки \gamedata\textures\в этом выражении Frect():set(0,215,163,105)0,215 - это координаты верхнего левого угла нужного изображения163,105 - его размерыРазмеры и координаты могут быть любыми, но полученное изображение будет увеличено или уменьшено до размеров 83х47В данном случае изображение тушкана из файла ui_npc_monster.dds было уменьшено.Параметр времени начала показа нужен, очевидно, для внесения некоторой живости в игру. Дело в том, что код Lua получает управление только в определённые моменты, по определённым событиям. В такой момент можно сгенерить пачку сообщений и раскидать их по времени с помощью этого параметра, создав таким образом некоторую имитацию жизнедеятельности на ближайшее время. Думаю, таким же образом можно имитировать диалоги. Просто генерится пачка сообщений и им задаётся вывод с небольшим интервалом. Изменено 21 Сентября 2014 пользователем World_Stalker 1 5 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Поделиться этим сообщением Ссылка на сообщение
Malandrinus 624 Опубликовано 24 Июля 2009 (изменено) Все мы знаем, что движок ведёт себя, как капризная невеста, и вылетает от малейшего чиха. 1. Синтаксические ошибки в коде. Как правило, лог при этом содержит что-то вроде attempt to index global '<имя файла>' (a nil value) т.е. движку не удалось скомпилить модуль и его значение (модуля) равно нулю 2. Ошибки времени выполнения Lua. Лог обычно содержит указание ошибки модуль и строку, в которой ошибка произошла. Типа такого: LUA error: ....k.e.r. 1.0004\gamedata\scripts\<имя файла>.script:<номер строки>: attempt to index global 'npc' (a nil value) К типичным ошибка относятся попытки вызвать несуществующий метод класса (и вообще индексировать что-бы то ни было несуществующим ключом) или, к примеру, конкатенация строки с nil. 3. Ошибки времени выполнения самого игрового движка. Лога обычно нет, т.е. движок тихо слетает, сам в лог ничего не пишет, а забуферизированный до этого момента лог теряется. Иногда, правда, что-то в логе есть, но бывает это весьма редко. Ошибки движка вызваны неправильным использованием функций самого движка. Например, попытка заспавнить объект с несуществующей секцией. Кроме того скрипты выполняются с прекомпиляцией, поэтому для запуска изменённого скрипта надо как минимум перегрузить сохранёнку. Кстати, для справки. в отличие от скриптов файлы конфигураций подгружаются уже при старте программы ещё до загрузки сохранения. Посему для тестирования изменения в файлах конфигураций надо перезапускать программу. Довольно часто надо проверить некий фрагмент кода, заспавнить объект, проверить некую синтаксическую конструкцию и т.п. 1. Пишу скрипт и получаю вылет игры без лога, если влез в один из заранее прекомпилируемых файлов и сделал там тупую синтаксическую ошибку. 2. Выношу скрипт в отдельный файл и получаю вылет игры с до ужаса информативным сообщением в лог-файле "attempt to index global 'my_cool_script' (a nil value)". Это значит, что тупую синтаксическую ошибку я сделал в файле my_cool_script.script Можно до хрипоты говорить о пользе предварительной синтаксической проверки, но как часто мы её не делаем, не так ли? 3. Наконец-то умнею и проверяю скрипт на вшивость заранее. Запускаю игру, загружаю уровень. загружаю уровень... загружаю уровень...... Получаю вылет игры, лезу в лог и ищу в конце коротенькое сообщение, из которого следует, что надо поменять пару символов или добавить/стереть одну строчку. 4. Меняю скрипт, повторяю всё заново, скрипт отрабатывает, но делает не то, что мне надо. Чтобы изменения в скрипте вступили в силу, надо перегрузить уровень. так что повторяю пп.3 и 4, пока не получу то, что мне было нужно. Вот для частичного решения этой проблемы в своё время был сделан специальный тестовый полигон, который позволяет менять и вызывать определённый скрипт, не выходя из игры. Кроме того, часть ошибок (а именно ошибки Lua) блокируются и не приводят к вылетам. Полигон этот сэкономил мне кучу времени. [spoiler=Тестирование скриптов без обрушения и перезапуска игры]Тестирование скриптов без обрушения и перезапуска игры Ключевыми элементами являются две функции из стандартной библиотеки Lua: dofile - позволяет загрузить и выполнить как chunk внешний файл. pcall - позволяет выполнить произвольную функцию в т.н. защищённом режиме. Это значит, что любая ошибка интерпретатора Lua не выйдет за пределы функции, вызванной таким образом. Напомню, что обычно любая ошибка времени выполнения Lua передаётся хост-программе, т.е. игре, и вызывает её вылет. Ну и собственно рецепт пилюли следующий фрагмент может, скажем, выполняться по горячей клавише из главного меню. local res, err = pcall(my_proxy.start_chunk_from_file) get_console():execute(res and "Succesfull!" or string.gsub(err, " ", "_")) --имитация тернарного оператора pcall возвращает два значения. Первое - результат запуска. Если все прошло пучком, то true. Второе - добавочное значение с сообщением об ошибке, если ошибка была. Иначе nil. файл my_proxy.script содержит всего одну функцию function start_chunk_from_file() local chunk, err = dofile("..\\gamedata\\scripts\\test.lua") if err then get_console():execute("err="..tostring(string.gsub(err, " ", "_"))) end end dofile принимает имя файла со скриптом, загружает его и выполняет как функцию. Путём экспериментов я выяснил, что текущим каталогом Lua считает папку bin. Так что если хотим держать скрипт там, где и все остальные, то пишем путь относительно этой папки "..\\gamedata\\scripts\\test.lua". test.lua - это отлаживаемый скрипт. Обработка ошибок осуществляется аналогично pcall. Так можно не выходя из игры и даже не перегружая уровень копаться в скрипте test.lua и запускать его по много раз, существенно менее рискуя обрушить игру. Особенно удобно, если есть два компа и сетка. В этом случае даже Alt-Tab не нужен. Редактируешь файл по сети на одном компе, а запускаешь в загруженной игре на другом. Само-собой, если сделаем что-то, что фатально затронет игровую логику или что-то из ошибок категории 3, то обрушение может всё равно случиться. Сразу или в другом месте. В основном, в таких случаях уже ничего не поделаешь. Однако, довольно часто можно свести потенциальную ошибку времени выполнения движка к ошибке Lua. Например, если вызвать функцию get_console():execute(<аргумент>) с нулевым аргументом, то получим вылет без лога. Однако ничто не мешает сделать так: get_console():execute("value="..obj) В этом случае сначала будет выполняться конкатенация двух строк. Если obj будет равно nil, то получим ошибку Lua, а её тестовый полигон не пропустит, и обрушения игры не будет. И ещё. Это всё работает для ТЧ. Для ЧН мне наладить такой же полигон не удалось. Что-то там изменено в движке, что вызывает у него аллергию на функцию pcall. Изменено 24 Июля 2009 пользователем malandrinus 1 3 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Поделиться этим сообщением Ссылка на сообщение
Malandrinus 624 Опубликовано 7 Сентября 2009 Сразу оговорюсь, что вся информация ниже предназначена для ТЧ. В ЧН экспортировано пространство имён io и проблемы как-бы и нет вовсе. Имеются экспортированные в Lua функции log(string), error_log(string) и flush(). И эти функции не работают! По крайней мере никому до сих пор не удалось заставить их работать в релизной версии движка. Так что общепринятым способом является использование консоли. Известно, что всё, что пишется в консоль, пишется одновременно и в лог-файл. Файл этот находится в месте, определяемом переменной $logs$, которая в свою очередь находится в файле fsgame.ltx. Для управления консолью имеется класс CConsole. Экземпляр этого класса можно получить с помощью глобальной функции get_console(). В этом классе нет функций, которые позволили бы вывести на консоль (и соответственно в лог) произвольную строку. Однако имеется функция execute(string), которая предназначена для выполнения команд консоли. Разумеется, выполнить можно только те команды, которые поддерживает движок. Если команда не поддерживается, то в консоль выводится сообщение: ! Unknown command: <команда_которую_пытался_выполнить> Вот так и можно вывести некий текст. Пишем его вместо команды и он попадёт в консоль и в лог в составе сообщения об ошибке. Т.е. можно выводить сообщения так: get_console():execute(<моё_сообщение>) У метода есть недостатки. Первое, сообщение не должно быть командой. Обычно с этим не возникает проблем, но всё-таки следить надо. Второе, если в сообщении есть пробельные символы, то движок воспринимает как команду только последовательность символов до первого такого символа. Остальное - это как бы аргументы команды. В лог выведется только "команда", а остальное пропадёт. Есть два способа для решения этой проблемы. Первый состоит в замене всех пробелов на другой символ, например знак подчеркивания. Тогда команда вывода в лог будет выглядеть так: get_console():execute(string.gsub(<моё_сообщение>, " ", "_")) Функция string.gsub(str, ptrn, rep) заменяет в строке str все вхождения строки ptrn на строку rep. Второй способ решения проблемы заключается в использовании функции команды консоли load. Эта команда загружает сейв с определённым именем. Если такого нет, то в лог выводится ещё и имя не найденного сейва. get_console():execute("load ~~~ "..<сообщение>) Поскольку файла, начинающегося с "~~~" точно не существует, то это сработает всегда. Сообщение в логе в этом случае выглядит так: ! Cannot find saved game ~~~ моё сообщение При таком способе в сообщении могут быть пробелы и они отобразятся нормально. Кроме того, нет риска, что сообщение окажется командой консоли. Недостаток метода - лишнее обращение к жёсткому диску на предмет проверки существования сейва. Кроме того, этот метод аккуратно обходится только с пробелами. Прочие специальные символы конвертируются в пробелы. Полезно также упомянуть про команду консоли "flush", которая позволяет принудительно записать на диск всё, что на данный момент было выведено в консоль. Это может быть весьма полезно, поскольку при краше игры лог потеряется, если только не был записан этой командой. Так что вот такую строчку рекомендуется ставить перед потенциально опасными местами: get_console():execute("flush") В любом случае описанные методы нагружают сообщение лишними частями. Кроме того, в тот же лог выводятся и другие сообщения от движка. Хотя указанные методы чрезвычайно полезны, их недостатки иногда сильно мешают. Но есть и ещё один способ, который может компенсировать эти недостатки. Оказывается, можно перенаправить консольный вывод движка в текстовый файл. После этого в этот файл можно выводить что угодно функцией Lua print(). Описание этой функции можно посмотреть на сайте Lua. Скажу только, что в сочетании с функцией форматирования текста string.format() так можно вывести в файл совершенно произвольно оформленный текст. Для реализации метода можно запускать игру через батник примерно такого содержания: XR_3DA.exe -nointro >> log.txt У метода замечен только один недостаток. Нет никакой возможности принудительно записать файл на диск. Функция Flush осталась в неэкспортированном пространстве имён io. Хотя в отличие от консольного лога этот файл записывается на диск сам, но из-за буферизации часть его может таки пропасть в случае краша игры. При нормальном выходе из игры всё естественно запишется как надо. 1 3 Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Поделиться этим сообщением Ссылка на сообщение