В этой теме на конкретных примерах (Уроках) разбирается весь процесс создания мода.
Правила работы в теме: Любой пользователь обладающий опытом и знаниями в модостроение, создает урок по определенной теме (отражено в задаче урока), после этого описывает выполнение поставленной задачи в виде отдельного файла и (или) напрямую в своем посте спрятав его под спойлер. В дополнении к уроку желательно выложить содержимое папки gamedata с выполненным уроком, чтобы пользователь скачав его мог проверить правильность выполнения урока самостоятельно. Обсуждение уроков ведется в теме: "Общие вопросы модостроения". Также в данной теме могут быть размещены дополнительные материалы помогающие пользователю освоить все аспекты модостроения.
Будем пытаться не использовать специфические файлы, поэтому можно работать в чистом ЗП, SGM и т.д. **
Не рекомендуется размещать информацию автором которой вы не являетесь, но если она полезна для пользователей сайта указывайте автора. Не рекомендуется размещать информацию доступную на других ресурсах повещенных модостроению - пользователи и так смогут ее найти. Запрещено размещать непроверенную информацию. В любом случае модераторы раздела оставляют за собой право удалить пост с Уроком или Справочной информацией в случае нарушения автором правил данного форума или правил данной темы.
*В любом уроке, вопросы рассмотренные полно в предыдущем уроке, могут быть описаны весьма сжато, поэтому при возникновении вопросов, прежде всего посмотрите предыдущие. ** Если нет специальных пояснений то урок делался на чистом ЗП 1.6002, в противном случае указан мод и версия для которой урок предназначен.
[cut=Рекомендации по оформлению]1. Заголовок сообщения из которого понятно назначение поста (Урок, Справочная информация или другое) 2. Если это урок то он должен иметь четко обозначенную задачу и желательно иметь ссылку на файл с выполненным уроком, для того чтобы выполняющие его могли скачать и посмотреть выполнение урока на примере. 3. Придерживайтесь устоявшейся цветовой схемы: а) Путь к файлу и (или) имя файла - Зеленый б) Имя параметра, функции и т.п. в файле - Желтый в) Не рекомендуется использовать оранжевый - это цвет комментариев кураторов раздела и к тому же близок к цвету ссылок. 4. Куски кодов и конфигурационных файлов обязательно помещайте в контейнер
Код
Исходный текст файла
. 5. Выделение текста изменением размера шрифта, его типом на усмотрение автора, главное не перебарщивать.[/cut]
Тема предназначена только для публикации уроков и справочной информации, вопросы в другой теме! Флуд и оффтоп будет наказан без предупреждения, а пост удалён, что бы там не находилось.
Эти 0 пользователя(ей) поблагодарили tracker за это полезное сообщение:
Урок 56 (б) Задача: создание инвентарного комплекса (см. урок 56) в ТЧ и ЧН, способом добавления функции коллбека использования предметов из ЗП
Прежде всего я хотел бы поблагодарить моего друга и коллегу VlAleVas'а за то, что действительно помог мне разобраться в этих скриптах. А теперь к делу. Сегодня мы будем разбирать исключительно скрипты. Конфиги и прочее можете глянуть в прошлом уроке. Мне многие задавали вопрос, как перенести коллбек на использование предмета в ТЧ и ЧН. Причиной невозможности работы коллбека в этих частях является отсутствие в bind_stalker.script функции function actor_binder:use_inventory_item(obj). Напоминаю, для рабочего комплекса она должна выглядеть так:
Код
function actor_binder:use_inventory_item(obj) if(obj) then local s_obj = alife():object(obj:id()) if(s_obj) and (s_obj:section_name()=="drug_anabiotic") then xr_effects.disable_ui_only(db.actor, nil) level.add_cam_effector("camera_effects\\surge_02.anm", 10, false, "bind_stalker.anabiotic_callback") level.add_pp_effector("surge_fade.ppe", 11, false) give_info("anabiotic_in_process") _G.mus_vol = get_console():get_float("snd_volume_music") _G.amb_vol = get_console():get_float("snd_volume_eff") get_console():execute("snd_volume_music 0") get_console():execute("snd_volume_eff 0") end end if obj~=nil then my_callbacks.on_use_item(obj) --Отсылка к нашему скрипту с функцией on_use_item(obj) end end
Тогда давайте мы ее зарегистрируем и добавим. Все функции типа actor_binder зарегистрированы в другой функции в этом же файле. Называется function actor_binder:reinit(). В ЧН и ТЧ она такая:
Код
function actor_binder:reinit() object_binder.reinit(self)
2) Теперь где-нибудь пристроим саму функцию в файле. В этом случае переносить полностью функцию из ЗП не стоит. Там есть несколько лишних вещей. В ТЧ/ЧН функция actor_binder:use_inventory_item(obj) должна выглядеть следующим образом:
Код
function actor_binder:use_inventory_item(obj) if obj~=nil then my_callbacks.on_use_item(obj) end end
3) Все, осталось создать скрипт my_callbacks.script с коллбеком на использование предмета. Как это сделать опять же можно глянуть в прошлом уроке. Только не забываем, что в ТЧ и ЧН такие понятия, как "анабиотик" и "геркулес" отсутствуют.
Урок 61 Тема: Текстурирование.Создание золотистой текстуры оружия Требуемая утилита:Paint.Net
Перед началом я хотел бы сказать, что способ этот далеко не единственный в своем роде. Но для людей, начинающих разбирать текстурирование, самое то. Paint.Net, я считаю, будет самой удобной утилитой в данном случае. Начнем. 1. Открываем любую текстуру оружия. Я выбрал Форт-12, то есть текстура textures/wpn/wpn_fort.dds 2. Выделяем нужный нам участок, или всю текстуру, это уже по Вашему усмотрению. 3. Сверху в инструментах выбираем Эффекты --> Для фотографий --> Портретный. У нас есть три параметра: Смягчение, Освещенность, Теплота. В нашем случае нам важна как раз Теплота. Хотя остальные два параметра тоже можно использовать, они очень даже полезны во многих случаях. Я взял Теплоту на максимум. 4. Не снимая выделения, переходим к Коррекция --> Оттенок и насыщенность и редактируем Оттенок, Осветленность, Насыщенность так, как пожелаем. В нашем случае главная цель использование этого инструмента - сделать оттенок более желтым. Насыщенность я тоже немного увеличил для презентабельности. 5. По окончанию работы экспортим в DDS. По хорошему можно было бы сделать новый бамп и бамп#, но на статике роли не играет. И не факт, что будет играть на динамике.
Урок 62 Тема: Создание артефакта в Сталкер Зов Припяти (полный разбор) Ссылка на урок
Сообщение отредактировал FantomICW - Чт, 04.07.2013, 19:27
Эти 0 пользователя(ей) поблагодарили FantomICW за это полезное сообщение:
Урок 63 Задача: Исправление скрипта отвечающего за спавн сквадов на стартовых позициях в начале новой игры
Если вы добавляли локации при помощи программы от АМК для склейки геймграфа или при помощи SDK, то может возникнуть проблема с спавном сквадов на стартовых позициях (через конфиг-файл simulation.ltx). Для устранения этой проблемы нужно исправить скрипт sim_board.script функция sim_board:fill_start_position() на такой:
Код
--' Заполнение стартового расположения function sim_board:fill_start_position() if self.start_position_filled == true then return end self.start_position_filled = true for level in game_graph():levels() do local section_name = "start_position_" .. alife():level_name(level.id) if setting_ini:section_exist(section_name) then local n = setting_ini:line_count(section_name) if n~=0 then for i=0,n-1 do local result, id, value = setting_ini:r_line(section_name,i,"","") local smrt_names = utils.parse_names(value) for k,v in pairs(smrt_names) do local smart = self.smarts_by_names[v] if smart == nil then abort("Wrong smart name [%s] in start position", tostring(v)) end local squad = self:create_squad(smart, id) self:enter_smart(squad, smart.id) end end end end end end
1. Скопировать мод-источник в отдельную папку (чтобы не запароть исходный)
2. Распаковть в папку spawns архив с universal acdc.
3. Первое что нужно сделать, настроить программу на распознание неизвестных ей секций, для этого декомпилируем all.spawn коммандой:
Код
universal_acdc.pl -d all.spawn -out all
и внимательно смотрим лог, там будут сообщения типа "unknown class for section stalker_outfit_anom", что значит что программа не знает к какому классу объектов отнести объект с именем секции stalker_outfit_anom. Из редми к программе мне стало известно, что нужно в файл clsids.ini добавить секции путем добавления строк, одна беда как я ни пытался программа не видела внесенные изменения в этот файл (если у кого получиться - сообщите как удалось). Поэтому я пошел другим путем - прямое редактирование скрипта stkutils\scan.pm, там после строки:
Код
'zone_mine_steam_weak' => 'ZS_MBALD',
я добавил:
Код
'stalker_outfit_anom' => 'E_STLK',
Почему E_STLK? Потому что объект с именем секции stalker_outfit_anom вероятнее всего бронежилет, а он имеет идентификатор clsid равный E_STLK, что соответствует классу cse_alife_item_custom_outfit. Если после добавления строки декомпиляция не проходит до конца и вылет отличается от unknown class for section, то вероятнее всего класс определен не верно и нужно пытаться выбрать другой. Итак определяя классы недостающих секций добираемся до момента когда all.spawn будет полностью декомпилирован.
4. Итак секции настроены пора приступить к разбивке all.spawn на группу файлов level.spawn (спавн объекты) и level.game (точки путей), это делается командой:
Тут вероятна ситуация как у меня, файлы появились при этом level.game сформированы нормально (еще бы формат точек путей не менялся со времен древних билдов), а вот при формировании level.spawn программа вылетела с ошибкой. Что делать? Вероятно формат all.spawn не совсем подходит universal acdc, но он же его декомпилировал! Вот и выход: в третьем шаге файл all.spawn был полностью декомпилирован, вот теперь мы его скомпилируем снова командой:
На этот раз разбивка на файлы прошла без ошибок и можно заменить файлы в папке gamedata\levels\<имя уровня> на полученные файлы level.spawn и level.game.
5. Декомпилируем локацию программой converter.exe (тут подробно не останавливаюсь благо есть много инструкций), но возможна опять "назадача" и программа при декомпиляции уровня споткнется как раз на файле level.spawn! Что-же остается? Лечить!
6. Если level.spawn не подошел декомпилятору converter.exe, то попробуем получить его другим путем, благо universal acdc предоставляет последнюю возможность. Переименуем файл alife_<имя локации>.ltx в level_spawn.ltx и запустим компиляцию файла level.spawn из файла level_spawn.ltx командой:
Код
universal_acdc.pl -compile all -l
Полученный файл level.spawn поместим в папку уровня и снова пробуем декомпиляцию уровня в формат SDK, на этот раз все проходит успешно (по крайней мере у меня).
7. Открываем нашу локацию в SDK (надеюсь без вылетов) и видим все спавн-элементы и пути теперь доступны для редактирования в более удобной форме.
"Он ловко выхватил из-под себя табуретку и очень метко и сильно бросил ее в докладчика..." (Чугунный всадник)
Эти 0 пользователя(ей) поблагодарили denis2000 за это полезное сообщение:
Урок 65 Тема: Перенос объектов из архивов СДК в игровой формат, жесткая привязка к локации (объект динамический, но никуда не будет улетать от выстрела). Необходимы утилиты:Milkshape 1.8.4 и выше с плагинами Сталкера, SDK 0.7 Модель объекта (готовая, в OGF)
Итак, сейчас будем рассматривать достаточно взаимосвязанные темы. Бывает, просматривая библиотеку объектов СДК видим очень неплохие модели, которые ну просто хотелось бы вставить игру через спавн (как динамический объект). При этом модель должна оставаться неподфижной (жеткая привязка). Хочу напомнить, что у всех динамических объектов (деревянные ящики, компы, кастрюли, огнетушители, вертолет, стулья) есть как минимум одна кость. Вообщем-то, именно через кость модель привязывается к локации. А в большинстве объектов формата .object из rawdata кость отсутствует. В целом наша задача - добавить модели кость, настроить заново в СДК, заспванить через all.spawn. Кто читал урок 40 и подобные, для вас я сильно нового не открою. 1. Для начала нужно определимся, какую модель переносим. Я выбрал rawdata/objects/statics/army_base/prop_army_base_16a.object. 2. Теперь загружаем его в Actor Editor и просто эскопртируем в OGF. 3. Импортируем эту модель в Milkshape 1.8.4. 4. Теперь нужно добавить кость. В панели инструментов выбираем инструмент Joint. И ставим джоинт где-то примерно посредине ящика. Рекомендую использовать поле вида сверху. . 5. Переходим во вкладку Joints, выделяем наш новый сустав. Ниже правее кнопки Rename высветится название. Переименуем на link и нажмем на Rename. 6. Выделяем всю модель (удобней через вкладку Groups), возвращаемся ко вкладке Joints. Выбираем сустав link и нажимаем кнопку Assign, то есть "привязать". 7. Экспортируем модель. Загружаем ее в SDK AE. Дальше нужно ее настроить. 8. Настраиваем. Тут уже кому как, зависит от предмета и цели. Если же вы редактируете тот же объект, что и я, то настройки ниже.
во вкладке Surfaces редактируем шейдеры текстуры (prop_dangerbox_S): [table][tr][td]Название параметра[/td][td]Значение[/td][/tr][tr][td]Shader[/td][td]def_shaders/def_vertex[/td][/tr][tr][td]Compile[/td][td]default[/td][/tr][tr][td]Game Mtl[/td][td]default_object[/td][/tr][/table]
во вкладке Bones в параметре Generate Shape ставим All, там же в link выставляем Type на Box, а Game Mtl на objects/metal_box
9. Экспортим в OGF. 10. Осталось только добавить в файл alife_локация.ltx из all.spawn секцию нашего объекта:
Код
[6467] ; cse_abstract properties section_name = physic_object name = pri_test_army_box position = 291.035,0.675,225.117 direction = 0, 0, 0 ; cse_alife_object properties game_vertex_id = 681 level_vertex_id = 475497 object_flags = 0xffffff7a
; cse_ph_skeleton properties ; cse_alife_object_physic properties physic_type = 0x3 mass = 10 fixed_bones = link
Обычная секция с известными параметрами. Путь к визуалу новой модели указан в параметре visual_name. Но главное - это параметр fixed_bones. Он означает кости, которые фиксирует локация. У нас в значении стоит кость нашей модели - link. На этом все. Компилируем, тестируем.
Сообщение отредактировал FantomICW - Ср, 28.08.2013, 01:20
Эти 0 пользователя(ей) поблагодарили FantomICW за это полезное сообщение:
Урок 66 (автор NIV) Платформа: "Зов Припяти" Задача: Создание квеста на поиск перехода на другую локацию. Переход будет доступен после активации по сюжету. Установка меток на квестовую область и место перехода. Использование окна подтверждения (GUI) для подтверждения перехода или возврата на текущую локацию (как это было в ТЧ и ЧН). Сложность: выше средней. Необходимые навыки и инструменты: разбор all.spawn (ACDC); прописать координаты ГГ на экран в моде "Повелитель Зоны". Представлять, как работает логика рестрикторов.
Создание нового перехода с локации на локацию является архиважной задачей для сюжетного модостроителя. К сожалению, в "ЗП" процесс перехода осуществлялся путём использованием гидов, и у Актора не было возможности самому дотопать до перехода и нажать заветную кнопку "Перейти на другую локацию? Да!", как это было в предыдущих частях. Давайте исправим этот досадный пробел. P.S. В SGM это всё сделано, однако мне оказалось проще написать самому, чем пробовать вытащить оттуда.
Пункт первый. (К поставленной задаче слабо относящийся, но тем не менее, очень важный). [cut]Давайте заспаним Актора на Юпитере. Распаковываем all.spawn, в файле alife_zaton.ltx меняем координаты в секции [982] (section_name = actor) в ДВУХ (!) местах на те, которые нам нужны. Кстати, любые изменения в all.spawn (создание рестрикторов, левел-чейнджеров, точек путей и др.) требуют новой игры!!! Пусть ГГ у нас стартует около моста близ цементного завода. Для того, чтобы он появился действительно там, а не на Янове, есть несколько методов. Лично я ковырял файлы configs\scripts\jupiter\ jup_b47_logic.ltx и zat_b215_logic_jupiter.ltx. Что именно там изменено, можно посмотреть, сравнив файлы из моего архива с оригинальными. Кстати, для Припяти надо ковырять файлы configs\scripts\pripyat\ pri_a15_sr_cutscene.ltx и pri_a15_sr_exit.ltx. Итак, новая игра, и ГГ у нас появляется на Юпитере.[/cut] Пункт второй. Выдача квеста. [cut]Пусть ГГ получит задачу: "Найти переход на Затон. Переход искать в районе разрушенного моста". Задачу можно выдать из диалога, из логики рестриктора, либо из любой функции, как вам будет удобно. Открываем configs\misc\tm_jupiter.ltxи пишем туда описание квеста:
Код
[jup_find_level_changer_to_zaton_quest] ;уникальное имя квеста icon = ui_inGame2_Put_v_pripyat ;иконка квеста. Описание в configs\ui\textures_descr\ui_actor_newsmanager_icons.xml. Сама текстура - в textures\ui\ui_actor_newsmanager_icons.dds prior = 1 ; приоритет? storyline = true ;тип метки для задания, но без использования поля target метка не ставится (а его у нас здесь нет) title = jup_find_level_changer_to_zaton_quest_name ;заголовок квеста - пишем в configs\text\rus\st_quests_niv_jupiter.xml descr = jup_find_level_changer_to_zaton_quest_text ;описание квеста там же condlist_0 = {+jup_level_changer_to_zaton_finded} complete ;условия для выполнения: засчитается при выдаче инфопоршня jup_level_changer_to_zaton_finded
Сам квест в данном примере выдадим из логики рестриктора, но об это чуть по-позже. Вернёмся к метке на задание. В "ЗП" метки ставились конкретно на объект (target = obg_sid) в виде маленьких кружков, цвет которых зависел от параметра storyline = true / false. В то же время, если у нас задача "Поискать в какой-то зоне", то надо ставить большую серую круглую метку, как это было в "ТЧ". Поэтому придётся сделать, как было там. А именно, восстановим колл-бэк на инфопоршни в файле scripts\ bind_stalker.script. В пустую функцию actor_binder:info_callback дописываем:
Код
mapspots.process_info_portion(info_id)
Эта функция будет ставить и удалять метки по выданным инфопоршням. Создаём файл scripts\mapspots.script, в нём пишем функцию process_info_portion(info_id), которой в качестве параметра будут передаваться все инфопоршни, а она уже будет думать, что с ними делать. Сама функция состоит из двух других (потом можно добавить функции для других локаций):
Код
function process_info_portion(info_id) if db.actor == nil then return end -- перечислим функции, которые обрабатываем add_niv_level_changer_spot() -- на переходы jupiter_process_info_portion(info_id) -- на события на Юпитере end
Из событий на Юпитере у нас пока только поставить метку на область поисков при выдаче задания и снять её при выполнении задания.
Код
-- функция для обработки инфопоршней на метки на Юпитере function jupiter_process_info_portion(info_id)
local jup_d8_bridge_id_id = get_story_object_id("jup_d8_bridge_id") --определяем id объекта по story_id
if info_id == "jup_strelok_hiring_end" then level.map_add_object_spot_ser(jup_d8_bridge_id_id, "crlc_small", "jup_d8_bridge_map_spot_text") --ставим метку на мост на d8, параметры: id, тип метки (кружок), текст метки. elseif info_id == "jup_level_changer_to_zaton_finded" then --удаляем метку с моста на d8 level.map_remove_object_spot(jup_d8_bridge_id_id, "crlc_small") return false end end
ВАЖНО: поставить метку можно только на объект, обладающий story_id![/cut] Пункт третий. Создадим объект (спейс-рестриктор), на который мы будем ставить метку.[cut] Найдём в распакованном спауне файл alife_jupiter.ltx, а в нем - любую секцию рестриктора (section_name = space_restrictor). Скопируем её в конец файла и присвоим номер [6122] (для чистого ЗП only!). Выйдем на мост, получим координаты и впишем их в эту секцию (position, game_vertex_id, level_vertex_id), придумаем уникальное имя (name = jup_d8_bridge_sr) и, самое главное, пропишем кастом_дату:
Код
custom_data = <<END [story_object] story_id = jup_d8_bridge_id ; вот где story_id пишется! [logic] cfg = scripts\jupiter\jup_d8_bridge_sr.ltx ; путь к логике относительно папки configs END
Осталось еще указать форму и размер рестриктора, например сферу с радиусом 4 м.
Т.е. для обнаружения перехода на другую локацию Актор должен зайти внутрь рестриктора.[/cut] Пункт четвертый. Логика рестриктора. [cut]Просто зайти в рестриктор - это тривиально. Пусть ГГ ищет переход... глядя в бинокль, благо с моста смотреть удобно! И тогда через 30 игровых секунд переход сам найдётся! Для этого пишем логику в файле configs\scripts\jupiter\jup_d8_bridge_sr.ltx
Код
[logic] active = sr_idle@control
[sr_idle@control] on_info = {-jup_strelok_hiring_end} %+jup_strelok_hiring_end =give_task(jup_find_level_changer_to_zaton_quest)% ;проверяем, есть ли инфо jup_strelok_hiring_end. Если нет - то выдаем его и задачу jup_find_level_changer_to_zaton_quest. ;обращаем внимание, что из логики функции вызываются немного по другому. Так, из обычной функции выдача квеста происходила бы так: task_manager.get_task_manager():give_task("jup_find_level_changer_to_zaton_quest ")
on_actor_inside = {+jup_strelok_hiring_end =look_binocular} sr_idle@message ;помним, что функции для проверки условий {=функция} должны находиться в файле scripts\xr_conditions.script ; в условии {} сначала проверяем наличие инфопоршня - вдруг по сюжету его еще не давали. Тогда сколько ни гляди в бинокль - ничего не найдёшь. Если вы считаете по-другому - уберите этот инфопоршень отсюда.
[sr_idle@message] on_game_timer = 30|sr_idle@nil %+jup_level_changer_to_zaton_finded =new_way_finded_message +jup_level_changer_to_zaton_activated =give_task(jup_go_to_zaton_quest)% ;помним, что функции, выполняемые из логики, должны находиться в файле scripts\xr_effects.script. ; В этой секции через 30 игровых секунд выдаём инфо jup_level_changer_to_zaton_finded по которому задание засчитывается и метка с рестриктора снимается. ; только в качестве примера сразу же выдадим разрешение на активацию телепорта jup_level_changer_to_zaton_activated и квест "Попасть на Затон" (jup_go_to_zaton_quest). По сюжету это инфо и задание у меня даются намного позднее. А без инфо переход не сработает.
[sr_idle@nil] ;выключаем рестриктор
Ищем переход в бинокль (на самом деле проверяем, что активное оружие - бинокль):
Код
function look_binocular () local active_item = db.actor:active_item()
if active_item ~= nil then local section_name = active_item:section() if section_name == "wpn_binoc" then return true end end return false
end
Говорим сами с собой (или по радио) про обнаруженный переход (у меня ГГ любит поговорить с умным и уважаемым человеком!):
Код
function new_way_finded_message () local news_caption = game.translate_string("st_tip") local news_text = "new_way_finded_message_text" local texture = "ui_iconsTotal_yan_scientist_job" db.actor:give_game_news(news_caption, news_text, texture, 0, 15*1000) end
[/cut] Пункт пятый. Рестриктор, управляющий переходом. [cut]Итак, переход найден. И это опять спейс-рестриктор! Пусть его секция будет [6123], name = jup_d9_lev_ch_zaton_sr, story_id = jup_d9_lev_ch_zaton_sr_id, cfg = scripts\jupiter\jup_d9_lev_ch_zaton_sr.ltx Сперва поставим на него метку. Помните, в mapspots.process_info_portion(info_id) была функция add_niv_level_changer_spot()? Напишем так:
Код
-- список инфопоршней для открытия переходов и имена левел-чейнджеров
--ставим метку на левел-чейнджер при обнаружении его
function add_niv_level_changer_spot() local i for i = 1,#niv_level_changer_table do if db.actor:has_info(niv_level_changer_table[i][1]) then if db.actor:dont_has_info(niv_level_changer_table[i][2]) then
local obj_id = get_story_object_id(niv_level_changer_table[i][3]) level.map_add_object_spot_ser(obj_id, niv_level_changer_table[i][4], niv_level_changer_table[i][5])
db.actor:give_info_portion(niv_level_changer_table[i][2]) end end end
end
ИНТЕРЕСНО: знак # в #niv_level_changer_table возвращает число строк в массиве, т.о., дописывая потом данные в массив, мы можем не менять ничего в тексте функции!
Метки на переход (level_changer_up_right, level_changer_down_left и др.) находятся в файлах configs\ui\map_spots.xml и map_spots_16.xml Для того, чтобы на мини-карте отображалась метка перехода, необходимо слегка отредактировать все метки: дописать внутрь тег mini_map и указать для него такой же spot, как и для level_map.
Итак, место перехода отмечено - идём туда. Как работает рестриктор? Пусть в нём появляется окно "Перейти на другую локацию?" Если "ДА" - то идет телепорт в ЛевелЧейнджер (точка actor_go_to_zaton_walk:actor_go_to_zaton_look), расположенный в недоступном месте (за границей локации, например). Если "НЕТ" - то Актора телепортирует немного назад и разворачивает на 180 градусов (точка actor_doesnt_go_to_zaton_walk:actor_doesnt_go_to_zaton_look). Эта же точка будет точкой выхода при обратном переходе Затон - Юпитер. А может рано ещё уходить с этой локации? Нет проблем - пишем такую логику в configs\scripts\jupiter\jup_d9_lev_ch_zaton_sr.ltx:
Код
[logic] active = sr_idle@control
[sr_idle@control] on_info = {+actor_go_from_zaton_to_jupiter =actor_on_level(jupiter)} %-actor_go_from_zaton_to_jupiter =teleport_actor(actor_doesnt_go_to_zaton_walk:actor_doesnt_go_to_zaton_look)% ;телепортирует актора при возвращении с Затона и забирает инфо actor_go_from_zaton_to_jupiter
on_actor_inside = {+jup_level_changer_to_zaton_finded -jup_level_changer_to_zaton_activated} sr_idle@inside_lc_na %=you_cant_leave_locat_message%, {+jup_level_changer_to_zaton_activated} sr_idle@teleport %+level_changer_need_choise%, sr_idle@inside_lc_na ;условия выполняются слева направо: 1) если нашли переход, но не активировали (нет разрешения) - выдаём сообщение you_cant_leave_locat_message и переходим в пустую секцию sr_idle@inside_lc_na (чтобы сообщение выдавалось только 1 раз). 2) если переход активирован - идем в секцию телепорта и выдаем инфо level_changer_need_choise. 3) Если вдруг в начале игры мы зашли в это место и еще не знаем, что здесь переход, то ничего и не происходит.
[sr_idle@teleport] on_info = {+level_changer_choise_yes} sr_idle@control %+actor_go_from_jupiter_to_zaton -level_changer_need_choise -level_changer_choise_yes =run_postprocess(black:6127:true) =forward_game_time(4) =teleport_actor(actor_go_to_zaton_walk:actor_go_to_zaton_look)% ;если выбран переход на Затон - выдаем инфо actor_go_from_jupiter_to_zaton, обнуляем инфо level_changer_need_choise и level_changer_choise_yes, затемняем экран, телепортируем ГГ в левел-чейнджер и переводим время вперед на 4 часа.
on_info2 = {+level_changer_choise_no} sr_idle@control %-level_changer_need_choise -level_changer_choise_no =teleport_actor(actor_doesnt_go_to_zaton_walk:actor_doesnt_go_to_zaton_look)% ;аналогично сбрасываем инфо и телепортируем ГГ обратно на локацию.
Точки actor_go_to_zaton_walk : actor_go_to_zaton_look, actor_doesnt_go_to_zaton_walk : actor_doesnt_go_to_zaton_look необходимо добавить в спаун (way_jupiter.ltx) как-то так:
Важно, чтобы у точек look, соответствующим точкам walk совпадали флаги: flags = 0x1.
Помните, всё начиналось с того, чтобы сделать окно подтверждения перехода, как в ТЧ? Так вот, заставить нормально работать его из логики рестриктора мне не удалось. Поэтому, запускать его мы будем в другом месте, окно будет выдавать инфопоршни level_changer_choise_yes или level_changer_choise_no, по которым мы и будем управлять переходом.[/cut] Пункт шестой. GUI или окно управления. [cut]Графический интерфейс пользователя (GUI) - это любое окно или меню на экране, куда можно тыкать мышью, например и выбирать - переходить на другую локацию, или нет. Основы создания GUI приведены в КМБ, урок 36 (Автор: XOBAH), подробности смотрите там. Всё, что нам надо - создать две кнопки "Да" и "Нет". За GUI у нас будет отвечать файл scripts\ ui_level_changer.script. Интересный факт: несмотря на то, что окна выбора в ЗП нет, форма для него осталась: \configs\ui\ message_box.xml и message_box_16.xml, элемент <message_box_change_level>. Её и будем использовать, осталось только увеличить <message_text> и поставить font="graffiti32". Пропишем в scripts\ ui_level_changer.script следующие колл-бэки и функции, они и будут выдавать управляющие инфопоршни level_changer_choise_no и level_changer_choise_yes:
Код
function ui_level_changer:InitCallBacks_no() self:AddCallback("button_no", ui_events.BUTTON_CLICKED, self.OnButton_no_clicked, self) end
function ui_level_changer:OnButton_no_clicked() --;возврат на этот же уровень = точка выхода self:HideDialog() db.actor:give_info_portion("level_changer_choise_no") end
function ui_level_changer:InitCallBacks_yes() self:AddCallback("button_yes", ui_events.BUTTON_CLICKED, self.OnButton_yes_clicked, self) end
function ui_level_changer:OnButton_yes_clicked() --;переход на другой уровень = точка выхода self:HideDialog() db.actor:give_info_portion("level_changer_choise_yes") end
function level_changer_choise(info_id) if info_id == "level_changer_need_choise" then run_gui(this.ui_level_changer()) end end
Помните, в логике рестриктора выдавалось инфо level_changer_need_choise? Возвращаемся к колл-бэку на инфопоршни в файле scripts\ bind_stalker.script. В функцию actor_binder:info_callback(npc, info_id) дописываем ui_level_changer.level_changer_choise(info_id). Таким образом, мы и будем запускать GUI (run_gui(this.ui_level_changer())). Осталось только определить эту глобальную функцию в файле scripts\_g.script
Код
function run_gui(gui, close_inv) if close_inv == true then gui:ShowDialog(true) game_hide_menu() level.show_weapon(false) else gui:ShowDialog(true) end end
[/cut] Пункт седьмой. Левел-Чейнджер. [cut]В оригинале "ЗП" левел-чейнджеры располагались где-то на недоступных простым смертным крышах. Мы поставим его просто за границей локации. Для снятия координат попадаем туда при помощи "Повелителя Зоны". Копируем в спауне (alife_jupiter.ltx) секцию левел-чейнджера (section_name = level_changer), присваиваем ей следующий за нашей последней секцией номер. ВАЖНО - имя левел-чейнджера должно оканчиваться на _level_changer, например jup_to_zaton_level_changer! Прописываем ему координаты на Юпитере, а также выходные координаты на Затоне (dest_game_vertex_id, dest_level_vertex_id, dest_position, dest_direction, dest_level_name = zaton). dest_graph_point можно не указывать. Несомненно, параметр dest_direction влияет на поворот Актора, но мне не удалось его удовлетворительно подобрать. Именно для того, чтобы после появления на уровне развернуть ГГ правильно и применяется телепорт в секции[sr_idle@control] управляющего рестриктора. [/cut]
Пункт восьмой. Всё остальное. [cut]Осталось создать теперь уже на Затоне левел-чейнджер и управляющий рестриктор - в принципе, всё то же самое. На Затоне нет телепортирующих рестрикторов, однако жизнь портит рестриктор с логикой configs\scripts\zaton\zat_a1_logic.ltx. Вымарываем там половину, оставляем следующее:
[sr_idle@nil] ;При попадании на Затон в первый раз засчитывается задание "Попасть на Затон" и сразу выдаются инфопоршни на обнаружение и активацию рестриктора на обратный путь. Если вы любите поизмываться над ГГ и игроками - не давайте их здесь, а заставьте выполнить "пару заданий". Остальные инфо - сюжетные из оригинала.
Modmaker's Notes™ Тема заметки: Логика НПС, стреляющего в другого персонажа Требуемая утилита:Notepad ++ (обычный блокнот тоже пойдет, но файл подготавливался именно для синтаксиса Notepad ++) [cut=Немного пояснений ]Новая серия моих модостроительских работ. Уроками их назвать сложно, тем не менее, они могут оказаться вполне полезными в некоторых случаях. Скриптовые заметки буду выкладывать в файлах с расширением .lua, а конфиговые - в .properties. [/cut] Ссылка на справочный материал Modmaker's Notes™ Тема заметки: Скриптовые методы спавна трупа Требуемая утилита:Notepad ++ (обычный блокнот тоже пойдет, но файл подготавливался именно для синтаксиса Notepad ++) Ссылка на справочный материал
Сообщение отредактировал FantomICW - Ср, 18.09.2013, 20:52
Эти 0 пользователя(ей) поблагодарили FantomICW за это полезное сообщение:
Урок 67 (автор NIV) Платформа: "Зов Припяти" Задача: Ограничение времени на выполнение квеста и вывод таймера с остатком времени на экран. Сложность: не очень сложно. Необходимые навыки и инструменты: Notepad++
Введение: Помните, в "Тенях Чернобыля" были квесты, ограниченные по времени, иногда порядком портившие людям нервы? Возможно, из-за этого в "ЗП" отсутствует ограничение по времени на выполнение квестов. Тем не менее, иногда это ограничение может быть оправданно по сюжету и мне захотелось его восстановить. Простого решения, как добавить такую запись в ПДА, я не нашёл, и решил вместо этого выводить на экран таймер, на котором будет идти обратный отсчёт времени. Таймер должен продолжать идти при переходе с локации на локацию, правильно отражая перемотку времени, производимое в игре (переходы по локации и между ними, сон). Примечание: Если такой квест происходит у вас на одной локации, то можно обойтись штатными средствами (рестриктор с логикой [sr_timer] ). При этом, если вы уходите с локации, таймер исчезает, а когда снова заходите - таймер продолжает идти, как это было в Солянке на ЧАЭС. Но для наших целей придётся делать по-другому.
Пункт первый. Выдача задания. [cut]Всё стандартно - выдадим задание (configs\misc\ tm_zaton.ltx) из логики рестриктора configs\scripts\zaton\ zat_a1_logic.ltx при старте новой игры.[/cut]
Пункт второй. Таймер. [cut]Создаём файл scripts\ niv_timer.script и в нём создаём класс "niv_quest_timer", у которого будут следующие методы: save, load, start(info_id), и timer_update(), который включает в себя функции update_timer() и update_hud(). Одновременно редактируем файл scripts\ bind_stalker.script, как указано ниже.
start(info_id). [cut]
Код
function niv_quest_timer:start(info_id) if info_id == "time_limited_quest_started" then niv_quest_timer_active=true -- засекается стартовое игровое время self.timer_start_value = game.get_game_time() end end
Таймер запускается при получении инфопоршня "time_limited_quest_started" через колл-бэк на инфопрошни в файле bind_stalker.script
Код
function actor_binder:info_callback(npc, info_id) printf("*INFO*: npc='%s' id='%s'", npc:name(), info_id) niv_timer.niv_quest_timer:start(info_id) end
[/cut] timer_update() [cut]
Код
function niv_quest_timer:timer_update() niv_quest_timer:update_timer() niv_quest_timer:update_hud() end
update_timer() - основная функция. [cut]Чтобы изменить время, отводимое на квест, задайте другое значение niv_quest_timer_limit_timer_value в секундах, у меня оно равняется 36 часам.
Код
function niv_quest_timer:update_timer() -- время в секундах , 36*60*60 local niv_quest_timer_limit_timer_value = 129600 --проверка на то, что квест начат, не выполнен, время не кончилось, и квест не провален по какой-то другой причине if db.actor:has_info("time_limited_quest_started") and db.actor:dont_has_info("time_limited_quest_done") and db.actor:dont_has_info("time_is_up") and db.actor:dont_has_info("time_limited_quest_failed") then if get_hud():GetCustomStatic("hud_timer") == nil then niv_quest_timer_active=true --если квест действителен, а худа по какой-то причине нет, открываем его niv_quest_timer:update_hud() end --вычитаем из лимита времени разность текущего игрового времени и запомненного начального времени. Получаем значение таймера в игровых секундах local niv_quest_timer_value = niv_quest_timer_limit_timer_value - game.get_game_time():diffSec(self.timer_start_value) . --переводим игровые секунды в часы и формируем строку вывода local hours = math.floor(niv_quest_timer_value/3600) local minutes = math.floor(niv_quest_timer_value/60 - hours*60) local seconds = math.floor(niv_quest_timer_value/1 - hours*3600 - minutes*60) niv_quest_timer_value_string = tostring(hours).." h "..time2str(minutes).." min"
-- по достижению таймером нуля, выдаём инфо об окончании времени if niv_quest_timer_value <=0 then db.actor:give_info_portion("time_is_up") end --вывод сообщений при окончании времени (xr_effects.time_is_up()) или провале задания (xr_effects.task_failed()) elseif db.actor:has_info("time_is_up") and db.actor:dont_has_info("message_time_is_up") then niv_quest_timer_active=false niv_quest_timer:update_hud() xr_effects.time_is_up() db.actor:give_info_portion("message_time_is_up") elseif db.actor:has_info("time_limited_quest_failed") and db.actor:dont_has_info("message_time_limited_quest_failed") then niv_quest_timer_active=false niv_quest_timer:update_hud() xr_effects.task_failed() db.actor:give_info_portion("message_time_limited_quest_failed") end end
Апдейт таймера запускается в апдейте actor_binder:update(delta) в файле bind_stalker.script:
Код
niv_timer.niv_quest_timer:timer_update()
[/cut][/cut] [/cut] Пункт третий. Самое главное. [cut]Чтобы таймер работал, как часы (гм, какой оборот речи, однако!), необходимо запомнить время старта. Это достигается следующей модификацией bind_stalker.script : 1) Добавляем менеджер таймера в: function actor_binder:__init (obj) super(obj)
2) Сохраняем переменную: function actor_binder:save(packet)
Код
self.niv_quest_timer_manager:save(packet)
3) Считываем её же: function actor_binder:load(reader)
Код
self.niv_quest_timer_manager:load(reader)
4) Сами методы save и load несложные:
Код
function niv_quest_timer:save(packet) utils.w_CTime(packet, self.timer_start_value) end
function niv_quest_timer:load(packet) self.timer_start_value = utils.r_CTime(packet) end
В каком формате сохраняется время, возвращаемое функцией game.get_game_time() можно посмотреть в файле utils.script, функция w_CTime.[/cut]
Пункт четвёртый. Худ. [cut]Вызывается на экран функциями типа
Код
function niv_quest_timer:hud_utils() if niv_quest_timer_active==true then add_hud("hud_timer",niv_quest_timer_value_string) add_hud("hud_timer_text") else niv_quest_timer:hud_release() end end
function niv_quest_timer:hud_release() release_hud("hud_timer") release_hud("hud_timer_text") end
Функции add_hud, release_hud и некоторые другие взяты из SGM (за что его автору выражается огромная благодарность) и находятся в файле _g.script. В процессе также были немного отредактированы описания "hud_timer" и "hud_timer_text" из файла configs\ui\ui_custom_msgs.xml[/cut]
На этом обустройство таймера можно считать выполненным. Если вы обнаружите условия, при которых таймер сбивается или не работает, просьба писать автору. Да пребудет с Вами Сила выполнять задания в срок!
При работе над данным материалом автору помогали ценными мыслями и советами участники форума denis2000 и FantomICW.
Modmaker's Notes™ Тема заметки: Скрываем метку НПС на карте (как отряд Петрухи в ТЧ) Требуемая утилита:Notepad ++ для красивой подсветки синтаксиса Ссылка на справочный материал Урок 68 (Ретро-серия) Тема: Текстурирование | Создаем насечки на текстуре оружия Автор: BigBabay Ссылка на PDF Modmaker's Notes™ Тема заметки: Выдаем рандомную награду ГГ Требуемая утилита:Notepad ++ для красивой подсветки синтаксиса Ссылка на справочный материал
Сообщение отредактировал FantomICW - Вс, 15.12.2013, 13:59
Эти 0 пользователя(ей) поблагодарили FantomICW за это полезное сообщение:
Урок 69 (Автор strelok200) Тема: GUI Итак, уважаемые, прежде чем приступить к этому уроку я вам советую прочесть статью от FantomICW, которая поможет вам научиться работать с текстурами GUI, правильно размещать соответствующие элементы Ну а если вы уже ознакомились, то давайте приступим к созданию окошка с новым элементом:шкала В игре примерно выглядит вот так.
Сегодня мы разберем как создается такой элемент Возьмем соответственно две основные текстуры http://yadi.sk/d/4jwtk_aAEYH3e Рассматривать размещение элементов, координаты, коллбеки я не буду, перейду сразу к скриптовой части. Цель следующая: ГГ должен выставить ползунок шкалы в определенном месте и нажать кнопку. В том случае, если ГГ выставил ползунок "верно" и нажал на кнопку, то выдадим ему инфопоршень. Для начала определим, ту зону, в которой мы будем считать, что ползунок выставлен верно и соответственно код будет следующим
Код
----------------Прибор радиста-------------------------------------------------- class "radist_pribor" (CUIScriptWnd) --Регистрация нового класса
function radist_pribor:__init() super() self:InitControls()--Иницилизация класса. Ссылка к функции self:InitCallBacks()--Иницилизация класса. Ссылка к функции end
function radist_pribor:__finalize() --ф-ция завершения работы нового класса end
function radist_pribor:InitControls() --функция, отвечающая за элементы окна local xml = CScriptXmlInit() self:SetWndRect(Frect():set(0,0,1024,768)) --установка размеров файла xml:ParseFile("radist_pribor.xml") --парсим ui файл self.Element1 = xml:InitStatic("Element1", self) --регистрация первого элемента с текстурой self.Element2 = xml:InitStatic("Element1:Element2", self.Element1) --регистрация второго элемента с текстурой self.shkala = xml:InitTrackBar("Element1:Element2:shkala", self.Element2) --регистрация шкалы self:Register(xml:Init3tButton("Element1:button1", self.Element1), "button1") --регистрация кнопки
end
function radist_pribor:InitCallBacks() --ф-ции колбеков self:AddCallback("button1", ui_events.BUTTON_CLICKED, self.button1_button_clicked, self) --коллбек кнопки end
function radist_pribor:trackbar2_GetValue() --шкала. Получаем определенное значение return math.floor(0 + (self.shkala:GetFValue() * (100 - 0))) --возвращаем это значение end
function radist_pribor:button1_button_clicked() --ф-ция, при нажатии на кнопку local text = "Значение = "..tostring( self.shkala:GetFValue() ) --выведем на экран значение переменной news_manager.send_tip(db.actor,text,0,"ui_inGame2_Dolg_2",1000,nil,"sender") self:HideDialog() --скрыть окно end
function radist_gui(folder) folder:ShowDialog(true) end
Теперь заходим в игру, выставляем ползунок как хотим, нажимаем кнопку. На экран будет выведено значение переменной self.shkala:GetFValue() Запоминаем это число, затем выставляем ползунок в другом месте, на экране увидим новое значение. У меня грубо говоря получилось значение 0.4 и 0.5 Теперь сделаем проверку: если значение переменной self.shkala:GetFValue() больше 0.4 и меньше 0.5, то выдадим инфопоршень. Код будет следующим
Код
----------------Прибор радиста-------------------------------------------------- class "radist_pribor" (CUIScriptWnd) --Регистрация нового класса
function radist_pribor:__init() super() self:InitControls()--Иницилизация класса. Ссылка к функции self:InitCallBacks()--Иницилизация класса. Ссылка к функции end
function radist_pribor:__finalize() --ф-ция завершения работы нового класса end
function radist_pribor:InitControls() --функция, отвечающая за элементы окна local xml = CScriptXmlInit() self:SetWndRect(Frect():set(0,0,1024,768)) --установка размеров файла xml:ParseFile("radist_pribor.xml") --парсим ui файл self.Element1 = xml:InitStatic("Element1", self) --регистрация первого элемента с текстурой self.Element2 = xml:InitStatic("Element1:Element2", self.Element1) --регистрация второго элемента с текстурой self.shkala = xml:InitTrackBar("Element1:Element2:shkala", self.Element2) --регистрация шкалы self:Register(xml:Init3tButton("Element1:button1", self.Element1), "button1") --регистрация кнопки
end
function radist_pribor:InitCallBacks() --ф-ции колбеков self:AddCallback("button1", ui_events.BUTTON_CLICKED, self.button1_button_clicked, self) --коллбек кнопки end
function radist_pribor:trackbar2_GetValue() --шкала. Получаем определенное значение return math.floor(0 + (self.shkala:GetFValue() * (100 - 0))) --возвращаем это значение end
function radist_pribor:button1_button_clicked() --ф-ция, при нажатии на кнопку if self.shkala:GetFValue()>=0.4 and self.shkala:GetFValue()<=0.5 then --если значение переменной больше 0.4 и меньше 0.5, то db.actor:give_info_portion("actor_gui_info") --выдадим ГГ инфопоршень self:HideDialog() end end
function radist_gui(folder) folder:ShowDialog(true) end
Содержание файла radist_pribor.xml (подробно рассматривать не буду)
Всех с наступающим! Modmaker's Notes™ Тема заметки: Скриптовое действие на позиции НПС (вычисляем позицию НПС в реальном времени) Требуемая утилита:Notepad ++ для красивой подсветки синтаксиса Ссылка на справочный материал
Добавлено (23.01.2014, 19:18) --------------------------------------------- Урок 70 Тема: GUI | Создания инвентарного Gui-комплекса + основы Gui Тема-статья на ап-про
Урок №72 (Автор: Я то бишь Сахар) Тема: 3D моделирование | "Maya: вандализм или проламывание стены." Требующиеся программы: Autodesk Maya (от 8.5 до 2014) плагин для импорта/экспорта геометрий. [cut] [cut=Для новичков] [/cut] [cut=«Лофтим» эскиз дыры ]Для начала нам нужно создать кривую для этого нам нужно перейти на полку Curves. На этой полке выбираем инструмент Pencil Curve Tool. Выбрав данный инструмент курсор изменится со стрелочки на карандаш. После нужно зажать клавишу x (привязка к сетки) нарисовать кривую окружность. Дальше нам нужно создать копию данной кривой для этого выделяем нашу линию и жмём Ctrl + D (если нечего не произошло, то проверьте раскладку клавиатуры, а так же посмотрите, не включен ли у вас Caps Lock). После чего выделяем одну из кривой и переключаемся на инструмент move (или клавиша W). Дальше с помощь появившегося контролера тянем одну из кривых наверх Теперь переходим к самому интересному, создадим из этих двух кривых полигонную модель. Для этого выделяем обе кривые и жмём F4 (переключаемся на инструменты для работы со сплайтами и кривыми) после выбираем в панели главного меню (там где file, edit и т.д) кнопку Surfaces у нас «выкатится меню» и рядом с надписью Loft жмём на кубик. У нас появится окно под название Loft Options устанавливаем параметры как на скрииншоте который приведён ниже. И жмём на кнопку "Loft" после чего у нас должно получится что-то вроде этого. [/cut] [cut=Само проламывание стены.]Теперь нам надо подогнать и отмасштабировать получившийся эскиз. Ну и наконец, кульминация создаём отверстие для этого жмём на F3 после выделим нашу модель (в моём случай это забор) после наш эскиз. И применим к ним команду Mesh --> Booleans --> Difference (на название команды, а не на кубик.) В итоге мы получаем красивый пролом в стене, останется только положить на него X-Ray шейдер и затекстурировать. [/cut] [/cut]
Добавлено (13.12.2014, 05:10) --------------------------------------------- Урок №73 (Автор: Сахар) Тема: 3D моделирование | "Maya: " Эксперименты над людьми или перенос модели из Сталкер Чень Чернобыля в Сталкер Чистое Небо и Сталкер Зов Припять без потери «весов»." Требующиеся программы: Autodesk Maya (от 8.5 до 2014) плагин для импорта/экспорта геометрий и X-Ray SDK 0.7 или X-Ray SDK 0.6/0.5. [cut=Часть первая подготовка модели] Для начала нам нужно импортировать (никоем случи не открыть!) модель которую мы будем переносить. (думаю мне не надо подробно разъяснять как экспортировать модель.) Теперь нам нужно её подготовить для жмём на две кнопки, которые показаны на скриншоте ниже. Теперь нам нужно удалить старый character для этого нам нужно нажать на главной пенели window --> Outliner или так смотрим скриншот. Теперь жмём на «синего человечка» (если он у вас не появился, то нужно в окне Outliner – ра нажать Show --> Show All) и нажимаем клавишу Delete. Всем модель подготовлена. [/cut]
[cut=Создаём новые кости] Теперь нам надо создать и добавить к скелету две недостающие кости для этого переходим на полку Animation и там выбираем инструмент Joint Tool Вместо курсора у нас появится прицел теперь зажав клавишу Х (привязка к сетке) жмем на сетку между ног нашей модели примерно так. Теперь отпускаем клавишу Х и зажимаем V (привязка к вершинам в нашем случай к джоинтам (суставам) поэтому мы и отключили меш в самом начале) Теперь нам осталось соединить два скелета для этого выделяем кость bip01_pelvis и joint1(выбираем первую кость зажимаем Сtrl и жмём на вторую кость. Именно так если сначала мы выберем joint1 а только потом bip01_pelvis то в СДК у нас будет вылет! ) после чего выполняем команду connect Joint Кстати кости удобнее выбирать в окне Outliner. Теперь переименовываем кость joint1 в root_stalker а joint2 в bip01 для этого в окне Outliner – ра дважды жмем на надпись joint1 после вводим нужное название и нажимаем интер (именно интер иначе новое имя не сохранится) Теперь опять выделяем кость bip01_pelvis (всё в том же Outliner -ре) зажимаем колёсико мыши и тянем на кость bip01 в итоге у нас кость bip01_pelvis должна пропасть а слева от кости bip01 появится +. Все иерархия скелета восстановлена. Всё модель перенесена осталось её толь экспорировать. [/cut] [cut=Скрин Дока] Как видно я тут с бампом накосяцил [/cut]
Сообщение отредактировал Сахар - Вс, 14.12.2014, 05:41
Эти 0 пользователя(ей) поблагодарили Сахар за это полезное сообщение:
Урок №74 (Автор: makdm) Тема: Создание вертолёта через скрипт
[cut=Урок №74]Первое: Создаём "болванку" скрипта в файле xr_effects.script Пусть функция будет называться create_helicopter В качестве аргумента передаём секцию пути, указанную в all.spawn, на которой будет создан вертолёт, эта же секция пути будет использована в логике вертолёта, как точка, над которой вертолёт будет "висеть"
Пишем:
function create_helicopter( actor, obj, p ) if p and p[ 1 ] then --проверяем, что секция пути в функцию передана --проверяем, есть ли такая секция в all.spawn if not level.patrol_path_exists( p[ 1 ] ) then --если нет, то вылетаем на рабочий стол abort("Path %s doesnt exist. Function 'create_helicopter'", tostring( p[ 1 ] ) ) end local ptr = patrol( p[ 1 ] ) local position = ptr:point( 0 ) -- получаем позицию ( вектор ) точки спавна local lv_id = ptr:level_vertex_id( 0 ) -- получаем левел вертекс точки спавна local gv_id = ptr:game_vertex_id( 0 ) -- получаем гейм вертекс точки спавна local section = "helicopter" -- секцию берём из gamedata\configs\creatures\helicopter.ltx --создаём серверный объект вертолёта local s_obj = alife():create( section, position, lv_id, gv_id ) end end
Теперь подготовим логику вертолёта. Пусть вертолёт будет просто висеть на месте и нос вертолёта всегда будет направлен в сторону игрока. Запретим вертолёту стрелять ракетами и стрелять из пулемёта.
Теперь добавим логику в нашу функцию Наша функция теперь будет выглядеть так
function create_helicopter( actor, obj, p ) if p and p[ 1 ] then --проверяем, что секция пути в функцию передана if not level.patrol_path_exists( p[ 1 ] ) then --проверяем есть ли такая секция в all.spawn --если нет, то вылетаем на рабочий стол abort("Path %s doesnt exist. Function 'create_helicopter'", tostring( p[ 1 ] ) ) end local logic_helicopter = "[logic]\n".. "active = heli_move@idle\n".. "[heli_move@idle]\n".. "path_move = "..tostring( p[ 1 ] ).."\n".. "path_look = actor\n".. "invulnerable = true\n".. "max_velocity = 50\n".. "enemy = nil\n".. "immortal = true\n".. "use_rocket = false\n".. "use_mgun = false\n".. "upd_vis = 3\n".. "engine_sound = true\n" local ptr = patrol( p[ 1 ] ) local position = ptr:point( 0 ) -- получаем позицию ( вектор ) точки спавна local lv_id = ptr:level_vertex_id( 0 ) -- получаем левел вертекс точки спавна local gv_id = ptr:game_vertex_id( 0 ) -- получаем гейм вертекс точки спавна local section = "helicopter" -- секцию берём из gamedata\configs\creatures\helicopter.ltx local s_obj = alife():create( section, position, lv_id, gv_id ) --создаём серверный объект вертолёта end end
Если всё оставить, как есть, то при вызове функции получим вылет на рабочий стол. Это связано с тем, что для "правильного" спавна вертолёта не хватает исходных данных.
Эти данные необходимо добавить в STATE часть нет-пакета вертолёта. Для этого сначала мы прочитаем нет-пакет вертолёта. Делается это так:
local packet = net_packet() --берём пустой нет-пакет packet:w_begin( 0 ) -- указатель на начало записи s_obj:STATE_Write( packet ) -- пишем STATE часть нет-пакета вертолёта в наш пустой нет-пакет packet:r_seek( 2 ) -- начинаем читать наш пакет local game_vertex_id = packet:r_u16() local distance = packet:r_float() local direct_control = packet:r_s32() local level_vertex_id = packet:r_s32() local object_flags = packet:r_s32() local custom_data = packet:r_stringZ() local story_id = packet:r_s32() local spawn_story_id = packet:r_s32() local visual_name = packet:r_stringZ() local visual_flags = packet:r_u8() local motion_name = packet:r_stringZ() local skeleton_name = packet:r_stringZ() local skeleton_flags = packet:r_u8() local source_id = packet:r_u16() local startup_animation = packet:r_stringZ() local engine_sound = packet:r_stringZ()
Добавим этот код в нашу функцию Получим
function create_helicopter( actor, obj, p ) if p and p[ 1 ] then --проверяем, что секция пути в функцию передана if not level.patrol_path_exists( p[ 1 ] ) then --проверяем есть ли такая секция в all.spawn --если нет, то вылетаем на рабочий стол abort("Path %s doesnt exist. Function 'create_helicopter'", tostring( p[ 1 ] ) ) end local logic_helicopter = "[logic]\n".. "active = heli_move@idle\n".. "[heli_move@idle]\n".. "path_move = "..tostring( p[ 1 ] ).."\n".. "path_look = actor\n".. "invulnerable = true\n".. "max_velocity = 50\n".. "enemy = nil\n".. "immortal = true\n".. "use_rocket = false\n".. "use_mgun = false\n".. "upd_vis = 3\n".. "engine_sound = true\n" local ptr = patrol( p[ 1 ] ) local position = ptr:point( 0 ) -- получаем позицию ( вектор ) точки спавна local lv_id = ptr:level_vertex_id( 0 ) -- получаем левел вертекс точки спавна local gv_id = ptr:game_vertex_id( 0 ) -- получаем гейм вертекс точки спавна local section = "helicopter" -- секцию берём из gamedata\configs\creatures\helicopter.ltx local s_obj = alife():create( section, position, lv_id, gv_id ) --создаём серверный объект вертолёта local packet = net_packet() --берём пустой нет-пакет packet:w_begin( 0 ) -- начинаем запись с нулевого байта s_obj:STATE_Write( packet ) -- пишем STATE часть нет-пакета вертолёта в наш пустой нет-пакет packet:r_seek( 2 ) -- начинаем читать наш пакет local game_vertex_id = packet:r_u16() local distance = packet:r_float() local direct_control = packet:r_s32() local level_vertex_id = packet:r_s32() local object_flags = packet:r_s32() local custom_data = packet:r_stringZ() local story_id = packet:r_s32() local spawn_story_id = packet:r_s32() local visual_name = packet:r_stringZ() local visual_flags = packet:r_u8() local motion_name = packet:r_stringZ() local skeleton_name = packet:r_stringZ() local skeleton_flags = packet:r_u8() local source_id = packet:r_u16() local startup_animation = packet:r_stringZ() local engine_sound = packet:r_stringZ() end end
Теперь нам нужно изменить некоторые значения, хранящиеся в нет пакете, чтобы наш вертолёт "заработал". Добавляем логику, параметры движения и анимации, звук, визуал вертолёта.
Мы использовали логику logic_helicopter, которая сразу записана в нашей функции. Если мы хотим "вынести" нашу логику в отдельный файл, то в нашей функции нужно прописать путь к файлу, в котором будет записана логика нашего вертолёта. Например логика находится в файле logic_helicopter.ltx, тогда в функцию пишем путь до файла.
local logic_helicopter = "[logic]\ncfg = scripts\\zaton\\logic_helicopter.ltx"
Добавим наши изменения в функцию и запишем их в нет-пакет вертолёта
function create_helicopter( actor, obj, p ) if p and p[ 1 ] then --проверяем, что секция пути в функцию передана if not level.patrol_path_exists( p[ 1 ] ) then --проверяем есть ли такая секция в all.spawn --если нет, то вылетаем на рабочий стол abort("Path %s doesnt exist. Function 'create_helicopter'", tostring( p[ 1 ] ) ) end local logic_helicopter = "[logic]\n".. "active = heli_move@idle\n".. "[heli_move@idle]\n".. "path_move = "..tostring( p[ 1 ] ).."\n".. "path_look = actor\n".. "invulnerable = true\n".. "max_velocity = 50\n".. "enemy = nil\n".. "immortal = true\n".. "use_rocket = false\n".. "use_mgun = false\n".. "upd_vis = 3\n".. "engine_sound = true\n" local ptr = patrol( p[ 1 ] ) local position = ptr:point( 0 ) -- получаем позицию ( вектор ) точки спавна local lv_id = ptr:level_vertex_id( 0 ) -- получаем левел вертекс точки спавна local gv_id = ptr:game_vertex_id( 0 ) -- получаем гейм вертекс точки спавна local section = "helicopter" -- секцию берём из gamedata\configs\creatures\helicopter.ltx local s_obj = alife():create( section, position, lv_id, gv_id ) --создаём серверный объект вертолёта local packet = net_packet() --берём пустой нет-пакет packet:w_begin( 0 ) -- указатель записи -- пишем STATE часть нет-пакета вертолёта в наш пустой нет-пакет s_obj:STATE_Write( packet ) packet:r_seek( 2 ) -- начинаем читать наш пакет local game_vertex_id = packet:r_u16() local distance = packet:r_float() local direct_control = packet:r_s32() local level_vertex_id = packet:r_s32() local object_flags = packet:r_s32() local custom_data = packet:r_stringZ() local story_id = packet:r_s32() local spawn_story_id = packet:r_s32() local visual_name = packet:r_stringZ() local visual_flags = packet:r_u8() local motion_name = packet:r_stringZ() local skeleton_name = packet:r_stringZ() local skeleton_flags = packet:r_u8() local source_id = packet:r_u16() local startup_animation = packet:r_stringZ() local engine_sound = packet:r_stringZ() -- меняем параметры, прочитанные из нет-пакета custom_data = logic_helicopter motion_name = "idle" skeleton_name = "idle" startup_animation = "idle" visual_name = "dynamics\\vehicles\\mi24\\veh_mi24_u_01" engine_sound = "vehicles\\helicopter\\helicopter" -- начинаем писать в наш пакет изменённые параметры packet:w_begin( 0 ) packet:w_u16( game_vertex_id ) packet:w_float( distance ) packet:w_s32( direct_control ) packet:w_s32( level_vertex_id ) packet:w_s32( object_flags ) packet:w_stringZ( custom_data ) packet:w_s32( story_id ) packet:w_s32( spawn_story_id ) packet:w_stringZ( visual_name ) packet:w_u8( visual_flags ) packet:w_stringZ( motion_name ) packet:w_stringZ( skeleton_name ) packet:w_u8( skeleton_flags ) packet:w_u16( source_id ) packet:w_stringZ( startup_animation ) packet:w_stringZ( engine_sound ) packet:r_seek( 2 ) -- пишем STATE часть из нашего нет-пакета в нет-пакет вертолёта s_obj:STATE_Read( packet, packet:w_tell() ) end end
Теперь мы хотим увидеть наш вертолёт в игре. Сделаем это для оригинально ЗП. Создадим в корневой папке игры папку gamedata В папке gamedata создадим две папки configs и scripts В папке configs создадим папку scripts, а в ней папку zaton Из распакованных ресурсов игры возьмём файл xr_effects.script и поместим его в папку gamedata\scripts В сам файл в самый его конец запишем нашу функцию. Теперь возьмём файл gamedata\configs\scripts\zaton\zat_a1_logic.ltx из ресурсов игры и поместим его в нашу папку. Путь тот же gamedata\configs\scripts\zaton\ Откроем файл zat_a1_logic.ltx и вместо строчки on_info = sr_idle@timer %+zat_a1_game_start% напишем on_info = sr_idle@timer %+zat_a1_game_start =create_helicopter(zat_sim_15_spawn_for_a1)%
Мы добавили выполнение нашей функции в логику рестриктора, которая работает при старте игры. В качестве точки спавна мы использовали точку спавна сталкеров, которые появляются в самом начале игры и идут навстречу игроку. Запускаем ЗП, начинаем новую игру и, если сделали всё правильно, то слышим звук вертолёта, а, пробежав, метров 100 вперёд уже видим его.[/cut]
Добавлено (07.03.2015, 22:31) --------------------------------------------- Урок №75 (Автор: makdm) Тема: Вызов различных функций или визуальных эффектов при использовании бустеров без использования проверок на секцию ( if_then_elseif ).
Написано для платформы Зов Припяти.
[cut=Урок № 75]Задача: сделать, чтобы при использовании предмета срабатывал колбэк, которой жёстко привязан к этому предмету. В качестве предметов, к которым привяжем колбэк возьмём три аптечки. Секции аптечек: [medkit], [medkit_army], [medkit_scientic]
В файл bind_stalker.script в метод function actor_binder:use_inventory_item(obj) пишем
xr_s.use_inventory_item( obj:section() ) -- здесь мы вызываем колбэк
Теперь открываем файл xr_s.script и в таблицу callbacks допишем следующее
Теперь добавляем две функции регистрирующие колбэк и удаляющие колбэк.
function reg_use_inv_item_callback( name, sect, func, userobj ) callbacks[name][sect][func]={userobj=userobj} end
function unreg_use_inv_item_callback( name, sect, func ) callbacks[name][sect][func]=nil end
И сама функция, которая обрабатывает колбэк.
function use_inventory_item( section ) if callbacks.use_inventory_item[section] == nil then return end for func,o in pairs( callbacks.use_inventory_item[section] ) do func( section, o.userobj ) end end
Пусть наши функции-колбэки хранятся в файле use_item_callbacks.script Добавим наш файл в функцию init() function init() init_module_if_exists("use_item_callbacks") end
Теперь переходим к файлу use_item_callbacks.script В нём пишем функцию регистации колбэка и саму функцию, которая будет выполнена при вызове
function medkit( section ) -- здесь мы обрабатываем событие - использование аптечки end function medkit_army( section ) -- здесь мы обрабатываем событие - использование армейской аптечки end function medkit_scientic( section ) -- здесь мы обрабатываем событие - использование научной аптечки end
В этот файл по аналогии можно впихнуть хоть все бустеры в игре и прописать их по аналогии в xr_s.script в таблицу callbacks, тогда при использовании каждого бустера будет вызываться функция , которая жёстко привязана к этому предмету.
Всё тоже можно сделать и для on_drop_item и для on_take_item
Как видите никаких проверок по секциям if_then_elseif не проводится. Секция предмета - это название функции, которая сразу же вызывается при использовании бустера.[/cut] Терпение...... И все получится!
Сообщение отредактировал makdm - Сб, 07.03.2015, 22:57
Эти 0 пользователя(ей) поблагодарили makdm за это полезное сообщение:
Урок №78 (автор _SkIf_ (!SkIF!)) Тема: создаем новую иконку для квеста. Что нужно: Paint.net, любой текстовый редактор. [cut=Что делаем] 1. Откроем в Paint.net файл ui_actor_newsmanager_icons.dds, он лежит по пути gamedata\textures\ui Там видим все иконки заданий и достижений, которые используются в игре [cut=скрин][/cut] Под иконкой "Особого заказа" рисуем свою иконку [cut=скрин][/cut] Далее выделяем иконку с помощью "выделить прямоугольную область [cut=скрин][/cut] И в самом низу будут координаты и размер иконки. Далее сохраняем файл и идем дальше. 2. В файле ui_actor_newsmanager_icons.xml по пути gamedata\configs\ui\textures_descr в самом низу, под <texture id="ui_inGame2_Osobiy_zakaz" x="332" y="799" width="83" height="47" /> , но выше </file> </w> пишем <texture id="ui_inGame2_Metall_box" x="332" y="846" width="82" height="49" /> Теперь разберем ui_inGame2_Metall_box - ваше имя иконки, потом его и нужно будет писать в icon квеста. x="332" y="846" width="83" height="48" - это координаты, которые я взял в красную рамку в Paint.net 3. В файле tm_локация по пути gamedata\configs\misc в секции любого квеста ставим свою иконку, я взял секцию квест на исследование места падения Ската-2 [zat_b100_heli_2_crash] icon = ui_inGame2_Metall_box - новая иконка prior = 104 storyline = true title = zat_b100_heli_2_crash_name descr = zat_b100_heli_2_crash_text target = zat_b100_heli_2 condlist_0 = {+zat_b100_heli_2_searched} complete, {+pri_b305_actor_wondered_done} complete Запускаем игру и смотрим. Удачи в моддинге! [/cut]
Повторяю второй раз:вставь изображения в виде превью.И изучи пункты правил 4.10, 4.13.наговицын
Сообщение отредактировал _SkIf_ - Пт, 28.08.2015, 22:24
Эти 0 пользователя(ей) поблагодарили _SkIf_ за это полезное сообщение:
В дополнении к уроку №31.. Урок №79 Тема: Диалоги | Создание диалога, в котором персонаж, с которым начинаем диалог, будет говорить уникальную фразу(Вроде Полковника Ковальского, который всегда говорит "Слушаю, майор")
Да кстати, прежде чем тыкать на спойлер, ознакомьтесь с уроком №31. Под спойлером вы не увидите ссылки на требуемые файлы, или расположение изменяемых строк. В 31 уроке всё подробно расписанно. [cut=Тык]Начнем с того, что у нас есть персонаж без привязанных к нему стартовых диалогов(2 start_dialog в профиле нпс карается движковым вылетом). Мы хотим, чтобы при разговоре с ним, стартовой фразой он говорил что-то уникальное, вместо генерирующихся фраз. Рассмотрим пример от мною созданного персонажа:
Итак. Как мы видим, первая кость нашего диалога не имеет текста, как и вторая. Почему? Так устроен движок, и мы не в силах изменять его правила построения диалогов. Дальше идёт 3 кость нашего диалога, и уже в ней есть наша реплика. Реплику придумывайте какую хотите, но самое главное, что она будет появляться всегда(если, конечно же, у Вашего персонажа нет actor_dialog, в котором первым говорит нпс). Продолжаем. Мы создали, и сохранили наш диалог. Что же дальше? Этот диалог у нас является стартовым, который нам скажет персонаж, с которым мы будем говорить. Собственно, из-за этого нам придётся в профиле созданного НПС, добавить две строки:
Код
<start_dialog>capitol_leader_hello</start_dialog> -- Сам диалог, который мы добавили. <actor_dialog>actor_break_dialog</actor_dialog> -- Блокировка рандомных фраз
Вот и всё. Заходим в игру и проверяем результат. :)
Напоминаю, что данный урок происходит для бездиалогового персонажа! Есть куча персонажей, у которых actor_dialog начинается с разговора не ГГ, а НПС. В этом случае, созданная нами фраза будет игнорироваться движком до тех пор, пока есть actor_dialog(Ну, или его часть. В зависимости от разветвления данного диалога), заставляющий говорить первым НПС.
В СГМ имеются дистанционные заряды, которые можно устанавливать в разных местах. Затем подрывать с помощью пульта. У меня возникло желание сделать подобное в чистом Зове Припяти. Полного подобия добиться не удалось. Но результат оказался не хуже.
Необходимо уметь распаковывать ресурсы Зова Припяти.
Будем создавать закладываемый заряд взрывчатки с возможностью установки таймера задержки взрыва от 1 до 10 секунд. С возможностью подрыва 3-х зарядов с одного пульта.
[cut=Конфиги]1. Открываем файл gamedata\configs\gameplay\character_desc_general. Прописываем ГГ в стартовый набор 4 заряда - remote_explosive_charge. После начала новой игры они окажутся в рюкзаке игрока. Добавляем в секции actor (она первая в этом файле) под строкой conserva = 2 \n строку remote_explosive_charge = 4 \n. Сохраняем файл.
2. Откроем файл gamedata\configs\misc\devices. Добавляем в него в конец файла следующие секции.
Пульт является квестовым предметом. Его нельзя продать или выкинуть из инвентаря.
Не буду останавливаться на прописывании зарядов в торговлю или настройке интерфейсных иконок для заряда и пульта. На эту тему есть другие уроки. В приложенной к уроку геймдате иконки настроены.
Сохраняем файл.
4. Откроем файл gamedata\configs\text\rus\st_items_equipment. Добавим в него текстовые описания бустеров.
<string id="st_remote_explosive_charge"> <text>Заряд РС-15 с блок-детонатором</text> </string> <string id="st_remote_explosive_charge_descr"> <text>Подрывной заряд. Идет в комплекте с пультом управления. Для установки заряда его необходимо использовать. После установки заряда необходимо установить таймер взрывателя. Задержку взрыва можно выставить в диапазоне от одной до десяти секунд.</text> </string> <string id="st_remote_explosive_pda"> <text>Пульт управления зарядом РС-15</text> </string> <string id="st_remote_explosive_pda_descr"> <text>Для детонации установленных зарядов РС-15 пульт необходимо использовать. Для надёжного срабатывания установленных зарядов они должны нахлдиться не далее 150 метров от игрока.</text> </string>
Сохраняем файл.
5. Откроем файлы gamedata\configs\ui\map_spots и map_spots_16. Находим в них секцию
Мы зарегистрировали текстуру метки заряда на карте. И задали её вид. Метка заряда аналогична метке тайника. Если вы умеете править dds файлы, то можете открыть файл gamedata\textures\ui\ui_actor_hint_wnd и выбрать другой значок для метки тайника. Или добавить свой собственнцй значок. В приложенной к уроку геймдате это сделано. На редактировании dds файлов я останавливаться не буду.
Сохраняем файл.
7. Откроем файл gamedata\configs\text\rus\ui_st_pda. Добавим в него метку заряда
и так далее мы выбираем время задержки подрыва заряда. При попытке юзания заряда появится кодовое окно, в котором мы выберем число от 1 до 10. Время задержки взрыва в секундах. Если таймер не установлен, то взрыв произойдёт немедленно после команды на подрыв.
и так далее отсчитывают время до подрыва заряда. И производят сам подрыв.
В файлах remote_explosive_box_2.ltx и remote_explosive_box_3.ltx прописываем однотипную логику. Меняем функцию подрыва remote_explosive_box_1_activate на remote_explosive_box_2_activate и remote_explosive_box_3_activate соответственно.
Инфопорции таймеров remote_1_1_explosive, remote_1_2_explosive, remote_1_3_explosive и т.д. меняем на remote_2_1_explosive, remote_2_2_explosive, remote_2_3_explosive и т.д. для файла remote_explosive_box_2.ltx и remote_3_1_explosive, remote_3_2_explosive, remote_3_3_explosive и т.д. для файла remote_explosive_box_3.ltx.
Сохраняем файлы.[/cut]
[cut=Скрипты]1. Открываем файл gamedata\scrips\_g.script. Добавим в него следующие функции.
-- 'Удаление нескольких инфопортаций по возрастающим номерам. function disable_several_info(l_part,r_part,count_a,count_b) if count_b==nil then count_b=count_a count_a=1 end for i=count_a,count_b do disable_info(l_part..i..r_part) end end
-- 'Удалить все обьекты с указанной секцией. function release_objects_by_section(find_string) for a=1,65534 do local obj = alife():object(a) if obj then if obj:section_name()~=nil and string.find(obj:section_name(),find_string) then local sect=obj:section_name() remove_object_by_id(obj.id) end end end end
-- 'Удалить все обьекты с указанным именем. function release_objects_by_name(find_string) for a=1,65534 do local obj = alife():object(a) if obj then if obj:name()~=nil and string.find(obj:name(),find_string) then local sect=obj:name() remove_object_by_id(obj.id) end end end end
-- 'Удаление обьекта по его ID. function remove_object_by_id(item_id) if item_id~=nil and alife():object(item_id) then alife():release(alife():object(item_id),true) end end
-- 'Закрыть инвентарь/КПК. function game_hide_menu(type) if type==1 then get_hud():HideActorMenu() elseif type==2 then get_hud():HidePdaMenu() else get_hud():HideActorMenu() get_hud():HidePdaMenu() end end
-- 'Создание предмета в рюкзаке ГГ. function give_object_to_actor(section,count) if count==nil then count=1 end for i=1, count do alife():create(section,db.actor:position(),db.actor:level_vertex_id(),db.actor:game_vertex_id(),db.actor:id()) end end
function actor_binder:use_inventory_item(obj) if(obj) then local s_obj = alife():object(obj:id()) if(s_obj) and (s_obj:section_name()=="drug_anabiotic") then xr_effects.disable_ui_only(db.actor, nil) level.add_cam_effector("camera_effects\\surge_02.anm", 10, false, "bind_stalker.anabiotic_callback") level.add_pp_effector("surge_fade.ppe", 11, false) give_info("anabiotic_in_process") _G.mus_vol = get_console():get_float("snd_volume_music") _G.amb_vol = get_console():get_float("snd_volume_eff") get_console():execute("snd_volume_music 0") get_console():execute("snd_volume_eff 0") end end end
Дописываем в неё колбэки на использование наших бустеров. Так
function actor_binder:use_inventory_item(obj) if(obj) then local s_obj = alife():object(obj:id()) if(s_obj) and (s_obj:section_name()=="drug_anabiotic") then xr_effects.disable_ui_only(db.actor, nil) level.add_cam_effector("camera_effects\\surge_02.anm", 10, false, "bind_stalker.anabiotic_callback") level.add_pp_effector("surge_fade.ppe", 11, false) give_info("anabiotic_in_process") _G.mus_vol = get_console():get_float("snd_volume_music") _G.amb_vol = get_console():get_float("snd_volume_eff") get_console():execute("snd_volume_music 0") get_console():execute("snd_volume_eff 0") end if(s_obj) and s_obj:section_name()=="remote_explosive_charge" then addon_callbacks.remote_explosive_charge_init() end if(s_obj) and s_obj:section_name()=="remote_explosive_pda" then addon_callbacks.remote_explosive_pda_init() end end end
Сохраняем файл.
3. Создаем в папке gamedata\scrips пустой файл addon_callbacks.script. Добавим в него наши функции.
function remote_explosive_charge_init() local actor = db.actor if has_alife_info("remote_explosive_pda_init") then release_objects_by_section("remote_explosive_box_") release_objects_by_section("remote_explosive_bomb_") if has_alife_info("remote_explosive_1_install") then disable_info("remote_explosive_1_install") end if has_alife_info("remote_explosive_2_install") then disable_info("remote_explosive_2_install") end if has_alife_info("remote_explosive_3_install") then disable_info("remote_explosive_3_install") end disable_info("remote_explosive_pda_init") end if db.actor:object("remote_explosive_pda") == nil then give_object_to_actor("remote_explosive_pda") end if not has_alife_info("remote_explosive_1_install") then disable_several_info("remote_1_","_explosive",1,10) local sobj=alife():create("remote_explosive_box_1",actor:position(),actor:level_vertex_id(),actor:game_vertex_id()) give_info("remote_explosive_1_install") level.map_add_object_spot_ser(sobj.id,"explosive","") xr_effects.send_tip(db.actor,nil,{"st_remote_explosive_install","got_ammo"}) elseif not has_alife_info("remote_explosive_2_install") and has_alife_info("remote_explosive_1_install") then disable_several_info("remote_2_","_explosive",1,10) local sobj=alife():create("remote_explosive_box_2",actor:position(),actor:level_vertex_id(),actor:game_vertex_id()) give_info("remote_explosive_2_install") level.map_add_object_spot_ser(sobj.id,"explosive","") xr_effects.send_tip(db.actor,nil,{"st_remote_explosive_install","got_ammo"}) elseif not has_alife_info("remote_explosive_3_install") and has_alife_info("remote_explosive_2_install") then disable_several_info("remote_3_","_explosive",1,10) local sobj=alife():create("remote_explosive_box_3",actor:position(),actor:level_vertex_id(),actor:game_vertex_id()) give_info("remote_explosive_3_install") level.map_add_object_spot_ser(sobj.id,"explosive","") xr_effects.send_tip(db.actor,nil,{"st_remote_explosive_install","got_ammo"}) else game.start_tutorial("remote_explosive_no_use") give_object_to_actor("remote_explosive_charge") end game_hide_menu() end
--Данная функция выполняет следующие задачи:
--Проверка наличия инфопорции подрыва - remote_explosive_pda_init. Если инфопорция имеется - (после предыдущего подрыва новых зарядов не ставили) - то локации проверяются на наличии установленных и не сработавших зарядов и бомб. Все несработавшие объекты удаляются. Инфопорции сбрасываются.
--Проверка наличия у игрока пульта для подрыва. Выдача пульта, если его нет.
--Проверка на кол-во установленных зарядов. Установка зарядов в определенном порядке. 1, 2 и 3. Установка меток на карте. Выдача служебных сообщений. Сброс инфопорций таймеров от предыдущих зарядов. Отказ в установке 4 заряда.
function remote_explosive_pda_init() give_info("remote_explosive_pda_init") xr_effects.send_tip(db.actor,nil,{"st_remote_explosive_pda_init","got_ammo"}) game_hide_menu() end
--Эта функция посылает служебное сообщение и выдает инфопорцию на подрыв.
Сохраняем файл.
4. Открываем файл gamedata\scrips\ph_code.
Находим функцию.
function codepad:update(delta) end
Добавляем в неё возможность перехода на другой тип логики. Это нужно для нормальной работы зарядов. Так.
function codepad:update(delta) if xr_logic.try_switch_to_another_section(self.object, self.st, db.actor) then return end end
Сохраняем файл.
5. Открываем файл gamedata\scrips\xr_effects. Добавим в неё исполняемые функции.
function remote_explosive_box_1_activate(actor,obj) alife():create("remote_explosive_bomb_1",vector():set(obj:position()),obj:level_vertex_id(),obj:game_vertex_id()) level.add_call( function() if get_story_object("remote_explosive_bomb_1") ~= nil then return true end end, function() expl_obj = get_story_object("remote_explosive_bomb_1") expl_obj:explode(0) end ) if xr_conditions.object_exist(nil,nil,{"remote_explosive_box_1"}) then xr_effects.destroy_object(actor,npc,{"story","remote_explosive_box_1"}) end if xr_conditions.object_exist(nil,nil,{"remote_explosive_bomb_1"}) then xr_effects.destroy_object(actor,npc,{"story","remote_explosive_bomb_1"}) end end
function remote_explosive_box_2_activate(actor,obj) alife():create("remote_explosive_bomb_2",vector():set(obj:position()),obj:level_vertex_id(),obj:game_vertex_id()) level.add_call( function() if get_story_object("remote_explosive_bomb_2") ~= nil then return true end end, function() expl_obj = get_story_object("remote_explosive_bomb_2") expl_obj:explode(0) end ) if xr_conditions.object_exist(nil,nil,{"remote_explosive_box_2"}) then xr_effects.destroy_object(actor,npc,{"story","remote_explosive_box_2"}) end if xr_conditions.object_exist(nil,nil,{"remote_explosive_bomb_2"}) then xr_effects.destroy_object(actor,npc,{"story","remote_explosive_bomb_2"}) end end
function remote_explosive_box_3_activate(actor,obj) alife():create("remote_explosive_bomb_3",vector():set(obj:position()),obj:level_vertex_id(),obj:game_vertex_id()) level.add_call( function() if get_story_object("remote_explosive_bomb_3") ~= nil then return true end end, function() expl_obj = get_story_object("remote_explosive_bomb_3") expl_obj:explode(0) end ) if xr_conditions.object_exist(nil,nil,{"remote_explosive_box_3"}) then xr_effects.destroy_object(actor,npc,{"story","remote_explosive_box_3"}) end if xr_conditions.object_exist(nil,nil,{"remote_explosive_bomb_3"}) then xr_effects.destroy_object(actor,npc,{"story","remote_explosive_bomb_3"}) end end
Эти функции выполняют основную задачу - производят подрыв зарядов.
Хочу выразить огромную благодарность автору SGM Gelorge за идею зарядов и скриптовые функции. Так же хочу поблагодарить denisa2000 и makdm за помощь в составлении скриптов.
Сообщение отредактировал sergej5500 - Вт, 14.06.2016, 22:43
Эти 0 пользователя(ей) поблагодарили sergej5500 за это полезное сообщение: